diff options
Diffstat (limited to 'src/game/g_maprotation.c')
-rw-r--r-- | src/game/g_maprotation.c | 1316 |
1 files changed, 1316 insertions, 0 deletions
diff --git a/src/game/g_maprotation.c b/src/game/g_maprotation.c new file mode 100644 index 0000000..60fd696 --- /dev/null +++ b/src/game/g_maprotation.c @@ -0,0 +1,1316 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2006 Tim Angus + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +// g_maprotation.c -- the map rotation system + +#include "g_local.h" + +mapRotations_t mapRotations; + + +static qboolean G_GetVotedMap( char *name, int size, int rotation, int map ); + +/* +=============== +G_MapExists + +Check if a map exists +=============== +*/ +qboolean G_MapExists( char *name ) +{ + return trap_FS_FOpenFile( va( "maps/%s.bsp", name ), NULL, FS_READ ); +} + +/* +=============== +G_RotationExists + +Check if a rotation exists +=============== +*/ +static qboolean G_RotationExists( char *name ) +{ + int i; + + for( i = 0; i < mapRotations.numRotations; i++ ) + { + if( Q_strncmp( mapRotations.rotations[ i ].name, name, MAX_QPATH ) == 0 ) + return qtrue; + } + + return qfalse; +} + +/* +=============== +G_ParseCommandSection + +Parse a map rotation command section +=============== +*/ +static qboolean G_ParseMapCommandSection( mapRotationEntry_t *mre, char **text_p ) +{ + char *token; + + // read optional parameters + while( 1 ) + { + token = COM_Parse( text_p ); + + if( !token ) + break; + + if( !Q_stricmp( token, "" ) ) + return qfalse; + + if( !Q_stricmp( token, "}" ) ) + return qtrue; //reached the end of this command section + + if( !Q_stricmp( token, "layouts" ) ) + { + token = COM_ParseExt( text_p, qfalse ); + mre->layouts[ 0 ] = '\0'; + while( token && token[ 0 ] != 0 ) + { + Q_strcat( mre->layouts, sizeof( mre->layouts ), token ); + Q_strcat( mre->layouts, sizeof( mre->layouts ), " " ); + token = COM_ParseExt( text_p, qfalse ); + } + continue; + } + + Q_strncpyz( mre->postCmds[ mre->numCmds ], token, sizeof( mre->postCmds[ 0 ] ) ); + Q_strcat( mre->postCmds[ mre->numCmds ], sizeof( mre->postCmds[ 0 ] ), " " ); + + token = COM_ParseExt( text_p, qfalse ); + + while( token && token[ 0 ] != 0 ) + { + Q_strcat( mre->postCmds[ mre->numCmds ], sizeof( mre->postCmds[ 0 ] ), token ); + Q_strcat( mre->postCmds[ mre->numCmds ], sizeof( mre->postCmds[ 0 ] ), " " ); + token = COM_ParseExt( text_p, qfalse ); + } + + if( mre->numCmds == MAX_MAP_COMMANDS ) + { + G_Printf( S_COLOR_RED "ERROR: maximum number of map commands (%d) reached\n", + MAX_MAP_COMMANDS ); + return qfalse; + } + else + mre->numCmds++; + } + + return qfalse; +} + +/* +=============== +G_ParseMapRotation + +Parse a map rotation section +=============== +*/ +static qboolean G_ParseMapRotation( mapRotation_t *mr, char **text_p ) +{ + char *token; + qboolean mnSet = qfalse; + mapRotationEntry_t *mre = NULL; + mapRotationCondition_t *mrc; + + // read optional parameters + while( 1 ) + { + token = COM_Parse( text_p ); + + if( !token ) + break; + + if( !Q_stricmp( token, "" ) ) + return qfalse; + + if( !Q_stricmp( token, "{" ) ) + { + if( !mnSet ) + { + G_Printf( S_COLOR_RED "ERROR: map settings section with no name\n" ); + return qfalse; + } + + if( !G_ParseMapCommandSection( mre, text_p ) ) + { + G_Printf( S_COLOR_RED "ERROR: failed to parse map command section\n" ); + return qfalse; + } + + mnSet = qfalse; + continue; + } + else if( !Q_stricmp( token, "goto" ) ) + { + token = COM_Parse( text_p ); + + if( !token ) + break; + + mrc = &mre->conditions[ mre->numConditions ]; + mrc->unconditional = qtrue; + Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) ); + + if( mre->numConditions == MAX_MAP_ROTATION_CONDS ) + { + G_Printf( S_COLOR_RED "ERROR: maximum number of conditions for one map (%d) reached\n", + MAX_MAP_ROTATION_CONDS ); + return qfalse; + } + else + mre->numConditions++; + + continue; + } + else if( !Q_stricmp( token, "if" ) ) + { + token = COM_Parse( text_p ); + + if( !token ) + break; + + mrc = &mre->conditions[ mre->numConditions ]; + + if( !Q_stricmp( token, "numClients" ) ) + { + mrc->lhs = MCV_NUMCLIENTS; + + token = COM_Parse( text_p ); + + if( !token ) + break; + + if( !Q_stricmp( token, "<" ) ) + mrc->op = MCO_LT; + else if( !Q_stricmp( token, ">" ) ) + mrc->op = MCO_GT; + else if( !Q_stricmp( token, "=" ) ) + mrc->op = MCO_EQ; + else + { + G_Printf( S_COLOR_RED "ERROR: invalid operator in expression: %s\n", token ); + return qfalse; + } + + token = COM_Parse( text_p ); + + if( !token ) + break; + + mrc->numClients = atoi( token ); + } + else if( !Q_stricmp( token, "lastWin" ) ) + { + mrc->lhs = MCV_LASTWIN; + + token = COM_Parse( text_p ); + + if( !token ) + break; + + if( !Q_stricmp( token, "aliens" ) ) + mrc->lastWin = PTE_ALIENS; + else if( !Q_stricmp( token, "humans" ) ) + mrc->lastWin = PTE_HUMANS; + else + { + G_Printf( S_COLOR_RED "ERROR: invalid right hand side in expression: %s\n", token ); + return qfalse; + } + } + else if( !Q_stricmp( token, "random" ) ) + mrc->lhs = MCV_RANDOM; + else + { + G_Printf( S_COLOR_RED "ERROR: invalid left hand side in expression: %s\n", token ); + return qfalse; + } + + token = COM_Parse( text_p ); + + if( !token ) + break; + + mrc->unconditional = qfalse; + Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) ); + + if( mre->numConditions == MAX_MAP_ROTATION_CONDS ) + { + G_Printf( S_COLOR_RED "ERROR: maximum number of conditions for one map (%d) reached\n", + MAX_MAP_ROTATION_CONDS ); + return qfalse; + } + else + mre->numConditions++; + + continue; + } + else if( !Q_stricmp( token, "*VOTE*" ) ) + { + if( mr->numMaps == MAX_MAP_ROTATION_MAPS ) + { + G_Printf( S_COLOR_RED "ERROR: maximum number of maps in one rotation (%d) reached\n", + MAX_MAP_ROTATION_MAPS ); + return qfalse; + } + mre = &mr->maps[ mr->numMaps ]; + Q_strncpyz( mre->name, token, sizeof( mre->name ) ); + + token = COM_Parse( text_p ); + + if( !Q_stricmp( token, "{" ) ) + { + while( 1 ) + { + token = COM_Parse( text_p ); + + if( !token ) + break; + + if( !Q_stricmp( token, "}" ) ) + { + break; + } + else + { + if( mre->numConditions < MAX_MAP_ROTATION_CONDS ) + { + mrc = &mre->conditions[ mre->numConditions ]; + mrc->lhs = MCV_VOTE; + mrc->unconditional = qfalse; + Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) ); + + mre->numConditions++; + } + else + { + G_Printf( S_COLOR_YELLOW "WARNING: maximum number of maps for one vote (%d) reached\n", + MAX_MAP_ROTATION_CONDS ); + } + } + } + if( !mre->numConditions ) + { + G_Printf( S_COLOR_YELLOW "WARNING: no maps in *VOTE* section\n" ); + } + else + { + mr->numMaps++; + mnSet = qtrue; + } + } + else + { + G_Printf( S_COLOR_RED "ERROR: *VOTE* with no section\n" ); + return qfalse; + } + + continue; + } + else if( !Q_stricmp( token, "*RANDOM*" ) ) + { + if( mr->numMaps == MAX_MAP_ROTATION_MAPS ) + { + G_Printf( S_COLOR_RED "ERROR: maximum number of maps in one rotation (%d) reached\n", + MAX_MAP_ROTATION_MAPS ); + return qfalse; + } + mre = &mr->maps[ mr->numMaps ]; + Q_strncpyz( mre->name, token, sizeof( mre->name ) ); + + token = COM_Parse( text_p ); + + if( !Q_stricmp( token, "{" ) ) + { + while( 1 ) + { + token = COM_Parse( text_p ); + + if( !token ) + break; + + if( !Q_stricmp( token, "}" ) ) + { + break; + } + else + { + if( mre->numConditions < MAX_MAP_ROTATION_CONDS ) + { + mrc = &mre->conditions[ mre->numConditions ]; + mrc->lhs = MCV_SELECTEDRANDOM; + mrc->unconditional = qfalse; + Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) ); + + mre->numConditions++; + } + else + { + G_Printf( S_COLOR_YELLOW "WARNING: maximum number of maps for one Random Slot (%d) reached\n", + MAX_MAP_ROTATION_CONDS ); + } + } + } + if( !mre->numConditions ) + { + G_Printf( S_COLOR_YELLOW "WARNING: no maps in *RANDOM* section\n" ); + } + else + { + mr->numMaps++; + mnSet = qtrue; + } + } + else + { + G_Printf( S_COLOR_RED "ERROR: *RANDOM* with no section\n" ); + return qfalse; + } + + continue; + } + else if( !Q_stricmp( token, "}" ) ) + return qtrue; //reached the end of this map rotation + + mre = &mr->maps[ mr->numMaps ]; + + if( mr->numMaps == MAX_MAP_ROTATION_MAPS ) + { + G_Printf( S_COLOR_RED "ERROR: maximum number of maps in one rotation (%d) reached\n", + MAX_MAP_ROTATION_MAPS ); + return qfalse; + } + else + mr->numMaps++; + + Q_strncpyz( mre->name, token, sizeof( mre->name ) ); + mnSet = qtrue; + } + + return qfalse; +} + +/* +=============== +G_ParseMapRotationFile + +Load the map rotations from a map rotation file +=============== +*/ +static qboolean G_ParseMapRotationFile( const char *fileName ) +{ + char *text_p; + int i, j, k; + int len; + char *token; + char text[ 20000 ]; + char mrName[ MAX_QPATH ]; + qboolean mrNameSet = qfalse; + fileHandle_t f; + + // load the file + len = trap_FS_FOpenFile( fileName, &f, FS_READ ); + if( len < 0 ) + return qfalse; + + if( len == 0 || len >= sizeof( text ) - 1 ) + { + trap_FS_FCloseFile( f ); + G_Printf( S_COLOR_RED "ERROR: map rotation file %s is %s\n", fileName, + len == 0 ? "empty" : "too long" ); + return qfalse; + } + + trap_FS_Read( text, len, f ); + text[ len ] = 0; + trap_FS_FCloseFile( f ); + + // parse the text + text_p = text; + + // read optional parameters + while( 1 ) + { + token = COM_Parse( &text_p ); + + if( !token ) + break; + + if( !Q_stricmp( token, "" ) ) + break; + + if( !Q_stricmp( token, "{" ) ) + { + if( mrNameSet ) + { + //check for name space clashes + for( i = 0; i < mapRotations.numRotations; i++ ) + { + if( !Q_stricmp( mapRotations.rotations[ i ].name, mrName ) ) + { + G_Printf( S_COLOR_RED "ERROR: a map rotation is already named %s\n", mrName ); + return qfalse; + } + } + + Q_strncpyz( mapRotations.rotations[ mapRotations.numRotations ].name, mrName, MAX_QPATH ); + + if( !G_ParseMapRotation( &mapRotations.rotations[ mapRotations.numRotations ], &text_p ) ) + { + G_Printf( S_COLOR_RED "ERROR: %s: failed to parse map rotation %s\n", fileName, mrName ); + return qfalse; + } + + //start parsing map rotations again + mrNameSet = qfalse; + + if( mapRotations.numRotations == MAX_MAP_ROTATIONS ) + { + G_Printf( S_COLOR_RED "ERROR: maximum number of map rotations (%d) reached\n", + MAX_MAP_ROTATIONS ); + return qfalse; + } + else + mapRotations.numRotations++; + + continue; + } + else + { + G_Printf( S_COLOR_RED "ERROR: unamed map rotation\n" ); + return qfalse; + } + } + + if( !mrNameSet ) + { + Q_strncpyz( mrName, token, sizeof( mrName ) ); + mrNameSet = qtrue; + } + else + { + G_Printf( S_COLOR_RED "ERROR: map rotation already named\n" ); + return qfalse; + } + } + + for( i = 0; i < mapRotations.numRotations; i++ ) + { + for( j = 0; j < mapRotations.rotations[ i ].numMaps; j++ ) + { + if( Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*VOTE*") != 0 && + Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*RANDOM*") != 0 && + !G_MapExists( mapRotations.rotations[ i ].maps[ j ].name ) ) + { + G_Printf( S_COLOR_RED "ERROR: map \"%s\" doesn't exist\n", + mapRotations.rotations[ i ].maps[ j ].name ); + return qfalse; + } + + for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numConditions; k++ ) + { + if( !G_MapExists( mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest ) && + !G_RotationExists( mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest ) ) + { + G_Printf( S_COLOR_RED "ERROR: conditional destination \"%s\" doesn't exist\n", + mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest ); + return qfalse; + } + + } + + } + } + + return qtrue; +} + +/* +=============== +G_PrintRotations + +Print the parsed map rotations +=============== +*/ +void G_PrintRotations( void ) +{ + int i, j, k; + + G_Printf( "Map rotations as parsed:\n\n" ); + + for( i = 0; i < mapRotations.numRotations; i++ ) + { + G_Printf( "rotation: %s\n{\n", mapRotations.rotations[ i ].name ); + + for( j = 0; j < mapRotations.rotations[ i ].numMaps; j++ ) + { + G_Printf( " map: %s\n {\n", mapRotations.rotations[ i ].maps[ j ].name ); + + for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numCmds; k++ ) + { + G_Printf( " command: %s\n", + mapRotations.rotations[ i ].maps[ j ].postCmds[ k ] ); + } + + G_Printf( " }\n" ); + + for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numConditions; k++ ) + { + G_Printf( " conditional: %s\n", + mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest ); + } + + } + + G_Printf( "}\n" ); + } + + G_Printf( "Total memory used: %d bytes\n", sizeof( mapRotations ) ); +} + +/* +=============== +G_GetCurrentMapArray + +Fill a static array with the current map of each rotation +=============== +*/ +static int *G_GetCurrentMapArray( void ) +{ + static int currentMap[ MAX_MAP_ROTATIONS ]; + int i = 0; + char text[ MAX_MAP_ROTATIONS * 2 ]; + char *text_p, *token; + + Q_strncpyz( text, g_currentMap.string, sizeof( text ) ); + + text_p = text; + + while( 1 ) + { + token = COM_Parse( &text_p ); + + if( !token ) + break; + + if( !Q_stricmp( token, "" ) ) + break; + + currentMap[ i++ ] = atoi( token ); + } + + return currentMap; +} + +/* +=============== +G_SetCurrentMap + +Set the current map in some rotation +=============== +*/ +static void G_SetCurrentMap( int currentMap, int rotation ) +{ + char text[ MAX_MAP_ROTATIONS * 2 ] = { 0 }; + int *p = G_GetCurrentMapArray( ); + int i; + + p[ rotation ] = currentMap; + + for( i = 0; i < mapRotations.numRotations; i++ ) + Q_strcat( text, sizeof( text ), va( "%d ", p[ i ] ) ); + + trap_Cvar_Set( "g_currentMap", text ); + trap_Cvar_Update( &g_currentMap ); +} + +/* +=============== +G_GetCurrentMap + +Return the current map in some rotation +=============== +*/ +int G_GetCurrentMap( int rotation ) +{ + int *p = G_GetCurrentMapArray( ); + + return p[ rotation ]; +} + +/* +=============== +G_IssueMapChange + +Send commands to the server to actually change the map +=============== +*/ +static void G_IssueMapChange( int rotation ) +{ + int i; + int map = G_GetCurrentMap( rotation ); + char cmd[ MAX_TOKEN_CHARS ]; + char newmap[ MAX_CVAR_VALUE_STRING ]; + + Q_strncpyz( newmap, mapRotations.rotations[rotation].maps[map].name, sizeof( newmap )); + + if (!Q_stricmp( newmap, "*VOTE*") ) + { + fileHandle_t f; + + G_GetVotedMap( newmap, sizeof( newmap ), rotation, map ); + if( trap_FS_FOpenFile( va("maps/%s.bsp", newmap), &f, FS_READ ) > 0 ) + { + trap_FS_FCloseFile( f ); + } + else + { + G_AdvanceMapRotation(); + return; + } + } + else if(!Q_stricmp( newmap, "*RANDOM*") ) + { + fileHandle_t f; + + G_GetRandomMap( newmap, sizeof( newmap ), rotation, map ); + if( trap_FS_FOpenFile( va("maps/%s.bsp", newmap), &f, FS_READ ) > 0 ) + { + trap_FS_FCloseFile( f ); + } + else + { + G_AdvanceMapRotation(); + return; + } + } + + // allow a manually defined g_layouts setting to override the maprotation + if( !g_layouts.string[ 0 ] && + mapRotations.rotations[ rotation ].maps[ map ].layouts[ 0 ] ) + { + trap_Cvar_Set( "g_layouts", + mapRotations.rotations[ rotation ].maps[ map ].layouts ); + } + + trap_SendConsoleCommand( EXEC_APPEND, va( "map %s\n", + newmap ) ); + + // load up map defaults if g_mapConfigs is set + G_MapConfigs( newmap ); + + for( i = 0; i < mapRotations.rotations[ rotation ].maps[ map ].numCmds; i++ ) + { + Q_strncpyz( cmd, mapRotations.rotations[ rotation ].maps[ map ].postCmds[ i ], + sizeof( cmd ) ); + Q_strcat( cmd, sizeof( cmd ), "\n" ); + trap_SendConsoleCommand( EXEC_APPEND, cmd ); + } +} + +/* +=============== +G_ResolveConditionDestination + +Resolve the destination of some condition +=============== +*/ +static mapConditionType_t G_ResolveConditionDestination( int *n, char *name ) +{ + int i; + + //search the current rotation first... + for( i = 0; i < mapRotations.rotations[ g_currentMapRotation.integer ].numMaps; i++ ) + { + if( !Q_stricmp( mapRotations.rotations[ g_currentMapRotation.integer ].maps[ i ].name, name ) ) + { + *n = i; + return MCT_MAP; + } + } + + //...then search the rotation names + for( i = 0; i < mapRotations.numRotations; i++ ) + { + if( !Q_stricmp( mapRotations.rotations[ i ].name, name ) ) + { + *n = i; + return MCT_ROTATION; + } + } + + return MCT_ERR; +} + +/* +=============== +G_EvaluateMapCondition + +Evaluate a map condition +=============== +*/ +static qboolean G_EvaluateMapCondition( mapRotationCondition_t *mrc ) +{ + switch( mrc->lhs ) + { + case MCV_RANDOM: + return rand( ) & 1; + break; + + case MCV_NUMCLIENTS: + switch( mrc->op ) + { + case MCO_LT: + return level.numConnectedClients < mrc->numClients; + break; + + case MCO_GT: + return level.numConnectedClients > mrc->numClients; + break; + + case MCO_EQ: + return level.numConnectedClients == mrc->numClients; + break; + } + break; + + case MCV_LASTWIN: + return level.lastWin == mrc->lastWin; + break; + + case MCV_VOTE: + // ignore vote for conditions; + break; + case MCV_SELECTEDRANDOM: + // ignore vote for conditions; + break; + + default: + case MCV_ERR: + G_Printf( S_COLOR_RED "ERROR: malformed map switch condition\n" ); + break; + } + + return qfalse; +} + +/* +=============== +G_AdvanceMapRotation + +Increment the current map rotation +=============== +*/ +qboolean G_AdvanceMapRotation( void ) +{ + mapRotation_t *mr; + mapRotationEntry_t *mre; + mapRotationCondition_t *mrc; + int currentRotation, currentMap, nextMap; + int i, n; + mapConditionType_t mct; + + if( ( currentRotation = g_currentMapRotation.integer ) == NOT_ROTATING ) + return qfalse; + + currentMap = G_GetCurrentMap( currentRotation ); + + mr = &mapRotations.rotations[ currentRotation ]; + mre = &mr->maps[ currentMap ]; + nextMap = ( currentMap + 1 ) % mr->numMaps; + + for( i = 0; i < mre->numConditions; i++ ) + { + mrc = &mre->conditions[ i ]; + + if( mrc->unconditional || G_EvaluateMapCondition( mrc ) ) + { + mct = G_ResolveConditionDestination( &n, mrc->dest ); + + switch( mct ) + { + case MCT_MAP: + nextMap = n; + break; + + case MCT_ROTATION: + //need to increment the current map before changing the rotation + //or you get infinite loops with some conditionals + G_SetCurrentMap( nextMap, currentRotation ); + G_StartMapRotation( mrc->dest, qtrue ); + return qtrue; + break; + + default: + case MCT_ERR: + G_Printf( S_COLOR_YELLOW "WARNING: map switch destination could not be resolved: %s\n", + mrc->dest ); + break; + } + } + } + + G_SetCurrentMap( nextMap, currentRotation ); + G_IssueMapChange( currentRotation ); + + return qtrue; +} + +/* +=============== +G_StartMapRotation + +Switch to a new map rotation +=============== +*/ +qboolean G_StartMapRotation( char *name, qboolean changeMap ) +{ + int i; + + for( i = 0; i < mapRotations.numRotations; i++ ) + { + if( !Q_stricmp( mapRotations.rotations[ i ].name, name ) ) + { + trap_Cvar_Set( "g_currentMapRotation", va( "%d", i ) ); + trap_Cvar_Update( &g_currentMapRotation ); + + if( changeMap ) + G_IssueMapChange( i ); + break; + } + } + + if( i == mapRotations.numRotations ) + return qfalse; + else + return qtrue; +} + +/* +=============== +G_StopMapRotation + +Stop the current map rotation +=============== +*/ +void G_StopMapRotation( void ) +{ + trap_Cvar_Set( "g_currentMapRotation", va( "%d", NOT_ROTATING ) ); + trap_Cvar_Update( &g_currentMapRotation ); +} + +/* +=============== +G_MapRotationActive + +Test if any map rotation is currently active +=============== +*/ +qboolean G_MapRotationActive( void ) +{ + return ( g_currentMapRotation.integer != NOT_ROTATING ); +} + +/* +=============== +G_InitMapRotations + +Load and intialise the map rotations +=============== +*/ +void G_InitMapRotations( void ) +{ + const char *fileName = "maprotation.cfg"; + + //load the file if it exists + if( trap_FS_FOpenFile( fileName, NULL, FS_READ ) ) + { + if( !G_ParseMapRotationFile( fileName ) ) + G_Printf( S_COLOR_RED "ERROR: failed to parse %s file\n", fileName ); + } + else + G_Printf( "%s file not found.\n", fileName ); + + if( g_currentMapRotation.integer == NOT_ROTATING ) + { + if( g_initialMapRotation.string[ 0 ] != 0 ) + { + G_StartMapRotation( g_initialMapRotation.string, qfalse ); + + trap_Cvar_Set( "g_initialMapRotation", "" ); + trap_Cvar_Update( &g_initialMapRotation ); + } + } +} + +static char rotationVoteList[ MAX_MAP_ROTATION_CONDS ][ MAX_QPATH ]; +static int rotationVoteLen = 0; + +static int rotationVoteClientPosition[ MAX_CLIENTS ]; +static int rotationVoteClientSelection[ MAX_CLIENTS ]; + +/* +=============== +G_CheckMapRotationVote +=============== +*/ +qboolean G_CheckMapRotationVote( void ) +{ + mapRotation_t *mr; + mapRotationEntry_t *mre; + mapRotationCondition_t *mrc; + int currentRotation, currentMap, nextMap; + int i; + + rotationVoteLen = 0; + + if( g_mapRotationVote.integer < 1 ) + return qfalse; + + if( ( currentRotation = g_currentMapRotation.integer ) == NOT_ROTATING ) + return qfalse; + + currentMap = G_GetCurrentMap( currentRotation ); + + mr = &mapRotations.rotations[ currentRotation ]; + nextMap = ( currentMap + 1 ) % mr->numMaps; + mre = &mr->maps[ nextMap ]; + + for( i = 0; i < mre->numConditions; i++ ) + { + mrc = &mre->conditions[ i ]; + + if( mrc->lhs == MCV_VOTE ) + { + Q_strncpyz( rotationVoteList[ rotationVoteLen ], mrc->dest, + sizeof( rotationVoteList[ rotationVoteLen ] ) ); + rotationVoteLen++; + if( rotationVoteLen >= MAX_MAP_ROTATION_CONDS ) + break; + } + } + + if( !rotationVoteLen ) + return qfalse; + + for( i = 0; i < MAX_CLIENTS; i++ ) + { + rotationVoteClientPosition[ i ] = 0; + rotationVoteClientSelection[ i ] = -1; + } + + return qtrue; +} + +typedef struct { + int votes; + int map; +} MapVoteResultsSort_t; + +static int SortMapVoteResults( const void *av, const void *bv ) +{ + const MapVoteResultsSort_t *a = av; + const MapVoteResultsSort_t *b = bv; + + if( a->votes > b->votes ) + return -1; + if( a->votes < b->votes ) + return 1; + + if( a->map > b->map ) + return 1; + if( a->map < b->map ) + return -1; + + return 0; +} + +static int G_GetMapVoteWinner( int *winvotes, int *totalvotes, int *resultorder ) +{ + MapVoteResultsSort_t results[ MAX_MAP_ROTATION_CONDS ]; + int tv = 0; + int i, n; + + for( i = 0; i < MAX_MAP_ROTATION_CONDS; i++ ) + { + results[ i ].votes = 0; + results[ i ].map = i; + } + for( i = 0; i < MAX_CLIENTS; i++ ) + { + n = rotationVoteClientSelection[ i ]; + if( n >=0 && n < MAX_MAP_ROTATION_CONDS ) + { + results[ n ].votes += 1; + tv++; + } + } + + qsort ( results, MAX_MAP_ROTATION_CONDS, sizeof( results[ 0 ] ), SortMapVoteResults ); + + if( winvotes != NULL ) + *winvotes = results[ 0 ].votes; + if( totalvotes != NULL ) + *totalvotes = tv; + + if( resultorder != NULL ) + { + for( i = 0; i < MAX_MAP_ROTATION_CONDS; i++ ) + resultorder[ results[ i ].map ] = i; + } + + return results[ 0 ].map; +} + +qboolean G_IntermissionMapVoteWinner( void ) +{ + int winner, winvotes, totalvotes; + int nonvotes; + + winner = G_GetMapVoteWinner( &winvotes, &totalvotes, NULL ); + if( winvotes * 2 > level.numConnectedClients ) + return qtrue; + nonvotes = level.numConnectedClients - totalvotes; + if( nonvotes < 0 ) + nonvotes = 0; + if( winvotes > nonvotes + ( totalvotes - winvotes ) ) + return qtrue; + + return qfalse; +} + +static qboolean G_GetVotedMap( char *name, int size, int rotation, int map ) +{ + mapRotation_t *mr; + mapRotationEntry_t *mre; + mapRotationCondition_t *mrc; + int i, n; + int winner; + qboolean found = qfalse; + + if( !rotationVoteLen ) + return qfalse; + + winner = G_GetMapVoteWinner( NULL, NULL, NULL ); + + mr = &mapRotations.rotations[ rotation ]; + mre = &mr->maps[ map ]; + + n = 0; + for( i = 0; i < mre->numConditions && n < rotationVoteLen; i++ ) + { + mrc = &mre->conditions[ i ]; + + if( mrc->lhs == MCV_VOTE ) + { + if( n == winner ) + { + Q_strncpyz( name, mrc->dest, size ); + found = qtrue; + break; + } + n++; + } + } + + rotationVoteLen = 0; + + return found; +} + +static void G_IntermissionMapVoteMessageReal( gentity_t *ent, int winner, int winvotes, int totalvotes, int *ranklist ) +{ + int clientNum; + char string[ MAX_STRING_CHARS ]; + char entry[ MAX_STRING_CHARS ]; + int ourlist[ MAX_MAP_ROTATION_CONDS ]; + int len = 0; + int index, selection; + int i; + char *color; + char *rank; + + clientNum = ent-g_entities; + + index = rotationVoteClientSelection[ clientNum ]; + selection = rotationVoteClientPosition[ clientNum ]; + + if( winner < 0 || winner >= MAX_MAP_ROTATION_CONDS || ranklist == NULL ) + { + ranklist = &ourlist[0]; + winner = G_GetMapVoteWinner( &winvotes, &totalvotes, ranklist ); + } + + Q_strncpyz( string, "^7Attack = down ^0/^7 Repair = up ^0/^7 F1 = vote\n\n" + "^2Map Vote Menu\n" + "^7+------------------+\n", sizeof( string ) ); + for( i = 0; i < rotationVoteLen; i++ ) + { + if( !G_MapExists( rotationVoteList[ i ] ) ) + continue; + + if( i == selection ) + color = "^5"; + else if( i == index ) + color = "^1"; + else + color = "^7"; + + switch( ranklist[ i ] ) + { + case 0: + rank = "^7---"; + break; + case 1: + rank = "^7--"; + break; + case 2: + rank = "^7-"; + break; + default: + rank = ""; + break; + } + + Com_sprintf( entry, sizeof( entry ), "^7%s%s%s%s %s %s%s^7%s\n", + ( i == index ) ? "^1>>>" : "", + ( i == selection ) ? "^7(" : " ", + rank, + color, + rotationVoteList[ i ], + rank, + ( i == selection ) ? "^7)" : " ", + ( i == index ) ? "^1<<<" : "" ); + + Q_strcat( string, sizeof( string ), entry ); + len += strlen( entry ); + } + + Com_sprintf( entry, sizeof( entry ), + "\n^7+----------------+\nleader: ^3%s^7 with %d vote%s\nvoters: %d\ntime left: %d", + rotationVoteList[ winner ], + winvotes, + ( winvotes == 1 ) ? "" : "s", + totalvotes, + ( level.mapRotationVoteTime - level.time ) / 1000 ); + Q_strcat( string, sizeof( string ), entry ); + + trap_SendServerCommand( ent-g_entities, va( "cp \"%s\"\n", string ) ); +} + +void G_IntermissionMapVoteMessageAll( void ) +{ + int ranklist[ MAX_MAP_ROTATION_CONDS ]; + int winner; + int winvotes, totalvotes; + int i; + + winner = G_GetMapVoteWinner( &winvotes, &totalvotes, &ranklist[ 0 ] ); + for( i = 0; i < level.maxclients; i++ ) + { + if( level.clients[ i ].pers.connected == CON_CONNECTED ) + G_IntermissionMapVoteMessageReal( g_entities + i, winner, winvotes, totalvotes, ranklist ); + } +} + +void G_IntermissionMapVoteMessage( gentity_t *ent ) +{ + G_IntermissionMapVoteMessageReal( ent, -1, 0, 0, NULL ); +} + +void G_IntermissionMapVoteCommand( gentity_t *ent, qboolean next, qboolean choose ) +{ + int clientNum; + int n; + + clientNum = ent-g_entities; + + if( choose ) + { + rotationVoteClientSelection[ clientNum ] = rotationVoteClientPosition[ clientNum ]; + } + else + { + n = rotationVoteClientPosition[ clientNum ]; + if( next ) + n++; + else + n--; + + if( n >= rotationVoteLen ) + n = rotationVoteLen - 1; + if( n < 0 ) + n = 0; + + rotationVoteClientPosition[ clientNum ] = n; + } + + G_IntermissionMapVoteMessage( ent ); +} + +static qboolean G_GetRandomMap( char *name, int size, int rotation, int map ) +{ + mapRotation_t *mr; + mapRotationEntry_t *mre; + mapRotationCondition_t *mrc; + int i, nummaps; + int randompick = 0; + int maplist[ 32 ]; + + mr = &mapRotations.rotations[ rotation ]; + mre = &mr->maps[ map ]; + + nummaps = 0; + //count the number of map votes + for( i = 0; i < mre->numConditions; i++ ) + { + mrc = &mre->conditions[ i ]; + + if( mrc->lhs == MCV_SELECTEDRANDOM ) + { + //map doesnt exist + if( !G_MapExists( mrc->dest ) ) { + continue; + } + maplist[ nummaps ] = i; + nummaps++; + } + } + + if( nummaps == 0 ) { + return qfalse; + } + + randompick = (int)( random() * nummaps ); + + Q_strncpyz( name, mre->conditions[ maplist[ randompick ] ].dest, size ); + + return qtrue; +} |