diff options
author | Mikko Tiusanen <ams@daug.net> | 2014-05-04 01:18:52 +0300 |
---|---|---|
committer | Mikko Tiusanen <ams@daug.net> | 2014-05-04 01:18:52 +0300 |
commit | 01beb9919b95479d8be040bec74abc5cc67a5e43 (patch) | |
tree | 65f0b79e793848491832756a4c3a32b23668fab3 /src/game/g_maprotation.c | |
parent | 191d731da136b7ee959a17e63111c9146219a768 (diff) |
Initial import.
Diffstat (limited to 'src/game/g_maprotation.c')
-rw-r--r-- | src/game/g_maprotation.c | 1339 |
1 files changed, 1339 insertions, 0 deletions
diff --git a/src/game/g_maprotation.c b/src/game/g_maprotation.c new file mode 100644 index 0000000..05b9969 --- /dev/null +++ b/src/game/g_maprotation.c @@ -0,0 +1,1339 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +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 +=========================================================================== +*/ +/* +=========================================================================== +TREMULOUS EDGE MOD SRC FILE +=========================================================================== +*/ +// g_maprotation.c -- the map rotation system + +#include "g_local.h" + +#define MAX_MAP_ROTATIONS 64 +#define MAX_MAP_ROTATION_MAPS 256 + +#define NOT_ROTATING -1 + +typedef enum +{ + CV_ERR, + CV_RANDOM, + CV_NUMCLIENTS, + CV_LASTWIN +} conditionVariable_t; + +typedef enum +{ + CO_LT, + CO_EQ, + CO_GT +} conditionOp_t; + +typedef struct condition_s +{ + struct node_s *target; + + conditionVariable_t lhs; + conditionOp_t op; + + int numClients; + team_t lastWin; +} condition_t; + +typedef struct map_s +{ + char name[ MAX_QPATH ]; + + char postCommand[ MAX_STRING_CHARS ]; + char layouts[ MAX_CVAR_VALUE_STRING ]; +} map_t; + +typedef struct label_s +{ + char name[ MAX_QPATH ]; +} label_t; + +typedef enum +{ + NT_MAP, + NT_CONDITION, + NT_GOTO, + NT_RESUME, + NT_LABEL, + NT_RETURN +} nodeType_t; + +typedef struct node_s +{ + nodeType_t type; + + union + { + map_t map; + condition_t condition; + label_t label; + } u; + +} node_t; + +typedef struct mapRotation_s +{ + char name[ MAX_QPATH ]; + + node_t *nodes[ MAX_MAP_ROTATION_MAPS ]; + int numNodes; + int currentNode; +} mapRotation_t; + +typedef struct mapRotations_s +{ + mapRotation_t rotations[ MAX_MAP_ROTATIONS ]; + int numRotations; +} mapRotations_t; + +static mapRotations_t mapRotations; + +static int G_NodeIndexAfter( int currentNode, int rotation ); + +static int G_CurrentNodeIndex( int rotation ); + +/* +=============== +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_LabelExists + +Check if a label exists in a rotation +=============== +*/ +static qboolean G_LabelExists( int rotation, char *name ) +{ + mapRotation_t *mr = &mapRotations.rotations[ rotation ]; + int i; + + for( i = 0; i < mr->numNodes; i++ ) + { + node_t *node = mr->nodes[ i ]; + + if( node->type == NT_LABEL && + !Q_stricmp( name, node->u.label.name ) ) + return qtrue; + + if( node->type == NT_MAP && + !Q_stricmp( name, node->u.map.name ) ) + return qtrue; + } + + return qfalse; +} + +/* +=============== +G_AllocateNode + +Allocate memory for a node_t +=============== +*/ +static node_t *G_AllocateNode( void ) +{ + node_t *node = BG_Alloc( sizeof( node_t ) ); + + return node; +} + +/* +=============== +G_ParseMapCommandSection + +Parse a map rotation command section +=============== +*/ +static qboolean G_ParseMapCommandSection( node_t *node, char **text_p ) +{ + char *token; + map_t *map = &node->u.map; + int commandLength = 0; + + // read optional parameters + while( 1 ) + { + token = COM_Parse( text_p ); + + if( !*token ) + break; + + if( !Q_stricmp( token, "" ) ) + return qfalse; + + if( !Q_stricmp( token, "}" ) ) + { + if( commandLength > 0 ) + { + // Replace last ; with \n + map->postCommand[ commandLength - 1 ] = '\n'; + } + + return qtrue; //reached the end of this command section + } + + if( !Q_stricmp( token, "layouts" ) ) + { + token = COM_ParseExt( text_p, qfalse ); + map->layouts[ 0 ] = '\0'; + + while( token[ 0 ] != 0 ) + { + Q_strcat( map->layouts, sizeof( map->layouts ), token ); + Q_strcat( map->layouts, sizeof( map->layouts ), " " ); + token = COM_ParseExt( text_p, qfalse ); + } + + continue; + } + + // Parse the rest of the line into map->postCommand + Q_strcat( map->postCommand, sizeof( map->postCommand ), token ); + Q_strcat( map->postCommand, sizeof( map->postCommand ), " " ); + + token = COM_ParseExt( text_p, qfalse ); + + while( token[ 0 ] != 0 ) + { + Q_strcat( map->postCommand, sizeof( map->postCommand ), token ); + Q_strcat( map->postCommand, sizeof( map->postCommand ), " " ); + token = COM_ParseExt( text_p, qfalse ); + } + + commandLength = strlen( map->postCommand ); + map->postCommand[ commandLength - 1 ] = ';'; + } + + return qfalse; +} + +/* +=============== +G_ParseNode + +Parse a node +=============== +*/ +static qboolean G_ParseNode( node_t **node, char *token, char **text_p, qboolean conditional ) +{ + if( !Q_stricmp( token, "if" ) ) + { + condition_t *condition; + + (*node)->type = NT_CONDITION; + condition = &(*node)->u.condition; + + token = COM_Parse( text_p ); + + if( !*token ) + return qfalse; + + if( !Q_stricmp( token, "numClients" ) ) + { + condition->lhs = CV_NUMCLIENTS; + + token = COM_Parse( text_p ); + + if( !*token ) + return qfalse; + + if( !Q_stricmp( token, "<" ) ) + condition->op = CO_LT; + else if( !Q_stricmp( token, ">" ) ) + condition->op = CO_GT; + else if( !Q_stricmp( token, "=" ) ) + condition->op = CO_EQ; + else + { + G_Printf( S_COLOR_RED "ERROR: invalid operator in expression: %s\n", token ); + return qfalse; + } + + token = COM_Parse( text_p ); + + if( !*token ) + return qfalse; + + condition->numClients = atoi( token ); + } + else if( !Q_stricmp( token, "lastWin" ) ) + { + condition->lhs = CV_LASTWIN; + + token = COM_Parse( text_p ); + + if( !*token ) + return qfalse; + + if( !Q_stricmp( token, "aliens" ) ) + condition->lastWin = TEAM_ALIENS; + else if( !Q_stricmp( token, "humans" ) ) + condition->lastWin = TEAM_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" ) ) + condition->lhs = CV_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 ) + return qfalse; + + condition->target = G_AllocateNode( ); + *node = condition->target; + + return G_ParseNode( node, token, text_p, qtrue ); + } + else if( !Q_stricmp( token, "return" ) ) + { + (*node)->type = NT_RETURN; + } + else if( !Q_stricmp( token, "goto" ) || + !Q_stricmp( token, "resume" ) ) + { + label_t *label; + + if( !Q_stricmp( token, "goto" ) ) + (*node)->type = NT_GOTO; + else + (*node)->type = NT_RESUME; + label = &(*node)->u.label; + + token = COM_Parse( text_p ); + + if( !*token ) + { + G_Printf( S_COLOR_RED "ERROR: goto or resume without label\n" ); + return qfalse; + } + + Q_strncpyz( label->name, token, sizeof( label->name ) ); + } + else if( *token == '#' || conditional ) + { + label_t *label; + + (*node)->type = ( conditional ) ? NT_GOTO : NT_LABEL; + label = &(*node)->u.label; + + Q_strncpyz( label->name, token, sizeof( label->name ) ); + } + else + { + map_t *map; + + (*node)->type = NT_MAP; + map = &(*node)->u.map; + + Q_strncpyz( map->name, token, sizeof( map->name ) ); + map->postCommand[ 0 ] = '\0'; + } + + return qtrue; +} + +/* +=============== +G_ParseMapRotation + +Parse a map rotation section +=============== +*/ +static qboolean G_ParseMapRotation( mapRotation_t *mr, char **text_p ) +{ + char *token; + node_t *node = NULL; + + // read optional parameters + while( 1 ) + { + token = COM_Parse( text_p ); + + if( !*token ) + break; + + if( !Q_stricmp( token, "" ) ) + return qfalse; + + if( !Q_stricmp( token, "{" ) ) + { + if( node == NULL ) + { + G_Printf( S_COLOR_RED "ERROR: map command section with no associated map\n" ); + return qfalse; + } + + if( !G_ParseMapCommandSection( node, text_p ) ) + { + G_Printf( S_COLOR_RED "ERROR: failed to parse map command section\n" ); + return qfalse; + } + + continue; + } + else if( !Q_stricmp( token, "}" ) ) + { + // Reached the end of this map rotation + return qtrue; + } + + if( mr->numNodes == 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; + } + + node = G_AllocateNode( ); + mr->nodes[ mr->numNodes++ ] = node; + + if( !G_ParseNode( &node, token, text_p, qfalse ) ) + return qfalse; + } + + 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; + 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 + if( G_RotationExists( mrName ) ) + { + G_Printf( S_COLOR_RED "ERROR: a map rotation is already named %s\n", mrName ); + return 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; + } + + 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; + } + + mapRotations.numRotations++; + + //start parsing map rotations again + mrNameSet = qfalse; + + continue; + } + else + { + G_Printf( S_COLOR_RED "ERROR: unnamed 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++ ) + { + mapRotation_t *mr = &mapRotations.rotations[ i ]; + int mapCount = 0; + + for( j = 0; j < mr->numNodes; j++ ) + { + node_t *node = mr->nodes[ j ]; + + if( node->type == NT_MAP ) + { + mapCount++; + if( !G_MapExists( node->u.map.name ) ) + { + G_Printf( S_COLOR_RED "ERROR: rotation map \"%s\" doesn't exist\n", + node->u.map.name ); + return qfalse; + } + continue; + } + else if( node->type == NT_RETURN ) + continue; + else if( node->type == NT_LABEL ) + continue; + else while( node->type == NT_CONDITION ) + node = node->u.condition.target; + + if( ( node->type == NT_GOTO || node->type == NT_RESUME ) && + !G_LabelExists( i, node->u.label.name ) && + !G_RotationExists( node->u.label.name ) ) + { + G_Printf( S_COLOR_RED "ERROR: goto destination named \"%s\" doesn't exist\n", + node->u.label.name ); + return qfalse; + } + } + + if( mapCount == 0 ) + { + G_Printf( S_COLOR_RED "ERROR: rotation \"%s\" needs at least one map entry\n", + mr->name ); + return qfalse; + } + } + + return qtrue; +} + +/* +=============== +G_PrintSpaces +=============== +*/ +static void G_PrintSpaces( int spaces ) +{ + int i; + + for( i = 0; i < spaces; i++ ) + G_Printf( " " ); +} + +/* +=============== +G_PrintRotations + +Print the parsed map rotations +=============== +*/ +void G_PrintRotations( void ) +{ + int i, j; + int size = sizeof( mapRotations ); + + G_Printf( "Map rotations as parsed:\n\n" ); + + for( i = 0; i < mapRotations.numRotations; i++ ) + { + mapRotation_t *mr = &mapRotations.rotations[ i ]; + + G_Printf( "rotation: %s\n{\n", mr->name ); + + size += mr->numNodes * sizeof( node_t ); + + for( j = 0; j < mr->numNodes; j++ ) + { + node_t *node = mr->nodes[ j ]; + int indentation = 0; + + while( node->type == NT_CONDITION ) + { + G_PrintSpaces( indentation ); + G_Printf( " condition\n" ); + node = node->u.condition.target; + + size += sizeof( node_t ); + + indentation += 2; + } + + G_PrintSpaces( indentation ); + + switch( node->type ) + { + case NT_MAP: + G_Printf( " %s\n", node->u.map.name ); + + if( strlen( node->u.map.postCommand ) > 0 ) + G_Printf( " command: %s", node->u.map.postCommand ); + + break; + + case NT_RETURN: + G_Printf( " return\n" ); + break; + + case NT_LABEL: + G_Printf( " label: %s\n", node->u.label.name ); + break; + + case NT_GOTO: + G_Printf( " goto: %s\n", node->u.label.name ); + break; + + case NT_RESUME: + G_Printf( " resume: %s\n", node->u.label.name ); + break; + + default: + break; + } + } + + G_Printf( "}\n" ); + } + + G_Printf( "Total memory used: %d bytes\n", size ); +} + +/* +=============== +G_PrintCurrentRotation + +Print the current rotation to ent +=============== +*/ +void G_PrintCurrentRotation( gentity_t *ent, const char *command ) +{ + mapRotation_t *mr; + char currentMap[ MAX_QPATH ]; + int rotation; + int index; + int i; + qboolean condition = qfalse; + + rotation = g_currentMapRotation.integer; + if( !( rotation >= 0 && rotation < mapRotations.numRotations ) ) + { + ADMP( "There is no map rotation\n" ); + return; + } + + mr = &mapRotations.rotations[ rotation ]; + index = G_CurrentNodeIndex( rotation ); + trap_Cvar_VariableStringBuffer( "mapname", currentMap, sizeof( currentMap ) ); + + ADMBP_begin( ); + ADMBP( va( "%s^7 %s\n", command, mr->name ) ); + + for( i = 0; i < mr->numNodes; i++ ) + { + node_t *node = mr->nodes[ i ]; + + if( node->type == NT_MAP ) + { + char info[ MAX_STRING_CHARS ] = { "" }; + char *marker; + + marker = ( condition ) ? "^5 " : "^7 "; + + if( G_NodeIndexAfter( i, rotation ) == index ) + { + if( !Q_stricmp( node->u.map.name, currentMap ) ) + marker = "^2*"; + else + { + marker = "^1*"; + Com_sprintf( info, sizeof( info ), " ^7(^2%s^7)", currentMap ); + } + } + if( condition ) + Q_strcat( info, sizeof( info ), " ^5?" ); + condition = qfalse; + + ADMBP( va( " %s %s%s\n", + marker, node->u.map.name, info ) ); + } + else if( node->type != NT_LABEL ) + { + condition = qtrue; + } + } + + if( g_nextMap.string[ 0 ] ) + ADMBP( va( "^3the next map has been set to %s\n", g_nextMap.string ) ); + ADMBP_end( ); +} + +/* +=============== +G_ClearRotationStack + +Clear the rotation stack +=============== +*/ +void G_ClearRotationStack( void ) +{ + trap_Cvar_Set( "g_mapRotationStack", "" ); + trap_Cvar_Update( &g_mapRotationStack ); +} + +/* +=============== +G_PushRotationStack + +Push the rotation stack +=============== +*/ +static void G_PushRotationStack( int rotation ) +{ + char text[ MAX_CVAR_VALUE_STRING ]; + + Com_sprintf( text, sizeof( text ), "%d %s", + rotation, g_mapRotationStack.string ); + trap_Cvar_Set( "g_mapRotationStack", text ); + trap_Cvar_Update( &g_mapRotationStack ); +} + +/* +=============== +G_PopRotationStack + +Pop the rotation stack +=============== +*/ +static int G_PopRotationStack( void ) +{ + int value = -1; + char text[ MAX_CVAR_VALUE_STRING ]; + char *text_p, *token; + + Q_strncpyz( text, g_mapRotationStack.string, sizeof( text ) ); + + text_p = text; + token = COM_Parse( &text_p ); + + if( *token ) + value = atoi( token ); + + if( text_p ) + { + while ( *text_p == ' ' ) + text_p++; + trap_Cvar_Set( "g_mapRotationStack", text_p ); + trap_Cvar_Update( &g_mapRotationStack ); + } + else + G_ClearRotationStack( ); + + return value; +} + +/* +=============== +G_RotationNameByIndex + +Returns the name of a rotation by its index +=============== +*/ +static char *G_RotationNameByIndex( int index ) +{ + if( index >= 0 && index < mapRotations.numRotations ) + return mapRotations.rotations[ index ].name; + return NULL; +} + +/* +=============== +G_CurrentNodeIndexArray + +Fill a static array with the current node of each rotation +=============== +*/ +static int *G_CurrentNodeIndexArray( void ) +{ + static int currentNode[ MAX_MAP_ROTATIONS ]; + int i = 0; + char text[ MAX_MAP_ROTATIONS * 2 ]; + char *text_p, *token; + + Q_strncpyz( text, g_mapRotationNodes.string, sizeof( text ) ); + + text_p = text; + + while( 1 ) + { + token = COM_Parse( &text_p ); + + if( !*token ) + break; + + currentNode[ i++ ] = atoi( token ); + } + + return currentNode; +} + +/* +=============== +G_SetCurrentNodeByIndex + +Set the current map in some rotation +=============== +*/ +static void G_SetCurrentNodeByIndex( int currentNode, int rotation ) +{ + char text[ MAX_MAP_ROTATIONS * 4 ] = { 0 }; + int *p = G_CurrentNodeIndexArray( ); + int i; + + p[ rotation ] = currentNode; + + for( i = 0; i < mapRotations.numRotations; i++ ) + Q_strcat( text, sizeof( text ), va( "%d ", p[ i ] ) ); + + trap_Cvar_Set( "g_mapRotationNodes", text ); + trap_Cvar_Update( &g_mapRotationNodes ); +} + +/* +=============== +G_CurrentNodeIndex + +Return the current node index in some rotation +=============== +*/ +static int G_CurrentNodeIndex( int rotation ) +{ + int *p = G_CurrentNodeIndexArray( ); + + return p[ rotation ]; +} + +/* +=============== +G_NodeByIndex + +Return a node in a rotation by its index +=============== +*/ +static node_t *G_NodeByIndex( int index, int rotation ) +{ + if( rotation >= 0 && rotation < mapRotations.numRotations && + index >= 0 && index < mapRotations.rotations[ rotation ].numNodes ) + return mapRotations.rotations[ rotation ].nodes[ index ]; + + return NULL; +} + +/* +=============== +G_IssueMapChange + +Send commands to the server to actually change the map +=============== +*/ +static void G_IssueMapChange( int index, int rotation ) +{ + node_t *node = mapRotations.rotations[ rotation ].nodes[ index ]; + map_t *map = &node->u.map; + + // allow a manually defined g_layouts setting to override the maprotation + if( !g_layouts.string[ 0 ] && map->layouts[ 0 ] ) + { + trap_Cvar_Set( "g_layouts", map->layouts ); + } + + trap_SendConsoleCommand( EXEC_APPEND, va( "map %s\n", map->name ) ); + + // Load up map defaults if g_mapConfigs is set + G_MapConfigs( map->name ); + + if( strlen( map->postCommand ) > 0 ) + trap_SendConsoleCommand( EXEC_APPEND, map->postCommand ); +} + +/* +=============== +G_GotoLabel + +Resolve the label of some condition +=============== +*/ +static qboolean G_GotoLabel( int rotation, int nodeIndex, char *name, + qboolean reset_index, int depth ) +{ + node_t *node; + int i; + + // Search the rotation names... + if( G_StartMapRotation( name, qtrue, qtrue, reset_index, depth ) ) + return qtrue; + + // ...then try labels in the rotation + for( i = 0; i < mapRotations.rotations[ rotation ].numNodes; i++ ) + { + node = mapRotations.rotations[ rotation ].nodes[ i ]; + + if( node->type == NT_LABEL && !Q_stricmp( node->u.label.name, name ) ) + { + G_SetCurrentNodeByIndex( G_NodeIndexAfter( i, rotation ), rotation ); + G_AdvanceMapRotation( depth ); + return qtrue; + } + } + + // finally check for a map by name + for( i = 0; i < mapRotations.rotations[ rotation ].numNodes; i++ ) + { + nodeIndex = G_NodeIndexAfter( nodeIndex, rotation ); + node = mapRotations.rotations[ rotation ].nodes[ nodeIndex ]; + + if( node->type == NT_MAP && !Q_stricmp( node->u.map.name, name ) ) + { + G_SetCurrentNodeByIndex( nodeIndex, rotation ); + G_AdvanceMapRotation( depth ); + return qtrue; + } + } + + return qfalse; +} + +/* +=============== +G_EvaluateMapCondition + +Evaluate a map condition +=============== +*/ +static qboolean G_EvaluateMapCondition( condition_t **condition ) +{ + qboolean result = qfalse; + condition_t *localCondition = *condition; + + switch( localCondition->lhs ) + { + case CV_RANDOM: + result = rand( ) / ( RAND_MAX / 2 + 1 ); + break; + + case CV_NUMCLIENTS: + switch( localCondition->op ) + { + case CO_LT: + result = level.numConnectedClients < localCondition->numClients; + break; + + case CO_GT: + result = level.numConnectedClients > localCondition->numClients; + break; + + case CO_EQ: + result = level.numConnectedClients == localCondition->numClients; + break; + } + break; + + case CV_LASTWIN: + result = level.lastWin == localCondition->lastWin; + break; + + default: + case CV_ERR: + G_Printf( S_COLOR_RED "ERROR: malformed map switch localCondition\n" ); + break; + } + + if( localCondition->target->type == NT_CONDITION ) + { + *condition = &localCondition->target->u.condition; + + return result && G_EvaluateMapCondition( condition ); + } + + return result; +} + +/* +=============== +G_NodeIndexAfter +=============== +*/ +static int G_NodeIndexAfter( int currentNode, int rotation ) +{ + mapRotation_t *mr = &mapRotations.rotations[ rotation ]; + + return ( currentNode + 1 ) % mr->numNodes; +} + +/* +=============== +G_StepMapRotation + +Run one node of a map rotation +=============== +*/ +qboolean G_StepMapRotation( int rotation, int nodeIndex, int depth ) +{ + node_t *node; + condition_t *condition; + int returnRotation; + qboolean step = qtrue; + + node = G_NodeByIndex( nodeIndex, rotation ); + depth++; + + // guard against inifinite loop in conditional code + if( depth > 32 && node->type != NT_MAP ) + { + if( depth > 64 ) + { + G_Printf( S_COLOR_RED "ERROR: infinite loop protection stopped at map rotation %s\n", + G_RotationNameByIndex( rotation ) ); + return qfalse; + } + + G_Printf( S_COLOR_YELLOW "WARNING: possible infinite loop in map rotation %s\n", + G_RotationNameByIndex( rotation ) ); + return qtrue; + } + + while( step ) + { + step = qfalse; + switch( node->type ) + { + case NT_CONDITION: + condition = &node->u.condition; + + if( G_EvaluateMapCondition( &condition ) ) + { + node = condition->target; + step = qtrue; + continue; + } + break; + + case NT_RETURN: + returnRotation = G_PopRotationStack( ); + if( returnRotation >= 0 ) + { + G_SetCurrentNodeByIndex( + G_NodeIndexAfter( nodeIndex, rotation ), rotation ); + if( G_StartMapRotation( G_RotationNameByIndex( returnRotation ), + qtrue, qfalse, qfalse, depth ) ) + { + return qfalse; + } + } + break; + + case NT_MAP: + if( G_MapExists( node->u.map.name ) ) + { + G_SetCurrentNodeByIndex( + G_NodeIndexAfter( nodeIndex, rotation ), rotation ); + G_IssueMapChange( nodeIndex, rotation ); + return qfalse; + } + + G_Printf( S_COLOR_YELLOW "WARNING: skipped missing map %s in rotation %s\n", + node->u.map.name, G_RotationNameByIndex( rotation ) ); + break; + + case NT_LABEL: + break; + + case NT_GOTO: + case NT_RESUME: + G_SetCurrentNodeByIndex( + G_NodeIndexAfter( nodeIndex, rotation ), rotation ); + if ( G_GotoLabel( rotation, nodeIndex, node->u.label.name, + ( node->type == NT_GOTO ), depth ) ) + return qfalse; + + G_Printf( S_COLOR_YELLOW "WARNING: label, map, or rotation %s not found in %s\n", + node->u.label.name, G_RotationNameByIndex( rotation ) ); + break; + } + } + + return qtrue; +} + +/* +=============== +G_AdvanceMapRotation + +Increment the current map rotation +=============== +*/ +void G_AdvanceMapRotation( int depth ) +{ + node_t *node; + int rotation; + int nodeIndex; + + rotation = g_currentMapRotation.integer; + if( rotation < 0 || rotation >= MAX_MAP_ROTATIONS ) + return; + + nodeIndex = G_CurrentNodeIndex( rotation ); + node = G_NodeByIndex( nodeIndex, rotation ); + if( !node ) + { + G_Printf( S_COLOR_YELLOW "WARNING: index incorrect for map rotation %s, trying 0\n", + G_RotationNameByIndex( rotation) ); + nodeIndex = 0; + node = G_NodeByIndex( nodeIndex, rotation ); + } + + while( node && G_StepMapRotation( rotation, nodeIndex, depth ) ) + { + nodeIndex = G_NodeIndexAfter( nodeIndex, rotation ); + node = G_NodeByIndex( nodeIndex, rotation ); + depth++; + } + + if( !node ) + G_Printf( S_COLOR_RED "ERROR: unexpected end of maprotation '%s'\n", + G_RotationNameByIndex( rotation) ); +} + +/* +=============== +G_StartMapRotation + +Switch to a new map rotation +=============== +*/ +qboolean G_StartMapRotation( char *name, qboolean advance, + qboolean putOnStack, qboolean reset_index, int depth ) +{ + int i; + int currentRotation = g_currentMapRotation.integer; + + for( i = 0; i < mapRotations.numRotations; i++ ) + { + if( !Q_stricmp( mapRotations.rotations[ i ].name, name ) ) + { + if( putOnStack && currentRotation >= 0 ) + G_PushRotationStack( currentRotation ); + + trap_Cvar_Set( "g_currentMapRotation", va( "%d", i ) ); + trap_Cvar_Update( &g_currentMapRotation ); + + if( advance ) + { + if( reset_index ) + G_SetCurrentNodeByIndex( 0, i ); + + G_AdvanceMapRotation( depth ); + } + + 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 initialise 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, qtrue, qfalse, 0 ); + + trap_Cvar_Set( "g_initialMapRotation", "" ); + trap_Cvar_Update( &g_initialMapRotation ); + } + } +} + +/* +=============== +G_FreeNode + +Free up memory used by a node +=============== +*/ +void G_FreeNode( node_t *node ) +{ + if( node->type == NT_CONDITION ) + G_FreeNode( node->u.condition.target ); + + BG_Free( node ); +} + +/* +=============== +G_ShutdownMapRotations + +Free up memory used by map rotations +=============== +*/ +void G_ShutdownMapRotations( void ) +{ + int i, j; + + for( i = 0; i < mapRotations.numRotations; i++ ) + { + mapRotation_t *mr = &mapRotations.rotations[ i ]; + + for( j = 0; j < mr->numNodes; j++ ) + { + node_t *node = mr->nodes[ j ]; + + G_FreeNode( node ); + } + } +} |