home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / g_utils.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  11.2 KB  |  538 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // g_utils.c -- misc utility functions for game module
  4.  
  5. #include "g_local.h"
  6.  
  7. /*
  8. =========================================================================
  9.  
  10. model / sound configstring indexes
  11.  
  12. =========================================================================
  13. */
  14.  
  15. /*
  16. ================
  17. G_FindConfigstringIndex
  18.  
  19. ================
  20. */
  21. int G_FindConfigstringIndex( char *name, int start, int max, qboolean create ) {
  22.     int        i;
  23.     char    s[MAX_STRING_CHARS];
  24.  
  25.     if ( !name || !name[0] ) {
  26.         return 0;
  27.     }
  28.  
  29.     for ( i=1 ; i<max ; i++ ) {
  30.         trap_GetConfigstring( start + i, s, sizeof( s ) );
  31.         if ( !s[0] ) {
  32.             break;
  33.         }
  34.         if ( !strcmp( s, name ) ) {
  35.             return i;
  36.         }
  37.     }
  38.  
  39.     if ( !create ) {
  40.         return 0;
  41.     }
  42.  
  43.     if ( i == max ) {
  44.         G_Error( "G_FindConfigstringIndex: overflow" );
  45.     }
  46.  
  47.     trap_SetConfigstring( start + i, name );
  48.  
  49.     return i;
  50. }
  51.  
  52.  
  53. int G_ModelIndex( char *name ) {
  54.     return G_FindConfigstringIndex (name, CS_MODELS, MAX_MODELS, qtrue);
  55. }
  56.  
  57. int G_SoundIndex( char *name ) {
  58.     return G_FindConfigstringIndex (name, CS_SOUNDS, MAX_SOUNDS, qtrue);
  59. }
  60.  
  61. //=====================================================================
  62.  
  63.  
  64. /*
  65. ================
  66. G_TeamCommand
  67.  
  68. Broadcasts a command to only a specific team
  69. ================
  70. */
  71. void G_TeamCommand( team_t team, char *cmd ) {
  72.     int        i;
  73.  
  74.     for ( i = 0 ; i < level.maxclients ; i++ ) {
  75.         if ( level.clients[i].pers.connected == CON_CONNECTED ) {
  76.             if ( level.clients[i].sess.sessionTeam == team ) {
  77.                 trap_SendServerCommand( i, va("%s", cmd ));
  78.             }
  79.         }
  80.     }
  81. }
  82.  
  83.  
  84. /*
  85. =============
  86. G_Find
  87.  
  88. Searches all active entities for the next one that holds
  89. the matching string at fieldofs (use the FOFS() macro) in the structure.
  90.  
  91. Searches beginning at the entity after from, or the beginning if NULL
  92. NULL will be returned if the end of the list is reached.
  93.  
  94. =============
  95. */
  96. gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match)
  97. {
  98.     char    *s;
  99.  
  100.     if (!from)
  101.         from = g_entities;
  102.     else
  103.         from++;
  104.  
  105.     for ( ; from < &g_entities[level.num_entities] ; from++)
  106.     {
  107.         if (!from->inuse)
  108.             continue;
  109.         s = *(char **) ((byte *)from + fieldofs);
  110.         if (!s)
  111.             continue;
  112.         if (!Q_stricmp (s, match))
  113.             return from;
  114.     }
  115.  
  116.     return NULL;
  117. }
  118.  
  119.  
  120. /*
  121. =============
  122. G_PickTarget
  123.  
  124. Selects a random entity from among the targets
  125. =============
  126. */
  127. #define MAXCHOICES    32
  128.  
  129. gentity_t *G_PickTarget (char *targetname)
  130. {
  131.     gentity_t    *ent = NULL;
  132.     int        num_choices = 0;
  133.     gentity_t    *choice[MAXCHOICES];
  134.  
  135.     if (!targetname)
  136.     {
  137.         G_Printf("G_PickTarget called with NULL targetname\n");
  138.         return NULL;
  139.     }
  140.  
  141.     while(1)
  142.     {
  143.         ent = G_Find (ent, FOFS(targetname), targetname);
  144.         if (!ent)
  145.             break;
  146.         choice[num_choices++] = ent;
  147.         if (num_choices == MAXCHOICES)
  148.             break;
  149.     }
  150.  
  151.     if (!num_choices)
  152.     {
  153.         G_Printf("G_PickTarget: target %s not found\n", targetname);
  154.         return NULL;
  155.     }
  156.  
  157.     return choice[rand() % num_choices];
  158. }
  159.  
  160.  
  161. /*
  162. ==============================
  163. G_UseTargets
  164.  
  165. "activator" should be set to the entity that initiated the firing.
  166.  
  167. Search for (string)targetname in all entities that
  168. match (string)self.target and call their .use function
  169.  
  170. ==============================
  171. */
  172. void G_UseTargets( gentity_t *ent, gentity_t *activator ) {
  173.     gentity_t        *t;
  174.     
  175.     if ( !ent ) {
  176.         return;
  177.     }
  178.     if ( !ent->target ) {
  179.         return;
  180.     }
  181.  
  182.     t = NULL;
  183.     while ( (t = G_Find (t, FOFS(targetname), ent->target)) != NULL ) {
  184.         if ( t == ent ) {
  185.             G_Printf ("WARNING: Entity used itself.\n");
  186.         } else {
  187.             if ( t->use ) {
  188.                 t->use (t, ent, activator);
  189.             }
  190.         }
  191.         if ( !ent->inuse ) {
  192.             G_Printf("entity was removed while using targets\n");
  193.             return;
  194.         }
  195.     }
  196. }
  197.  
  198.  
  199. /*
  200. =============
  201. TempVector
  202.  
  203. This is just a convenience function
  204. for making temporary vectors for function calls
  205. =============
  206. */
  207. float    *tv( float x, float y, float z ) {
  208.     static    int        index;
  209.     static    vec3_t    vecs[8];
  210.     float    *v;
  211.  
  212.     // use an array so that multiple tempvectors won't collide
  213.     // for a while
  214.     v = vecs[index];
  215.     index = (index + 1)&7;
  216.  
  217.     v[0] = x;
  218.     v[1] = y;
  219.     v[2] = z;
  220.  
  221.     return v;
  222. }
  223.  
  224.  
  225. /*
  226. =============
  227. VectorToString
  228.  
  229. This is just a convenience function
  230. for printing vectors
  231. =============
  232. */
  233. char    *vtos( const vec3_t v ) {
  234.     static    int        index;
  235.     static    char    str[8][32];
  236.     char    *s;
  237.  
  238.     // use an array so that multiple vtos won't collide
  239.     s = str[index];
  240.     index = (index + 1)&7;
  241.  
  242.     Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
  243.  
  244.     return s;
  245. }
  246.  
  247.  
  248. /*
  249. ===============
  250. G_SetMovedir
  251.  
  252. The editor only specifies a single value for angles (yaw),
  253. but we have special constants to generate an up or down direction.
  254. Angles will be cleared, because it is being used to represent a direction
  255. instead of an orientation.
  256. ===============
  257. */
  258. void G_SetMovedir( vec3_t angles, vec3_t movedir ) {
  259.     static vec3_t VEC_UP        = {0, -1, 0};
  260.     static vec3_t MOVEDIR_UP    = {0, 0, 1};
  261.     static vec3_t VEC_DOWN        = {0, -2, 0};
  262.     static vec3_t MOVEDIR_DOWN    = {0, 0, -1};
  263.  
  264.     if ( VectorCompare (angles, VEC_UP) ) {
  265.         VectorCopy (MOVEDIR_UP, movedir);
  266.     } else if ( VectorCompare (angles, VEC_DOWN) ) {
  267.         VectorCopy (MOVEDIR_DOWN, movedir);
  268.     } else {
  269.         AngleVectors (angles, movedir, NULL, NULL);
  270.     }
  271.     VectorClear( angles );
  272. }
  273.  
  274.  
  275. float vectoyaw( const vec3_t vec ) {
  276.     float    yaw;
  277.     
  278.     if (vec[YAW] == 0 && vec[PITCH] == 0) {
  279.         yaw = 0;
  280.     } else {
  281.         if (vec[PITCH]) {
  282.             yaw = ( atan2( vec[YAW], vec[PITCH]) * 180 / M_PI );
  283.         } else if (vec[YAW] > 0) {
  284.             yaw = 90;
  285.         } else {
  286.             yaw = 270;
  287.         }
  288.         if (yaw < 0) {
  289.             yaw += 360;
  290.         }
  291.     }
  292.  
  293.     return yaw;
  294. }
  295.  
  296.  
  297. void G_InitGentity( gentity_t *e ) {
  298.     e->inuse = qtrue;
  299.     e->classname = "noclass";
  300.     e->s.number = e - g_entities;
  301.     e->r.ownerNum = ENTITYNUM_NONE;
  302. }
  303.  
  304. /*
  305. =================
  306. G_Spawn
  307.  
  308. Either finds a free entity, or allocates a new one.
  309.  
  310.   The slots from 0 to MAX_CLIENTS-1 are always reserved for clients, and will
  311. never be used by anything else.
  312.  
  313. Try to avoid reusing an entity that was recently freed, because it
  314. can cause the client to think the entity morphed into something else
  315. instead of being removed and recreated, which can cause interpolated
  316. angles and bad trails.
  317. =================
  318. */
  319. gentity_t *G_Spawn( void ) {
  320.     int            i, force;
  321.     gentity_t    *e;
  322.  
  323.     e = NULL;    // shut up warning
  324.     i = 0;        // shut up warning
  325.     for ( force = 0 ; force < 2 ; force++ ) {
  326.         // if we go through all entities and can't find one to free,
  327.         // override the normal minimum times before use
  328.         e = &g_entities[MAX_CLIENTS];
  329.         for ( i = MAX_CLIENTS ; i<level.num_entities ; i++, e++) {
  330.             if ( e->inuse ) {
  331.                 continue;
  332.             }
  333.  
  334.             // the first couple seconds of server time can involve a lot of
  335.             // freeing and allocating, so relax the replacement policy
  336.             if ( !force && e->freetime > level.startTime + 2000 && level.time - e->freetime < 1000 ) {
  337.                 continue;
  338.             }
  339.  
  340.             // reuse this slot
  341.             G_InitGentity( e );
  342.             return e;
  343.         }
  344.         if ( i != MAX_GENTITIES ) {
  345.             break;
  346.         }
  347.     }
  348.     if ( i == ENTITYNUM_MAX_NORMAL ) {
  349.         G_Error( "G_Spawn: no free entities" );
  350.     }
  351.     
  352.     // open up a new slot
  353.     level.num_entities++;
  354.  
  355.     // let the server system know that there are more entities
  356.     trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ), 
  357.         &level.clients[0].ps, sizeof( level.clients[0] ) );
  358.  
  359.     G_InitGentity( e );
  360.     return e;
  361. }
  362.  
  363.  
  364. /*
  365. =================
  366. G_FreeEntity
  367.  
  368. Marks the entity as free
  369. =================
  370. */
  371. void G_FreeEntity( gentity_t *ed ) {
  372.     trap_UnlinkEntity (ed);        // unlink from world
  373.  
  374.     if ( ed->neverFree ) {
  375.         return;
  376.     }
  377.  
  378.     memset (ed, 0, sizeof(*ed));
  379.     ed->classname = "freed";
  380.     ed->freetime = level.time;
  381.     ed->inuse = qfalse;
  382. }
  383.  
  384. /*
  385. =================
  386. G_TempEntity
  387.  
  388. Spawns an event entity that will be auto-removed
  389. The origin will be snapped to save net bandwidth, so care
  390. must be taken if the origin is right on a surface (snap towards start vector first)
  391. =================
  392. */
  393. gentity_t *G_TempEntity( vec3_t origin, int event ) {
  394.     gentity_t        *e;
  395.     vec3_t        snapped;
  396.  
  397.     e = G_Spawn();
  398.     e->s.eType = ET_EVENTS + event;
  399.  
  400.     e->classname = "tempEntity";
  401.     e->eventTime = level.time;
  402.     e->freeAfterEvent = qtrue;
  403.  
  404.     VectorCopy( origin, snapped );
  405.     SnapVector( snapped );        // save network bandwidth
  406.     G_SetOrigin( e, snapped );
  407.  
  408.     // find cluster for PVS
  409.     trap_LinkEntity( e );
  410.  
  411.     return e;
  412. }
  413.  
  414.  
  415.  
  416. /*
  417. ==============================================================================
  418.  
  419. Kill box
  420.  
  421. ==============================================================================
  422. */
  423.  
  424. /*
  425. =================
  426. G_KillBox
  427.  
  428. Kills all entities that would touch the proposed new positioning
  429. of ent.  Ent should be unlinked before calling this!
  430. =================
  431. */
  432. void G_KillBox (gentity_t *ent) {
  433.     int            i, num;
  434.     int            touch[MAX_GENTITIES];
  435.     gentity_t    *hit;
  436.     vec3_t        mins, maxs;
  437.  
  438.     VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
  439.     VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
  440.     num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
  441.  
  442.     for (i=0 ; i<num ; i++) {
  443.         hit = &g_entities[touch[i]];
  444.         if ( !hit->client ) {
  445.             continue;
  446.         }
  447.  
  448.         // nail it
  449.         G_Damage ( hit, ent, ent, NULL, NULL,
  450.             100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
  451.     }
  452.  
  453. }
  454.  
  455. //==============================================================================
  456.  
  457. /*
  458. ===============
  459. G_AddPredictableEvent
  460.  
  461. Use for non-pmove events that would also be predicted on the
  462. client side: jumppads and item pickups
  463. Adds an event+parm and twiddles the event counter
  464. ===============
  465. */
  466. void G_AddPredictableEvent( gentity_t *ent, int event, int eventParm ) {
  467.     if ( !ent->client ) {
  468.         return;
  469.     }
  470.     BG_AddPredictableEventToPlayerstate( event, eventParm, &ent->client->ps );
  471. }
  472.  
  473.  
  474. /*
  475. ===============
  476. G_AddEvent
  477.  
  478. Adds an event+parm and twiddles the event counter
  479. ===============
  480. */
  481. void G_AddEvent( gentity_t *ent, int event, int eventParm ) {
  482.     int        bits;
  483.  
  484.     if ( !event ) {
  485.         G_Printf( "G_AddEvent: zero event added for entity %i\n", ent->s.number );
  486.         return;
  487.     }
  488.  
  489.     // clients need to add the event in playerState_t instead of entityState_t
  490.     if ( ent->client ) {
  491.         bits = ent->client->ps.externalEvent & EV_EVENT_BITS;
  492.         bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
  493.         ent->client->ps.externalEvent = event | bits;
  494.         ent->client->ps.externalEventParm = eventParm;
  495.         ent->client->ps.externalEventTime = level.time;
  496.     } else {
  497.         bits = ent->s.event & EV_EVENT_BITS;
  498.         bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
  499.         ent->s.event = event | bits;
  500.         ent->s.eventParm = eventParm;
  501.     }
  502.     ent->eventTime = level.time;
  503. }
  504.  
  505.  
  506. /*
  507. =============
  508. G_Sound
  509. =============
  510. */
  511. void G_Sound( gentity_t *ent, int channel, int soundIndex ) {
  512.     gentity_t    *te;
  513.  
  514.     te = G_TempEntity( ent->r.currentOrigin, EV_GENERAL_SOUND );
  515.     te->s.eventParm = soundIndex;
  516. }
  517.  
  518.  
  519. //==============================================================================
  520.  
  521.  
  522. /*
  523. ================
  524. G_SetOrigin
  525.  
  526. Sets the pos trajectory for a fixed position
  527. ================
  528. */
  529. void G_SetOrigin( gentity_t *ent, vec3_t origin ) {
  530.     VectorCopy( origin, ent->s.pos.trBase );
  531.     ent->s.pos.trType = TR_STATIONARY;
  532.     ent->s.pos.trTime = 0;
  533.     ent->s.pos.trDuration = 0;
  534.     VectorClear( ent->s.pos.trDelta );
  535.  
  536.     VectorCopy( origin, ent->r.currentOrigin );
  537. }
  538.