mirror of
https://github.com/DrBeef/ioq3quest.git
synced 2024-11-29 23:32:23 +00:00
3377f9981a
In September 2017 I moved loading arenas.txt/*.arena files from entering start server menu to at startup to fix running out of memory in Team Arena UI after opening the start server menu several times. However, Team Arena completely replaces the uiInfo.mapList array when switching between single player and start server menus. So after my change, entering single player and then entering start server would only display single player maps. It caused SP endofgame menu to use MP map list for replay/next map since arenas were loaded after gameinfo.txt. Continue loading arena info at start up to avoid reallocating arena info but move setting up uiInfo.mapList to when entering the start server menu.
334 lines
7.6 KiB
C
334 lines
7.6 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena 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 III Arena 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 III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
//
|
|
//
|
|
// gameinfo.c
|
|
//
|
|
|
|
#include "ui_local.h"
|
|
|
|
|
|
//
|
|
// arena and bot info
|
|
//
|
|
|
|
|
|
int ui_numBots;
|
|
static char *ui_botInfos[MAX_BOTS];
|
|
|
|
static int ui_numArenas;
|
|
static char *ui_arenaInfos[MAX_ARENAS];
|
|
|
|
#ifndef MISSIONPACK
|
|
static int ui_numSinglePlayerArenas;
|
|
static int ui_numSpecialSinglePlayerArenas;
|
|
#endif
|
|
|
|
/*
|
|
===============
|
|
UI_ParseInfos
|
|
===============
|
|
*/
|
|
int UI_ParseInfos( char *buf, int max, char *infos[] ) {
|
|
char *token;
|
|
int count;
|
|
char key[MAX_TOKEN_CHARS];
|
|
char info[MAX_INFO_STRING];
|
|
|
|
count = 0;
|
|
|
|
while ( 1 ) {
|
|
token = COM_Parse( &buf );
|
|
if ( !token[0] ) {
|
|
break;
|
|
}
|
|
if ( strcmp( token, "{" ) ) {
|
|
Com_Printf( "Missing { in info file\n" );
|
|
break;
|
|
}
|
|
|
|
if ( count == max ) {
|
|
Com_Printf( "Max infos exceeded\n" );
|
|
break;
|
|
}
|
|
|
|
info[0] = '\0';
|
|
while ( 1 ) {
|
|
token = COM_ParseExt( &buf, qtrue );
|
|
if ( !token[0] ) {
|
|
Com_Printf( "Unexpected end of info file\n" );
|
|
break;
|
|
}
|
|
if ( !strcmp( token, "}" ) ) {
|
|
break;
|
|
}
|
|
Q_strncpyz( key, token, sizeof( key ) );
|
|
|
|
token = COM_ParseExt( &buf, qfalse );
|
|
if ( !token[0] ) {
|
|
strcpy( token, "<NULL>" );
|
|
}
|
|
Info_SetValueForKey( info, key, token );
|
|
}
|
|
//NOTE: extra space for arena number
|
|
infos[count] = UI_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1);
|
|
if (infos[count]) {
|
|
strcpy(infos[count], info);
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
UI_LoadArenasFromFile
|
|
===============
|
|
*/
|
|
static void UI_LoadArenasFromFile( char *filename ) {
|
|
int len;
|
|
fileHandle_t f;
|
|
char buf[MAX_ARENAS_TEXT];
|
|
|
|
len = trap_FS_FOpenFile( filename, &f, FS_READ );
|
|
if ( !f ) {
|
|
trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) );
|
|
return;
|
|
}
|
|
if ( len >= MAX_ARENAS_TEXT ) {
|
|
trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i\n", filename, len, MAX_ARENAS_TEXT ) );
|
|
trap_FS_FCloseFile( f );
|
|
return;
|
|
}
|
|
|
|
trap_FS_Read( buf, len, f );
|
|
buf[len] = 0;
|
|
trap_FS_FCloseFile( f );
|
|
|
|
ui_numArenas += UI_ParseInfos( buf, MAX_ARENAS - ui_numArenas, &ui_arenaInfos[ui_numArenas] );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
UI_LoadArenas
|
|
===============
|
|
*/
|
|
void UI_LoadArenas( void ) {
|
|
int numdirs;
|
|
vmCvar_t arenasFile;
|
|
char filename[128];
|
|
char dirlist[1024];
|
|
char* dirptr;
|
|
int i;
|
|
int dirlen;
|
|
|
|
ui_numArenas = 0;
|
|
|
|
trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM );
|
|
if( *arenasFile.string ) {
|
|
UI_LoadArenasFromFile(arenasFile.string);
|
|
}
|
|
else {
|
|
UI_LoadArenasFromFile("scripts/arenas.txt");
|
|
}
|
|
|
|
// get all arenas from .arena files
|
|
numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 );
|
|
dirptr = dirlist;
|
|
for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
|
|
dirlen = strlen(dirptr);
|
|
strcpy(filename, "scripts/");
|
|
strcat(filename, dirptr);
|
|
UI_LoadArenasFromFile(filename);
|
|
}
|
|
trap_Print( va( "%i arenas parsed\n", ui_numArenas ) );
|
|
if (UI_OutOfMemory()) {
|
|
trap_Print(S_COLOR_YELLOW"WARNING: not enough memory in pool to load all arenas\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
UI_LoadArenasIntoMapList
|
|
===============
|
|
*/
|
|
void UI_LoadArenasIntoMapList( void ) {
|
|
int n;
|
|
char *type;
|
|
|
|
uiInfo.mapCount = 0;
|
|
|
|
for( n = 0; n < ui_numArenas; n++ ) {
|
|
// determine type
|
|
|
|
uiInfo.mapList[uiInfo.mapCount].cinematic = -1;
|
|
uiInfo.mapList[uiInfo.mapCount].mapLoadName = String_Alloc(Info_ValueForKey(ui_arenaInfos[n], "map"));
|
|
uiInfo.mapList[uiInfo.mapCount].mapName = String_Alloc(Info_ValueForKey(ui_arenaInfos[n], "longname"));
|
|
uiInfo.mapList[uiInfo.mapCount].levelShot = -1;
|
|
uiInfo.mapList[uiInfo.mapCount].imageName = String_Alloc(va("levelshots/%s", uiInfo.mapList[uiInfo.mapCount].mapLoadName));
|
|
uiInfo.mapList[uiInfo.mapCount].typeBits = 0;
|
|
|
|
type = Info_ValueForKey( ui_arenaInfos[n], "type" );
|
|
// if no type specified, it will be treated as "ffa"
|
|
if( *type ) {
|
|
if( strstr( type, "ffa" ) ) {
|
|
uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_FFA);
|
|
}
|
|
if( strstr( type, "tourney" ) ) {
|
|
uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_TOURNAMENT);
|
|
}
|
|
if( strstr( type, "ctf" ) ) {
|
|
uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_CTF);
|
|
}
|
|
if( strstr( type, "oneflag" ) ) {
|
|
uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_1FCTF);
|
|
}
|
|
if( strstr( type, "overload" ) ) {
|
|
uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_OBELISK);
|
|
}
|
|
if( strstr( type, "harvester" ) ) {
|
|
uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_HARVESTER);
|
|
}
|
|
} else {
|
|
uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_FFA);
|
|
}
|
|
|
|
uiInfo.mapCount++;
|
|
if (uiInfo.mapCount >= MAX_MAPS) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
UI_LoadBotsFromFile
|
|
===============
|
|
*/
|
|
static void UI_LoadBotsFromFile( char *filename ) {
|
|
int len;
|
|
fileHandle_t f;
|
|
char buf[MAX_BOTS_TEXT];
|
|
|
|
len = trap_FS_FOpenFile( filename, &f, FS_READ );
|
|
if ( !f ) {
|
|
trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) );
|
|
return;
|
|
}
|
|
if ( len >= MAX_BOTS_TEXT ) {
|
|
trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i\n", filename, len, MAX_BOTS_TEXT ) );
|
|
trap_FS_FCloseFile( f );
|
|
return;
|
|
}
|
|
|
|
trap_FS_Read( buf, len, f );
|
|
buf[len] = 0;
|
|
trap_FS_FCloseFile( f );
|
|
|
|
COM_Compress(buf);
|
|
|
|
ui_numBots += UI_ParseInfos( buf, MAX_BOTS - ui_numBots, &ui_botInfos[ui_numBots] );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
UI_LoadBots
|
|
===============
|
|
*/
|
|
void UI_LoadBots( void ) {
|
|
vmCvar_t botsFile;
|
|
int numdirs;
|
|
char filename[128];
|
|
char dirlist[1024];
|
|
char* dirptr;
|
|
int i;
|
|
int dirlen;
|
|
|
|
ui_numBots = 0;
|
|
|
|
trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM );
|
|
if( *botsFile.string ) {
|
|
UI_LoadBotsFromFile(botsFile.string);
|
|
}
|
|
else {
|
|
UI_LoadBotsFromFile("scripts/bots.txt");
|
|
}
|
|
|
|
// get all bots from .bot files
|
|
numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 );
|
|
dirptr = dirlist;
|
|
for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
|
|
dirlen = strlen(dirptr);
|
|
strcpy(filename, "scripts/");
|
|
strcat(filename, dirptr);
|
|
UI_LoadBotsFromFile(filename);
|
|
}
|
|
trap_Print( va( "%i bots parsed\n", ui_numBots ) );
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
UI_GetBotInfoByNumber
|
|
===============
|
|
*/
|
|
char *UI_GetBotInfoByNumber( int num ) {
|
|
if( num < 0 || num >= ui_numBots ) {
|
|
trap_Print( va( S_COLOR_RED "Invalid bot number: %i\n", num ) );
|
|
return NULL;
|
|
}
|
|
return ui_botInfos[num];
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
UI_GetBotInfoByName
|
|
===============
|
|
*/
|
|
char *UI_GetBotInfoByName( const char *name ) {
|
|
int n;
|
|
char *value;
|
|
|
|
for ( n = 0; n < ui_numBots ; n++ ) {
|
|
value = Info_ValueForKey( ui_botInfos[n], "name" );
|
|
if ( !Q_stricmp( value, name ) ) {
|
|
return ui_botInfos[n];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int UI_GetNumBots( void ) {
|
|
return ui_numBots;
|
|
}
|
|
|
|
|
|
char *UI_GetBotNameByNumber( int num ) {
|
|
char *info = UI_GetBotInfoByNumber(num);
|
|
if (info) {
|
|
return Info_ValueForKey( info, "name" );
|
|
}
|
|
return "Sarge";
|
|
}
|