summaryrefslogtreecommitdiff
path: root/src/game/g_maprotation.c
diff options
context:
space:
mode:
authorIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
committerIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
commit425decdf7e9284d15aa726e3ae96b9942fb0e3ea (patch)
tree6c0dd7edfefff1be7b9e75fe0b3a0a85fe1595f3 /src/game/g_maprotation.c
parentccb0b2e4d6674a7a00c9bf491f08fc73b6898c54 (diff)
create tremded branch
Diffstat (limited to 'src/game/g_maprotation.c')
-rw-r--r--src/game/g_maprotation.c1538
1 files changed, 744 insertions, 794 deletions
diff --git a/src/game/g_maprotation.c b/src/game/g_maprotation.c
index a846c79..90a51cf 100644
--- a/src/game/g_maprotation.c
+++ b/src/game/g_maprotation.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,8 +17,8 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
@@ -25,10 +26,91 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "g_local.h"
-mapRotations_t mapRotations;
+#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;
-static qboolean G_GetVotedMap( char *name, int size, int rotation, int map );
+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 );
/*
===============
@@ -37,9 +119,21 @@ G_MapExists
Check if a map exists
===============
*/
-qboolean G_MapExists( char *name )
+qboolean G_MapExists( const char *name )
+{
+ return trap_FS_FOpenFile( va( "maps/%s.bsp", name ), NULL, FS_READ ) > 0;
+}
+
+/*
+===============
+G_LayoutExists
+
+Check if a layout exists for a map
+===============
+*/
+qboolean G_LayoutExists( const char *map, const char *layout )
{
- return trap_FS_FOpenFile( va( "maps/%s.bsp", name ), NULL, FS_READ );
+ return !Q_stricmp( layout, "*BUILTIN*" ) || trap_FS_FOpenFile( va( "layouts/%s/%s.dat", map, layout ), NULL, FS_READ ) > 0;
}
/*
@@ -64,62 +158,111 @@ static qboolean G_RotationExists( char *name )
/*
===============
-G_ParseCommandSection
+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( mapRotationEntry_t *mre, char **text_p )
+static qboolean G_ParseMapCommandSection( node_t *node, char **text_p )
{
- char *token;
+ char *token;
+ map_t *map = &node->u.map;
+ int commandLength = 0;
// read optional parameters
while( 1 )
{
token = COM_Parse( text_p );
- if( !token )
+ 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 );
- mre->layouts[ 0 ] = '\0';
- while( token && token[ 0 ] != 0 )
+ map->layouts[ 0 ] = '\0';
+
+ while( token[ 0 ] != 0 )
{
- Q_strcat( mre->layouts, sizeof( mre->layouts ), token );
- Q_strcat( mre->layouts, sizeof( mre->layouts ), " " );
+ Q_strcat( map->layouts, sizeof( map->layouts ), token );
+ Q_strcat( map->layouts, sizeof( map->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 ] ), " " );
+ // 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 && token[ 0 ] != 0 )
+ while( token[ 0 ] != 0 )
{
- Q_strcat( mre->postCmds[ mre->numCmds ], sizeof( mre->postCmds[ 0 ] ), token );
- Q_strcat( mre->postCmds[ mre->numCmds ], sizeof( mre->postCmds[ 0 ] ), " " );
+ Q_strcat( map->postCommand, sizeof( map->postCommand ), token );
+ Q_strcat( map->postCommand, sizeof( map->postCommand ), " " );
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++;
+ commandLength = strlen( map->postCommand );
+ map->postCommand[ commandLength - 1 ] = ';';
}
return qfalse;
@@ -127,291 +270,195 @@ static qboolean G_ParseMapCommandSection( mapRotationEntry_t *mre, char **text_p
/*
===============
-G_ParseMapRotation
+G_ParseNode
-Parse a map rotation section
+Parse a node
===============
*/
-static qboolean G_ParseMapRotation( mapRotation_t *mr, char **text_p )
+static qboolean G_ParseNode( node_t **node, char *token, char **text_p, qboolean conditional )
{
- char *token;
- qboolean mnSet = qfalse;
- mapRotationEntry_t *mre = NULL;
- mapRotationCondition_t *mrc;
-
- // read optional parameters
- while( 1 )
+ if( !Q_stricmp( token, "if" ) )
{
- token = COM_Parse( text_p );
+ condition_t *condition;
- if( !token )
- break;
+ (*node)->type = NT_CONDITION;
+ condition = &(*node)->u.condition;
- if( !Q_stricmp( token, "" ) )
+ token = COM_Parse( text_p );
+
+ if( !*token )
return qfalse;
- if( !Q_stricmp( token, "{" ) )
+ if( !Q_stricmp( token, "numClients" ) )
{
- if( !mnSet )
- {
- G_Printf( S_COLOR_RED "ERROR: map settings section with no name\n" );
+ condition->lhs = CV_NUMCLIENTS;
+
+ token = COM_Parse( text_p );
+
+ if( !*token )
return qfalse;
- }
- if( !G_ParseMapCommandSection( mre, text_p ) )
+ 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: failed to parse map command section\n" );
+ G_Printf( S_COLOR_RED "ERROR: invalid operator in expression: %s\n", token );
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 );
+ if( !*token )
return qfalse;
- }
- else
- mre->numConditions++;
- continue;
+ condition->numClients = atoi( token );
}
- else if( !Q_stricmp( token, "if" ) )
+ else if( !Q_stricmp( token, "lastWin" ) )
{
- token = COM_Parse( text_p );
+ condition->lhs = CV_LASTWIN;
- if( !token )
- break;
+ token = COM_Parse( text_p );
- mrc = &mre->conditions[ mre->numConditions ];
+ if( !*token )
+ return qfalse;
- if( !Q_stricmp( token, "numClients" ) )
+ if( !Q_stricmp( token, "aliens" ) )
+ condition->lastWin = TEAM_ALIENS;
+ else if( !Q_stricmp( token, "humans" ) )
+ condition->lastWin = TEAM_HUMANS;
+ else
{
- mrc->lhs = MCV_NUMCLIENTS;
+ 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 );
+ token = COM_Parse( text_p );
- if( !token )
- break;
+ if( !*token )
+ return qfalse;
- 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;
- }
+ condition->target = G_AllocateNode( );
+ *node = condition->target;
- token = COM_Parse( text_p );
+ 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( !token )
- break;
+ if( !Q_stricmp( token, "goto" ) )
+ (*node)->type = NT_GOTO;
+ else
+ (*node)->type = NT_RESUME;
+ label = &(*node)->u.label;
- mrc->numClients = atoi( token );
- }
- else if( !Q_stricmp( token, "lastWin" ) )
- {
- mrc->lhs = MCV_LASTWIN;
+ token = COM_Parse( text_p );
- token = COM_Parse( text_p );
+ if( !*token )
+ {
+ G_Printf( S_COLOR_RED "ERROR: goto or resume without label\n" );
+ return qfalse;
+ }
- if( !token )
- break;
+ Q_strncpyz( label->name, token, sizeof( label->name ) );
+ }
+ else if( *token == '#' || conditional )
+ {
+ label_t *label;
- 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;
- }
+ (*node)->type = ( conditional ) ? NT_GOTO : NT_LABEL;
+ label = &(*node)->u.label;
- token = COM_Parse( text_p );
+ Q_strncpyz( label->name, token, sizeof( label->name ) );
+ }
+ else
+ {
+ map_t *map;
- if( !token )
- break;
+ (*node)->type = NT_MAP;
+ map = &(*node)->u.map;
- mrc->unconditional = qfalse;
- Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) );
+ Q_strncpyz( map->name, token, sizeof( map->name ) );
+ map->postCommand[ 0 ] = '\0';
+ }
- 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++;
+ return qtrue;
+}
- 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 ) );
+/*
+===============
+G_ParseMapRotation
- token = COM_Parse( text_p );
+Parse a map rotation section
+===============
+*/
+static qboolean G_ParseMapRotation( mapRotation_t *mr, char **text_p )
+{
+ char *token;
+ node_t *node = NULL;
- if( !Q_stricmp( token, "{" ) )
- {
- while( 1 )
- {
- token = COM_Parse( text_p );
+ // read optional parameters
+ while( 1 )
+ {
+ token = COM_Parse( text_p );
- if( !token )
- break;
+ 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;
- }
+ if( !Q_stricmp( token, "" ) )
+ return qfalse;
- continue;
- }
- else if( !Q_stricmp( token, "*RANDOM*" ) )
+ if( !Q_stricmp( token, "{" ) )
{
- if( mr->numMaps == MAX_MAP_ROTATION_MAPS )
+ if( node == NULL )
{
- G_Printf( S_COLOR_RED "ERROR: maximum number of maps in one rotation (%d) reached\n",
- MAX_MAP_ROTATION_MAPS );
+ G_Printf( S_COLOR_RED "ERROR: map command section with no associated map\n" );
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
+ if( !G_ParseMapCommandSection( node, text_p ) )
{
- G_Printf( S_COLOR_RED "ERROR: *RANDOM* with no section\n" );
+ G_Printf( S_COLOR_RED "ERROR: failed to parse map command section\n" );
return qfalse;
}
continue;
}
else if( !Q_stricmp( token, "}" ) )
- return qtrue; //reached the end of this map rotation
-
- mre = &mr->maps[ mr->numMaps ];
+ {
+ // Reached the end of this map rotation
+ return qtrue;
+ }
- if( mr->numMaps == MAX_MAP_ROTATION_MAPS )
+ 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;
}
- else
- mr->numMaps++;
- Q_strncpyz( mre->name, token, sizeof( mre->name ) );
- mnSet = qtrue;
+ node = G_AllocateNode( );
+ mr->nodes[ mr->numNodes++ ] = node;
+
+ if( !G_ParseNode( &node, token, text_p, qfalse ) )
+ return qfalse;
}
return qfalse;
@@ -427,7 +474,7 @@ Load the map rotations from a map rotation file
static qboolean G_ParseMapRotationFile( const char *fileName )
{
char *text_p;
- int i, j, k;
+ int i, j;
int len;
char *token;
char text[ 20000 ];
@@ -460,7 +507,7 @@ static qboolean G_ParseMapRotationFile( const char *fileName )
{
token = COM_Parse( &text_p );
- if( !token )
+ if( !*token )
break;
if( !Q_stricmp( token, "" ) )
@@ -471,13 +518,17 @@ static qboolean G_ParseMapRotationFile( const char *fileName )
if( mrNameSet )
{
//check for name space clashes
- for( i = 0; i < mapRotations.numRotations; i++ )
+ if( G_RotationExists( mrName ) )
{
- 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;
- }
+ 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 );
@@ -488,23 +539,16 @@ static qboolean G_ParseMapRotationFile( const char *fileName )
return qfalse;
}
+ mapRotations.numRotations++;
+
//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" );
+ G_Printf( S_COLOR_RED "ERROR: unnamed map rotation\n" );
return qfalse;
}
}
@@ -523,29 +567,46 @@ static qboolean G_ParseMapRotationFile( const char *fileName )
for( i = 0; i < mapRotations.numRotations; i++ )
{
- for( j = 0; j < mapRotations.rotations[ i ].numMaps; j++ )
+ mapRotation_t *mr = &mapRotations.rotations[ i ];
+ int mapCount = 0;
+
+ for( j = 0; j < mr->numNodes; 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;
- }
+ node_t *node = mr->nodes[ j ];
- for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numConditions; k++ )
+ if( node->type == NT_MAP )
{
- if( !G_MapExists( mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest ) &&
- !G_RotationExists( mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest ) )
+ mapCount++;
+ if( !G_MapExists( node->u.map.name ) )
{
- G_Printf( S_COLOR_RED "ERROR: conditional destination \"%s\" doesn't exist\n",
- mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest );
+ 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;
}
}
@@ -554,6 +615,19 @@ static qboolean G_ParseMapRotationFile( const char *fileName )
/*
===============
+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
@@ -561,55 +635,167 @@ Print the parsed map rotations
*/
void G_PrintRotations( void )
{
- int i, j, k;
+ int i, j;
+ int size = sizeof( mapRotations );
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 );
+ mapRotation_t *mr = &mapRotations.rotations[ i ];
- for( j = 0; j < mapRotations.rotations[ i ].numMaps; j++ )
+ G_Printf( "rotation: %s\n{\n", mr->name );
+
+ size += mr->numNodes * sizeof( node_t );
+
+ for( j = 0; j < mr->numNodes; j++ )
{
- G_Printf( " map: %s\n {\n", mapRotations.rotations[ i ].maps[ j ].name );
+ node_t *node = mr->nodes[ j ];
+ int indentation = 0;
- for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numCmds; k++ )
+ while( node->type == NT_CONDITION )
{
- G_Printf( " command: %s\n",
- mapRotations.rotations[ i ].maps[ j ].postCmds[ k ] );
+ G_PrintSpaces( indentation );
+ G_Printf( " condition\n" );
+ node = node->u.condition.target;
+
+ size += sizeof( node_t );
+
+ indentation += 2;
}
- G_Printf( " }\n" );
+ G_PrintSpaces( indentation );
- for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numConditions; k++ )
+ switch( node->type )
{
- G_Printf( " conditional: %s\n",
- mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest );
- }
+ 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", sizeof( mapRotations ) );
+ G_Printf( "Total memory used: %d bytes\n", size );
+}
+
+/*
+===============
+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_GetCurrentMapArray
+G_RotationNameByIndex
-Fill a static array with the current map of each rotation
+Returns the name of a rotation by its index
===============
*/
-static int *G_GetCurrentMapArray( void )
+static char *G_RotationNameByIndex( int index )
{
- static int currentMap[ MAX_MAP_ROTATIONS ];
+ 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_currentMap.string, sizeof( text ) );
+ Q_strncpyz( text, g_mapRotationNodes.string, sizeof( text ) );
text_p = text;
@@ -617,166 +803,138 @@ static int *G_GetCurrentMapArray( void )
{
token = COM_Parse( &text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "" ) )
+ if( !*token )
break;
- currentMap[ i++ ] = atoi( token );
+ currentNode[ i++ ] = atoi( token );
}
- return currentMap;
+ return currentNode;
}
/*
===============
-G_SetCurrentMap
+G_SetCurrentNodeByIndex
Set the current map in some rotation
===============
*/
-static void G_SetCurrentMap( int currentMap, int rotation )
+static void G_SetCurrentNodeByIndex( int currentNode, int rotation )
{
- char text[ MAX_MAP_ROTATIONS * 2 ] = { 0 };
- int *p = G_GetCurrentMapArray( );
+ char text[ MAX_MAP_ROTATIONS * 4 ] = { 0 };
+ int *p = G_CurrentNodeIndexArray( );
int i;
- p[ rotation ] = currentMap;
+ p[ rotation ] = currentNode;
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 );
+ trap_Cvar_Set( "g_mapRotationNodes", text );
+ trap_Cvar_Update( &g_mapRotationNodes );
}
/*
===============
-G_GetCurrentMap
+G_CurrentNodeIndex
-Return the current map in some rotation
+Return the current node index in some rotation
===============
*/
-int G_GetCurrentMap( int rotation )
+static int G_CurrentNodeIndex( int rotation )
{
- int *p = G_GetCurrentMapArray( );
+ int *p = G_CurrentNodeIndexArray( );
return p[ rotation ];
}
-static qboolean G_GetRandomMap( char *name, int size, int rotation, int map );
-
/*
===============
-G_IssueMapChange
+G_NodeByIndex
-Send commands to the server to actually change the map
+Return a node in a rotation by its index
===============
*/
-static void G_IssueMapChange( int rotation )
+static node_t *G_NodeByIndex( int index, int rotation )
{
- int i;
- int map = G_GetCurrentMap( rotation );
- char cmd[ MAX_TOKEN_CHARS ];
- char newmap[ MAX_CVAR_VALUE_STRING ];
- char currentmap[ MAX_CVAR_VALUE_STRING ];
-
- Q_strncpyz( newmap, mapRotations.rotations[rotation].maps[map].name, sizeof( newmap ));
- trap_Cvar_VariableStringBuffer( "mapname", currentmap, sizeof( currentmap ));
+ if( rotation >= 0 && rotation < mapRotations.numRotations &&
+ index >= 0 && index < mapRotations.rotations[ rotation ].numNodes )
+ return mapRotations.rotations[ rotation ].nodes[ index ];
- if (!Q_stricmp( newmap, "*VOTE*") )
- {
- fileHandle_t f;
+ return NULL;
+}
- 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_IssueMapChange
- 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;
- }
- }
+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 ] &&
- mapRotations.rotations[ rotation ].maps[ map ].layouts[ 0 ] )
+ // allow a manually defined g_nextLayout setting to override the maprotation
+ if( !g_nextLayout.string[ 0 ] && map->layouts[ 0 ] )
{
- trap_Cvar_Set( "g_layouts",
- mapRotations.rotations[ rotation ].maps[ map ].layouts );
+ trap_Cvar_Set( "g_nextLayout", map->layouts );
}
- if( !Q_stricmp( currentmap, newmap ) )
- {
- trap_SendConsoleCommand( EXEC_APPEND, "map_restart\n" );
- }
- else
- {
- trap_SendConsoleCommand( EXEC_APPEND, va( "map %s\n", newmap ) );
- }
+ G_MapConfigs( map->name );
- // load up map defaults if g_mapConfigs is set
- G_MapConfigs( newmap );
+ trap_SendConsoleCommand( EXEC_APPEND, va( "map \"%s\"\n", 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 );
- }
+ if( strlen( map->postCommand ) > 0 )
+ trap_SendConsoleCommand( EXEC_APPEND, map->postCommand );
}
/*
===============
-G_ResolveConditionDestination
+G_GotoLabel
-Resolve the destination of some condition
+Resolve the label of some condition
===============
*/
-static mapConditionType_t G_ResolveConditionDestination( int *n, char *name )
+static qboolean G_GotoLabel( int rotation, int nodeIndex, char *name,
+ qboolean reset_index, int depth )
{
- int i;
+ node_t *node;
+ int i;
+
+ // Search the rotation names...
+ if( G_StartMapRotation( name, qtrue, qtrue, reset_index, depth ) )
+ return qtrue;
- //search the current rotation first...
- for( i = 0; i < mapRotations.rotations[ g_currentMapRotation.integer ].numMaps; i++ )
+ // ...then try labels in the rotation
+ for( i = 0; i < mapRotations.rotations[ rotation ].numNodes; i++ )
{
- if( !Q_stricmp( mapRotations.rotations[ g_currentMapRotation.integer ].maps[ i ].name, name ) )
+ node = mapRotations.rotations[ rotation ].nodes[ i ];
+
+ if( node->type == NT_LABEL && !Q_stricmp( node->u.label.name, name ) )
{
- *n = i;
- return MCT_MAP;
+ G_SetCurrentNodeByIndex( G_NodeIndexAfter( i, rotation ), rotation );
+ G_AdvanceMapRotation( depth );
+ return qtrue;
}
}
- //...then search the rotation names
- for( i = 0; i < mapRotations.numRotations; i++ )
+ // finally check for a map by name
+ for( i = 0; i < mapRotations.rotations[ rotation ].numNodes; i++ )
{
- if( !Q_stricmp( mapRotations.rotations[ i ].name, name ) )
+ nodeIndex = G_NodeIndexAfter( nodeIndex, rotation );
+ node = mapRotations.rotations[ rotation ].nodes[ nodeIndex ];
+
+ if( node->type == NT_MAP && !Q_stricmp( node->u.map.name, name ) )
{
- *n = i;
- return MCT_ROTATION;
+ G_SetCurrentNodeByIndex( nodeIndex, rotation );
+ G_AdvanceMapRotation( depth );
+ return qtrue;
}
}
- return MCT_ERR;
+ return qfalse;
}
/*
@@ -786,133 +944,231 @@ G_EvaluateMapCondition
Evaluate a map condition
===============
*/
-static qboolean G_EvaluateMapCondition( mapRotationCondition_t *mrc )
+static qboolean G_EvaluateMapCondition( condition_t **condition )
{
- switch( mrc->lhs )
+ qboolean result = qfalse;
+ condition_t *localCondition = *condition;
+
+ switch( localCondition->lhs )
{
- case MCV_RANDOM:
- return rand( ) & 1;
+ case CV_RANDOM:
+ result = rand( ) / ( RAND_MAX / 2 + 1 );
break;
- case MCV_NUMCLIENTS:
- switch( mrc->op )
+ case CV_NUMCLIENTS:
+ switch( localCondition->op )
{
- case MCO_LT:
- return level.numConnectedClients < mrc->numClients;
+ case CO_LT:
+ result = level.numConnectedClients < localCondition->numClients;
break;
- case MCO_GT:
- return level.numConnectedClients > mrc->numClients;
+ case CO_GT:
+ result = level.numConnectedClients > localCondition->numClients;
break;
- case MCO_EQ:
- return level.numConnectedClients == mrc->numClients;
+ case CO_EQ:
+ result = level.numConnectedClients == localCondition->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;
+ case CV_LASTWIN:
+ result = level.lastWin == localCondition->lastWin;
break;
default:
- case MCV_ERR:
- G_Printf( S_COLOR_RED "ERROR: malformed map switch condition\n" );
+ case CV_ERR:
+ G_Printf( S_COLOR_RED "ERROR: malformed map switch localCondition\n" );
break;
}
- return qfalse;
+ if( localCondition->target->type == NT_CONDITION )
+ {
+ *condition = &localCondition->target->u.condition;
+
+ return result && G_EvaluateMapCondition( condition );
+ }
+
+ return result;
}
/*
===============
-G_AdvanceMapRotation
-
-Increment the current map rotation
+G_NodeIndexAfter
===============
*/
-qboolean G_AdvanceMapRotation( void )
+static int G_NodeIndexAfter( int currentNode, int rotation )
{
- 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;
+ mapRotation_t *mr = &mapRotations.rotations[ rotation ];
+
+ return ( currentNode + 1 ) % mr->numNodes;
+}
+
+/*
+===============
+G_StepMapRotation
- currentMap = G_GetCurrentMap( currentRotation );
+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;
- mr = &mapRotations.rotations[ currentRotation ];
- mre = &mr->maps[ currentMap ];
- nextMap = ( currentMap + 1 ) % mr->numMaps;
+ node = G_NodeByIndex( nodeIndex, rotation );
+ depth++;
- for( i = 0; i < mre->numConditions; i++ )
+ // guard against inifinite loop in conditional code
+ if( depth > 32 && node->type != NT_MAP )
{
- mrc = &mre->conditions[ i ];
+ if( depth > 64 )
+ {
+ G_Printf( S_COLOR_RED "ERROR: infinite loop protection stopped at map rotation %s\n",
+ G_RotationNameByIndex( rotation ) );
+ return qfalse;
+ }
- if( mrc->unconditional || G_EvaluateMapCondition( mrc ) )
+ 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 )
{
- mct = G_ResolveConditionDestination( &n, mrc->dest );
+ case NT_CONDITION:
+ condition = &node->u.condition;
- switch( mct )
- {
- case MCT_MAP:
- nextMap = n;
- break;
+ if( G_EvaluateMapCondition( &condition ) )
+ {
+ node = condition->target;
+ step = qtrue;
+ continue;
+ }
+ 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;
+ 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;
- default:
- case MCT_ERR:
- G_Printf( S_COLOR_YELLOW "WARNING: map switch destination could not be resolved: %s\n",
- mrc->dest );
- 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;
}
}
- G_SetCurrentMap( nextMap, currentRotation );
- G_IssueMapChange( currentRotation );
-
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 changeMap )
+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( changeMap )
- G_IssueMapChange( i );
+ if( advance )
+ {
+ if( reset_index )
+ G_SetCurrentNodeByIndex( 0, i );
+
+ G_AdvanceMapRotation( depth );
+ }
+
break;
}
}
@@ -952,14 +1208,14 @@ qboolean G_MapRotationActive( void )
===============
G_InitMapRotations
-Load and intialise the map rotations
+Load and initialise the map rotations
===============
*/
void G_InitMapRotations( void )
{
- const char *fileName = "maprotation.cfg";
+ const char *fileName = "maprotation.cfg";
- //load the file if it exists
+ // Load the file if it exists
if( trap_FS_FOpenFile( fileName, NULL, FS_READ ) )
{
if( !G_ParseMapRotationFile( fileName ) )
@@ -972,7 +1228,7 @@ void G_InitMapRotations( void )
{
if( g_initialMapRotation.string[ 0 ] != 0 )
{
- G_StartMapRotation( g_initialMapRotation.string, qfalse );
+ G_StartMapRotation( g_initialMapRotation.string, qfalse, qtrue, qfalse, 0 );
trap_Cvar_Set( "g_initialMapRotation", "" );
trap_Cvar_Update( &g_initialMapRotation );
@@ -980,347 +1236,41 @@ void G_InitMapRotations( void )
}
}
-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
+G_FreeNode
+
+Free up memory used by a node
===============
*/
-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 )
+void G_FreeNode( node_t *node )
{
- int winner, winvotes, totalvotes;
- int nonvotes;
+ if( node->type == NT_CONDITION )
+ G_FreeNode( node->u.condition.target );
- 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;
+ BG_Free( node );
}
-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;
-}
+/*
+===============
+G_ShutdownMapRotations
-static void G_IntermissionMapVoteMessageReal( gentity_t *ent, int winner, int winvotes, int totalvotes, int *ranklist )
+Free up memory used by map rotations
+===============
+*/
+void G_ShutdownMapRotations( void )
{
- 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 );
- }
+ int i, j;
- 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++ )
+ for( i = 0; i < mapRotations.numRotations; i++ )
{
- if( !G_MapExists( rotationVoteList[ i ] ) )
- continue;
-
- if( i == selection )
- color = "^5";
- else if( i == index )
- color = "^1";
- else
- color = "^7";
+ mapRotation_t *mr = &mapRotations.rotations[ i ];
- switch( ranklist[ i ] )
+ for( j = 0; j < mr->numNodes; j++ )
{
- 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 );
+ node_t *node = mr->nodes[ j ];
- 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++;
+ G_FreeNode( node );
}
}
-
- if( nummaps == 0 ) {
- return qfalse;
- }
-
- randompick = (int)( random() * nummaps );
-
- Q_strncpyz( name, mre->conditions[ maplist[ randompick ] ].dest, size );
-
- return qtrue;
}