rpgxef/code/game/g_spawn.cpp

756 lines
22 KiB
C++
Raw Normal View History

2014-08-13 21:29:27 +00:00
// Copyright (C) 1999-2000 Id Software, Inc.
//
#include "g_local.h"
#include "g_spawn.h"
#include "g_items.h"
#include "g_lua.h"
#include "g_syscalls.h"
2014-08-13 21:29:27 +00:00
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 },
{ "paintarget", FOFS(paintarget), F_LSTRING },
{ "targetname", FOFS(targetname), F_LSTRING },
{ "message", FOFS(message), F_LSTRING },
{ "team", FOFS(team), F_LSTRING },
{ "splashDamage", FOFS(splashDamage), F_INT },
{ "splashRadius", FOFS(splashRadius), F_INT },
{ "wait", FOFS(wait), F_FLOAT },
{ "random", FOFS(random), F_FLOAT },
{ "count", FOFS(count), F_INT },
{ "material", FOFS(s.powerups), 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 },
{ "pos2", FOFS(pos2), F_VECTOR },
{ "apos1", FOFS(apos1), F_VECTOR },
{ "apos2", FOFS(apos2), F_VECTOR },
{ "swapname", FOFS(swapname), F_LSTRING }, //RPG-X Modification | Phenix | 13/06/2004
{ "truename", FOFS(truename), F_LSTRING },
{ "falsename", FOFS(falsename), F_LSTRING },
{ "truetarget", FOFS(truetarget), F_LSTRING },
{ "falsetarget", FOFS(falsetarget), F_LSTRING },
{ "booleanstate", FOFS(booleanstate), F_INT },
{ "distance", FOFS(distance), F_FLOAT }, // VALKYRIE: for rotating doors
{ "targetname2", FOFS(targetname2), F_LSTRING },
{ "bluename", FOFS(bluename), F_LSTRING },
{ "greensnd", FOFS(greensound), F_LSTRING },
{ "yellowsnd", FOFS(yellowsound), F_LSTRING },
{ "redsnd", FOFS(redsound), F_LSTRING },
{ "bluesnd", FOFS(bluesound), F_LSTRING },
{ "targetShaderName", FOFS(targetShaderName), F_LSTRING },
{ "targetShaderNewName", FOFS(targetShaderNewName), F_LSTRING },
#ifdef G_LUA
{ "luaThink", FOFS(luaThink), F_LSTRING },
{ "luaTouch", FOFS(luaTouch), F_LSTRING },
{ "luaUse", FOFS(luaUse), F_LSTRING },
{ "luaHurt", FOFS(luaHurt), F_LSTRING },
{ "luaDie", FOFS(luaDie), F_LSTRING },
{ "luaFree", FOFS(luaFree), F_LSTRING },
{ "luaTrigger", FOFS(luaTrigger), F_LSTRING },
{ "luaReached", FOFS(luaReached), F_LSTRING },
{ "luaReachedAngular", FOFS(luaReachedAngular), F_LSTRING },
{ "luaSpawn", FOFS(luaSpawn), F_LSTRING },
{ "luaParm1", FOFS(luaParm1), F_LSTRING },
{ "luaParm2", FOFS(luaParm2), F_LSTRING },
{ "luaParm3", FOFS(luaParm3), F_LSTRING },
{ "luaParm4", FOFS(luaParm4), F_LSTRING },
{ "luaEntity", FOFS(luaEntity), F_INT },
#endif
{ "startRGBA", FOFS(startRGBA), F_VECTOR4 },
{ "finalRGBA", FOFS(finalRGBA), F_VECTOR4 },
{ NULL }
};
qboolean G_SpawnString(const char *key, const char *defaultString, char **out) {
int i;
if (!level.spawning) {
*out = (char *)defaultString;
}
for (i = 0; i < level.numSpawnVars; i++) {
if (!strcmp(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;
}
typedef struct {
char *name;
void(*spawn)(gentity_t *ent);
} spawn_t;
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_deathmatch },
{ "NPC_BioHulk", SP_info_player_deathmatch },
{ "NPC_starfleet", SP_info_player_deathmatch },
{ "NPC_starfleet_random", SP_info_player_deathmatch },
{ "NPC_Tuvok", SP_info_player_deathmatch },
{ "NPC_Kim", SP_info_player_deathmatch },
{ "NPC_Doctor", SP_info_player_deathmatch },
{ "NPC_Paris", SP_info_player_deathmatch },
{ "NPC_Torres", SP_info_player_deathmatch },
{ "NPC_Janeway", SP_info_player_deathmatch },
{ "NPC_Seven", SP_info_player_deathmatch },
{ "NPC_Chakotay", SP_info_player_deathmatch },
{ "NPC_Neelix", SP_info_player_deathmatch },
{ "NPC_Vorik", SP_info_player_deathmatch },
{ "NPC_Foster", SP_info_player_deathmatch },
{ "NPC_Munro", SP_info_player_deathmatch },
{ "NPC_MunroScav", SP_info_player_deathmatch },
{ "NPC_Telsia", SP_info_player_deathmatch },
{ "NPC_Biessman", SP_info_player_deathmatch },
{ "NPC_Chang", SP_info_player_deathmatch },
{ "NPC_Chell", SP_info_player_deathmatch },
{ "NPC_Jurot", SP_info_player_deathmatch },
{ "NPC_borg", SP_info_player_deathmatch },
{ "NPC_klingon", SP_info_player_deathmatch },
{ "NPC_Malon", SP_info_player_deathmatch },
{ "NPC_Hirogen", SP_info_player_deathmatch },
{ "NPC_Hirogen_Alpha", SP_info_player_deathmatch },
{ "NPC_Imperial", SP_info_player_deathmatch },
{ "NPC_Imperial_Blue", SP_info_player_deathmatch },
{ "NPC_Imperial_Gold", SP_info_player_deathmatch },
{ "NPC_Imperial_Raider", SP_info_player_deathmatch },
{ "NPC_Stasis", SP_info_player_deathmatch },
{ "NPC_Species8472", SP_info_player_deathmatch },
{ "NPC_Reaver", SP_info_player_deathmatch },
{ "NPC_ReaverGuard", SP_info_player_deathmatch },
{ "NPC_Avatar", SP_info_player_deathmatch },
{ "NPC_Vohrsoth", SP_info_player_deathmatch },
{ "NPC_Desperado", SP_info_player_deathmatch },
{ "NPC_Paladin", SP_info_player_deathmatch },
{ "NPC_ChaoticaGuard", SP_info_player_deathmatch },
{ "NPC_Chaotica", SP_info_player_deathmatch },
{ "NPC_CaptainProton", SP_info_player_deathmatch },
{ "NPC_SatansRobot", SP_info_player_deathmatch },
{ "NPC_Buster", SP_info_player_deathmatch },
{ "NPC_Goodheart", SP_info_player_deathmatch },
{ "info_player_deathmatch", SP_info_player_deathmatch },
{ "info_player_intermission", SP_info_player_intermission },
{ "info_null", SP_info_null },
{ "info_notnull", SP_info_notnull },
{ "info_camp", SP_info_camp },
{ "func_plat", SP_func_plat },
{ "func_button", SP_func_button },
{ "func_door", SP_func_door },
{ "func_forcefield", SP_func_forcefield },
{ "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_usable", SP_func_usable },
{ "func_breakable", SP_func_breakable },
{ "func_door_rotating", SP_func_door_rotating },
{ "func_brushmodel", SP_func_brushmodel }, // Hijack me haha
{ "func_lightchange", SP_func_lightchange },
{ "func_targetmover", SP_func_targetmover },
{ "func_stasis_door", SP_func_stasis_door },
// 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_transporter", SP_trigger_transporter },
{ "trigger_radiation", SP_trigger_radiation },
// 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_teleporter", SP_target_teleporter },
{ "target_relay", SP_target_relay },
{ "target_kill", SP_target_kill },
{ "target_position", SP_info_notnull },
{ "target_location", SP_target_location },
{ "target_push", SP_target_push },
{ "target_counter", SP_target_counter },
{ "target_objective", SP_target_objective },
{ "target_boolean", SP_target_boolean }, // RPG-X | Phenix | 13/06/2004
{ "target_gravity", SP_target_gravity }, //RPG-X Phenix/J2J 03/08/04
{ "target_shake", SP_target_shake }, //RPG-X Phenix/J2J 16/11/04
{ "target_evosuit", SP_target_evosuit }, //RPG-X Phenix/J2J 16/11/04 - RedTechie: Fixed a typo you have evo suit pointing to shake function
{ "target_turbolift", SP_target_turbolift },
{ "target_doorlock", SP_target_doorLock }, //RPG-X | GSIO01 | 08/05/2009
{ "target_repair", SP_target_repair }, //RPG-X | GSIO01 | 09/05/2009
{ "target_alert", SP_target_alert }, //RPG-X | GSIO01
{ "target_warp", SP_target_warp }, //RPG-X | GSIO01 | 19/05/2009
{ "target_deactivate", SP_target_deactivate },
{ "target_serverchange", SP_target_serverchange },
{ "target_levelchange", SP_target_levelchange },
{ "target_shaderremap", SP_target_shaderremap },
{ "target_selfdestruct", SP_target_selfdestruct },
{ "target_safezone", SP_target_zone },
{ "target_zone", SP_target_zone },
{ "target_shiphealth", SP_target_shiphealth },
{ "target_sequence", SP_target_sequence },
{ "light", SP_light },
{ "path_corner", SP_path_corner },
{ "misc_teleporter_dest", SP_info_notnull },
{ "misc_model", SP_misc_model },
{ "misc_model_breakable", SP_misc_model_breakable },
{ "misc_portal_surface", SP_misc_portal_surface },
{ "misc_portal_camera", SP_misc_portal_camera },
{ "misc_turret", SP_misc_turret },
{ "misc_laser", SP_laser_arm },
{ "misc_ammo_station", SP_misc_ammo_station },
{ "shooter_rocket", SP_shooter_rocket },
{ "shooter_grenade", SP_shooter_grenade },
{ "shooter_plasma", SP_shooter_plasma },
{ "shooter_torpedo", SP_shooter_torpedo },
{ "team_CTF_redplayer", SP_info_player_deathmatch },
{ "team_CTF_blueplayer", SP_info_player_deathmatch },
{ "team_CTF_redspawn", SP_info_player_deathmatch },
{ "team_CTF_bluespawn", SP_info_player_deathmatch },
// extra Trek stuff
{ "fx_spark", SP_fx_spark },
{ "fx_steam", SP_fx_steam },
{ "fx_bolt", SP_fx_bolt },
{ "fx_transporter", SP_fx_transporter },
{ "fx_drip", SP_fx_drip },
{ "fx_fountain", SP_fx_fountain },
{ "fx_surface_explosion", SP_fx_surface_explosion },
{ "fx_blow_chunks", SP_fx_blow_chunks },
{ "fx_smoke", SP_fx_smoke },
{ "fx_electrical_explosion", SP_fx_electrical_explosion },
{ "fx_phaser", SP_fx_phaser },
{ "fx_torpedo", SP_fx_torpedo },
{ "fx_particle_fire", SP_fx_particleFire },
{ "fx_fire", SP_fx_fire },
// Additional ports from SP by Harry Young
{ "fx_cooking_steam", SP_fx_cooking_steam },
{ "fx_elecfire", SP_fx_electricfire },
//{"fx_forge_bolt", SP_fx_forge_bolt},
//{"fx_plasma", SP_fx_plasma},
//{"fx_energy_stream", SP_fx_stream},
//{"fx_transporter_stream", SP_fx_transporter_stream},
//{"fx_explosion_trail", SP_fx_explosion_trail},
//{"fx_borg_energy_beam", SP_fx_borg_energy_beam},
{ "fx_shimmery_thing", SP_fx_shimmery_thing },
{ "fx_borg_bolt", SP_fx_borg_bolt },
{ "func_mover", SP_func_mover },
{ "path_point", SP_path_point },
// ui entities
{ "ui_transporter", SP_ui_transporter },
{ "ui_msd", SP_ui_msd },
{ "ui_holodeck", SP_ui_holodeck },
{ "ref_tag", SP_info_notnull },
// cinematic entities
{ "cinematic_camera", SP_cinematic_camera },
{ 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 == NULL) {
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)) { // found it
if (item->giType == IT_TEAM && g_gametype.integer != GT_CTF) {
return qfalse;
}
G_SpawnItem(ent, item);
#ifdef G_LUA
if (ent->luaSpawn) {
LuaHook_G_EntitySpawn(ent->luaSpawn, ent->s.number);
}
#endif
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;
}
}
if (Q_stricmp("item_botroam", ent->classname) != 0) {//suppress error message about botroams as those are actually valid
DEVELOPER(G_Printf(S_COLOR_RED "%s doesn't have a spawn function\n", ent->classname););
}
#ifdef G_LUA
if (ent->luaSpawn) {
LuaHook_G_EntitySpawn(ent->luaSpawn, ent->s.number);
}
#endif
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;
if (string == NULL) {
return NULL;
}
l = strlen(string) + 1;
newb = (char *)G_Alloc(l);
if (newb == NULL) {
return NULL;
}
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
===============
*/
qboolean 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], &vec[1], &vec[2], &vec[3]);
((float *)(b + f->ofs))[0] = vec4[0];
((float *)(b + f->ofs))[0] = vec4[1];
((float *)(b + f->ofs))[0] = vec4[2];
((float *)(b + f->ofs))[0] = 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:
return qfalse;
break;
}
return qtrue;
}
}
return qfalse;
}
/*
===================
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", "tournament", "single", "team", "ctf" };
// 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 "notteam" / "notfree" flags
if (g_gametype.integer == GT_SINGLE_PLAYER) {
G_SpawnInt("notsingle", "0", &i);
if (i) {
G_FreeEntity(ent);
return;
}
}
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;
}
}
if (G_SpawnString("gametype", "", &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_VARS");
}
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))) {
Com_Printf(S_COLOR_RED "G_ParseSpawnVars: Keyname - %s\n", keyname);
G_Error("G_ParseSpawnVars: EOF without closing brace");
}
if (keyname[0] == '}') {
break;
}
// parse value
if (!trap_GetEntityToken(com_token, sizeof(com_token))) {
Com_Printf(S_COLOR_RED "G_ParseSpawnVars: Token - %s\n", 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) ?
-----DESCRIPTION-----
Every map should have exactly one worldspawn.
It holds some general information on the map.
-----SPAWNFLAGS-----
none
-----KEYS-----
"music" - path to WAV or MP3 files (e.g. "music\intro.mp3 music\loopfile.mp3")
"gravity" - 800 is default gravity
"message" - Text to print during connection process
Keys irrelevant for RPG-X
"fraglimit" - overrides server's limit
"capturelimit" - overrides server's capturelimit (use with team AddScores)
"timelimit" - overrides server's timelimit
"timelimitWinningTeam" - "red" or "blue" - this team will win when the timelimit runs out
q3map2:
"_blocksize" block size for unconditional BSP subdivisions
"_celshader" use the specified cel shader for the world
"_lightmapscale" set the lightmapscale for the world
"_ignoreleaks" when set, no leak test is performed
"_foghull" must be set to a sky shader when _fog is used
"_fog" if set, the whole map is fogged using the given shader name
"gridsize" resolution of the light grid
"_ambient" amount of ambient light
"_minvertexlight" amount of minimum vertex light
"_mingridlight" amount of minimum grid light
"_minlight" amount of minimum light
"_keepLights" if set, light entities are not stripped from the BSP file when compiling
"_style42rgbgen" |rgbGen|-like shader definition string for light style 42 (works the same way for all style numbers)
"_style42alphagen" |alphaGen|-like shader definition string for light style 42 (works the same way for all style numbers)
*/
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
trap_SetConfigstring(CS_CON_FAIL, rpg_passMessage.string);
G_SpawnString("gravity", "800", &s);
trap_Cvar_Set("g_gravity", s);
//FIXME: in some cases, want to carry over from previous running of this map
G_SpawnString("fraglimit", "0", &s);
if (s && atoi(s) != 0) {
trap_Cvar_Set("fraglimit", s);
}
G_SpawnString("capturelimit", "0", &s);
if (s && atoi(s) != 0) {
trap_Cvar_Set("capturelimit", s);
}
G_SpawnString("timelimit", "0", &s);
if (s && atoi(s) != 0) {
trap_Cvar_Set("timelimit", s);
}
G_SpawnString("timelimitWinningTeam", "", &s);
if (s) {
trap_Cvar_Set("timelimitWinningTeam", s);
}
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) {
level.warmupTime = 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
}