reaction/code/game/g_spawn.c
2013-01-04 13:27:22 +00:00

995 lines
28 KiB
C

//-----------------------------------------------------------------------------
//
// $Id$
//
//-----------------------------------------------------------------------------
//
// $Log$
// Revision 1.52 2005/09/07 20:27:42 makro
// Entity attachment trees
//
// Revision 1.51 2005/02/15 16:33:39 makro
// Tons of updates (entity tree attachment system, UI vectors)
//
// Revision 1.50 2004/01/26 21:26:08 makro
// no message
//
// Revision 1.49 2003/09/18 00:05:05 makro
// Lens flares. Opendoor trigger_multiple fixes
//
// Revision 1.48 2003/09/10 22:46:05 makro
// Cooler breath puffs. Locked r_fastSky on maps with global fog.
// Some other things I can't remember.
//
// Revision 1.47 2003/09/08 19:19:20 makro
// New code for respawning entities in TP
//
// Revision 1.46 2003/09/07 20:02:51 makro
// no message
//
// Revision 1.45 2003/08/10 20:13:26 makro
// no message
//
// Revision 1.44 2003/04/26 22:33:06 jbravo
// Wratted all calls to G_FreeEnt() to avoid crashing and provide debugging
//
// Revision 1.43 2003/02/27 19:52:34 makro
// dlights
//
// Revision 1.42 2003/01/06 00:23:29 makro
// no message
//
// Revision 1.41 2003/01/05 22:36:50 makro
// Added "inactive" field for entities
// New "target_activate" entity
//
// Revision 1.40 2002/09/01 21:15:08 makro
// Sky portal tweaks
//
// Revision 1.39 2002/08/30 00:00:16 makro
// Sky portals
//
// Revision 1.38 2002/08/25 00:46:52 niceass
// q3f atmosphere
//
// Revision 1.37 2002/07/19 04:29:18 niceass
// typo fix
//
// Revision 1.36 2002/07/13 22:43:59 makro
// Semi-working fog hull, semi-working sky portals (cgame code commented out)
// Basically, semi-working stuff :P
//
// Revision 1.35 2002/06/29 04:15:15 jbravo
// CTF is now CTB. no weapons while the case is in hand other than pistol or knife
//
// Revision 1.34 2002/06/24 05:51:51 jbravo
// CTF mode is now semi working
//
// Revision 1.33 2002/06/21 21:02:07 niceass
// worldspawn laserfog check
//
// Revision 1.32 2002/06/16 20:06:14 jbravo
// Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap"
//
// Revision 1.31 2002/06/16 17:38:00 jbravo
// Removed the MISSIONPACK ifdefs and missionpack only code.
//
// Revision 1.30 2002/06/12 11:14:35 makro
// Fixed knives/pistols not spawning bug
//
// Revision 1.29 2002/06/08 11:41:48 makro
// weapon_grenadelauncher = weapon_pistol
//
// Revision 1.28 2002/05/30 21:18:28 makro
// Bots should reload/bandage when roaming around
// Added "pathtarget" key to all the entities
//
// Revision 1.27 2002/05/25 10:40:31 makro
// Loading screen
//
// Revision 1.26 2002/05/23 15:55:25 makro
// Elevators
//
// Revision 1.25 2002/05/11 12:45:25 makro
// Spectators can go through breakables and doors with
// a targetname or health. Bots should crouch more/jump less
// often when attacking at long range
//
// Revision 1.24 2002/05/05 15:18:02 makro
// Fixed some crash bugs. Bot stuff. Triggerable func_statics.
// Made flags only spawn in CTF mode
//
// Revision 1.23 2002/05/04 06:28:58 makro
// no message
//
// Revision 1.22 2002/05/04 01:03:43 makro
// Bots
//
// Revision 1.21 2002/05/02 23:05:25 makro
// Loading screen. Jump kicks. Bot stuff
//
// Revision 1.20 2002/05/02 12:44:58 makro
// Customizable color for the loading screen text. Bot stuff
//
// Revision 1.19 2002/04/30 12:23:35 jbravo
// Warningfix
//
// Revision 1.18 2002/04/30 11:54:37 makro
// Bots rule ! Also, added clips to give all. Maybe some other things
//
// Revision 1.17 2002/04/23 06:01:39 niceass
// pressure stuff
//
// Revision 1.16 2002/04/20 02:34:57 jbravo
// Changed weapon and ammo classnames at Sze's request
//
// Revision 1.15 2002/04/03 15:51:01 jbravo
// Small warning fixes
//
// Revision 1.14 2002/04/03 03:13:16 blaze
// NEW BREAKABLE CODE - will break all old breakables(wont appear in maps)
//
// Revision 1.13 2002/03/31 03:31:24 jbravo
// Compiler warning cleanups
//
// Revision 1.12 2002/03/23 05:17:43 jbravo
// Major cleanup of game -> cgame communication with LCA vars.
//
// Revision 1.11 2002/03/20 22:58:27 blaze
// changed dlight to light_d
//
// Revision 1.10 2002/03/10 21:42:44 blaze
// allow RQ3_weapon_name to map to weapon_name to make radiant work better
//
// Revision 1.9 2002/01/24 14:20:53 jbravo
// Adding func_explosive and a few new surfaceparms
//
// Revision 1.8 2002/01/11 20:20:58 jbravo
// Adding TP to main branch
//
// Revision 1.7 2002/01/11 19:48:30 jbravo
// Formatted the source in non DOS format.
//
// Revision 1.6 2001/12/31 16:28:42 jbravo
// I made a Booboo with the Log tag.
//
//
//-----------------------------------------------------------------------------
// 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_STRING,
F_VECTOR,
F_ANGLEHACK,
} fieldtype_t;
typedef struct {
char *name;
size_t ofs;
fieldtype_t type;
} field_t;
field_t fields[] = {
{"classname", FOFS(classname), F_STRING},
{"origin", FOFS(s.origin), F_VECTOR},
{"model", FOFS(model), F_STRING},
{"model2", FOFS(model2), F_STRING},
{"spawnflags", FOFS(spawnflags), F_INT},
{"speed", FOFS(speed), F_FLOAT},
{"target", FOFS(target), F_STRING},
{"targetname", FOFS(targetname), F_STRING},
{"message", FOFS(message), F_STRING},
{"team", FOFS(team), F_STRING},
{"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},
//Makro - only used by the sky portal code
{"portalspeed", FOFS(movedir), F_VECTOR},
{"angle", FOFS(s.angles), F_ANGLEHACK},
{"targetShaderName", FOFS(targetShaderName), F_STRING},
{"targetShaderNewName", FOFS(targetShaderNewName), F_STRING},
{"distance", FOFS(distance), F_FLOAT}, // VALKYRIE: for rotating doors
{"targetinactive", FOFS(targetInactive), F_STRING}, // Makro - target to be fired when inactive
{"pathtarget", FOFS(pathtarget), F_STRING}, // Makro - for func_trains
{"inactive", FOFS(inactive), F_INT}, // Makro - for inactive objects
{"activatename", FOFS(activatename), F_STRING},
{"alias", FOFS(alias), F_STRING}, //Makro - entity id strings
{"moveparent", FOFS(moveParent), F_STRING}, //Makro - entity id strings
{"attachto", FOFS(moveParent), F_STRING}, //
{"noreset", FOFS(noreset), F_INT}, //Makro - for entities that shouldn't respawn in TP
{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_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_train(gentity_t * ent);
void SP_func_timer(gentity_t * self);
void SP_func_breakable(gentity_t * ent); //Blaze: Breakable glass
void SP_func_pressure(gentity_t * ent); //NiceAss: pressure entity
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);
//Makro - added
void SP_target_activate(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_dlight(gentity_t * self); // Elder: dlight entity
void SP_misc_lens_flare(gentity_t *ent); //Makro - lens flare
void SP_func_shadow(gentity_t *ent); //Makro - fake shadow
void SP_misc_corona(gentity_t *ent); //Makro - fake shadow
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);
//Makro - sky portals
void SP_misc_sky_portal(gentity_t * ent);
//Blaze: These functions are nolonger here
//void SP_shooter_rocket( gentity_t *ent );
//void SP_shooter_plasma( gentity_t *ent );
//void SP_shooter_grenade( 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_func_door_rotating(gentity_t * ent); // VALKYRIE: for rotating doors
// JBravo: SP_item_botroam doesnt really exsist.
// Makro - still, bots are supposed to use these
void SP_item_botroam(gentity_t * ent) {}
//Blaze: merged func_explosive into func_breakable
// JBravo: adding explosive
//void SP_func_explosive (gentity_t *self);
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},
{"info_null", SP_info_null},
{"info_notnull", SP_info_notnull}, // use target_position instead
{"info_camp", SP_info_camp},
{"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}, // Blaze: Breakable glass
{"func_pressure", SP_func_pressure}, // NiceAss: pressure entity
// 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},
//Makro - added
{"target_activate", SP_target_activate},
{"target_position", SP_target_position},
{"target_location", SP_target_location},
{"target_push", SP_target_push},
{"light", SP_light},
{"func_dlite", SP_dlight}, // Elder: dlight entity
{"light_d", SP_dlight}, //Makro - for compatibility with older maps
{"func_shadow", SP_func_shadow}, //Makro - fake shadow
{"misc_corona", SP_misc_corona}, //Makro - coronas
{"misc_lens_flare", SP_misc_lens_flare}, //Makro - lens flare
{"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},
//Makro - sky portal !
{"misc_sky_portal", SP_misc_sky_portal},
//Blaze: This removes rocket traps I think
// {"shooter_rocket", SP_shooter_rocket},
// {"shooter_grenade", SP_shooter_grenade},
// {"shooter_plasma", SP_shooter_plasma},
{"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},
{"func_door_rotating", SP_func_door_rotating}, // VALKYRIE: for rotating doors
//Blaze: Merged func_explosive into func_breakable
// {"func_explosive", SP_func_explosive}, // JBravo: for explosive.
{"item_botroam", SP_item_botroam},
{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;
gitem_t *item;
if (!ent->classname) {
G_Printf("G_CallSpawn: NULL classname\n");
return qfalse;
}
//Blaze: allow for Reaction specific spawns to be used
//Elder: map Q3DM weapons -> RQ3 weapons
if (!strcmp(ent->classname, "weapon_gauntlet"))
ent->classname = "weapon_knife";
else if (!strcmp(ent->classname, "weapon_machinegun"))
ent->classname = "weapon_pistol";
else if (!strcmp(ent->classname, "weapon_shotgun"))
ent->classname = "weapon_m3";
else if (!strcmp(ent->classname, "weapon_plasmagun"))
ent->classname = "weapon_mp5";
else if (!strcmp(ent->classname, "weapon_rocketlauncher"))
ent->classname = "weapon_handcannon";
else if (!strcmp(ent->classname, "weapon_railgun"))
ent->classname = "weapon_ssg3000";
else if (!strcmp(ent->classname, "weapon_bfg"))
ent->classname = "weapon_m4";
else if (!strcmp(ent->classname, "ammo_grenades"))
ent->classname = "weapon_grenade";
//Makro - this was missing
else if (!strcmp(ent->classname, "weapon_grenadelauncher"))
ent->classname = "weapon_pistol";
//Elder: map Q3DM ammo -> RQ3 ammo
if (!strcmp(ent->classname, "ammo_bullets"))
ent->classname = "ammo_mk23";
else if (!strcmp(ent->classname, "ammo_slugs"))
ent->classname = "ammo_ssg3000";
else if (!strcmp(ent->classname, "ammo_cells"))
ent->classname = "ammo_mp5";
else if (!strcmp(ent->classname, "ammo_bfg"))
ent->classname = "ammo_m4";
else if (!strcmp(ent->classname, "ammo_rockets"))
ent->classname = "ammo_shells";
//Blaze: let us support RQ3_weapon name so radiant works nicer
if (!strcmp(ent->classname, "RQ3weapon_knife"))
ent->classname = "weapon_knife";
else if (!strcmp(ent->classname, "RQ3weapon_pistol"))
ent->classname = "weapon_pistol";
else if (!strcmp(ent->classname, "RQ3weapon_m3"))
ent->classname = "weapon_m3";
else if (!strcmp(ent->classname, "RQ3weapon_mp5"))
ent->classname = "weapon_mp5";
else if (!strcmp(ent->classname, "RQ3weapon_handcannon"))
ent->classname = "weapon_handcannon";
else if (!strcmp(ent->classname, "RQ3weapon_ssg3000"))
ent->classname = "weapon_ssg3000";
else if (!strcmp(ent->classname, "RQ3weapon_m4"))
ent->classname = "weapon_m4";
else if (!strcmp(ent->classname, "RQ3weapon_grenade"))
ent->classname = "weapon_grenade";
// JBravo: Briefcases
else if (!strcmp(ent->classname, "RQ3case_black"))
ent->classname = "team_CTF_blueflag";
else if (!strcmp(ent->classname, "RQ3case_silver"))
ent->classname = "team_CTF_redflag";
//Elder: map Q3DM ammo -> RQ3 ammo
if (!strcmp(ent->classname, "RQ3ammo_mk23"))
ent->classname = "ammo_mk23";
else if (!strcmp(ent->classname, "RQ3ammo_ssg3000"))
ent->classname = "ammo_ssg3000";
else if (!strcmp(ent->classname, "RQ3ammo_mp5"))
ent->classname = "ammo_mp5";
else if (!strcmp(ent->classname, "RQ3ammo_m4"))
ent->classname = "ammo_m4";
else if (!strcmp(ent->classname, "RQ3ammo_shells"))
ent->classname = "ammo_shells";
/*
//Elder: old stuff
if (!strcmp(ent->classname,"weapon_gauntlet")) ent->classname = "weapon_knife";
else if (!strcmp(ent->classname,"weapon_railgun")) ent->classname = "weapon_ssg3000";
else if (!strcmp(ent->classname,"weapon_shotgun")) ent->classname = "weapon_m3";
else if (!strcmp(ent->classname,"weapon_machinegun")) ent->classname = "weapon_mp5";
else if (!strcmp(ent->classname,"weapon_rocketlauncher")) ent->classname = "weapon_handcannon";
else if (!strcmp(ent->classname,"weapon_bfg")) ent->classname = "weapon_m4";
else if (!strcmp(ent->classname,"weapon_grenadelauncher")) ent->classname = "weapon_pistol";
else if (!strcmp(ent->classname,"ammo_grenades")) ent->classname = "weapon_grenade";
*/
// check item spawn functions
// JBravo: No weapons and items on the maps in teamplay
/*
if (g_gametype.integer != GT_TEAMPLAY) {
for ( item=bg_itemlist+1 ; item->classname ; item++ ) {
if ( !strcmp(item->classname, ent->classname) ) {
G_SpawnItem( ent, item );
return qtrue;
}
}
} */
//Makro - new code
for (item = bg_itemlist + 1; item->classname; item++) {
if (!strcmp(item->classname, ent->classname)) {
//if it's a flag
if (item->giType == IT_TEAM && (item->giTag == PW_REDFLAG || item->giTag == PW_BLUEFLAG)) {
//only spawn it in CTF
if (g_gametype.integer == GT_CTF) {
G_SpawnItem(ent, item);
}
return qtrue;
} else {
// JBravo: no spawning in CTF
if (g_gametype.integer != GT_CTF) {
G_SpawnItem(ent, item);
G_BotOnlyItem(ent);
}
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;
}
}
// JBravo: getting rid of warnings when the game starts up.
// JBravo: FIXME! This is just supressing the message, not the problem.
if (g_gametype.integer != GT_TEAMPLAY)
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_STRING:
*(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;
}
}
}
#define ADJUST_AREAPORTAL() \
if(ent->s.eType == ET_MOVER) \
{ \
trap_LinkEntity(ent); \
trap_AdjustAreaPortalState(ent, qtrue); \
}
/*
===================
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;
// JBravo: added teamplay
static char *gametypeNames[] =
{ "ffa", "tournament", "single", "team", "teamplay", "ctf", "oneflag", "obelisk", "harvester",
"teamtournament"
};
// 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) {
ADJUST_AREAPORTAL();
G_FreeEntity(ent);
return;
}
}
//Makro - check for "notgametype" key
if (G_SpawnInt("notgametype", "0", &i)) {
if ((i & (1 << g_gametype.integer)) != 0) {
ADJUST_AREAPORTAL();
G_FreeEntity(ent);
return;
}
// check for "notteam" flag (GT_FFA, GT_TOURNAMENT, GT_SINGLE_PLAYER)
} else if (g_gametype.integer >= GT_TEAM) {
G_SpawnInt("notteam", "0", &i);
if (i) {
ADJUST_AREAPORTAL();
G_FreeEntity(ent);
return;
}
} else {
G_SpawnInt("notfree", "0", &i);
if (i) {
ADJUST_AREAPORTAL();
G_FreeEntity(ent);
return;
}
}
G_SpawnInt("notq3a", "0", &i);
if (i) {
ADJUST_AREAPORTAL();
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) {
ADJUST_AREAPORTAL();
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);
}
//Makro - is the entity in a sky portal ?
if (G_SpawnInt("skyportalent", "0", &i)) {
if (i) {
ent->s.eFlags |= EF_HEADLESS;
}
}
}
/*
====================
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_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;
vec3_t color;
//int nodetail = 0;
int i;
char info[MAX_INFO_STRING];
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
/* Makro - no longer
//Makro - color for the loading screen text
G_SpawnVector( "_text_color", "0.75 0.75 0.75", color );
Info_SetValueForKey(info, "r1", va("%f", color[0]));
Info_SetValueForKey(info, "g1", va("%f", color[1]));
Info_SetValueForKey(info, "b1", va("%f", color[2]));
G_SpawnVector( "_text_color2", "1 1 1", color );
Info_SetValueForKey(info, "r2", va("%f", color[0]));
Info_SetValueForKey(info, "g2", va("%f", color[1]));
Info_SetValueForKey(info, "b2", va("%f", color[2]));
//skip detail ?
G_SpawnInt( "nodetail", "0", &nodetail );
Info_SetValueForKey(info, "nodetail", va("%i", nodetail));
//save settings
trap_SetConfigstring( CS_LOADINGSCREEN, info );
*/
//Makro - fog hull replacement
if (G_SpawnVector("_rq3_fog_color", "0 0 0", color)) {
memset(info, 0, sizeof(info));
Info_SetValueForKey(info, "r", va("%f", color[0]));
Info_SetValueForKey(info, "g", va("%f", color[1]));
Info_SetValueForKey(info, "b", va("%f", color[2]));
trap_SetConfigstring( CS_FOGHULL, info );
}
trap_SetConfigstring(CS_MOTD, g_motd.string); // message of the day
G_SpawnString("gravity", "800", &s);
trap_Cvar_Set("g_gravity", s);
G_SpawnString("enableDust", "0", &s);
trap_Cvar_Set("g_enableDust", s);
G_SpawnString("enableBreath", "0", &s);
trap_Cvar_Set("g_enableBreath", s);
//Makro - read func_breakable types
if (G_SpawnInt("numbreakabletypes", "0", &i)) {
int j;
for (j=0; j<i && j<RQ3_MAX_BREAKABLES; j++) {
char *type, *force, *lift;
if (!G_SpawnString(va("b%d_type", j), "", &type))
continue;
G_SpawnString(va("b%d_force", j), "7", &force);
G_SpawnString(va("b%d_lift", j), "5", &lift);
info[0] = 0;
Info_SetValueForKey(info, "type", type);
Info_SetValueForKey(info, "velocity", force);
Info_SetValueForKey(info, "jump", lift);
trap_SetConfigstring(CS_BREAKABLES + j, info);
}
}
// q3f atmospheric stuff:
G_SpawnString( "atmosphere", "", &s );
trap_SetConfigstring( CS_ATMOSEFFECT, s ); // Atmospheric effect
G_SpawnString("enableLaserFog", "1", &s);
trap_Cvar_Set("g_enableLaserFog", s);
g_entities[ENTITYNUM_WORLD].s.number = ENTITYNUM_WORLD;
g_entities[ENTITYNUM_WORLD].r.ownerNum = ENTITYNUM_NONE;
g_entities[ENTITYNUM_WORLD].classname = "worldspawn";
g_entities[ENTITYNUM_NONE].s.number = ENTITYNUM_NONE;
g_entities[ENTITYNUM_NONE].r.ownerNum = ENTITYNUM_NONE;
g_entities[ENTITYNUM_NONE].classname = "nothing";
// see if we want a warmup time
trap_SetConfigstring(CS_WARMUP, "");
if (g_restarted.integer) {
trap_Cvar_Set("g_restarted", "0");
level.warmupTime = 0;
} else if (g_doWarmup.integer) { // Turn it on
level.warmupTime = -1;
trap_SetConfigstring(CS_WARMUP, va("%i", level.warmupTime));
G_LogPrintf("Warmup:\n");
}
}
/*
==============
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
}