Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

g_utils.c (11131B)


      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 //	gi.dprintf("TARGET: activating %s\n", ent->target);
    227 
    228 //
    229 // fire targets
    230 //
    231 	if (ent->target)
    232 	{
    233 		t = NULL;
    234 		while ((t = G_Find (t, FOFS(targetname), ent->target)))
    235 		{
    236 			// doors fire area portals in a specific way
    237 			if (!Q_stricmp(t->classname, "func_areaportal") &&
    238 				(!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
    239 				continue;
    240 
    241 			if (t == ent)
    242 			{
    243 				gi.dprintf ("WARNING: Entity used itself.\n");
    244 			}
    245 			else
    246 			{
    247 				if (t->use)
    248 					t->use (t, ent, activator);
    249 			}
    250 			if (!ent->inuse)
    251 			{
    252 				gi.dprintf("entity was removed while using targets\n");
    253 				return;
    254 			}
    255 		}
    256 	}
    257 }
    258 
    259 
    260 /*
    261 =============
    262 TempVector
    263 
    264 This is just a convenience function
    265 for making temporary vectors for function calls
    266 =============
    267 */
    268 float	*tv (float x, float y, float z)
    269 {
    270 	static	int		index;
    271 	static	vec3_t	vecs[8];
    272 	float	*v;
    273 
    274 	// use an array so that multiple tempvectors won't collide
    275 	// for a while
    276 	v = vecs[index];
    277 	index = (index + 1)&7;
    278 
    279 	v[0] = x;
    280 	v[1] = y;
    281 	v[2] = z;
    282 
    283 	return v;
    284 }
    285 
    286 
    287 /*
    288 =============
    289 VectorToString
    290 
    291 This is just a convenience function
    292 for printing vectors
    293 =============
    294 */
    295 char	*vtos (vec3_t v)
    296 {
    297 	static	int		index;
    298 	static	char	str[8][32];
    299 	char	*s;
    300 
    301 	// use an array so that multiple vtos won't collide
    302 	s = str[index];
    303 	index = (index + 1)&7;
    304 
    305 	Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
    306 
    307 	return s;
    308 }
    309 
    310 
    311 vec3_t VEC_UP		= {0, -1, 0};
    312 vec3_t MOVEDIR_UP	= {0, 0, 1};
    313 vec3_t VEC_DOWN		= {0, -2, 0};
    314 vec3_t MOVEDIR_DOWN	= {0, 0, -1};
    315 
    316 void G_SetMovedir (vec3_t angles, vec3_t movedir)
    317 {
    318 	if (VectorCompare (angles, VEC_UP))
    319 	{
    320 		VectorCopy (MOVEDIR_UP, movedir);
    321 	}
    322 	else if (VectorCompare (angles, VEC_DOWN))
    323 	{
    324 		VectorCopy (MOVEDIR_DOWN, movedir);
    325 	}
    326 	else
    327 	{
    328 		AngleVectors (angles, movedir, NULL, NULL);
    329 	}
    330 
    331 	VectorClear (angles);
    332 }
    333 
    334 
    335 float vectoyaw (vec3_t vec)
    336 {
    337 	float	yaw;
    338 	
    339 	if (/* vec[YAW] == 0 && */ vec[PITCH] == 0) 
    340 	{
    341 		yaw = 0;
    342 		if (vec[YAW] > 0)
    343 			yaw = 90;
    344 		else if (vec[YAW] < 0)
    345 			yaw = -90;
    346 	}
    347 	else
    348 	{
    349 		yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
    350 		if (yaw < 0)
    351 			yaw += 360;
    352 	}
    353 
    354 	return yaw;
    355 }
    356 
    357 
    358 void vectoangles (vec3_t value1, vec3_t angles)
    359 {
    360 	float	forward;
    361 	float	yaw, pitch;
    362 	
    363 	if (value1[1] == 0 && value1[0] == 0)
    364 	{
    365 		yaw = 0;
    366 		if (value1[2] > 0)
    367 			pitch = 90;
    368 		else
    369 			pitch = 270;
    370 	}
    371 	else
    372 	{
    373 		if (value1[0])
    374 			yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
    375 		else if (value1[1] > 0)
    376 			yaw = 90;
    377 		else
    378 			yaw = -90;
    379 		if (yaw < 0)
    380 			yaw += 360;
    381 
    382 		forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
    383 		pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
    384 		if (pitch < 0)
    385 			pitch += 360;
    386 	}
    387 
    388 	angles[PITCH] = -pitch;
    389 	angles[YAW] = yaw;
    390 	angles[ROLL] = 0;
    391 }
    392 
    393 char *G_CopyString (char *in)
    394 {
    395 	char	*out;
    396 	
    397 	out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
    398 	strcpy (out, in);
    399 	return out;
    400 }
    401 
    402 
    403 void G_InitEdict (edict_t *e)
    404 {
    405 	e->inuse = true;
    406 	e->classname = "noclass";
    407 	e->gravity = 1.0;
    408 	e->s.number = e - g_edicts;
    409 }
    410 
    411 /*
    412 =================
    413 G_Spawn
    414 
    415 Either finds a free edict, or allocates a new one.
    416 Try to avoid reusing an entity that was recently freed, because it
    417 can cause the client to think the entity morphed into something else
    418 instead of being removed and recreated, which can cause interpolated
    419 angles and bad trails.
    420 =================
    421 */
    422 edict_t *G_Spawn (void)
    423 {
    424 	int			i;
    425 	edict_t		*e;
    426 
    427 	e = &g_edicts[(int)maxclients->value+1];
    428 	for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
    429 	{
    430 		// the first couple seconds of server time can involve a lot of
    431 		// freeing and allocating, so relax the replacement policy
    432 		if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
    433 		{
    434 			G_InitEdict (e);
    435 			return e;
    436 		}
    437 	}
    438 	
    439 	if (i == game.maxentities)
    440 		gi.error ("ED_Alloc: no free edicts");
    441 		
    442 	globals.num_edicts++;
    443 	G_InitEdict (e);
    444 	return e;
    445 }
    446 
    447 /*
    448 =================
    449 G_FreeEdict
    450 
    451 Marks the edict as free
    452 =================
    453 */
    454 void G_FreeEdict (edict_t *ed)
    455 {
    456 	gi.unlinkentity (ed);		// unlink from world
    457 
    458 	if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
    459 	{
    460 //		gi.dprintf("tried to free special edict\n");
    461 		return;
    462 	}
    463 
    464 	memset (ed, 0, sizeof(*ed));
    465 	ed->classname = "freed";
    466 	ed->freetime = level.time;
    467 	ed->inuse = false;
    468 }
    469 
    470 
    471 /*
    472 ============
    473 G_TouchTriggers
    474 
    475 ============
    476 */
    477 void	G_TouchTriggers (edict_t *ent)
    478 {
    479 	int			i, num;
    480 	edict_t		*touch[MAX_EDICTS], *hit;
    481 
    482 	// dead things don't activate triggers!
    483 	if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
    484 		return;
    485 
    486 	num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
    487 		, MAX_EDICTS, AREA_TRIGGERS);
    488 
    489 	// be careful, it is possible to have an entity in this
    490 	// list removed before we get to it (killtriggered)
    491 	for (i=0 ; i<num ; i++)
    492 	{
    493 		hit = touch[i];
    494 		if (!hit->inuse)
    495 			continue;
    496 		if (!hit->touch)
    497 			continue;
    498 		hit->touch (hit, ent, NULL, NULL);
    499 	}
    500 }
    501 
    502 /*
    503 ============
    504 G_TouchSolids
    505 
    506 Call after linking a new trigger in during gameplay
    507 to force all entities it covers to immediately touch it
    508 ============
    509 */
    510 void	G_TouchSolids (edict_t *ent)
    511 {
    512 	int			i, num;
    513 	edict_t		*touch[MAX_EDICTS], *hit;
    514 
    515 	num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
    516 		, MAX_EDICTS, AREA_SOLID);
    517 
    518 	// be careful, it is possible to have an entity in this
    519 	// list removed before we get to it (killtriggered)
    520 	for (i=0 ; i<num ; i++)
    521 	{
    522 		hit = touch[i];
    523 		if (!hit->inuse)
    524 			continue;
    525 		if (ent->touch)
    526 			ent->touch (hit, ent, NULL, NULL);
    527 		if (!ent->inuse)
    528 			break;
    529 	}
    530 }
    531 
    532 
    533 
    534 
    535 /*
    536 ==============================================================================
    537 
    538 Kill box
    539 
    540 ==============================================================================
    541 */
    542 
    543 /*
    544 =================
    545 KillBox
    546 
    547 Kills all entities that would touch the proposed new positioning
    548 of ent.  Ent should be unlinked before calling this!
    549 =================
    550 */
    551 qboolean KillBox (edict_t *ent)
    552 {
    553 	trace_t		tr;
    554 
    555 	while (1)
    556 	{
    557 		tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
    558 		if (!tr.ent)
    559 			break;
    560 
    561 		// nail it
    562 		T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
    563 
    564 		// if we didn't kill it, fail
    565 		if (tr.ent->solid)
    566 			return false;
    567 	}
    568 
    569 	return true;		// all clear
    570 }