summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author/dev/humancontroller <devhc@example.com>2017-02-06 19:19:33 +0100
committer/dev/humancontroller <devhc@example.com>2017-03-09 13:51:16 +0100
commitec2f1dc2f6444d33f3d112be486e9d7675358bd6 (patch)
tree57f10fa18f031cbb0531eecfc9a1417988cb6cc0
parentff4fd573a91bc027c935f32fab107eca66659141 (diff)
implement layout arithmetic and the addlayout command
layout arithmetic features: layout filtering by buildable team or type, loading of union of filtered layouts, saving filtered layouts addlayout: an admin command to place layout elements into the game
-rw-r--r--src/game/g_admin.c28
-rw-r--r--src/game/g_admin.h1
-rw-r--r--src/game/g_buildable.c144
-rw-r--r--src/game/g_local.h2
-rw-r--r--src/game/g_main.c2
-rw-r--r--src/game/g_svcmds.c10
6 files changed, 169 insertions, 18 deletions
diff --git a/src/game/g_admin.c b/src/game/g_admin.c
index 62a45572..18f317d4 100644
--- a/src/game/g_admin.c
+++ b/src/game/g_admin.c
@@ -38,6 +38,15 @@ static char g_bfb[ 32000 ];
// note: list ordered alphabetically
g_admin_cmd_t g_admin_cmds[ ] =
{
+ {"addlayout", G_admin_addlayout, qfalse, "addlayout",
+ "place layout elements into the game. the elements are specified by a "
+ "union of filtered layouts. the syntax is demonstrated by an example: "
+ "^5westside|reactor,telenode+sewers|alien^7 will place only the "
+ "reactor and telenodes from the westside layout, and also all alien "
+ "layout elements from the sewers layout",
+ "[^3layoutelements^7]"
+ },
+
{"adjustban", G_admin_adjustban, qfalse, "ban",
"change the IP address mask, duration or reason of a ban. mask is "
"prefixed with '/'. duration is specified as numbers followed by units "
@@ -1858,6 +1867,25 @@ qboolean G_admin_unban( gentity_t *ent )
return qtrue;
}
+qboolean G_admin_addlayout( gentity_t *ent )
+{
+ char layout[ MAX_QPATH ];
+
+ if( trap_Argc( ) != 2 )
+ {
+ ADMP( "^3addlayout: ^7usage: addlayout <layoutelements>\n" );
+ return qfalse;
+ }
+
+ trap_Argv( 1, layout, sizeof( layout ) );
+
+ G_LayoutLoad( layout );
+
+ AP( va( "print \"^3addlayout: ^7some layout elements have been placed by %s\n\"",
+ ent ? ent->client->pers.netname : "console" ) );
+ return qtrue;
+}
+
qboolean G_admin_adjustban( gentity_t *ent )
{
int bnum;
diff --git a/src/game/g_admin.h b/src/game/g_admin.h
index de51562b..fd9f6b26 100644
--- a/src/game/g_admin.h
+++ b/src/game/g_admin.h
@@ -157,6 +157,7 @@ void G_admin_authlog( gentity_t *ent );
qboolean G_admin_time( gentity_t *ent );
qboolean G_admin_setlevel( gentity_t *ent );
qboolean G_admin_kick( gentity_t *ent );
+qboolean G_admin_addlayout( gentity_t *ent );
qboolean G_admin_adjustban( gentity_t *ent );
qboolean G_admin_ban( gentity_t *ent );
qboolean G_admin_unban( gentity_t *ent );
diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c
index a5e64d36..dd984d33 100644
--- a/src/game/g_buildable.c
+++ b/src/game/g_buildable.c
@@ -3943,13 +3943,74 @@ void G_SpawnBuildable( gentity_t *ent, buildable_t buildable )
ent->think = G_SpawnBuildableThink;
}
+void G_ParseCSVBuildablePlusList( const char *string, int *buildables, int buildablesSize )
+{
+ char buffer[ MAX_STRING_CHARS ];
+ int i = 0;
+ char *p, *q;
+ qboolean EOS = qfalse;
+
+ Q_strncpyz( buffer, string, MAX_STRING_CHARS );
+
+ p = q = buffer;
+
+ while( *p != '\0' && i < buildablesSize - 1 )
+ {
+ //skip to first , or EOS
+ while( *p != ',' && *p != '\0' )
+ p++;
+
+ if( *p == '\0' )
+ EOS = qtrue;
+
+ *p = '\0';
+
+ //strip leading whitespace
+ while( *q == ' ' )
+ q++;
+
+ if( !Q_stricmp( q, "alien" ) )
+ {
+ buildable_t b;
+ for( b = BA_A_SPAWN; b <= BA_A_HIVE && i < buildablesSize - 1; ++b )
+ buildables[ i++ ] = b;
+ }
+ else if( !Q_stricmp( q, "human" ) )
+ {
+ buildable_t b;
+ for( b = BA_H_SPAWN; b <= BA_H_REPEATER && i < buildablesSize - 1; ++b )
+ buildables[ i++ ] = b;
+ }
+ else
+ {
+ buildables[ i ] = BG_BuildableByName( q )->number;
+ if( buildables[ i ] == BA_NONE )
+ Com_Printf( S_COLOR_YELLOW "WARNING: unknown buildable or special identifier %s\n", q );
+ else
+ i++;
+ }
+
+ if( !EOS )
+ {
+ p++;
+ q = p;
+ }
+ else
+ break;
+ }
+
+ buildables[ i ] = BA_NONE;
+}
+
/*
============
G_LayoutSave
============
*/
-void G_LayoutSave( char *name )
+void G_LayoutSave( char *lstr )
{
+ char *lstrPipePtr;
+ qboolean bAllowed[ BA_NUM_BUILDABLES ];
char map[ MAX_QPATH ];
char fileName[ MAX_OSPATH ];
fileHandle_t f;
@@ -3964,7 +4025,26 @@ void G_LayoutSave( char *name )
G_Printf( "LayoutSave( ): no map is loaded\n" );
return;
}
- Com_sprintf( fileName, sizeof( fileName ), "layouts/%s/%s.dat", map, name );
+
+ if( ( lstrPipePtr = strchr( lstr, '|' ) ) )
+ {
+ int bList[ BA_NUM_BUILDABLES ];
+ *lstrPipePtr = '\0';
+ G_ParseCSVBuildablePlusList( lstrPipePtr + 1, &bList[ 0 ], sizeof( bList ) / sizeof( bList[ 0 ] ) );
+ memset( bAllowed, 0, sizeof( bAllowed ) );
+ for( i = 0; bList[ i ] != BA_NONE; i++ )
+ bAllowed[ bList[ i ] ] = qtrue;
+ }
+ else
+ {
+ for( i = 0; i < BA_NUM_BUILDABLES; i++ )
+ bAllowed[ i ] = qtrue;
+ }
+
+ Com_sprintf( fileName, sizeof( fileName ), "layouts/%s/%s.dat", map, lstr );
+
+ if( lstrPipePtr )
+ *lstrPipePtr = '|';
len = trap_FS_FOpenFile( fileName, &f, FS_WRITE );
if( len < 0 )
@@ -3981,6 +4061,9 @@ void G_LayoutSave( char *name )
if( ent->s.eType != ET_BUILDABLE )
continue;
+ if( !bAllowed[ ent->s.modelindex ] )
+ continue;
+
s = va( "%s %f %f %f %f %f %f %f %f %f %f %f %f\n",
BG_Buildable( ent->s.modelindex )->name,
ent->r.currentOrigin[ 0 ],
@@ -4093,7 +4176,7 @@ void G_LayoutSelect( void )
if( !*s )
break;
- if( G_LayoutExists( map, s ) )
+ if( strchr( s, '+' ) || strchr( s, '|' ) || G_LayoutExists( map, s ) )
{
Q_strcat( layouts, sizeof( layouts ), s );
Q_strcat( layouts, sizeof( layouts ), " " );
@@ -4148,13 +4231,12 @@ static void G_LayoutBuildItem( buildable_t buildable, vec3_t origin,
/*
============
G_LayoutLoad
-
-load the layout .dat file indicated by level.layout and spawn buildables
-as if a builder was creating them
============
*/
-void G_LayoutLoad( void )
+void G_LayoutLoad( char *lstr )
{
+ char *lstrPlusPtr, *lstrPipePtr;
+ qboolean bAllowed[ BA_NUM_BUILDABLES ];
fileHandle_t f;
int len;
char *layout, *layoutHead;
@@ -4166,29 +4248,50 @@ void G_LayoutLoad( void )
vec3_t origin2 = { 0.0f, 0.0f, 0.0f };
vec3_t angles2 = { 0.0f, 0.0f, 0.0f };
char line[ MAX_STRING_CHARS ];
- int i = 0;
+ int i;
- if( !level.layout[ 0 ] || !Q_stricmp( level.layout, "*BUILTIN*" ) )
+ if( !lstr[ 0 ] || !Q_stricmp( lstr, "*BUILTIN*" ) )
return;
+ addAnotherLayout:
+ lstrPlusPtr = strchr( lstr, '+' );
+ if( lstrPlusPtr )
+ *lstrPlusPtr = '\0';
+
+ if( ( lstrPipePtr = strchr( lstr, '|' ) ) )
+ {
+ int bList[ BA_NUM_BUILDABLES ];
+ *lstrPipePtr = '\0';
+ G_ParseCSVBuildablePlusList( lstrPipePtr + 1, &bList[ 0 ], sizeof( bList ) / sizeof( bList[ 0 ] ) );
+ memset( bAllowed, 0, sizeof( bAllowed ) );
+ for( i = 0; bList[ i ] != BA_NONE; i++ )
+ bAllowed[ bList[ i ] ] = qtrue;
+ }
+ else
+ {
+ for( i = 0; i < BA_NUM_BUILDABLES; i++ )
+ bAllowed[ i ] = qtrue;
+ }
+
trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) );
- len = trap_FS_FOpenFile( va( "layouts/%s/%s.dat", map, level.layout ),
+ len = trap_FS_FOpenFile( va( "layouts/%s/%s.dat", map, lstr ),
&f, FS_READ );
if( len < 0 )
{
- G_Printf( "ERROR: layout %s could not be opened\n", level.layout );
- return;
+ G_Printf( "ERROR: layout %s could not be opened\n", lstr );
+ goto restoreLstr;
}
layoutHead = layout = BG_Alloc( len + 1 );
trap_FS_Read( layout, len, f );
layout[ len ] = '\0';
trap_FS_FCloseFile( f );
+ i = 0;
while( *layout )
{
if( i >= sizeof( line ) - 1 )
{
G_Printf( S_COLOR_RED "ERROR: line overflow in %s before \"%s\"\n",
- va( "layouts/%s/%s.dat", map, level.layout ), line );
+ va( "layouts/%s/%s.dat", map, lstr ), line );
break;
}
line[ i++ ] = *layout;
@@ -4208,11 +4311,24 @@ void G_LayoutLoad( void )
G_Printf( S_COLOR_YELLOW "WARNING: bad buildable name (%s) in layout."
" skipping\n", buildName );
else
- G_LayoutBuildItem( buildable, origin, angles, origin2, angles2 );
+ {
+ if( bAllowed[ buildable ] )
+ G_LayoutBuildItem( buildable, origin, angles, origin2, angles2 );
+ }
}
layout++;
}
BG_Free( layoutHead );
+
+ restoreLstr:
+ if( lstrPipePtr )
+ *lstrPipePtr = '|';
+ if( lstrPlusPtr )
+ {
+ *lstrPlusPtr = '+';
+ lstr = lstrPlusPtr + 1;
+ goto addAnotherLayout;
+ }
}
/*
diff --git a/src/game/g_local.h b/src/game/g_local.h
index 1852c783..36dfff88 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -818,7 +818,7 @@ void FinishSpawningBuildable( gentity_t *ent );
void G_LayoutSave( char *name );
int G_LayoutList( const char *map, char *list, int len );
void G_LayoutSelect( void );
-void G_LayoutLoad( void );
+void G_LayoutLoad( char *lstr );
void G_BaseSelfDestruct( team_t team );
int G_NextQueueTime( int queuedBP, int totalBP, int queueBaseRate );
void G_QueueBuildPoints( gentity_t *self );
diff --git a/src/game/g_main.c b/src/game/g_main.c
index d91b685d..937c578f 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -652,7 +652,7 @@ void G_InitGame( int levelTime, int randomSeed, int restart )
G_SpawnEntitiesFromString( );
// load up a custom building layout if there is one
- G_LayoutLoad( );
+ G_LayoutLoad( level.layout );
// the map might disable some things
BG_InitAllowedGameElements( );
diff --git a/src/game/g_svcmds.c b/src/game/g_svcmds.c
index c2d61ec3..b5eeda7b 100644
--- a/src/game/g_svcmds.c
+++ b/src/game/g_svcmds.c
@@ -212,6 +212,7 @@ static void Svcmd_LayoutSave_f( void )
char str2[ MAX_QPATH - 4 ];
char *s;
int i = 0;
+ qboolean pipeEncountered = qfalse;
if( trap_Argc( ) != 2 )
{
@@ -224,12 +225,17 @@ static void Svcmd_LayoutSave_f( void )
s = &str[ 0 ];
while( *s && i < sizeof( str2 ) - 1 )
{
- if( isalnum( *s ) || *s == '-' || *s == '_' )
+ if( isalnum( *s ) || *s == '-' || *s == '_' ||
+ pipeEncountered || *s == '|' )
+ {
str2[ i++ ] = *s;
+ if( *s == '|' )
+ pipeEncountered = qtrue;
+ }
s++;
}
- if( i == 0 )
+ if( i == 0 || str2[ 0 ] == '|' )
{
G_Printf( "layoutsave: invalid name \"%s\"\n", str );
return;