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