File: game\g_utils.c

    1 /*
    2 Copyright (C) 1997-2001 Id Software, Inc.
    3 
    4 This program is free software; you can redistribute it and/or
    5 modify it under the terms of the GNU General Public License
    6 as published by the Free Software Foundation; either version 2
    7 of the License, or (at your option) any later version.
    8 
    9 This program is distributed in the hope that it will be useful,
   10 but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
   12 
   13 See the GNU General Public License for more details.
   14 
   15 You should have received a copy of the GNU General Public License
   16 along with this program; if not, write to the Free Software
   17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   18 
   19 */
   20 // g_utils.c -- misc utility functions for game module
   21 
   22 #include "g_local.h"
   23 
   24 
   25 void G_ProjectSource (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)
   26 {
   27         result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1];
   28         result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1];
   29         result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2];
   30 }
   31 
   32 
   33 /*
   34 =============
   35 G_Find
   36 
   37 Searches all active entities for the next one that holds
   38 the matching string at fieldofs (use the FOFS() macro) in the structure.
   39 
   40 Searches beginning at the edict after from, or the beginning if NULL
   41 NULL will be returned if the end of the list is reached.
   42 
   43 =============
   44 */
   45 edict_t *G_Find (edict_t *from, int fieldofs, char *match)
   46 {
   47         char    *s;
   48 
   49         if (!from)
   50                 from = g_edicts;
   51         else
   52                 from++;
   53 
   54         for ( ; from < &g_edicts[globals.num_edicts] ; from++)
   55         {
   56                 if (!from->inuse)
   57                         continue;
   58                 s = *(char **) ((byte *)from + fieldofs);
   59                 if (!s)
   60                         continue;
   61                 if (!Q_stricmp (s, match))
   62                         return from;
   63         }
   64 
   65         return NULL;
   66 }
   67 
   68 
   69 /*
   70 =================
   71 findradius
   72 
   73 Returns entities that have origins within a spherical area
   74 
   75 findradius (origin, radius)
   76 =================
   77 */
   78 edict_t *findradius (edict_t *from, vec3_t org, float rad)
   79 {
   80         vec3_t  eorg;
   81         int             j;
   82 
   83         if (!from)
   84                 from = g_edicts;
   85         else
   86                 from++;
   87         for ( ; from < &g_edicts[globals.num_edicts]; from++)
   88         {
   89                 if (!from->inuse)
   90                         continue;
   91                 if (from->solid == SOLID_NOT)
   92                         continue;
   93                 for (j=0 ; j<3 ; j++)
   94                         eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
   95                 if (VectorLength(eorg) > rad)
   96                         continue;
   97                 return from;
   98         }
   99 
  100         return NULL;
  101 }
  102 
  103 
  104 /*
  105 =============
  106 G_PickTarget
  107 
  108 Searches all active entities for the next one that holds
  109 the matching string at fieldofs (use the FOFS() macro) in the structure.
  110 
  111 Searches beginning at the edict after from, or the beginning if NULL
  112 NULL will be returned if the end of the list is reached.
  113 
  114 =============
  115 */
  116 #define MAXCHOICES      8
  117 
  118 edict_t *G_PickTarget (char *targetname)
  119 {
  120         edict_t *ent = NULL;
  121         int             num_choices = 0;
  122         edict_t *choice[MAXCHOICES];
  123 
  124         if (!targetname)
  125         {
  126                 gi.dprintf("G_PickTarget called with NULL targetname\n");
  127                 return NULL;
  128         }
  129 
  130         while(1)
  131         {
  132                 ent = G_Find (ent, FOFS(targetname), targetname);
  133                 if (!ent)
  134                         break;
  135                 choice[num_choices++] = ent;
  136                 if (num_choices == MAXCHOICES)
  137                         break;
  138         }
  139 
  140         if (!num_choices)
  141         {
  142                 gi.dprintf("G_PickTarget: target %s not found\n", targetname);
  143                 return NULL;
  144         }
  145 
  146         return choice[rand() % num_choices];
  147 }
  148 
  149 
  150 
  151 void Think_Delay (edict_t *ent)
  152 {
  153         G_UseTargets (ent, ent->activator);
  154         G_FreeEdict (ent);
  155 }
  156 
  157 /*
  158 ==============================
  159 G_UseTargets
  160 
  161 the global "activator" should be set to the entity that initiated the firing.
  162 
  163 If self.delay is set, a DelayedUse entity will be created that will actually
  164 do the SUB_UseTargets after that many seconds have passed.
  165 
  166 Centerprints any self.message to the activator.
  167 
  168 Search for (string)targetname in all entities that
  169 match (string)self.target and call their .use function
  170 
  171 ==============================
  172 */
  173 void G_UseTargets (edict_t *ent, edict_t *activator)
  174 {
  175         edict_t         *t;
  176 
  177 //
  178 // check for a delay
  179 //
  180         if (ent->delay)
  181         {
  182         // create a temp object to fire at a later time
  183                 t = G_Spawn();
  184                 t->classname = "DelayedUse";
  185                 t->nextthink = level.time + ent->delay;
  186                 t->think = Think_Delay;
  187                 t->activator = activator;
  188                 if (!activator)
  189                         gi.dprintf ("Think_Delay with no activator\n");
  190                 t->message = ent->message;
  191                 t->target = ent->target;
  192                 t->killtarget = ent->killtarget;
  193                 return;
  194         }
  195         
  196         
  197 //
  198 // print the message
  199 //
  200         if ((ent->message) && !(activator->svflags & SVF_MONSTER))
  201         {
  202                 gi.centerprintf (activator, "%s", ent->message);
  203                 if (ent->noise_index)
  204                         gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
  205                 else
  206                         gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
  207         }
  208 
  209 //
  210 // kill killtargets
  211 //
  212         if (ent->killtarget)
  213         {
  214                 t = NULL;
  215                 while ((t = G_Find (t, FOFS(targetname), ent->killtarget)))
  216                 {
  217                         G_FreeEdict (t);
  218                         if (!ent->inuse)
  219                         {
  220                                 gi.dprintf("entity was removed while using killtargets\n");
  221                                 return;
  222                         }
  223                 }
  224         }
  225 
  226 //
  227 // fire targets
  228 //
  229         if (ent->target)
  230         {
  231                 t = NULL;
  232                 while ((t = G_Find (t, FOFS(targetname), ent->target)))
  233                 {
  234                         // doors fire area portals in a specific way
  235                         if (!Q_stricmp(t->classname, "func_areaportal") &&
  236                                 (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
  237                                 continue;
  238 
  239                         if (t == ent)
  240                         {
  241                                 gi.dprintf ("WARNING: Entity used itself.\n");
  242                         }
  243                         else
  244                         {
  245                                 if (t->use)
  246                                         t->use (t, ent, activator);
  247                         }
  248                         if (!ent->inuse)
  249                         {
  250                                 gi.dprintf("entity was removed while using targets\n");
  251                                 return;
  252                         }
  253                 }
  254         }
  255 }
  256 
  257 
  258 /*
  259 =============
  260 TempVector
  261 
  262 This is just a convenience function
  263 for making temporary vectors for function calls
  264 =============
  265 */
  266 float   *tv (float x, float y, float z)
  267 {
  268         static  int             index;
  269         static  vec3_t  vecs[8];
  270         float   *v;
  271 
  272         // use an array so that multiple tempvectors won't collide
  273         // for a while
  274         v = vecs[index];
  275         index = (index + 1)&7;
  276 
  277         v[0] = x;
  278         v[1] = y;
  279         v[2] = z;
  280 
  281         return v;
  282 }
  283 
  284 
  285 /*
  286 =============
  287 VectorToString
  288 
  289 This is just a convenience function
  290 for printing vectors
  291 =============
  292 */
  293 char    *vtos (vec3_t v)
  294 {
  295         static  int             index;
  296         static  char    str[8][32];
  297         char    *s;
  298 
  299         // use an array so that multiple vtos won't collide
  300         s = str[index];
  301         index = (index + 1)&7;
  302 
  303         Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
  304 
  305         return s;
  306 }
  307 
  308 
  309 vec3_t VEC_UP           = {0, -1, 0};
  310 vec3_t MOVEDIR_UP       = {0, 0, 1};
  311 vec3_t VEC_DOWN         = {0, -2, 0};
  312 vec3_t MOVEDIR_DOWN     = {0, 0, -1};
  313 
  314 void G_SetMovedir (vec3_t angles, vec3_t movedir)
  315 {
  316         if (VectorCompare (angles, VEC_UP))
  317         {
  318                 VectorCopy (MOVEDIR_UP, movedir);
  319         }
  320         else if (VectorCompare (angles, VEC_DOWN))
  321         {
  322                 VectorCopy (MOVEDIR_DOWN, movedir);
  323         }
  324         else
  325         {
  326                 AngleVectors (angles, movedir, NULL, NULL);
  327         }
  328 
  329         VectorClear (angles);
  330 }
  331 
  332 
  333 float vectoyaw (vec3_t vec)
  334 {
  335         float   yaw;
  336         
  337         if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0) 
  338         {
  339                 yaw = 0;
  340                 if (vec[YAW] > 0)
  341                         yaw = 90;
  342                 else if (vec[YAW] < 0)
  343                         yaw = -90;
  344         } 
  345         else
  346         {
  347                 yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
  348                 if (yaw < 0)
  349                         yaw += 360;
  350         }
  351 
  352         return yaw;
  353 }
  354 
  355 
  356 void vectoangles (vec3_t value1, vec3_t angles)
  357 {
  358         float   forward;
  359         float   yaw, pitch;
  360         
  361         if (value1[1] == 0 && value1[0] == 0)
  362         {
  363                 yaw = 0;
  364                 if (value1[2] > 0)
  365                         pitch = 90;
  366                 else
  367                         pitch = 270;
  368         }
  369         else
  370         {
  371                 if (value1[0])
  372                         yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
  373                 else if (value1[1] > 0)
  374                         yaw = 90;
  375                 else
  376                         yaw = -90;
  377                 if (yaw < 0)
  378                         yaw += 360;
  379 
  380                 forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
  381                 pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
  382                 if (pitch < 0)
  383                         pitch += 360;
  384         }
  385 
  386         angles[PITCH] = -pitch;
  387         angles[YAW] = yaw;
  388         angles[ROLL] = 0;
  389 }
  390 
  391 char *G_CopyString (char *in)
  392 {
  393         char    *out;
  394         
  395         out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
  396         strcpy (out, in);
  397         return out;
  398 }
  399 
  400 
  401 void G_InitEdict (edict_t *e)
  402 {
  403         e->inuse = true;
  404         e->classname = "noclass";
  405         e->gravity = 1.0;
  406         e->s.number = e - g_edicts;
  407 }
  408 
  409 /*
  410 =================
  411 G_Spawn
  412 
  413 Either finds a free edict, or allocates a new one.
  414 Try to avoid reusing an entity that was recently freed, because it
  415 can cause the client to think the entity morphed into something else
  416 instead of being removed and recreated, which can cause interpolated
  417 angles and bad trails.
  418 =================
  419 */
  420 edict_t *G_Spawn (void)
  421 {
  422         int                     i;
  423         edict_t         *e;
  424 
  425         e = &g_edicts[(int)maxclients->value+1];
  426         for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
  427         {
  428                 // the first couple seconds of server time can involve a lot of
  429                 // freeing and allocating, so relax the replacement policy
  430                 if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
  431                 {
  432                         G_InitEdict (e);
  433                         return e;
  434                 }
  435         }
  436         
  437         if (i == game.maxentities)
  438                 gi.error ("ED_Alloc: no free edicts");
  439                 
  440         globals.num_edicts++;
  441         G_InitEdict (e);
  442         return e;
  443 }
  444 
  445 /*
  446 =================
  447 G_FreeEdict
  448 
  449 Marks the edict as free
  450 =================
  451 */
  452 void G_FreeEdict (edict_t *ed)
  453 {
  454         gi.unlinkentity (ed);           // unlink from world
  455 
  456         if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
  457         {
  458 //              gi.dprintf("tried to free special edict\n");
  459                 return;
  460         }
  461 
  462         memset (ed, 0, sizeof(*ed));
  463         ed->classname = "freed";
  464         ed->freetime = level.time;
  465         ed->inuse = false;
  466 }
  467 
  468 
  469 /*
  470 ============
  471 G_TouchTriggers
  472 
  473 ============
  474 */
  475 void    G_TouchTriggers (edict_t *ent)
  476 {
  477         int                     i, num;
  478         edict_t         *touch[MAX_EDICTS], *hit;
  479 
  480         // dead things don't activate triggers!
  481         if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
  482                 return;
  483 
  484         num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
  485                 , MAX_EDICTS, AREA_TRIGGERS);
  486 
  487         // be careful, it is possible to have an entity in this
  488         // list removed before we get to it (killtriggered)
  489         for (i=0 ; i<num ; i++)
  490         {
  491                 hit = touch[i];
  492                 if (!hit->inuse)
  493                         continue;
  494                 if (!hit->touch)
  495                         continue;
  496                 hit->touch (hit, ent, NULL, NULL);
  497         }
  498 }
  499 
  500 /*
  501 ============
  502 G_TouchSolids
  503 
  504 Call after linking a new trigger in during gameplay
  505 to force all entities it covers to immediately touch it
  506 ============
  507 */
  508 void    G_TouchSolids (edict_t *ent)
  509 {
  510         int                     i, num;
  511         edict_t         *touch[MAX_EDICTS], *hit;
  512 
  513         num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
  514                 , MAX_EDICTS, AREA_SOLID);
  515 
  516         // be careful, it is possible to have an entity in this
  517         // list removed before we get to it (killtriggered)
  518         for (i=0 ; i<num ; i++)
  519         {
  520                 hit = touch[i];
  521                 if (!hit->inuse)
  522                         continue;
  523                 if (ent->touch)
  524                         ent->touch (hit, ent, NULL, NULL);
  525                 if (!ent->inuse)
  526                         break;
  527         }
  528 }
  529 
  530 
  531 
  532 
  533 /*
  534 ==============================================================================
  535 
  536 Kill box
  537 
  538 ==============================================================================
  539 */
  540 
  541 /*
  542 =================
  543 KillBox
  544 
  545 Kills all entities that would touch the proposed new positioning
  546 of ent.  Ent should be unlinked before calling this!
  547 =================
  548 */
  549 qboolean KillBox (edict_t *ent)
  550 {
  551         trace_t         tr;
  552 
  553         while (1)
  554         {
  555                 tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
  556                 if (!tr.ent)
  557                         break;
  558 
  559                 // nail it
  560                 T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
  561 
  562                 // if we didn't kill it, fail
  563                 if (tr.ent->solid)
  564                         return false;
  565         }
  566 
  567         return true;            // all clear
  568 }
  569