home *** CD-ROM | disk | FTP | other *** search
- // 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 ( !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;
- }
-
-
-
- //
- // 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},
- {"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},
-
- {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_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_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_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 );
-
-
- 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?
-
- // 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_portal_surface", SP_misc_portal_surface},
- {"misc_portal_camera", SP_misc_portal_camera},
-
- {"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},
-
- {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) ) {
- // found it
- if( item->giType == IT_TEAM && g_gametype.integer != GT_CTF ) {
- return qfalse;
- }
- 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;
-
- // 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;
- }
- }
-
- // 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 ) ) ) {
- 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_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;
- } 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
- }
-
-