home *** CD-ROM | disk | FTP | other *** search
- // Copyright (C) 1999-2000 Id Software, Inc.
- //
- // g_utils.c -- misc utility functions for game module
-
- #include "g_local.h"
-
- /*
- =========================================================================
-
- model / sound configstring indexes
-
- =========================================================================
- */
-
- /*
- ================
- G_FindConfigstringIndex
-
- ================
- */
- int G_FindConfigstringIndex( char *name, int start, int max, qboolean create ) {
- int i;
- char s[MAX_STRING_CHARS];
-
- if ( !name || !name[0] ) {
- return 0;
- }
-
- for ( i=1 ; i<max ; i++ ) {
- trap_GetConfigstring( start + i, s, sizeof( s ) );
- if ( !s[0] ) {
- break;
- }
- if ( !strcmp( s, name ) ) {
- return i;
- }
- }
-
- if ( !create ) {
- return 0;
- }
-
- if ( i == max ) {
- G_Error( "G_FindConfigstringIndex: overflow" );
- }
-
- trap_SetConfigstring( start + i, name );
-
- return i;
- }
-
-
- int G_ModelIndex( char *name ) {
- return G_FindConfigstringIndex (name, CS_MODELS, MAX_MODELS, qtrue);
- }
-
- int G_SoundIndex( char *name ) {
- return G_FindConfigstringIndex (name, CS_SOUNDS, MAX_SOUNDS, qtrue);
- }
-
- //=====================================================================
-
-
- /*
- ================
- G_TeamCommand
-
- Broadcasts a command to only a specific team
- ================
- */
- void G_TeamCommand( team_t team, char *cmd ) {
- int i;
-
- for ( i = 0 ; i < level.maxclients ; i++ ) {
- if ( level.clients[i].pers.connected == CON_CONNECTED ) {
- if ( level.clients[i].sess.sessionTeam == team ) {
- trap_SendServerCommand( i, va("%s", cmd ));
- }
- }
- }
- }
-
-
- /*
- =============
- G_Find
-
- Searches all active entities for the next one that holds
- the matching string at fieldofs (use the FOFS() macro) in the structure.
-
- Searches beginning at the entity after from, or the beginning if NULL
- NULL will be returned if the end of the list is reached.
-
- =============
- */
- gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match)
- {
- char *s;
-
- if (!from)
- from = g_entities;
- else
- from++;
-
- for ( ; from < &g_entities[level.num_entities] ; from++)
- {
- if (!from->inuse)
- continue;
- s = *(char **) ((byte *)from + fieldofs);
- if (!s)
- continue;
- if (!Q_stricmp (s, match))
- return from;
- }
-
- return NULL;
- }
-
-
- /*
- =============
- G_PickTarget
-
- Selects a random entity from among the targets
- =============
- */
- #define MAXCHOICES 32
-
- gentity_t *G_PickTarget (char *targetname)
- {
- gentity_t *ent = NULL;
- int num_choices = 0;
- gentity_t *choice[MAXCHOICES];
-
- if (!targetname)
- {
- G_Printf("G_PickTarget called with NULL targetname\n");
- return NULL;
- }
-
- while(1)
- {
- ent = G_Find (ent, FOFS(targetname), targetname);
- if (!ent)
- break;
- choice[num_choices++] = ent;
- if (num_choices == MAXCHOICES)
- break;
- }
-
- if (!num_choices)
- {
- G_Printf("G_PickTarget: target %s not found\n", targetname);
- return NULL;
- }
-
- return choice[rand() % num_choices];
- }
-
-
- /*
- ==============================
- G_UseTargets
-
- "activator" should be set to the entity that initiated the firing.
-
- Search for (string)targetname in all entities that
- match (string)self.target and call their .use function
-
- ==============================
- */
- void G_UseTargets( gentity_t *ent, gentity_t *activator ) {
- gentity_t *t;
-
- if ( !ent ) {
- return;
- }
- if ( !ent->target ) {
- return;
- }
-
- t = NULL;
- while ( (t = G_Find (t, FOFS(targetname), ent->target)) != NULL ) {
- if ( t == ent ) {
- G_Printf ("WARNING: Entity used itself.\n");
- } else {
- if ( t->use ) {
- t->use (t, ent, activator);
- }
- }
- if ( !ent->inuse ) {
- G_Printf("entity was removed while using targets\n");
- return;
- }
- }
- }
-
-
- /*
- =============
- TempVector
-
- This is just a convenience function
- for making temporary vectors for function calls
- =============
- */
- float *tv( float x, float y, float z ) {
- static int index;
- static vec3_t vecs[8];
- float *v;
-
- // use an array so that multiple tempvectors won't collide
- // for a while
- v = vecs[index];
- index = (index + 1)&7;
-
- v[0] = x;
- v[1] = y;
- v[2] = z;
-
- return v;
- }
-
-
- /*
- =============
- VectorToString
-
- This is just a convenience function
- for printing vectors
- =============
- */
- char *vtos( const vec3_t v ) {
- static int index;
- static char str[8][32];
- char *s;
-
- // use an array so that multiple vtos won't collide
- s = str[index];
- index = (index + 1)&7;
-
- Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
-
- return s;
- }
-
-
- /*
- ===============
- G_SetMovedir
-
- The editor only specifies a single value for angles (yaw),
- but we have special constants to generate an up or down direction.
- Angles will be cleared, because it is being used to represent a direction
- instead of an orientation.
- ===============
- */
- void G_SetMovedir( vec3_t angles, vec3_t movedir ) {
- static vec3_t VEC_UP = {0, -1, 0};
- static vec3_t MOVEDIR_UP = {0, 0, 1};
- static vec3_t VEC_DOWN = {0, -2, 0};
- static vec3_t MOVEDIR_DOWN = {0, 0, -1};
-
- if ( VectorCompare (angles, VEC_UP) ) {
- VectorCopy (MOVEDIR_UP, movedir);
- } else if ( VectorCompare (angles, VEC_DOWN) ) {
- VectorCopy (MOVEDIR_DOWN, movedir);
- } else {
- AngleVectors (angles, movedir, NULL, NULL);
- }
- VectorClear( angles );
- }
-
-
- float vectoyaw( const vec3_t vec ) {
- float yaw;
-
- if (vec[YAW] == 0 && vec[PITCH] == 0) {
- yaw = 0;
- } else {
- if (vec[PITCH]) {
- yaw = ( atan2( vec[YAW], vec[PITCH]) * 180 / M_PI );
- } else if (vec[YAW] > 0) {
- yaw = 90;
- } else {
- yaw = 270;
- }
- if (yaw < 0) {
- yaw += 360;
- }
- }
-
- return yaw;
- }
-
-
- void G_InitGentity( gentity_t *e ) {
- e->inuse = qtrue;
- e->classname = "noclass";
- e->s.number = e - g_entities;
- e->r.ownerNum = ENTITYNUM_NONE;
- }
-
- /*
- =================
- G_Spawn
-
- Either finds a free entity, or allocates a new one.
-
- The slots from 0 to MAX_CLIENTS-1 are always reserved for clients, and will
- never be used by anything else.
-
- Try to avoid reusing an entity that was recently freed, because it
- can cause the client to think the entity morphed into something else
- instead of being removed and recreated, which can cause interpolated
- angles and bad trails.
- =================
- */
- gentity_t *G_Spawn( void ) {
- int i, force;
- gentity_t *e;
-
- e = NULL; // shut up warning
- i = 0; // shut up warning
- for ( force = 0 ; force < 2 ; force++ ) {
- // if we go through all entities and can't find one to free,
- // override the normal minimum times before use
- e = &g_entities[MAX_CLIENTS];
- for ( i = MAX_CLIENTS ; i<level.num_entities ; i++, e++) {
- if ( e->inuse ) {
- continue;
- }
-
- // the first couple seconds of server time can involve a lot of
- // freeing and allocating, so relax the replacement policy
- if ( !force && e->freetime > level.startTime + 2000 && level.time - e->freetime < 1000 ) {
- continue;
- }
-
- // reuse this slot
- G_InitGentity( e );
- return e;
- }
- if ( i != MAX_GENTITIES ) {
- break;
- }
- }
- if ( i == ENTITYNUM_MAX_NORMAL ) {
- G_Error( "G_Spawn: no free entities" );
- }
-
- // open up a new slot
- level.num_entities++;
-
- // let the server system know that there are more entities
- trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ),
- &level.clients[0].ps, sizeof( level.clients[0] ) );
-
- G_InitGentity( e );
- return e;
- }
-
-
- /*
- =================
- G_FreeEntity
-
- Marks the entity as free
- =================
- */
- void G_FreeEntity( gentity_t *ed ) {
- trap_UnlinkEntity (ed); // unlink from world
-
- if ( ed->neverFree ) {
- return;
- }
-
- memset (ed, 0, sizeof(*ed));
- ed->classname = "freed";
- ed->freetime = level.time;
- ed->inuse = qfalse;
- }
-
- /*
- =================
- G_TempEntity
-
- Spawns an event entity that will be auto-removed
- The origin will be snapped to save net bandwidth, so care
- must be taken if the origin is right on a surface (snap towards start vector first)
- =================
- */
- gentity_t *G_TempEntity( vec3_t origin, int event ) {
- gentity_t *e;
- vec3_t snapped;
-
- e = G_Spawn();
- e->s.eType = ET_EVENTS + event;
-
- e->classname = "tempEntity";
- e->eventTime = level.time;
- e->freeAfterEvent = qtrue;
-
- VectorCopy( origin, snapped );
- SnapVector( snapped ); // save network bandwidth
- G_SetOrigin( e, snapped );
-
- // find cluster for PVS
- trap_LinkEntity( e );
-
- return e;
- }
-
-
-
- /*
- ==============================================================================
-
- Kill box
-
- ==============================================================================
- */
-
- /*
- =================
- G_KillBox
-
- Kills all entities that would touch the proposed new positioning
- of ent. Ent should be unlinked before calling this!
- =================
- */
- void G_KillBox (gentity_t *ent) {
- int i, num;
- int touch[MAX_GENTITIES];
- gentity_t *hit;
- vec3_t mins, maxs;
-
- VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
- VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
- num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
-
- for (i=0 ; i<num ; i++) {
- hit = &g_entities[touch[i]];
- if ( !hit->client ) {
- continue;
- }
-
- // nail it
- G_Damage ( hit, ent, ent, NULL, NULL,
- 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
- }
-
- }
-
- //==============================================================================
-
- /*
- ===============
- G_AddPredictableEvent
-
- Use for non-pmove events that would also be predicted on the
- client side: jumppads and item pickups
- Adds an event+parm and twiddles the event counter
- ===============
- */
- void G_AddPredictableEvent( gentity_t *ent, int event, int eventParm ) {
- if ( !ent->client ) {
- return;
- }
- BG_AddPredictableEventToPlayerstate( event, eventParm, &ent->client->ps );
- }
-
-
- /*
- ===============
- G_AddEvent
-
- Adds an event+parm and twiddles the event counter
- ===============
- */
- void G_AddEvent( gentity_t *ent, int event, int eventParm ) {
- int bits;
-
- if ( !event ) {
- G_Printf( "G_AddEvent: zero event added for entity %i\n", ent->s.number );
- return;
- }
-
- // clients need to add the event in playerState_t instead of entityState_t
- if ( ent->client ) {
- bits = ent->client->ps.externalEvent & EV_EVENT_BITS;
- bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
- ent->client->ps.externalEvent = event | bits;
- ent->client->ps.externalEventParm = eventParm;
- ent->client->ps.externalEventTime = level.time;
- } else {
- bits = ent->s.event & EV_EVENT_BITS;
- bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
- ent->s.event = event | bits;
- ent->s.eventParm = eventParm;
- }
- ent->eventTime = level.time;
- }
-
-
- /*
- =============
- G_Sound
- =============
- */
- void G_Sound( gentity_t *ent, int channel, int soundIndex ) {
- gentity_t *te;
-
- te = G_TempEntity( ent->r.currentOrigin, EV_GENERAL_SOUND );
- te->s.eventParm = soundIndex;
- }
-
-
- //==============================================================================
-
-
- /*
- ================
- G_SetOrigin
-
- Sets the pos trajectory for a fixed position
- ================
- */
- void G_SetOrigin( gentity_t *ent, vec3_t origin ) {
- VectorCopy( origin, ent->s.pos.trBase );
- ent->s.pos.trType = TR_STATIONARY;
- ent->s.pos.trTime = 0;
- ent->s.pos.trDuration = 0;
- VectorClear( ent->s.pos.trDelta );
-
- VectorCopy( origin, ent->r.currentOrigin );
- }
-