mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-14 00:21:18 +00:00
2862 lines
67 KiB
C
2862 lines
67 KiB
C
/*
|
|
===========================================================================
|
|
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 <ctype.h>
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#endif
|
|
#include "../client/client.h"
|
|
#include "ui_local.h"
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
/*
|
|
=======================================================================
|
|
|
|
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<num;i++)
|
|
if (!Q_strcasecmp(check, list[i]))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
InsertInList
|
|
=================
|
|
*/
|
|
void InsertInList (char **list, char *insert, int len, int start)
|
|
{
|
|
int i;
|
|
if (!list) return;
|
|
|
|
for (i=start; i<len; i++)
|
|
{
|
|
if (!list[i])
|
|
{
|
|
list[i] = strdup(insert);
|
|
return;
|
|
}
|
|
}
|
|
list[len] = strdup(insert);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_IsValidImageFilename
|
|
==========================
|
|
*/
|
|
qboolean UI_IsValidImageFilename (char *name)
|
|
{
|
|
int len = (int)strlen(name);
|
|
|
|
if ( !strcmp(name+max(len-4,0), ".pcx")
|
|
|| !strcmp(name+max(len-4,0), ".tga")
|
|
#ifdef PNG_SUPPORT
|
|
|| !strcmp(name+max(len-4,0), ".png")
|
|
#endif // PNG_SUPPORT
|
|
|| !strcmp(name+max(len-4,0), ".jpg")
|
|
)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_ClampCvar
|
|
==========================
|
|
*/
|
|
void UI_ClampCvar (const char *varName, float cvarMin, float cvarMax)
|
|
{
|
|
Cvar_SetValue ((char *)varName, ClampCvar( cvarMin, cvarMax, Cvar_VariableValue((char *)varName) ));
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_GetIndexForStringValue
|
|
==========================
|
|
*/
|
|
int UI_GetIndexForStringValue (const char **item_values, char *value)
|
|
{
|
|
int i, index = 0, widlcardIndex = -1;
|
|
qboolean found = false;
|
|
|
|
// catch null array
|
|
if (!item_values) {
|
|
Com_Printf ("UI_GetIndexForStringValue: null itemValues!\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i=0; item_values[i]; i++)
|
|
{ // Store index of wildcard entry
|
|
if ( !Q_stricmp(va("%s", item_values[i]), UI_ITEMVALUE_WILDCARD) )
|
|
{ widlcardIndex = i; }
|
|
if ( !Q_strcasecmp(va("%s", item_values[i]), value) ) {
|
|
index = i;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
// Assign index of wildcard entry if not found
|
|
if ( !found && (widlcardIndex >= 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<UI_MAX_VIDMODES; i++)
|
|
{
|
|
if ( !VID_GetModeInfo(&w, &h, i) )
|
|
break;
|
|
|
|
if (w >= 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<numValues; i++)
|
|
{
|
|
if (i == 0)
|
|
ui_aniso_names[i] = (numValues == 1) ? strdup("not supported") : strdup("off");
|
|
else
|
|
ui_aniso_names[i] = strdup(va("%ix", 1<<i));
|
|
ui_aniso_values[i] = strdup(va("%i", 1<<i));
|
|
}
|
|
|
|
ui_num_aniso_values = numValues;
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_FreeAnisoValues
|
|
==========================
|
|
*/
|
|
void UI_FreeAnisoValues (void)
|
|
{
|
|
if (ui_num_aniso_values > 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; i<len; i++)
|
|
{
|
|
if (!list[i])
|
|
break;
|
|
|
|
if (strcmp( list[i], insert ))
|
|
{
|
|
for (j=len; j>i; 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<nItems && nItemNames < maxItems; i++)
|
|
{
|
|
if (!itemFiles || !itemFiles[i])
|
|
continue;
|
|
|
|
p = strrchr(itemFiles[i], '/'); p++;
|
|
|
|
if ( !checkName(p) )
|
|
continue;
|
|
|
|
if (stripExtension && (ext = strrchr(p, '.')) ) {
|
|
extLen = (int)strlen(ext);
|
|
baseLen = (int)strlen(p) - extLen;
|
|
p[baseLen] = 0; // NULL
|
|
}
|
|
curItem = p;
|
|
|
|
if (!FS_ItemInList(curItem, nItemNames, list))
|
|
{
|
|
// UI_InsertInAssetList (frontInsert) not needed due to sorting in FS_GetFileList()
|
|
FS_InsertInList (list, curItem, nItemNames, 1); // start=1 so first item stays first!
|
|
nItemNames++;
|
|
}
|
|
|
|
// restore extension so whole string gets deleted
|
|
if (stripExtension && ext)
|
|
p[baseLen] = '.';
|
|
}
|
|
#else
|
|
path = FS_NextPath (path);
|
|
while (path)
|
|
{
|
|
Com_sprintf (findName, sizeof(findName), "%s/%s/%s", path, dir, nameMask);
|
|
itemFiles = FS_ListFiles(findName, &nItems, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
|
|
|
|
for (i=0; i < nItems && nItemNames < maxItems; i++)
|
|
{
|
|
if (!itemFiles || !itemFiles[i])
|
|
continue;
|
|
|
|
p = strrchr(itemFiles[i], '/'); p++;
|
|
|
|
if ( !checkName(p) )
|
|
continue;
|
|
|
|
if (stripExtension && (ext = strrchr(p, '.')) ) {
|
|
extLen = (int)strlen(ext);
|
|
baseLen = (int)strlen(p) - extLen;
|
|
p[baseLen] = 0; // NULL
|
|
}
|
|
curItem = p;
|
|
|
|
if (!FS_ItemInList(curItem, nItemNames, list))
|
|
{
|
|
if (frontInsert)
|
|
UI_InsertInAssetList (list, curItem, nItemNames, 1); // start=1 so first item stays first!
|
|
else
|
|
FS_InsertInList (list, curItem, nItemNames, 1); // start=1 so first item stays first!
|
|
nItemNames++;
|
|
}
|
|
|
|
// restore extension so whole string gets deleted
|
|
if (stripExtension && ext)
|
|
p[baseLen] = '.';
|
|
}
|
|
if (nItems)
|
|
FS_FreeFileList (itemFiles, nItems);
|
|
|
|
path = FS_NextPath (path);
|
|
}
|
|
|
|
// check pak after
|
|
if (itemFiles = FS_ListPak(va("%s/", dir), &nItems))
|
|
{
|
|
for (i=0; i<nItems && nItemNames < maxItems; i++)
|
|
{
|
|
if (!itemFiles || !itemFiles[i])
|
|
continue;
|
|
|
|
p = strrchr(itemFiles[i], '/'); p++;
|
|
|
|
if ( !checkName(p) )
|
|
continue;
|
|
|
|
if (stripExtension && (ext = strrchr(p, '.')) ) {
|
|
extLen = (int)strlen(ext);
|
|
baseLen = (int)strlen(p) - extLen;
|
|
p[baseLen] = 0; // NULL
|
|
}
|
|
curItem = p;
|
|
|
|
if (!FS_ItemInList(curItem, nItemNames, list))
|
|
{
|
|
if (frontInsert)
|
|
UI_InsertInAssetList (list, curItem, nItemNames, 1); // start=1 so first item stays first!
|
|
else
|
|
FS_InsertInList (list, curItem, nItemNames, 1); // start=1 so first item stays first!
|
|
nItemNames++;
|
|
}
|
|
|
|
// restore extension so whole string gets deleted
|
|
if (stripExtension && ext)
|
|
p[baseLen] = '.';
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (nItems)
|
|
FS_FreeFileList (itemFiles, nItems);
|
|
|
|
// re-count list, nItemNames is somehow counted with 1 extra
|
|
for (i=0; list[i]; i++);
|
|
nItemNames = i;
|
|
|
|
if ( returnCount )
|
|
*returnCount = nItemNames;
|
|
|
|
return list;
|
|
}
|
|
|
|
/*
|
|
=======================================================================
|
|
|
|
FONT LOADING
|
|
|
|
=======================================================================
|
|
*/
|
|
|
|
#define UI_MAX_FONTS 32
|
|
char **ui_font_names = NULL;
|
|
int ui_numfonts = 0;
|
|
|
|
/*
|
|
==========================
|
|
UI_IsValidFontName
|
|
==========================
|
|
*/
|
|
qboolean UI_IsValidFontName (char *name)
|
|
{
|
|
return UI_IsValidImageFilename (name);
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_LoadFontNames
|
|
==========================
|
|
*/
|
|
void UI_LoadFontNames (void)
|
|
{
|
|
ui_font_names = UI_LoadAssetList ("fonts", "*.*", "default", &ui_numfonts, UI_MAX_FONTS, true, true, UI_IsValidFontName);
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_FreeFontNames
|
|
==========================
|
|
*/
|
|
void UI_FreeFontNames (void)
|
|
{
|
|
if (ui_numfonts > 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<i; j++)
|
|
{
|
|
// if (!list[j]) break;
|
|
if (!list[j] || !list[j+1]) continue;
|
|
if ( atoi(strdup(list[j]+2)) > 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<ui_numcrosshairs; i++)
|
|
ui_crosshair_values[i] = (i == 0) ? strdup("0") : strdup(strtok(ui_crosshair_names[i], "ch"));
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_FreeCrosshairs
|
|
==========================
|
|
*/
|
|
void UI_FreeCrosshairs (void)
|
|
{
|
|
if (ui_numcrosshairs > 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; i++)
|
|
{
|
|
// Com_sprintf (name, sizeof(name), "%s/save/kmq2save%03i/server.ssv", FS_Savegamedir(), i); // was FS_Gamedir()
|
|
#ifdef NOTTHIRTYFLIGHTS
|
|
Com_sprintf (name, sizeof(name), "%s/"SAVEDIRNAME"/kmq2save%03i/server.ssv", FS_Savegamedir(), i); // was FS_Gamedir()
|
|
#else
|
|
Com_sprintf (name, sizeof(name), "%s/"SAVEDIRNAME"/save%i/server.ssv", FS_Savegamedir(), i); // was FS_Gamedir()
|
|
#endif
|
|
|
|
old_timestamp = ui_savetimestamps[i];
|
|
stat(name, &st);
|
|
ui_savetimestamps[i] = st.st_mtime;
|
|
|
|
// doesn't need to be refreshed
|
|
if ( update && ui_savetimestamps[i] == old_timestamp ) {
|
|
ui_savechanged[i] = false;
|
|
continue;
|
|
}
|
|
|
|
fp = fopen (name, "rb");
|
|
if (!fp) {
|
|
// Com_Printf("Save file %s not found.\n", name);
|
|
Q_strncpyz (ui_savestrings[i], sizeof(ui_savestrings[i]), EMPTY_GAME_STRING);
|
|
ui_savevalid[i] = false;
|
|
ui_savetimestamps[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
fclose (fp);
|
|
// Com_sprintf (name, sizeof(name), "save/kmq2save%03i/server.ssv", i);
|
|
#ifdef NOTTHIRTYFLIGHTS
|
|
Com_sprintf (name, sizeof(name), SAVEDIRNAME"/kmq2save%03i/server.ssv", i);
|
|
#else
|
|
Com_sprintf (name, sizeof(name), SAVEDIRNAME"/save%i/server.ssv", i);
|
|
#endif
|
|
FS_FOpenFile (name, &f, FS_READ);
|
|
if (!f)
|
|
{
|
|
//Com_Printf("Save file %s not found.\n", name);
|
|
Q_strncpyz (ui_savestrings[i], sizeof(ui_savestrings[i]), EMPTY_GAME_STRING);
|
|
ui_savevalid[i] = false;
|
|
ui_savetimestamps[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
FS_Read (ui_savestrings[i], sizeof(ui_savestrings[i]), f);
|
|
|
|
if (i==0) { // grab mapname
|
|
FS_Read (mapname, sizeof(mapname), f);
|
|
if (mapname[0] == '*') // skip * marker
|
|
Com_sprintf (ui_mapname, sizeof(ui_mapname), mapname+1);
|
|
else
|
|
Com_sprintf (ui_mapname, sizeof(ui_mapname), mapname);
|
|
if (ch = strchr (ui_mapname, '$'))
|
|
*ch = 0; // terminate string at $ marker
|
|
}
|
|
FS_FCloseFile(f);
|
|
ui_savevalid[i] = true;
|
|
}
|
|
}
|
|
ui_savechanged[i] = (ui_savetimestamps[i] != old_timestamp);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_ValidateSaveshots
|
|
==========================
|
|
*/
|
|
void UI_ValidateSaveshots (void)
|
|
{
|
|
int i;
|
|
char shotname[MAX_QPATH];
|
|
|
|
for ( i = 0; i < UI_MAX_SAVEGAMES; i++ )
|
|
{
|
|
if ( !ui_savechanged[i] ) // doeesn't need to be reloaded
|
|
continue;
|
|
if ( ui_savevalid[i] )
|
|
{
|
|
if (i == 0)
|
|
Com_sprintf(shotname, sizeof(shotname), "/levelshots/%s.pcx", ui_mapname);
|
|
else
|
|
{ // free previously loaded shots
|
|
// Com_sprintf(shotname, sizeof(shotname), "save/kmq2save%03i/shot.jpg", i);
|
|
#ifdef NOTTHIRTYFLIGHTS
|
|
Com_sprintf(shotname, sizeof(shotname), SAVEDIRNAME"/kmq2save%03i/shot.jpg", i);
|
|
#else
|
|
Com_sprintf(shotname, sizeof(shotname), SAVEDIRNAME"/save%i/shot.jpg", i);
|
|
#endif
|
|
R_FreePic (shotname);
|
|
// Com_sprintf(shotname, sizeof(shotname), "/save/kmq2save%03i/shot.jpg", i);
|
|
#ifdef NOTTHIRTYFLIGHTS
|
|
Com_sprintf(shotname, sizeof(shotname), "/"SAVEDIRNAME"/kmq2save%03i/shot.jpg", i);
|
|
#else
|
|
Com_sprintf(shotname, sizeof(shotname), "/"SAVEDIRNAME"/save%i/shot.jpg", i);
|
|
#endif
|
|
}
|
|
if (R_DrawFindPic(shotname))
|
|
ui_saveshotvalid[i] = true;
|
|
else
|
|
ui_saveshotvalid[i] = false;
|
|
}
|
|
else
|
|
ui_saveshotvalid[i] = false;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_UpdateSaveshot
|
|
==========================
|
|
*/
|
|
char *UI_UpdateSaveshot (int index)
|
|
{
|
|
// check index
|
|
if (index < 0 || index >= 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; i++) {
|
|
ui_savetimestamps[i] = 0;
|
|
ui_savechanged[i] = true;
|
|
}
|
|
|
|
UI_Load_Savestrings (false);
|
|
UI_ValidateSaveshots (); // register saveshots
|
|
|
|
// register null saveshot, this is only done once
|
|
if (R_DrawFindPic("/gfx/ui/noscreen.pcx"))
|
|
ui_saveshotvalid[UI_MAX_SAVEGAMES] = true;
|
|
else
|
|
ui_saveshotvalid[UI_MAX_SAVEGAMES] = false;
|
|
|
|
ui_savevalid[UI_MAX_SAVEGAMES] = false; // this element is always false to handle the back action
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_SaveIsValid
|
|
==========================
|
|
*/
|
|
qboolean UI_SaveIsValid (int index)
|
|
{
|
|
// check index
|
|
if (index < 0 || index >= 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 <serverping>
|
|
|
|
=======================================================================
|
|
*/
|
|
|
|
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
|
|
//<serverping> 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<ui_num_servers; i++)
|
|
if ( strncmp(info, &ui_local_server_names[i][11], sizeof(ui_local_server_names[0])-10)==0 ) // crashes here
|
|
return;
|
|
|
|
iPing = 0 ;
|
|
for (i=0 ; i<UI_MAX_LOCAL_SERVERS ; i++)
|
|
{
|
|
if ( memcmp(&adr.ip, &global_adr_server_netadr[i].ip, sizeof(adr.ip))==0
|
|
&& adr.port == global_adr_server_netadr[i].port )
|
|
{
|
|
// bookmark server
|
|
iPing = Sys_Milliseconds() - global_adr_server_time[i] ;
|
|
pszProtocol = "bkm" ;
|
|
break;
|
|
}
|
|
}
|
|
if ( i == UI_MAX_LOCAL_SERVERS )
|
|
{
|
|
if ( adr.ip[0] > 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++;
|
|
}
|
|
// </serverping>
|
|
#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_num_servers ; i++)
|
|
if (!strcmp(info, ui_local_server_names[i]))
|
|
return;
|
|
|
|
ui_local_server_netadr[ui_num_servers] = adr;
|
|
strncpy (ui_local_server_names[ui_num_servers], info, sizeof(ui_local_server_names[0])-1);
|
|
ui_num_servers++;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_SearchLocalGames
|
|
==========================
|
|
*/
|
|
void UI_SearchLocalGames (void)
|
|
{
|
|
int i;
|
|
|
|
ui_num_servers = 0;
|
|
for (i=0 ; i<UI_MAX_LOCAL_SERVERS ; i++)
|
|
// strncpy (ui_local_server_names[i], NO_SERVER_STRING);
|
|
Q_strncpyz (ui_local_server_names[i], sizeof(ui_local_server_names[i]), NO_SERVER_STRING);
|
|
|
|
UI_DrawPopupMessage ("Searching for local servers.\nThis could take up to a minute,\nso please be patient.");
|
|
|
|
// send out info packets
|
|
CL_PingServers_f ();
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_JoinServer
|
|
==========================
|
|
*/
|
|
void UI_JoinServer (int index)
|
|
{
|
|
char buffer[128];
|
|
|
|
// check bounds
|
|
if (index < 0 || index >= 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<i; j++)
|
|
{
|
|
if (!list[j]) break;
|
|
s1 = strchr(list[j], '\n')+1;
|
|
s2 = strchr(list[j+1], '\n')+1;
|
|
if (Q_stricmp(s1, s2) > 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<NUM_MAPTYPES; i++)
|
|
{
|
|
if (ui_svr_arena_mapnames[i])
|
|
FS_FreeFileList (ui_svr_arena_mapnames[i], ui_svr_arena_nummaps[i]);
|
|
ui_svr_arena_nummaps[i] = 0;
|
|
ui_svr_arena_mapnames[i] = malloc( sizeof( char * ) * MAX_ARENAS );
|
|
memset( ui_svr_arena_mapnames[i], 0, sizeof( char * ) * MAX_ARENAS );
|
|
}
|
|
|
|
tmplist = malloc( sizeof( char * ) * MAX_ARENAS );
|
|
memset( tmplist, 0, sizeof( char * ) * MAX_ARENAS );
|
|
|
|
#if 1
|
|
arenafiles = FS_GetFileList ("scripts", "arena", &narenas);
|
|
for (i = 0; i < narenas && narenanames < MAX_ARENAS; i++)
|
|
{
|
|
if (!arenafiles || !arenafiles[i])
|
|
continue;
|
|
|
|
len = (int)strlen(arenafiles[i]);
|
|
if ( strcmp(arenafiles[i]+max(len-6,0), ".arena") )
|
|
continue;
|
|
|
|
p = arenafiles[i];
|
|
|
|
if (!FS_ItemInList(p, narenanames, tmplist)) // check if already in list
|
|
{
|
|
if (UI_ParseArenaFromFile (p, shortname, longname, gametypes, MAX_TOKEN_CHARS))
|
|
{
|
|
// Com_sprintf(scratch, sizeof(scratch), MAPLIST_FORMAT, longname, shortname);
|
|
Com_sprintf(scratch, sizeof(scratch), "%s\n%s", longname, shortname);
|
|
|
|
for (j=0; j<NUM_MAPTYPES; j++)
|
|
type_supported[j] = false;
|
|
s = gametypes;
|
|
tok = strdup(COM_Parse (&s));
|
|
while (s != NULL)
|
|
{
|
|
for (j=0; j<NUM_MAPTYPES; j++)
|
|
{
|
|
s2 = gametype_names[j].tokens;
|
|
tok2 = COM_Parse (&s2);
|
|
while (s2 != NULL) {
|
|
if ( !Q_strcasecmp(tok, tok2) )
|
|
type_supported[j] = true;
|
|
tok2 = COM_Parse (&s2);
|
|
}
|
|
}
|
|
if (tok) free (tok);
|
|
tok = strdup(COM_Parse(&s));
|
|
}
|
|
if (tok) free (tok);
|
|
|
|
for (j=0; j<NUM_MAPTYPES; j++)
|
|
if (type_supported[j]) {
|
|
nameSize = strlen(scratch) + 1;
|
|
ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]] = malloc(nameSize);
|
|
// strncpy(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], scratch);
|
|
Q_strncpyz (ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], nameSize, scratch);
|
|
ui_svr_arena_nummaps[j]++;
|
|
}
|
|
|
|
// Com_Printf ("UI_LoadArenas: successfully loaded arena file %s: mapname: %s levelname: %s gametypes: %s\n", p, shortname, longname, gametypes);
|
|
narenanames++;
|
|
FS_InsertInList(tmplist, p, narenanames, 0); // add to list
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
//
|
|
// search in searchpaths for .arena files
|
|
//
|
|
path = FS_NextPath (path);
|
|
while (path)
|
|
{
|
|
Com_sprintf (findName, sizeof(findName), "%s/scripts/*.arena", path);
|
|
arenafiles = FS_ListFiles(findName, &narenas, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
|
|
|
|
for (i=0; i < narenas && narenanames < MAX_ARENAS; i++)
|
|
{
|
|
if (!arenafiles || !arenafiles[i])
|
|
continue;
|
|
|
|
len = (int)strlen(arenafiles[i]);
|
|
if ( strcmp(arenafiles[i]+max(len-6,0), ".arena") )
|
|
continue;
|
|
|
|
p = arenafiles[i] + strlen(path) + 1; // skip over path and next slash
|
|
|
|
if (!FS_ItemInList(p, narenanames, tmplist)) // check if already in list
|
|
{
|
|
if (UI_ParseArenaFromFile (p, shortname, longname, gametypes, MAX_TOKEN_CHARS))
|
|
{
|
|
Com_sprintf(scratch, sizeof(scratch), "%s\n%s", longname, shortname);
|
|
|
|
for (j=0; j<NUM_MAPTYPES; j++)
|
|
type_supported[j] = false;
|
|
s = gametypes;
|
|
tok = strdup(COM_Parse (&s));
|
|
while (s != NULL)
|
|
{
|
|
for (j=0; j<NUM_MAPTYPES; j++)
|
|
{
|
|
s2 = gametype_names[j].tokens;
|
|
tok2 = COM_Parse (&s2);
|
|
while (s2 != NULL) {
|
|
if ( !Q_strcasecmp(tok, tok2) )
|
|
type_supported[j] = true;
|
|
tok2 = COM_Parse (&s2);
|
|
}
|
|
}
|
|
if (tok) free (tok);
|
|
tok = strdup(COM_Parse(&s));
|
|
}
|
|
if (tok) free (tok);
|
|
|
|
for (j=0; j<NUM_MAPTYPES; j++)
|
|
if (type_supported[j]) {
|
|
nameSize = strlen(scratch) + 1;
|
|
ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]] = malloc(nameSize);
|
|
// strncpy(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], scratch);
|
|
Q_strncpyz(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], nameSize, scratch);
|
|
ui_svr_arena_nummaps[j]++;
|
|
}
|
|
|
|
// Com_Printf ("UI_LoadArenas: successfully loaded arena file %s: mapname: %s levelname: %s gametypes: %s\n", p, shortname, longname, gametypes);
|
|
narenanames++;
|
|
FS_InsertInList(tmplist, p, narenanames, 0); // add to list
|
|
}
|
|
}
|
|
}
|
|
if (narenas)
|
|
FS_FreeFileList (arenafiles, narenas);
|
|
|
|
path = FS_NextPath (path);
|
|
}
|
|
|
|
//
|
|
// check in paks for .arena files
|
|
//
|
|
if (arenafiles = FS_ListPak("scripts/", &narenas))
|
|
{
|
|
for (i=0; i<narenas && narenanames<MAX_ARENAS; i++)
|
|
{
|
|
if (!arenafiles || !arenafiles[i])
|
|
continue;
|
|
|
|
len = (int)strlen(arenafiles[i]);
|
|
if ( strcmp(arenafiles[i]+max(len-6,0), ".arena") )
|
|
continue;
|
|
|
|
p = arenafiles[i];
|
|
|
|
if (!FS_ItemInList(p, narenanames, tmplist)) // check if already in list
|
|
{
|
|
if (UI_ParseArenaFromFile (p, shortname, longname, gametypes, MAX_TOKEN_CHARS))
|
|
{
|
|
Com_sprintf(scratch, sizeof(scratch), "%s\n%s", longname, shortname);
|
|
|
|
for (j=0; j<NUM_MAPTYPES; j++)
|
|
type_supported[j] = false;
|
|
s = gametypes;
|
|
tok = strdup(COM_Parse (&s));
|
|
while (s != NULL)
|
|
{
|
|
for (j=0; j<NUM_MAPTYPES; j++)
|
|
{
|
|
s2 = gametype_names[j].tokens;
|
|
tok2 = COM_Parse (&s2);
|
|
while (s2 != NULL) {
|
|
if ( !Q_strcasecmp(tok, tok2) )
|
|
type_supported[j] = true;
|
|
tok2 = COM_Parse (&s2);
|
|
}
|
|
}
|
|
if (tok) free (tok);
|
|
tok = strdup(COM_Parse(&s));
|
|
}
|
|
if (tok) free (tok);
|
|
|
|
for (j=0; j<NUM_MAPTYPES; j++)
|
|
if (type_supported[j]) {
|
|
nameSize = strlen(scratch) + 1;
|
|
ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]] = malloc(nameSize);
|
|
// strncpy(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], scratch);
|
|
Q_strncpyz(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], nameSize, scratch);
|
|
ui_svr_arena_nummaps[j]++;
|
|
}
|
|
|
|
//Com_Printf ("UI_LoadArenas: successfully loaded arena file %s: mapname: %s levelname: %s gametypes: %s\n", p, shortname, longname, gametypes);
|
|
narenanames++;
|
|
FS_InsertInList(tmplist, strdup(p), narenanames, 0); // add to list
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (narenas)
|
|
FS_FreeFileList (arenafiles, narenas);
|
|
|
|
if (narenanames)
|
|
FS_FreeFileList (tmplist, narenanames);
|
|
|
|
for (i=0; i<NUM_MAPTYPES; i++)
|
|
UI_SortArenas (ui_svr_arena_mapnames[i], ui_svr_arena_nummaps[i]);
|
|
|
|
// Com_Printf ("UI_LoadArenas: loaded %i arena file(s)\n", narenanames);
|
|
// for (i=0; i<NUM_MAPTYPES; i++)
|
|
// Com_Printf ("%s: %i arena file(s)\n", gametype_names[i].tokens, ui_svr_arena_nummaps[i]);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
UI_LoadMapList
|
|
===============
|
|
*/
|
|
void UI_LoadMapList (void)
|
|
{
|
|
char *buffer, *s;
|
|
char mapsname[1024];
|
|
int i, j, length;
|
|
size_t nameSize;
|
|
FILE *fp;
|
|
|
|
//
|
|
// free existing list
|
|
//
|
|
if (ui_svr_listfile_mapnames)
|
|
FS_FreeFileList (ui_svr_listfile_mapnames, ui_svr_listfile_nummaps);
|
|
ui_svr_listfile_nummaps = 0;
|
|
|
|
//
|
|
// load the list of map names
|
|
//
|
|
Com_sprintf( mapsname, sizeof( mapsname ), "%s/maps.lst", FS_Gamedir() ); // FIXME: should this be FS_Savegamedir()?
|
|
if ( ( fp = fopen( mapsname, "rb" ) ) == 0 )
|
|
{
|
|
if ( ( length = FS_LoadFile( "maps.lst", ( void ** ) &buffer ) ) == -1 )
|
|
Com_Error( ERR_DROP, "couldn't find maps.lst\n" );
|
|
}
|
|
else
|
|
{
|
|
#ifdef _WIN32
|
|
length = filelength( fileno( fp ) );
|
|
#else
|
|
fseek(fp, 0, SEEK_END);
|
|
length = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
#endif
|
|
buffer = malloc( length );
|
|
fread( buffer, length, 1, fp );
|
|
}
|
|
|
|
s = buffer;
|
|
|
|
i = 0;
|
|
while (i < length)
|
|
{
|
|
if (s[i] == '\r')
|
|
ui_svr_listfile_nummaps++;
|
|
i++;
|
|
}
|
|
|
|
if (ui_svr_listfile_nummaps == 0)
|
|
{ // hack in a default map list
|
|
ui_svr_listfile_nummaps = 1;
|
|
buffer = "base1 \"Outer Base\"\n";
|
|
}
|
|
|
|
ui_svr_listfile_mapnames = malloc( sizeof( char * ) * ( ui_svr_listfile_nummaps + 1 ) );
|
|
memset( ui_svr_listfile_mapnames, 0, sizeof( char * ) * ( ui_svr_listfile_nummaps + 1 ) );
|
|
|
|
s = buffer;
|
|
|
|
for (i = 0; i < ui_svr_listfile_nummaps; i++)
|
|
{
|
|
char shortname[MAX_TOKEN_CHARS];
|
|
char longname[MAX_TOKEN_CHARS];
|
|
char scratch[200];
|
|
|
|
// strncpy( shortname, COM_Parse( &s ) );
|
|
// strncpy( longname, COM_Parse( &s ) );
|
|
Q_strncpyz (shortname, sizeof(shortname), COM_Parse(&s));
|
|
Q_strncpyz (longname, sizeof(longname), COM_Parse(&s));
|
|
Com_sprintf (scratch, sizeof( scratch ), "%s\n%s", longname, shortname);
|
|
nameSize = strlen(scratch) + 1;
|
|
ui_svr_listfile_mapnames[i] = malloc( nameSize );
|
|
// strncpyz( ui_svr_listfile_mapnames[i], scratch );
|
|
Q_strncpyz (ui_svr_listfile_mapnames[i], nameSize, scratch);
|
|
}
|
|
ui_svr_listfile_mapnames[ui_svr_listfile_nummaps] = 0;
|
|
|
|
if ( fp != 0 )
|
|
{
|
|
fp = 0;
|
|
free( buffer );
|
|
}
|
|
else
|
|
FS_FreeFile( buffer );
|
|
|
|
UI_LoadArenas ();
|
|
|
|
// build composite map lists
|
|
for (i=0; i<NUM_MAPTYPES; i++)
|
|
{
|
|
if (ui_svr_maplists[i]) {
|
|
free (ui_svr_maplists[i]);
|
|
}
|
|
ui_svr_maplists[i] = NULL;
|
|
ui_svr_maplist_sizes[i] = ui_svr_listfile_nummaps + ui_svr_arena_nummaps[i];
|
|
ui_svr_maplists[i] = malloc( sizeof( char * ) * (ui_svr_maplist_sizes[i] + 1) );
|
|
memset( ui_svr_maplists[i], 0, sizeof( char * ) * (ui_svr_maplist_sizes[i] + 1) );
|
|
|
|
for (j = 0; j < ui_svr_maplist_sizes[i]; j++)
|
|
{
|
|
if (j < ui_svr_listfile_nummaps)
|
|
ui_svr_maplists[i][j] = ui_svr_listfile_mapnames[j];
|
|
else
|
|
ui_svr_maplists[i][j] = ui_svr_arena_mapnames[i][j-ui_svr_listfile_nummaps];
|
|
}
|
|
}
|
|
|
|
ui_svr_maptype = MAP_DM; // init maptype
|
|
ui_svr_mapnames = ui_svr_maplists[ui_svr_maptype];
|
|
|
|
UI_BuildStartSeverLevelshotTables ();
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
UI_FreeMapList
|
|
===============
|
|
*/
|
|
void UI_FreeMapList (void)
|
|
{
|
|
int i;
|
|
|
|
//
|
|
// free composite lists
|
|
//
|
|
for (i=0; i<NUM_MAPTYPES; i++)
|
|
{
|
|
if (ui_svr_maplists[i]) {
|
|
free (ui_svr_maplists[i]);
|
|
}
|
|
ui_svr_maplists[i] = NULL;
|
|
ui_svr_maplist_sizes[i] = 0;
|
|
}
|
|
ui_svr_mapnames = NULL;
|
|
|
|
//
|
|
// free list from file
|
|
//
|
|
if (ui_svr_listfile_mapnames) {
|
|
FS_FreeFileList (ui_svr_listfile_mapnames, ui_svr_listfile_nummaps);
|
|
}
|
|
ui_svr_listfile_mapnames = NULL;
|
|
ui_svr_listfile_nummaps = 0;
|
|
|
|
//
|
|
// free arena lists
|
|
//
|
|
for (i=0; i<NUM_MAPTYPES; i++)
|
|
{
|
|
if (ui_svr_arena_mapnames[i]) {
|
|
FS_FreeFileList (ui_svr_arena_mapnames[i], ui_svr_arena_nummaps[i]);
|
|
}
|
|
ui_svr_arena_mapnames[i] = NULL;
|
|
ui_svr_arena_nummaps[i] = 0;
|
|
}
|
|
|
|
UI_FreeStartSeverLevelshotTables ();
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
UI_UpdateMapList
|
|
===============
|
|
*/
|
|
void UI_UpdateMapList (maptype_t maptype)
|
|
{
|
|
ui_svr_maptype = maptype;
|
|
ui_svr_mapnames = ui_svr_maplists[ui_svr_maptype];
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
UI_BuildStartSeverLevelshotTables
|
|
===============
|
|
*/
|
|
void UI_BuildStartSeverLevelshotTables (void)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<NUM_MAPTYPES; i++)
|
|
{ // free existing list
|
|
if (ui_svr_mapshotvalid[i]) {
|
|
free(ui_svr_mapshotvalid[i]);
|
|
ui_svr_mapshotvalid[i] = NULL;
|
|
}
|
|
|
|
// alloc and zero new list
|
|
ui_svr_mapshotvalid[i] = malloc( sizeof( byte ) * ( ui_svr_maplist_sizes[i] + 1 ) );
|
|
memset( ui_svr_mapshotvalid[i], 0, sizeof( byte ) * ( ui_svr_maplist_sizes[i] + 1 ) );
|
|
|
|
// register null levelshot
|
|
if (ui_svr_mapshotvalid[i][ui_svr_maplist_sizes[i]] == M_UNSET) {
|
|
if (R_DrawFindPic(UI_NOSCREEN_NAME))
|
|
ui_svr_mapshotvalid[i][ui_svr_maplist_sizes[i]] = M_FOUND;
|
|
else
|
|
ui_svr_mapshotvalid[i][ui_svr_maplist_sizes[i]] = M_MISSING;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
UI_FreeStartSeverLevelshotTables
|
|
===============
|
|
*/
|
|
void UI_FreeStartSeverLevelshotTables (void)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<NUM_MAPTYPES; i++)
|
|
{
|
|
if (ui_svr_mapshotvalid[i]) {
|
|
free(ui_svr_mapshotvalid[i]);
|
|
ui_svr_mapshotvalid[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
UI_UpdateStartSeverLevelshot
|
|
===============
|
|
*/
|
|
char *UI_UpdateStartSeverLevelshot (int index)
|
|
{
|
|
char startmap[MAX_QPATH];
|
|
char mapshotname [MAX_QPATH];
|
|
|
|
// check index
|
|
if (index < 0 || index >= 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);
|
|
}
|