Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

g_spawn.c (16555B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 
     24 #include "g_local.h"
     25 
     26 qboolean	G_SpawnString( const char *key, const char *defaultString, char **out ) {
     27 	int		i;
     28 
     29 	if ( !level.spawning ) {
     30 		*out = (char *)defaultString;
     31 //		G_Error( "G_SpawnString() called while not spawning" );
     32 	}
     33 
     34 	for ( i = 0 ; i < level.numSpawnVars ; i++ ) {
     35 		if ( !Q_stricmp( key, level.spawnVars[i][0] ) ) {
     36 			*out = level.spawnVars[i][1];
     37 			return qtrue;
     38 		}
     39 	}
     40 
     41 	*out = (char *)defaultString;
     42 	return qfalse;
     43 }
     44 
     45 qboolean	G_SpawnFloat( const char *key, const char *defaultString, float *out ) {
     46 	char		*s;
     47 	qboolean	present;
     48 
     49 	present = G_SpawnString( key, defaultString, &s );
     50 	*out = atof( s );
     51 	return present;
     52 }
     53 
     54 qboolean	G_SpawnInt( const char *key, const char *defaultString, int *out ) {
     55 	char		*s;
     56 	qboolean	present;
     57 
     58 	present = G_SpawnString( key, defaultString, &s );
     59 	*out = atoi( s );
     60 	return present;
     61 }
     62 
     63 qboolean	G_SpawnVector( const char *key, const char *defaultString, float *out ) {
     64 	char		*s;
     65 	qboolean	present;
     66 
     67 	present = G_SpawnString( key, defaultString, &s );
     68 	sscanf( s, "%f %f %f", &out[0], &out[1], &out[2] );
     69 	return present;
     70 }
     71 
     72 
     73 
     74 //
     75 // fields are needed for spawning from the entity string
     76 //
     77 typedef enum {
     78 	F_INT, 
     79 	F_FLOAT,
     80 	F_LSTRING,			// string on disk, pointer in memory, TAG_LEVEL
     81 	F_GSTRING,			// string on disk, pointer in memory, TAG_GAME
     82 	F_VECTOR,
     83 	F_ANGLEHACK,
     84 	F_ENTITY,			// index on disk, pointer in memory
     85 	F_ITEM,				// index on disk, pointer in memory
     86 	F_CLIENT,			// index on disk, pointer in memory
     87 	F_IGNORE
     88 } fieldtype_t;
     89 
     90 typedef struct
     91 {
     92 	char	*name;
     93 	int		ofs;
     94 	fieldtype_t	type;
     95 	int		flags;
     96 } field_t;
     97 
     98 field_t fields[] = {
     99 	{"classname", FOFS(classname), F_LSTRING},
    100 	{"origin", FOFS(s.origin), F_VECTOR},
    101 	{"model", FOFS(model), F_LSTRING},
    102 	{"model2", FOFS(model2), F_LSTRING},
    103 	{"spawnflags", FOFS(spawnflags), F_INT},
    104 	{"speed", FOFS(speed), F_FLOAT},
    105 	{"target", FOFS(target), F_LSTRING},
    106 	{"targetname", FOFS(targetname), F_LSTRING},
    107 	{"message", FOFS(message), F_LSTRING},
    108 	{"team", FOFS(team), F_LSTRING},
    109 	{"wait", FOFS(wait), F_FLOAT},
    110 	{"random", FOFS(random), F_FLOAT},
    111 	{"count", FOFS(count), F_INT},
    112 	{"health", FOFS(health), F_INT},
    113 	{"light", 0, F_IGNORE},
    114 	{"dmg", FOFS(damage), F_INT},
    115 	{"angles", FOFS(s.angles), F_VECTOR},
    116 	{"angle", FOFS(s.angles), F_ANGLEHACK},
    117 	{"targetShaderName", FOFS(targetShaderName), F_LSTRING},
    118 	{"targetShaderNewName", FOFS(targetShaderNewName), F_LSTRING},
    119 
    120 	{NULL}
    121 };
    122 
    123 
    124 typedef struct {
    125 	char	*name;
    126 	void	(*spawn)(gentity_t *ent);
    127 } spawn_t;
    128 
    129 void SP_info_player_start (gentity_t *ent);
    130 void SP_info_player_deathmatch (gentity_t *ent);
    131 void SP_info_player_intermission (gentity_t *ent);
    132 void SP_info_firstplace(gentity_t *ent);
    133 void SP_info_secondplace(gentity_t *ent);
    134 void SP_info_thirdplace(gentity_t *ent);
    135 void SP_info_podium(gentity_t *ent);
    136 
    137 void SP_func_plat (gentity_t *ent);
    138 void SP_func_static (gentity_t *ent);
    139 void SP_func_rotating (gentity_t *ent);
    140 void SP_func_bobbing (gentity_t *ent);
    141 void SP_func_pendulum( gentity_t *ent );
    142 void SP_func_button (gentity_t *ent);
    143 void SP_func_door (gentity_t *ent);
    144 void SP_func_train (gentity_t *ent);
    145 void SP_func_timer (gentity_t *self);
    146 
    147 void SP_trigger_always (gentity_t *ent);
    148 void SP_trigger_multiple (gentity_t *ent);
    149 void SP_trigger_push (gentity_t *ent);
    150 void SP_trigger_teleport (gentity_t *ent);
    151 void SP_trigger_hurt (gentity_t *ent);
    152 
    153 void SP_target_remove_powerups( gentity_t *ent );
    154 void SP_target_give (gentity_t *ent);
    155 void SP_target_delay (gentity_t *ent);
    156 void SP_target_speaker (gentity_t *ent);
    157 void SP_target_print (gentity_t *ent);
    158 void SP_target_laser (gentity_t *self);
    159 void SP_target_character (gentity_t *ent);
    160 void SP_target_score( gentity_t *ent );
    161 void SP_target_teleporter( gentity_t *ent );
    162 void SP_target_relay (gentity_t *ent);
    163 void SP_target_kill (gentity_t *ent);
    164 void SP_target_position (gentity_t *ent);
    165 void SP_target_location (gentity_t *ent);
    166 void SP_target_push (gentity_t *ent);
    167 
    168 void SP_light (gentity_t *self);
    169 void SP_info_null (gentity_t *self);
    170 void SP_info_notnull (gentity_t *self);
    171 void SP_info_camp (gentity_t *self);
    172 void SP_path_corner (gentity_t *self);
    173 
    174 void SP_misc_teleporter_dest (gentity_t *self);
    175 void SP_misc_model(gentity_t *ent);
    176 void SP_misc_portal_camera(gentity_t *ent);
    177 void SP_misc_portal_surface(gentity_t *ent);
    178 
    179 void SP_shooter_rocket( gentity_t *ent );
    180 void SP_shooter_plasma( gentity_t *ent );
    181 void SP_shooter_grenade( gentity_t *ent );
    182 
    183 void SP_team_CTF_redplayer( gentity_t *ent );
    184 void SP_team_CTF_blueplayer( gentity_t *ent );
    185 
    186 void SP_team_CTF_redspawn( gentity_t *ent );
    187 void SP_team_CTF_bluespawn( gentity_t *ent );
    188 
    189 #ifdef MISSIONPACK
    190 void SP_team_blueobelisk( gentity_t *ent );
    191 void SP_team_redobelisk( gentity_t *ent );
    192 void SP_team_neutralobelisk( gentity_t *ent );
    193 #endif
    194 void SP_item_botroam( gentity_t *ent ) {};
    195 
    196 spawn_t	spawns[] = {
    197 	// info entities don't do anything at all, but provide positional
    198 	// information for things controlled by other processes
    199 	{"info_player_start", SP_info_player_start},
    200 	{"info_player_deathmatch", SP_info_player_deathmatch},
    201 	{"info_player_intermission", SP_info_player_intermission},
    202 	{"info_null", SP_info_null},
    203 	{"info_notnull", SP_info_notnull},		// use target_position instead
    204 	{"info_camp", SP_info_camp},
    205 
    206 	{"func_plat", SP_func_plat},
    207 	{"func_button", SP_func_button},
    208 	{"func_door", SP_func_door},
    209 	{"func_static", SP_func_static},
    210 	{"func_rotating", SP_func_rotating},
    211 	{"func_bobbing", SP_func_bobbing},
    212 	{"func_pendulum", SP_func_pendulum},
    213 	{"func_train", SP_func_train},
    214 	{"func_group", SP_info_null},
    215 	{"func_timer", SP_func_timer},			// rename trigger_timer?
    216 
    217 	// Triggers are brush objects that cause an effect when contacted
    218 	// by a living player, usually involving firing targets.
    219 	// While almost everything could be done with
    220 	// a single trigger class and different targets, triggered effects
    221 	// could not be client side predicted (push and teleport).
    222 	{"trigger_always", SP_trigger_always},
    223 	{"trigger_multiple", SP_trigger_multiple},
    224 	{"trigger_push", SP_trigger_push},
    225 	{"trigger_teleport", SP_trigger_teleport},
    226 	{"trigger_hurt", SP_trigger_hurt},
    227 
    228 	// targets perform no action by themselves, but must be triggered
    229 	// by another entity
    230 	{"target_give", SP_target_give},
    231 	{"target_remove_powerups", SP_target_remove_powerups},
    232 	{"target_delay", SP_target_delay},
    233 	{"target_speaker", SP_target_speaker},
    234 	{"target_print", SP_target_print},
    235 	{"target_laser", SP_target_laser},
    236 	{"target_score", SP_target_score},
    237 	{"target_teleporter", SP_target_teleporter},
    238 	{"target_relay", SP_target_relay},
    239 	{"target_kill", SP_target_kill},
    240 	{"target_position", SP_target_position},
    241 	{"target_location", SP_target_location},
    242 	{"target_push", SP_target_push},
    243 
    244 	{"light", SP_light},
    245 	{"path_corner", SP_path_corner},
    246 
    247 	{"misc_teleporter_dest", SP_misc_teleporter_dest},
    248 	{"misc_model", SP_misc_model},
    249 	{"misc_portal_surface", SP_misc_portal_surface},
    250 	{"misc_portal_camera", SP_misc_portal_camera},
    251 
    252 	{"shooter_rocket", SP_shooter_rocket},
    253 	{"shooter_grenade", SP_shooter_grenade},
    254 	{"shooter_plasma", SP_shooter_plasma},
    255 
    256 	{"team_CTF_redplayer", SP_team_CTF_redplayer},
    257 	{"team_CTF_blueplayer", SP_team_CTF_blueplayer},
    258 
    259 	{"team_CTF_redspawn", SP_team_CTF_redspawn},
    260 	{"team_CTF_bluespawn", SP_team_CTF_bluespawn},
    261 
    262 #ifdef MISSIONPACK
    263 	{"team_redobelisk", SP_team_redobelisk},
    264 	{"team_blueobelisk", SP_team_blueobelisk},
    265 	{"team_neutralobelisk", SP_team_neutralobelisk},
    266 #endif
    267 	{"item_botroam", SP_item_botroam},
    268 
    269 	{0, 0}
    270 };
    271 
    272 /*
    273 ===============
    274 G_CallSpawn
    275 
    276 Finds the spawn function for the entity and calls it,
    277 returning qfalse if not found
    278 ===============
    279 */
    280 qboolean G_CallSpawn( gentity_t *ent ) {
    281 	spawn_t	*s;
    282 	gitem_t	*item;
    283 
    284 	if ( !ent->classname ) {
    285 		G_Printf ("G_CallSpawn: NULL classname\n");
    286 		return qfalse;
    287 	}
    288 
    289 	// check item spawn functions
    290 	for ( item=bg_itemlist+1 ; item->classname ; item++ ) {
    291 		if ( !strcmp(item->classname, ent->classname) ) {
    292 			G_SpawnItem( ent, item );
    293 			return qtrue;
    294 		}
    295 	}
    296 
    297 	// check normal spawn functions
    298 	for ( s=spawns ; s->name ; s++ ) {
    299 		if ( !strcmp(s->name, ent->classname) ) {
    300 			// found it
    301 			s->spawn(ent);
    302 			return qtrue;
    303 		}
    304 	}
    305 	G_Printf ("%s doesn't have a spawn function\n", ent->classname);
    306 	return qfalse;
    307 }
    308 
    309 /*
    310 =============
    311 G_NewString
    312 
    313 Builds a copy of the string, translating \n to real linefeeds
    314 so message texts can be multi-line
    315 =============
    316 */
    317 char *G_NewString( const char *string ) {
    318 	char	*newb, *new_p;
    319 	int		i,l;
    320 	
    321 	l = strlen(string) + 1;
    322 
    323 	newb = G_Alloc( l );
    324 
    325 	new_p = newb;
    326 
    327 	// turn \n into a real linefeed
    328 	for ( i=0 ; i< l ; i++ ) {
    329 		if (string[i] == '\\' && i < l-1) {
    330 			i++;
    331 			if (string[i] == 'n') {
    332 				*new_p++ = '\n';
    333 			} else {
    334 				*new_p++ = '\\';
    335 			}
    336 		} else {
    337 			*new_p++ = string[i];
    338 		}
    339 	}
    340 	
    341 	return newb;
    342 }
    343 
    344 
    345 
    346 
    347 /*
    348 ===============
    349 G_ParseField
    350 
    351 Takes a key/value pair and sets the binary values
    352 in a gentity
    353 ===============
    354 */
    355 void G_ParseField( const char *key, const char *value, gentity_t *ent ) {
    356 	field_t	*f;
    357 	byte	*b;
    358 	float	v;
    359 	vec3_t	vec;
    360 
    361 	for ( f=fields ; f->name ; f++ ) {
    362 		if ( !Q_stricmp(f->name, key) ) {
    363 			// found it
    364 			b = (byte *)ent;
    365 
    366 			switch( f->type ) {
    367 			case F_LSTRING:
    368 				*(char **)(b+f->ofs) = G_NewString (value);
    369 				break;
    370 			case F_VECTOR:
    371 				sscanf (value, "%f %f %f", &vec[0], &vec[1], &vec[2]);
    372 				((float *)(b+f->ofs))[0] = vec[0];
    373 				((float *)(b+f->ofs))[1] = vec[1];
    374 				((float *)(b+f->ofs))[2] = vec[2];
    375 				break;
    376 			case F_INT:
    377 				*(int *)(b+f->ofs) = atoi(value);
    378 				break;
    379 			case F_FLOAT:
    380 				*(float *)(b+f->ofs) = atof(value);
    381 				break;
    382 			case F_ANGLEHACK:
    383 				v = atof(value);
    384 				((float *)(b+f->ofs))[0] = 0;
    385 				((float *)(b+f->ofs))[1] = v;
    386 				((float *)(b+f->ofs))[2] = 0;
    387 				break;
    388 			default:
    389 			case F_IGNORE:
    390 				break;
    391 			}
    392 			return;
    393 		}
    394 	}
    395 }
    396 
    397 
    398 
    399 
    400 /*
    401 ===================
    402 G_SpawnGEntityFromSpawnVars
    403 
    404 Spawn an entity and fill in all of the level fields from
    405 level.spawnVars[], then call the class specfic spawn function
    406 ===================
    407 */
    408 void G_SpawnGEntityFromSpawnVars( void ) {
    409 	int			i;
    410 	gentity_t	*ent;
    411 	char		*s, *value, *gametypeName;
    412 	static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "teamtournament"};
    413 
    414 	// get the next free entity
    415 	ent = G_Spawn();
    416 
    417 	for ( i = 0 ; i < level.numSpawnVars ; i++ ) {
    418 		G_ParseField( level.spawnVars[i][0], level.spawnVars[i][1], ent );
    419 	}
    420 
    421 	// check for "notsingle" flag
    422 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
    423 		G_SpawnInt( "notsingle", "0", &i );
    424 		if ( i ) {
    425 			G_FreeEntity( ent );
    426 			return;
    427 		}
    428 	}
    429 	// check for "notteam" flag (GT_FFA, GT_TOURNAMENT, GT_SINGLE_PLAYER)
    430 	if ( g_gametype.integer >= GT_TEAM ) {
    431 		G_SpawnInt( "notteam", "0", &i );
    432 		if ( i ) {
    433 			G_FreeEntity( ent );
    434 			return;
    435 		}
    436 	} else {
    437 		G_SpawnInt( "notfree", "0", &i );
    438 		if ( i ) {
    439 			G_FreeEntity( ent );
    440 			return;
    441 		}
    442 	}
    443 
    444 #ifdef MISSIONPACK
    445 	G_SpawnInt( "notta", "0", &i );
    446 	if ( i ) {
    447 		G_FreeEntity( ent );
    448 		return;
    449 	}
    450 #else
    451 	G_SpawnInt( "notq3a", "0", &i );
    452 	if ( i ) {
    453 		G_FreeEntity( ent );
    454 		return;
    455 	}
    456 #endif
    457 
    458 	if( G_SpawnString( "gametype", NULL, &value ) ) {
    459 		if( g_gametype.integer >= GT_FFA && g_gametype.integer < GT_MAX_GAME_TYPE ) {
    460 			gametypeName = gametypeNames[g_gametype.integer];
    461 
    462 			s = strstr( value, gametypeName );
    463 			if( !s ) {
    464 				G_FreeEntity( ent );
    465 				return;
    466 			}
    467 		}
    468 	}
    469 
    470 	// move editor origin to pos
    471 	VectorCopy( ent->s.origin, ent->s.pos.trBase );
    472 	VectorCopy( ent->s.origin, ent->r.currentOrigin );
    473 
    474 	// if we didn't get a classname, don't bother spawning anything
    475 	if ( !G_CallSpawn( ent ) ) {
    476 		G_FreeEntity( ent );
    477 	}
    478 }
    479 
    480 
    481 
    482 /*
    483 ====================
    484 G_AddSpawnVarToken
    485 ====================
    486 */
    487 char *G_AddSpawnVarToken( const char *string ) {
    488 	int		l;
    489 	char	*dest;
    490 
    491 	l = strlen( string );
    492 	if ( level.numSpawnVarChars + l + 1 > MAX_SPAWN_VARS_CHARS ) {
    493 		G_Error( "G_AddSpawnVarToken: MAX_SPAWN_CHARS" );
    494 	}
    495 
    496 	dest = level.spawnVarChars + level.numSpawnVarChars;
    497 	memcpy( dest, string, l+1 );
    498 
    499 	level.numSpawnVarChars += l + 1;
    500 
    501 	return dest;
    502 }
    503 
    504 /*
    505 ====================
    506 G_ParseSpawnVars
    507 
    508 Parses a brace bounded set of key / value pairs out of the
    509 level's entity strings into level.spawnVars[]
    510 
    511 This does not actually spawn an entity.
    512 ====================
    513 */
    514 qboolean G_ParseSpawnVars( void ) {
    515 	char		keyname[MAX_TOKEN_CHARS];
    516 	char		com_token[MAX_TOKEN_CHARS];
    517 
    518 	level.numSpawnVars = 0;
    519 	level.numSpawnVarChars = 0;
    520 
    521 	// parse the opening brace
    522 	if ( !trap_GetEntityToken( com_token, sizeof( com_token ) ) ) {
    523 		// end of spawn string
    524 		return qfalse;
    525 	}
    526 	if ( com_token[0] != '{' ) {
    527 		G_Error( "G_ParseSpawnVars: found %s when expecting {",com_token );
    528 	}
    529 
    530 	// go through all the key / value pairs
    531 	while ( 1 ) {	
    532 		// parse key
    533 		if ( !trap_GetEntityToken( keyname, sizeof( keyname ) ) ) {
    534 			G_Error( "G_ParseSpawnVars: EOF without closing brace" );
    535 		}
    536 
    537 		if ( keyname[0] == '}' ) {
    538 			break;
    539 		}
    540 		
    541 		// parse value	
    542 		if ( !trap_GetEntityToken( com_token, sizeof( com_token ) ) ) {
    543 			G_Error( "G_ParseSpawnVars: EOF without closing brace" );
    544 		}
    545 
    546 		if ( com_token[0] == '}' ) {
    547 			G_Error( "G_ParseSpawnVars: closing brace without data" );
    548 		}
    549 		if ( level.numSpawnVars == MAX_SPAWN_VARS ) {
    550 			G_Error( "G_ParseSpawnVars: MAX_SPAWN_VARS" );
    551 		}
    552 		level.spawnVars[ level.numSpawnVars ][0] = G_AddSpawnVarToken( keyname );
    553 		level.spawnVars[ level.numSpawnVars ][1] = G_AddSpawnVarToken( com_token );
    554 		level.numSpawnVars++;
    555 	}
    556 
    557 	return qtrue;
    558 }
    559 
    560 
    561 
    562 /*QUAKED worldspawn (0 0 0) ?
    563 
    564 Every map should have exactly one worldspawn.
    565 "music"		music wav file
    566 "gravity"	800 is default gravity
    567 "message"	Text to print during connection process
    568 */
    569 void SP_worldspawn( void ) {
    570 	char	*s;
    571 
    572 	G_SpawnString( "classname", "", &s );
    573 	if ( Q_stricmp( s, "worldspawn" ) ) {
    574 		G_Error( "SP_worldspawn: The first entity isn't 'worldspawn'" );
    575 	}
    576 
    577 	// make some data visible to connecting client
    578 	trap_SetConfigstring( CS_GAME_VERSION, GAME_VERSION );
    579 
    580 	trap_SetConfigstring( CS_LEVEL_START_TIME, va("%i", level.startTime ) );
    581 
    582 	G_SpawnString( "music", "", &s );
    583 	trap_SetConfigstring( CS_MUSIC, s );
    584 
    585 	G_SpawnString( "message", "", &s );
    586 	trap_SetConfigstring( CS_MESSAGE, s );				// map specific message
    587 
    588 	trap_SetConfigstring( CS_MOTD, g_motd.string );		// message of the day
    589 
    590 	G_SpawnString( "gravity", "800", &s );
    591 	trap_Cvar_Set( "g_gravity", s );
    592 
    593 	G_SpawnString( "enableDust", "0", &s );
    594 	trap_Cvar_Set( "g_enableDust", s );
    595 
    596 	G_SpawnString( "enableBreath", "0", &s );
    597 	trap_Cvar_Set( "g_enableBreath", s );
    598 
    599 	g_entities[ENTITYNUM_WORLD].s.number = ENTITYNUM_WORLD;
    600 	g_entities[ENTITYNUM_WORLD].classname = "worldspawn";
    601 
    602 	// see if we want a warmup time
    603 	trap_SetConfigstring( CS_WARMUP, "" );
    604 	if ( g_restarted.integer ) {
    605 		trap_Cvar_Set( "g_restarted", "0" );
    606 		level.warmupTime = 0;
    607 	} else if ( g_doWarmup.integer ) { // Turn it on
    608 		level.warmupTime = -1;
    609 		trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
    610 		G_LogPrintf( "Warmup:\n" );
    611 	}
    612 
    613 }
    614 
    615 
    616 /*
    617 ==============
    618 G_SpawnEntitiesFromString
    619 
    620 Parses textual entity definitions out of an entstring and spawns gentities.
    621 ==============
    622 */
    623 void G_SpawnEntitiesFromString( void ) {
    624 	// allow calls to G_Spawn*()
    625 	level.spawning = qtrue;
    626 	level.numSpawnVars = 0;
    627 
    628 	// the worldspawn is not an actual entity, but it still
    629 	// has a "spawn" function to perform any global setup
    630 	// needed by a level (setting configstrings or cvars, etc)
    631 	if ( !G_ParseSpawnVars() ) {
    632 		G_Error( "SpawnEntities: no entities" );
    633 	}
    634 	SP_worldspawn();
    635 
    636 	// parse ents
    637 	while( G_ParseSpawnVars() ) {
    638 		G_SpawnGEntityFromSpawnVars();
    639 	}	
    640 
    641 	level.spawning = qfalse;			// any future calls to G_Spawn*() will be errors
    642 }
    643