Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

g_utils.c (11074B)


      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 }