699 lines
19 KiB
C
699 lines
19 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
Copyright (C) 2000-2006 Tim Angus
|
|
|
|
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,
|
|
or (at your option) any later version.
|
|
|
|
Tremulous is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
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
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "g_local.h"
|
|
|
|
qboolean G_SpawnString( const char *key, const char *defaultString, char **out )
|
|
{
|
|
int i;
|
|
|
|
if( !level.spawning )
|
|
{
|
|
*out = (char *)defaultString;
|
|
// G_Error( "G_SpawnString() called while not spawning" );
|
|
}
|
|
|
|
for( i = 0; i < level.numSpawnVars; i++ )
|
|
{
|
|
if( !Q_stricmp( key, level.spawnVars[ i ][ 0 ] ) )
|
|
{
|
|
*out = level.spawnVars[ i ][ 1 ];
|
|
return qtrue;
|
|
}
|
|
}
|
|
|
|
*out = (char *)defaultString;
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G_SpawnFloat( const char *key, const char *defaultString, float *out )
|
|
{
|
|
char *s;
|
|
qboolean present;
|
|
|
|
present = G_SpawnString( key, defaultString, &s );
|
|
*out = atof( s );
|
|
return present;
|
|
}
|
|
|
|
qboolean G_SpawnInt( const char *key, const char *defaultString, int *out )
|
|
{
|
|
char *s;
|
|
qboolean present;
|
|
|
|
present = G_SpawnString( key, defaultString, &s );
|
|
*out = atoi( s );
|
|
return present;
|
|
}
|
|
|
|
qboolean G_SpawnVector( const char *key, const char *defaultString, float *out )
|
|
{
|
|
char *s;
|
|
qboolean present;
|
|
|
|
present = G_SpawnString( key, defaultString, &s );
|
|
sscanf( s, "%f %f %f", &out[ 0 ], &out[ 1 ], &out[ 2 ] );
|
|
return present;
|
|
}
|
|
|
|
qboolean G_SpawnVector4( const char *key, const char *defaultString, float *out )
|
|
{
|
|
char *s;
|
|
qboolean present;
|
|
|
|
present = G_SpawnString( key, defaultString, &s );
|
|
sscanf( s, "%f %f %f %f", &out[ 0 ], &out[ 1 ], &out[ 2 ], &out[ 3 ] );
|
|
return present;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// fields are needed for spawning from the entity string
|
|
//
|
|
typedef enum
|
|
{
|
|
F_INT,
|
|
F_FLOAT,
|
|
F_LSTRING, // string on disk, pointer in memory, TAG_LEVEL
|
|
F_GSTRING, // string on disk, pointer in memory, TAG_GAME
|
|
F_VECTOR,
|
|
F_VECTOR4, //TA
|
|
F_ANGLEHACK,
|
|
F_ENTITY, // index on disk, pointer in memory
|
|
F_ITEM, // index on disk, pointer in memory
|
|
F_CLIENT, // index on disk, pointer in memory
|
|
F_IGNORE
|
|
} fieldtype_t;
|
|
|
|
typedef struct
|
|
{
|
|
char *name;
|
|
int ofs;
|
|
fieldtype_t type;
|
|
int flags;
|
|
} field_t;
|
|
|
|
field_t fields[ ] =
|
|
{
|
|
{"classname", FOFS(classname), F_LSTRING},
|
|
{"origin", FOFS(s.origin), F_VECTOR},
|
|
{"model", FOFS(model), F_LSTRING},
|
|
{"model2", FOFS(model2), F_LSTRING},
|
|
{"spawnflags", FOFS(spawnflags), F_INT},
|
|
{"speed", FOFS(speed), F_FLOAT},
|
|
{"target", FOFS(target), F_LSTRING},
|
|
{"targetname", FOFS(targetname), F_LSTRING},
|
|
{"message", FOFS(message), F_LSTRING},
|
|
{"team", FOFS(team), F_LSTRING},
|
|
{"wait", FOFS(wait), F_FLOAT},
|
|
{"random", FOFS(random), F_FLOAT},
|
|
{"count", FOFS(count), F_INT},
|
|
{"health", FOFS(health), F_INT},
|
|
{"light", 0, F_IGNORE},
|
|
{"dmg", FOFS(damage), F_INT},
|
|
{"angles", FOFS(s.angles), F_VECTOR},
|
|
{"angle", FOFS(s.angles), F_ANGLEHACK},
|
|
{"bounce", FOFS(physicsBounce), F_FLOAT},
|
|
{"alpha", FOFS(pos1), F_VECTOR},
|
|
{"radius", FOFS(pos2), F_VECTOR},
|
|
{"acceleration", FOFS(acceleration), F_VECTOR},
|
|
{"animation", FOFS(animation), F_VECTOR4},
|
|
{"rotatorAngle", FOFS(rotatorAngle), F_FLOAT},
|
|
{"targetShaderName", FOFS(targetShaderName), F_LSTRING},
|
|
{"targetShaderNewName", FOFS(targetShaderNewName), F_LSTRING},
|
|
|
|
{NULL}
|
|
};
|
|
|
|
|
|
typedef struct
|
|
{
|
|
char *name;
|
|
void (*spawn)(gentity_t *ent);
|
|
} spawn_t;
|
|
|
|
void SP_info_player_start( gentity_t *ent );
|
|
void SP_info_player_deathmatch( gentity_t *ent );
|
|
void SP_info_player_intermission( gentity_t *ent );
|
|
|
|
void SP_info_alien_intermission( gentity_t *ent );
|
|
void SP_info_human_intermission( gentity_t *ent );
|
|
|
|
void SP_info_firstplace( gentity_t *ent );
|
|
void SP_info_secondplace( gentity_t *ent );
|
|
void SP_info_thirdplace( gentity_t *ent );
|
|
void SP_info_podium( gentity_t *ent );
|
|
|
|
void SP_func_plat( gentity_t *ent );
|
|
void SP_func_static( gentity_t *ent );
|
|
void SP_func_rotating( gentity_t *ent );
|
|
void SP_func_bobbing( gentity_t *ent );
|
|
void SP_func_pendulum( gentity_t *ent );
|
|
void SP_func_button( gentity_t *ent );
|
|
void SP_func_door( gentity_t *ent );
|
|
void SP_func_door_rotating( gentity_t *ent );
|
|
void SP_func_door_model( gentity_t *ent );
|
|
void SP_func_train( gentity_t *ent );
|
|
void SP_func_timer( gentity_t *self);
|
|
|
|
void SP_trigger_always( gentity_t *ent );
|
|
void SP_trigger_multiple( gentity_t *ent );
|
|
void SP_trigger_push( gentity_t *ent );
|
|
void SP_trigger_teleport( gentity_t *ent );
|
|
void SP_trigger_hurt( gentity_t *ent );
|
|
void SP_trigger_stage( gentity_t *ent );
|
|
void SP_trigger_win( gentity_t *ent );
|
|
void SP_trigger_buildable( gentity_t *ent );
|
|
void SP_trigger_class( gentity_t *ent );
|
|
void SP_trigger_equipment( gentity_t *ent );
|
|
void SP_trigger_gravity( gentity_t *ent );
|
|
void SP_trigger_heal( gentity_t *ent );
|
|
void SP_trigger_ammo( gentity_t *ent );
|
|
|
|
void SP_target_delay( gentity_t *ent );
|
|
void SP_target_speaker( gentity_t *ent );
|
|
void SP_target_print( gentity_t *ent );
|
|
void SP_target_character( gentity_t *ent );
|
|
void SP_target_score( gentity_t *ent );
|
|
void SP_target_teleporter( gentity_t *ent );
|
|
void SP_target_relay( gentity_t *ent );
|
|
void SP_target_kill( gentity_t *ent );
|
|
void SP_target_position( gentity_t *ent );
|
|
void SP_target_location( gentity_t *ent );
|
|
void SP_target_push( gentity_t *ent );
|
|
void SP_target_rumble( gentity_t *ent );
|
|
void SP_target_alien_win( gentity_t *ent );
|
|
void SP_target_human_win( gentity_t *ent );
|
|
void SP_target_hurt( gentity_t *ent );
|
|
|
|
void SP_light( gentity_t *self );
|
|
void SP_info_null( gentity_t *self );
|
|
void SP_info_notnull( gentity_t *self );
|
|
void SP_info_camp( gentity_t *self );
|
|
void SP_path_corner( gentity_t *self );
|
|
|
|
void SP_misc_teleporter_dest( gentity_t *self );
|
|
void SP_misc_model( gentity_t *ent );
|
|
void SP_misc_portal_camera( gentity_t *ent );
|
|
void SP_misc_portal_surface( gentity_t *ent );
|
|
|
|
void SP_shooter_rocket( gentity_t *ent );
|
|
void SP_shooter_plasma( gentity_t *ent );
|
|
void SP_shooter_grenade( gentity_t *ent );
|
|
|
|
void SP_misc_particle_system( gentity_t *ent );
|
|
void SP_misc_anim_model( gentity_t *ent );
|
|
void SP_misc_light_flare( gentity_t *ent );
|
|
|
|
spawn_t spawns[ ] =
|
|
{
|
|
// info entities don't do anything at all, but provide positional
|
|
// information for things controlled by other processes
|
|
{ "info_player_start", SP_info_player_start },
|
|
{ "info_player_deathmatch", SP_info_player_deathmatch },
|
|
{ "info_player_intermission", SP_info_player_intermission },
|
|
|
|
//TA: extra bits
|
|
{ "info_alien_intermission", SP_info_alien_intermission },
|
|
{ "info_human_intermission", SP_info_human_intermission },
|
|
|
|
{ "info_null", SP_info_null },
|
|
{ "info_notnull", SP_info_notnull }, // use target_position instead
|
|
|
|
{ "func_plat", SP_func_plat },
|
|
{ "func_button", SP_func_button },
|
|
{ "func_door", SP_func_door },
|
|
{ "func_door_rotating", SP_func_door_rotating }, //TA
|
|
{ "func_door_model", SP_func_door_model }, //TA
|
|
{ "func_static", SP_func_static },
|
|
{ "func_rotating", SP_func_rotating },
|
|
{ "func_bobbing", SP_func_bobbing },
|
|
{ "func_pendulum", SP_func_pendulum },
|
|
{ "func_train", SP_func_train },
|
|
{ "func_group", SP_info_null },
|
|
{ "func_timer", SP_func_timer }, // rename trigger_timer?
|
|
|
|
// Triggers are brush objects that cause an effect when contacted
|
|
// by a living player, usually involving firing targets.
|
|
// While almost everything could be done with
|
|
// a single trigger class and different targets, triggered effects
|
|
// could not be client side predicted (push and teleport).
|
|
{ "trigger_always", SP_trigger_always },
|
|
{ "trigger_multiple", SP_trigger_multiple },
|
|
{ "trigger_push", SP_trigger_push },
|
|
{ "trigger_teleport", SP_trigger_teleport },
|
|
{ "trigger_hurt", SP_trigger_hurt },
|
|
{ "trigger_stage", SP_trigger_stage },
|
|
{ "trigger_win", SP_trigger_win },
|
|
{ "trigger_buildable", SP_trigger_buildable },
|
|
{ "trigger_class", SP_trigger_class },
|
|
{ "trigger_equipment", SP_trigger_equipment },
|
|
{ "trigger_gravity", SP_trigger_gravity },
|
|
{ "trigger_heal", SP_trigger_heal },
|
|
{ "trigger_ammo", SP_trigger_ammo },
|
|
|
|
// targets perform no action by themselves, but must be triggered
|
|
// by another entity
|
|
{ "target_delay", SP_target_delay },
|
|
{ "target_speaker", SP_target_speaker },
|
|
{ "target_print", SP_target_print },
|
|
{ "target_score", SP_target_score },
|
|
{ "target_teleporter", SP_target_teleporter },
|
|
{ "target_relay", SP_target_relay },
|
|
{ "target_kill", SP_target_kill },
|
|
{ "target_position", SP_target_position },
|
|
{ "target_location", SP_target_location },
|
|
{ "target_push", SP_target_push },
|
|
{ "target_rumble", SP_target_rumble },
|
|
{ "target_alien_win", SP_target_alien_win },
|
|
{ "target_human_win", SP_target_human_win },
|
|
{ "target_hurt", SP_target_hurt },
|
|
|
|
{ "light", SP_light },
|
|
{ "path_corner", SP_path_corner },
|
|
|
|
{ "misc_teleporter_dest", SP_misc_teleporter_dest },
|
|
{ "misc_model", SP_misc_model },
|
|
{ "misc_portal_surface", SP_misc_portal_surface },
|
|
{ "misc_portal_camera", SP_misc_portal_camera },
|
|
|
|
{ "misc_particle_system", SP_misc_particle_system },
|
|
{ "misc_anim_model", SP_misc_anim_model },
|
|
{ "misc_light_flare", SP_misc_light_flare },
|
|
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
/*
|
|
===============
|
|
G_CallSpawn
|
|
|
|
Finds the spawn function for the entity and calls it,
|
|
returning qfalse if not found
|
|
===============
|
|
*/
|
|
qboolean G_CallSpawn( gentity_t *ent )
|
|
{
|
|
spawn_t *s;
|
|
buildable_t buildable;
|
|
|
|
if( !ent->classname )
|
|
{
|
|
G_Printf( "G_CallSpawn: NULL classname\n" );
|
|
return qfalse;
|
|
}
|
|
|
|
//check buildable spawn functions
|
|
if( ( buildable = BG_FindBuildNumForEntityName( ent->classname ) ) != BA_NONE )
|
|
{
|
|
// don't spawn built-in buildings if we are using a custom layout
|
|
if( level.layout[ 0 ] && Q_stricmp( level.layout, "*BUILTIN*" ) )
|
|
return qtrue;
|
|
|
|
if( buildable == BA_A_SPAWN || buildable == BA_H_SPAWN )
|
|
{
|
|
ent->s.angles[ YAW ] += 180.0f;
|
|
AngleNormalize360( ent->s.angles[ YAW ] );
|
|
}
|
|
|
|
G_SpawnBuildable( ent, buildable );
|
|
return qtrue;
|
|
}
|
|
|
|
// check normal spawn functions
|
|
for( s = spawns; s->name; s++ )
|
|
{
|
|
if( !strcmp( s->name, ent->classname ) )
|
|
{
|
|
// found it
|
|
s->spawn( ent );
|
|
return qtrue;
|
|
}
|
|
}
|
|
|
|
G_Printf( "%s doesn't have a spawn function\n", ent->classname );
|
|
return qfalse;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
G_NewString
|
|
|
|
Builds a copy of the string, translating \n to real linefeeds
|
|
so message texts can be multi-line
|
|
=============
|
|
*/
|
|
char *G_NewString( const char *string )
|
|
{
|
|
char *newb, *new_p;
|
|
int i,l;
|
|
|
|
l = strlen( string ) + 1;
|
|
|
|
newb = G_Alloc( l );
|
|
|
|
new_p = newb;
|
|
|
|
// turn \n into a real linefeed
|
|
for( i = 0 ; i < l ; i++ )
|
|
{
|
|
if( string[ i ] == '\\' && i < l - 1 )
|
|
{
|
|
i++;
|
|
if( string[ i ] == 'n' )
|
|
*new_p++ = '\n';
|
|
else
|
|
*new_p++ = '\\';
|
|
}
|
|
else
|
|
*new_p++ = string[ i ];
|
|
}
|
|
|
|
return newb;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
G_ParseField
|
|
|
|
Takes a key/value pair and sets the binary values
|
|
in a gentity
|
|
===============
|
|
*/
|
|
void G_ParseField( const char *key, const char *value, gentity_t *ent )
|
|
{
|
|
field_t *f;
|
|
byte *b;
|
|
float v;
|
|
vec3_t vec;
|
|
vec4_t vec4;
|
|
|
|
for( f = fields; f->name; f++ )
|
|
{
|
|
if( !Q_stricmp( f->name, key ) )
|
|
{
|
|
// found it
|
|
b = (byte *)ent;
|
|
|
|
switch( f->type )
|
|
{
|
|
case F_LSTRING:
|
|
*(char **)( b + f->ofs ) = G_NewString( value );
|
|
break;
|
|
|
|
case F_VECTOR:
|
|
sscanf( value, "%f %f %f", &vec[ 0 ], &vec[ 1 ], &vec[ 2 ] );
|
|
|
|
( (float *)( b + f->ofs ) )[ 0 ] = vec[ 0 ];
|
|
( (float *)( b + f->ofs ) )[ 1 ] = vec[ 1 ];
|
|
( (float *)( b + f->ofs ) )[ 2 ] = vec[ 2 ];
|
|
break;
|
|
|
|
case F_VECTOR4:
|
|
sscanf( value, "%f %f %f %f", &vec4[ 0 ], &vec4[ 1 ], &vec4[ 2 ], &vec4[ 3 ] );
|
|
|
|
( (float *)( b + f->ofs ) )[ 0 ] = vec4[ 0 ];
|
|
( (float *)( b + f->ofs ) )[ 1 ] = vec4[ 1 ];
|
|
( (float *)( b + f->ofs ) )[ 2 ] = vec4[ 2 ];
|
|
( (float *)( b + f->ofs ) )[ 3 ] = vec4[ 3 ];
|
|
break;
|
|
|
|
case F_INT:
|
|
*(int *)( b + f->ofs ) = atoi( value );
|
|
break;
|
|
|
|
case F_FLOAT:
|
|
*(float *)( b + f->ofs ) = atof( value );
|
|
break;
|
|
|
|
case F_ANGLEHACK:
|
|
v = atof( value );
|
|
( (float *)( b + f->ofs ) )[ 0 ] = 0;
|
|
( (float *)( b + f->ofs ) )[ 1 ] = v;
|
|
( (float *)( b + f->ofs ) )[ 2 ] = 0;
|
|
break;
|
|
|
|
default:
|
|
case F_IGNORE:
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
===================
|
|
G_SpawnGEntityFromSpawnVars
|
|
|
|
Spawn an entity and fill in all of the level fields from
|
|
level.spawnVars[], then call the class specfic spawn function
|
|
===================
|
|
*/
|
|
void G_SpawnGEntityFromSpawnVars( void )
|
|
{
|
|
int i;
|
|
gentity_t *ent;
|
|
|
|
// get the next free entity
|
|
ent = G_Spawn( );
|
|
|
|
for( i = 0 ; i < level.numSpawnVars ; i++ )
|
|
G_ParseField( level.spawnVars[ i ][ 0 ], level.spawnVars[ i ][ 1 ], ent );
|
|
|
|
G_SpawnInt( "notq3a", "0", &i );
|
|
|
|
if( i )
|
|
{
|
|
G_FreeEntity( ent );
|
|
return;
|
|
}
|
|
|
|
// move editor origin to pos
|
|
VectorCopy( ent->s.origin, ent->s.pos.trBase );
|
|
VectorCopy( ent->s.origin, ent->r.currentOrigin );
|
|
|
|
// if we didn't get a classname, don't bother spawning anything
|
|
if( !G_CallSpawn( ent ) )
|
|
G_FreeEntity( ent );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
====================
|
|
G_AddSpawnVarToken
|
|
====================
|
|
*/
|
|
char *G_AddSpawnVarToken( const char *string )
|
|
{
|
|
int l;
|
|
char *dest;
|
|
|
|
l = strlen( string );
|
|
if( level.numSpawnVarChars + l + 1 > MAX_SPAWN_VARS_CHARS )
|
|
G_Error( "G_AddSpawnVarToken: MAX_SPAWN_CHARS" );
|
|
|
|
dest = level.spawnVarChars + level.numSpawnVarChars;
|
|
memcpy( dest, string, l + 1 );
|
|
|
|
level.numSpawnVarChars += l + 1;
|
|
|
|
return dest;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
G_ParseSpawnVars
|
|
|
|
Parses a brace bounded set of key / value pairs out of the
|
|
level's entity strings into level.spawnVars[]
|
|
|
|
This does not actually spawn an entity.
|
|
====================
|
|
*/
|
|
qboolean G_ParseSpawnVars( void )
|
|
{
|
|
char keyname[ MAX_TOKEN_CHARS ];
|
|
char com_token[ MAX_TOKEN_CHARS ];
|
|
|
|
level.numSpawnVars = 0;
|
|
level.numSpawnVarChars = 0;
|
|
|
|
// parse the opening brace
|
|
if( !trap_GetEntityToken( com_token, sizeof( com_token ) ) )
|
|
{
|
|
// end of spawn string
|
|
return qfalse;
|
|
}
|
|
|
|
if( com_token[ 0 ] != '{' )
|
|
G_Error( "G_ParseSpawnVars: found %s when expecting {", com_token );
|
|
|
|
// go through all the key / value pairs
|
|
while( 1 )
|
|
{
|
|
// parse key
|
|
if( !trap_GetEntityToken( keyname, sizeof( keyname ) ) )
|
|
G_Error( "G_ParseSpawnVars: EOF without closing brace" );
|
|
|
|
if( keyname[0] == '}' )
|
|
break;
|
|
|
|
// parse value
|
|
if( !trap_GetEntityToken( com_token, sizeof( com_token ) ) )
|
|
G_Error( "G_ParseSpawnVars: EOF without closing brace" );
|
|
|
|
if( com_token[0] == '}' )
|
|
G_Error( "G_ParseSpawnVars: closing brace without data" );
|
|
|
|
if( level.numSpawnVars == MAX_SPAWN_VARS )
|
|
G_Error( "G_ParseSpawnVars: MAX_SPAWN_VARS" );
|
|
|
|
level.spawnVars[ level.numSpawnVars ][ 0 ] = G_AddSpawnVarToken( keyname );
|
|
level.spawnVars[ level.numSpawnVars ][ 1 ] = G_AddSpawnVarToken( com_token );
|
|
level.numSpawnVars++;
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
|
|
/*QUAKED worldspawn (0 0 0) ?
|
|
|
|
Every map should have exactly one worldspawn.
|
|
"music" music wav file
|
|
"gravity" 800 is default gravity
|
|
"message" Text to print during connection process
|
|
*/
|
|
void SP_worldspawn( void )
|
|
{
|
|
char *s;
|
|
|
|
G_SpawnString( "classname", "", &s );
|
|
|
|
if( Q_stricmp( s, "worldspawn" ) )
|
|
G_Error( "SP_worldspawn: The first entity isn't 'worldspawn'" );
|
|
|
|
// make some data visible to connecting client
|
|
trap_SetConfigstring( CS_GAME_VERSION, GAME_VERSION );
|
|
|
|
trap_SetConfigstring( CS_LEVEL_START_TIME, va( "%i", level.startTime ) );
|
|
|
|
G_SpawnString( "music", "", &s );
|
|
trap_SetConfigstring( CS_MUSIC, s );
|
|
|
|
G_SpawnString( "message", "", &s );
|
|
trap_SetConfigstring( CS_MESSAGE, s ); // map specific message
|
|
|
|
trap_SetConfigstring( CS_MOTD, g_motd.string ); // message of the day
|
|
|
|
G_SpawnString( "gravity", "800", &s );
|
|
trap_Cvar_Set( "g_gravity", s );
|
|
|
|
G_SpawnString( "humanBuildPoints", DEFAULT_HUMAN_BUILDPOINTS, &s );
|
|
trap_Cvar_Set( "g_humanBuildPoints", s );
|
|
|
|
G_SpawnString( "humanMaxStage", DEFAULT_HUMAN_MAX_STAGE, &s );
|
|
trap_Cvar_Set( "g_humanMaxStage", s );
|
|
|
|
G_SpawnString( "humanStage2Threshold", DEFAULT_HUMAN_STAGE2_THRESH, &s );
|
|
trap_Cvar_Set( "g_humanStage2Threshold", s );
|
|
|
|
G_SpawnString( "humanStage3Threshold", DEFAULT_HUMAN_STAGE3_THRESH, &s );
|
|
trap_Cvar_Set( "g_humanStage3Threshold", s );
|
|
|
|
G_SpawnString( "alienBuildPoints", DEFAULT_ALIEN_BUILDPOINTS, &s );
|
|
trap_Cvar_Set( "g_alienBuildPoints", s );
|
|
|
|
G_SpawnString( "alienMaxStage", DEFAULT_ALIEN_MAX_STAGE, &s );
|
|
trap_Cvar_Set( "g_alienMaxStage", s );
|
|
|
|
G_SpawnString( "alienStage2Threshold", DEFAULT_ALIEN_STAGE2_THRESH, &s );
|
|
trap_Cvar_Set( "g_alienStage2Threshold", s );
|
|
|
|
G_SpawnString( "alienStage3Threshold", DEFAULT_ALIEN_STAGE3_THRESH, &s );
|
|
trap_Cvar_Set( "g_alienStage3Threshold", s );
|
|
|
|
G_SpawnString( "enableDust", "0", &s );
|
|
trap_Cvar_Set( "g_enableDust", s );
|
|
|
|
G_SpawnString( "enableBreath", "0", &s );
|
|
trap_Cvar_Set( "g_enableBreath", s );
|
|
|
|
G_SpawnString( "disabledEquipment", "", &s );
|
|
trap_Cvar_Set( "g_disabledEquipment", s );
|
|
|
|
G_SpawnString( "disabledClasses", "", &s );
|
|
trap_Cvar_Set( "g_disabledClasses", s );
|
|
|
|
G_SpawnString( "disabledBuildables", "", &s );
|
|
trap_Cvar_Set( "g_disabledBuildables", s );
|
|
|
|
g_entities[ ENTITYNUM_WORLD ].s.number = ENTITYNUM_WORLD;
|
|
g_entities[ ENTITYNUM_WORLD ].classname = "worldspawn";
|
|
|
|
if( g_restarted.integer )
|
|
trap_Cvar_Set( "g_restarted", "0" );
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
G_SpawnEntitiesFromString
|
|
|
|
Parses textual entity definitions out of an entstring and spawns gentities.
|
|
==============
|
|
*/
|
|
void G_SpawnEntitiesFromString( void )
|
|
{
|
|
// allow calls to G_Spawn*()
|
|
level.spawning = qtrue;
|
|
level.numSpawnVars = 0;
|
|
|
|
// the worldspawn is not an actual entity, but it still
|
|
// has a "spawn" function to perform any global setup
|
|
// needed by a level (setting configstrings or cvars, etc)
|
|
if( !G_ParseSpawnVars( ) )
|
|
G_Error( "SpawnEntities: no entities" );
|
|
|
|
SP_worldspawn( );
|
|
|
|
// parse ents
|
|
while( G_ParseSpawnVars( ) )
|
|
G_SpawnGEntityFromSpawnVars( );
|
|
|
|
level.spawning = qfalse; // any future calls to G_Spawn*() will be errors
|
|
}
|
|
|