diff options
author | Tim Angus <tim@ngus.net> | 2005-12-10 03:23:37 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2005-12-10 03:23:37 +0000 |
commit | bf23ecf17f432cf8e47302ef7464612c17be9bbe (patch) | |
tree | 6e3d28ecaa038199d6ba63830e81f05270b199c3 /src/game/g_maprotation.c | |
parent | 22f322884cf7715c01500ef0b4579b87b1cb1973 (diff) |
* Move the game source from mod/src/ to src/
Diffstat (limited to 'src/game/g_maprotation.c')
-rw-r--r-- | src/game/g_maprotation.c | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/src/game/g_maprotation.c b/src/game/g_maprotation.c new file mode 100644 index 00000000..fb98c4a8 --- /dev/null +++ b/src/game/g_maprotation.c @@ -0,0 +1,709 @@ +// g_maprotation.c -- the map rotation system + +/* + * Portions Copyright (C) 2000-2001 Tim Angus + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the OSML - Open Source Modification License v1.0 as + * described in the file COPYING which is distributed with this source + * code. + * + * This program 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. + */ + +#include "g_local.h" + +static mapRotations_t mapRotations; + +/* +=============== +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 + + 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, "}" ) ) + 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; + 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 >= sizeof( text ) - 1 ) + { + G_Printf( S_COLOR_RED "ERROR: map rotation file %s too long\n", fileName ); + 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 particle systems 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; + } + } + + 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 +=============== +*/ +static 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 ]; + + trap_SendConsoleCommand( EXEC_APPEND, va( "map %s\n", + mapRotations.rotations[ rotation ].maps[ map ].name ) ); + + 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; + } + } + + //this should probably be prevented by a 2nd pass at compile time + //but i'm lazy (FIXME) + 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; + + 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: + 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"; + fileHandle_t f; + + //load the file if it exists + if( trap_FS_FOpenFile( fileName, &f, FS_READ ) > 0 ) + { + trap_FS_FCloseFile( f ); + + 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 ); + } + } +} |