/* =============== 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; } } 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++ ) { for( j = 0; j < mapRotations.rotations[ i ].numMaps; j++ ) { if( !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_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_PrintCurrentRotation Print the current rotation to an entity =============== */ void G_PrintCurrentRotation( gentity_t *ent ) { int mapRotationIndex = g_currentMapRotation.integer; mapRotation_t *mapRotation = G_MapRotationActive() ? &mapRotations.rotations[ mapRotationIndex ] : NULL; int i = 0; char currentMapName[ MAX_QPATH ]; node_t *node; if ( mapRotation == NULL ) { trap_SendServerCommand( ent - g_entities, "print \"^3listrotation^3: ^7there is no active map rotation on this server\n\"" ); return; } if ( mapRotation->numNodes == 0 ) { trap_SendServerCommand( ent - g_entities, "print \"^3listrotation^3: ^7there are no maps in the active map rotation\n\"" ); return; } trap_Cvar_VariableStringBuffer( "mapname", currentMapName, sizeof( currentMapName ) ); ADMBP_begin(); ADMBP( va( "%s:\n", mapRotation->name ) ); while ( ( node = mapRotation->nodes[ i++ ] ) ) { int colour = 7; char *prefix; char *suffix = ""; switch ( node->type ) { case NT_RETURN: prefix = "return"; break; case NT_LABEL: prefix = "label: "; break; case NT_GOTO: prefix = "goto: "; break; case NT_RESUME: prefix = "resume: "; break; default: prefix = ""; break; } if ( node->type == NT_MAP && !G_MapExists( node->u.map.name ) ) { colour = 1; } else if ( G_NodeIndexAfter( i - 1, mapRotationIndex ) == G_CurrentNodeIndex( mapRotationIndex ) ) { colour = 3; if ( node->type == NT_MAP && Q_stricmp( node->u.map.name, currentMapName ) ) { suffix = va( " (%s)", currentMapName ); } } ADMBP( va( " ^%i%3i %s%s%s\n", colour, i, prefix, node->type == NT_MAP ? node->u.map.name : node->u.label.name, suffix ) ); } if ( G_MapExists( g_nextMap.string ) ) { ADMBP( va( "^7The next map has been set to %s\n", g_nextMap.string ) ); } ADMBP_end(); }
/* =============== 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 ); if ( !G_MapExists( g_nextMap.string ) ) { 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; }