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