jedioutcast/CODE-mp/game/g_spawn.c

867 lines
21 KiB
C
Raw Normal View History

2013-04-04 14:52:42 +00:00
// Copyright (C) 1999-2000 Id Software, Inc.
//
#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;
}
//
// 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_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},
{"teamnodmg", FOFS(teamnodmg), F_INT},
{"roffname", FOFS(roffname), F_LSTRING},
{"rofftarget", FOFS(rofftarget), 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},
{"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_imperial (gentity_t *ent);
void SP_info_player_rebel (gentity_t *ent);
void SP_info_player_intermission (gentity_t *ent);
void SP_info_jedimaster_start (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_info_saga_objective (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_train (gentity_t *ent);
void SP_func_timer (gentity_t *self);
void SP_func_breakable (gentity_t *ent);
void SP_func_glass (gentity_t *ent);
void SP_func_usable( gentity_t *ent);
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_target_remove_powerups( gentity_t *ent );
void SP_target_give (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_laser (gentity_t *self);
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_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_G2model(gentity_t *ent);
void SP_misc_portal_camera(gentity_t *ent);
void SP_misc_portal_surface(gentity_t *ent);
void SP_misc_shield_floor_unit( gentity_t *ent );
void SP_misc_model_shield_power_converter( gentity_t *ent );
void SP_misc_model_ammo_power_converter( gentity_t *ent );
void SP_misc_model_health_power_converter( gentity_t *ent );
2013-04-04 18:02:27 +00:00
void SP_fx_runner( gentity_t *ent );
2013-04-04 14:52:42 +00:00
void SP_misc_holocron(gentity_t *ent);
void SP_shooter_blaster( gentity_t *ent );
void SP_team_CTF_redplayer( gentity_t *ent );
void SP_team_CTF_blueplayer( gentity_t *ent );
void SP_team_CTF_redspawn( gentity_t *ent );
void SP_team_CTF_bluespawn( gentity_t *ent );
void SP_item_botroam( gentity_t *ent )
{
}
void SP_emplaced_gun( 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_imperial", SP_info_player_imperial},
{"info_player_rebel", SP_info_player_rebel},
{"info_player_intermission", SP_info_player_intermission},
{"info_jedimaster_start", SP_info_jedimaster_start},
{"info_null", SP_info_null},
{"info_notnull", SP_info_notnull}, // use target_position instead
{"info_camp", SP_info_camp},
{"info_saga_objective", SP_info_saga_objective},
{"func_plat", SP_func_plat},
{"func_button", SP_func_button},
{"func_door", SP_func_door},
{"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?
{"func_breakable", SP_func_breakable},
{"func_glass", SP_func_glass},
{"func_usable", SP_func_usable},
// 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},
// targets perform no action by themselves, but must be triggered
// by another entity
{"target_give", SP_target_give},
{"target_remove_powerups", SP_target_remove_powerups},
{"target_delay", SP_target_delay},
{"target_speaker", SP_target_speaker},
{"target_print", SP_target_print},
{"target_laser", SP_target_laser},
{"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},
{"light", SP_light},
{"path_corner", SP_path_corner},
{"misc_teleporter_dest", SP_misc_teleporter_dest},
{"misc_model", SP_misc_model},
{"misc_G2model", SP_misc_G2model},
{"misc_portal_surface", SP_misc_portal_surface},
{"misc_portal_camera", SP_misc_portal_camera},
{"misc_shield_floor_unit", SP_misc_shield_floor_unit},
{"misc_model_shield_power_converter", SP_misc_model_shield_power_converter},
{"misc_model_ammo_power_converter", SP_misc_model_ammo_power_converter},
{"misc_model_health_power_converter", SP_misc_model_health_power_converter},
2013-04-04 18:02:27 +00:00
{"fx_runner", SP_fx_runner},
2013-04-04 14:52:42 +00:00
{"misc_holocron", SP_misc_holocron},
{"shooter_blaster", SP_shooter_blaster},
{"team_CTF_redplayer", SP_team_CTF_redplayer},
{"team_CTF_blueplayer", SP_team_CTF_blueplayer},
{"team_CTF_redspawn", SP_team_CTF_redspawn},
{"team_CTF_bluespawn", SP_team_CTF_bluespawn},
{"item_botroam", SP_item_botroam},
{"emplaced_gun", SP_emplaced_gun},
{0, 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;
gitem_t *item;
if ( !ent->classname ) {
G_Printf ("G_CallSpawn: NULL classname\n");
return qfalse;
}
// check item spawn functions
for ( item=bg_itemlist+1 ; item->classname ; item++ ) {
if ( !strcmp(item->classname, ent->classname) ) {
G_SpawnItem( ent, item );
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;
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_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;
char *s, *value, *gametypeName;
static char *gametypeNames[] = {"ffa", "holocron", "jedimaster", "duel", "single", "team", "saga", "ctf", "cty"};
// 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 );
}
// check for "notsingle" flag
if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
G_SpawnInt( "notsingle", "0", &i );
if ( i ) {
G_FreeEntity( ent );
return;
}
}
// check for "notteam" flag (GT_FFA, GT_TOURNAMENT, GT_SINGLE_PLAYER)
if ( g_gametype.integer >= GT_TEAM ) {
G_SpawnInt( "notteam", "0", &i );
if ( i ) {
G_FreeEntity( ent );
return;
}
} else {
G_SpawnInt( "notfree", "0", &i );
if ( i ) {
G_FreeEntity( ent );
return;
}
}
G_SpawnInt( "notta", "0", &i );
if ( i ) {
G_FreeEntity( ent );
return;
}
if( G_SpawnString( "gametype", NULL, &value ) ) {
if( g_gametype.integer >= GT_FFA && g_gametype.integer < GT_MAX_GAME_TYPE ) {
gametypeName = gametypeNames[g_gametype.integer];
s = strstr( value, gametypeName );
if( !s ) {
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;
}
static char *defaultStyles[32][3] =
{
{ // 0 normal
"z",
"z",
"z"
},
{ // 1 FLICKER (first variety)
"mmnmmommommnonmmonqnmmo",
"mmnmmommommnonmmonqnmmo",
"mmnmmommommnonmmonqnmmo"
},
{ // 2 SLOW STRONG PULSE
"abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcb",
"abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcb",
"abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcb"
},
{ // 3 CANDLE (first variety)
"mmmmmaaaaammmmmaaaaaabcdefgabcdefg",
"mmmmmaaaaammmmmaaaaaabcdefgabcdefg",
"mmmmmaaaaammmmmaaaaaabcdefgabcdefg"
},
{ // 4 FAST STROBE
"mamamamamama",
"mamamamamama",
"mamamamamama"
},
{ // 5 GENTLE PULSE 1
"jklmnopqrstuvwxyzyxwvutsrqponmlkj",
"jklmnopqrstuvwxyzyxwvutsrqponmlkj",
"jklmnopqrstuvwxyzyxwvutsrqponmlkj"
},
{ // 6 FLICKER (second variety)
"nmonqnmomnmomomno",
"nmonqnmomnmomomno",
"nmonqnmomnmomomno"
},
{ // 7 CANDLE (second variety)
"mmmaaaabcdefgmmmmaaaammmaamm",
"mmmaaaabcdefgmmmmaaaammmaamm",
"mmmaaaabcdefgmmmmaaaammmaamm"
},
{ // 8 CANDLE (third variety)
"mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa",
"mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa",
"mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa"
},
{ // 9 SLOW STROBE (fourth variety)
"aaaaaaaazzzzzzzz",
"aaaaaaaazzzzzzzz",
"aaaaaaaazzzzzzzz"
},
{ // 10 FLUORESCENT FLICKER
"mmamammmmammamamaaamammma",
"mmamammmmammamamaaamammma",
"mmamammmmammamamaaamammma"
},
{ // 11 SLOW PULSE NOT FADE TO BLACK
"abcdefghijklmnopqrrqponmlkjihgfedcba",
"abcdefghijklmnopqrrqponmlkjihgfedcba",
"abcdefghijklmnopqrrqponmlkjihgfedcba"
},
{ // 12 FAST PULSE FOR JEREMY
"mkigegik",
"mkigegik",
"mkigegik"
},
{ // 13 Test Blending
"abcdefghijklmqrstuvwxyz",
"zyxwvutsrqmlkjihgfedcba",
"aammbbzzccllcckkffyyggp"
},
{ // 14
"",
"",
""
},
{ // 15
"",
"",
""
},
{ // 16
"",
"",
""
},
{ // 17
"",
"",
""
},
{ // 18
"",
"",
""
},
{ // 19
"",
"",
""
},
{ // 20
"",
"",
""
},
{ // 21
"",
"",
""
},
{ // 22
"",
"",
""
},
{ // 23
"",
"",
""
},
{ // 24
"",
"",
""
},
{ // 25
"",
"",
""
},
{ // 26
"",
"",
""
},
{ // 27
"",
"",
""
},
{ // 28
"",
"",
""
},
{ // 29
"",
"",
""
},
{ // 30
"",
"",
""
},
{ // 31
"",
"",
""
}
};
2013-04-04 18:02:27 +00:00
void *precachedKyle = 0;
2013-04-04 14:52:42 +00:00
/*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 *text, temp[32];
int i;
int lengthRed, lengthBlue, lengthGreen;
G_SpawnString( "classname", "", &text );
if ( Q_stricmp( text, "worldspawn" ) ) {
G_Error( "SP_worldspawn: The first entity isn't 'worldspawn'" );
}
2013-04-04 18:02:27 +00:00
//The server will precache the standard model and animations, so that there is no hit
//when the first client connnects.
if (!BGPAFtextLoaded)
{
BG_ParseAnimationFile("models/players/_humanoid/animation.cfg");
}
if (!precachedKyle)
{
trap_G2API_InitGhoul2Model(&precachedKyle, "models/players/kyle/model.glm", 0, 0, -20, 0, 0);
}
if (!g2SaberInstance)
{
trap_G2API_InitGhoul2Model(&g2SaberInstance, "models/weapons2/saber/saber_w.glm", 0, 0, -20, 0, 0);
if (g2SaberInstance)
{
// indicate we will be bolted to model 0 (ie the player) on bolt 0 (always the right hand) when we get copied
trap_G2API_SetBoltInfo(g2SaberInstance, 0, 0);
// now set up the gun bolt on it
trap_G2API_AddBolt(g2SaberInstance, 0, "*flash");
}
}
2013-04-04 14:52:42 +00:00
// 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", "", &text );
trap_SetConfigstring( CS_MUSIC, text );
G_SpawnString( "message", "", &text );
trap_SetConfigstring( CS_MESSAGE, text ); // map specific message
trap_SetConfigstring( CS_MOTD, g_motd.string ); // message of the day
G_SpawnString( "gravity", "800", &text );
trap_Cvar_Set( "g_gravity", text );
G_SpawnString( "enableDust", "0", &text );
trap_Cvar_Set( "g_enableDust", text );
G_SpawnString( "enableBreath", "0", &text );
trap_Cvar_Set( "g_enableBreath", text );
g_entities[ENTITYNUM_WORLD].s.number = ENTITYNUM_WORLD;
g_entities[ENTITYNUM_WORLD].classname = "worldspawn";
// see if we want a warmup time
trap_SetConfigstring( CS_WARMUP, "" );
if ( g_restarted.integer ) {
trap_Cvar_Set( "g_restarted", "0" );
level.warmupTime = 0;
2013-04-04 18:24:26 +00:00
} else if ( g_doWarmup.integer && g_gametype.integer != GT_TOURNAMENT ) { // Turn it on
2013-04-04 14:52:42 +00:00
level.warmupTime = -1;
trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
G_LogPrintf( "Warmup:\n" );
}
trap_SetConfigstring(CS_LIGHT_STYLES+(LS_STYLES_START*3)+0, defaultStyles[0][0]);
trap_SetConfigstring(CS_LIGHT_STYLES+(LS_STYLES_START*3)+1, defaultStyles[0][1]);
trap_SetConfigstring(CS_LIGHT_STYLES+(LS_STYLES_START*3)+2, defaultStyles[0][2]);
for(i=1;i<LS_NUM_STYLES;i++)
{
Com_sprintf(temp, sizeof(temp), "ls_%dr", i);
G_SpawnString(temp, defaultStyles[i][0], &text);
lengthRed = strlen(text);
trap_SetConfigstring(CS_LIGHT_STYLES+((i+LS_STYLES_START)*3)+0, text);
Com_sprintf(temp, sizeof(temp), "ls_%dg", i);
G_SpawnString(temp, defaultStyles[i][1], &text);
lengthGreen = strlen(text);
trap_SetConfigstring(CS_LIGHT_STYLES+((i+LS_STYLES_START)*3)+1, text);
Com_sprintf(temp, sizeof(temp), "ls_%db", i);
G_SpawnString(temp, defaultStyles[i][2], &text);
lengthBlue = strlen(text);
trap_SetConfigstring(CS_LIGHT_STYLES+((i+LS_STYLES_START)*3)+2, text);
if (lengthRed != lengthGreen || lengthGreen != lengthBlue)
{
Com_Error(ERR_DROP, "Style %d has inconsistent lengths: R %d, G %d, B %d",
i, lengthRed, lengthGreen, lengthBlue);
}
}
}
/*
==============
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
}