1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7 
8 This file is part of the OpenJK source code.
9 
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23 
24 // g_utils.c -- misc utility functions for game module
25 
26 #include "../cgame/cg_local.h"
27 #include "Q3_Interface.h"
28 #include "g_local.h"
29 #include "g_functions.h"
30 #include "g_navigator.h"
31 #include "b_local.h"
32 #include "g_nav.h"
33 
34 #define ACT_ACTIVE		qtrue
35 #define ACT_INACTIVE	qfalse
36 extern void NPC_UseResponse ( gentity_t *self, gentity_t *user, qboolean useWhenDone );
37 extern qboolean PM_CrouchAnim( int anim );
38 /*
39 =========================================================================
40 
41 model / sound configstring indexes
42 
43 =========================================================================
44 */
45 
46 /*
47 ================
48 G_FindConfigstringIndex
49 
50 ================
51 */
52 extern void ForceTelepathy( gentity_t *self );
G_FindConfigstringIndex(const char * name,int start,int max,qboolean create)53 int G_FindConfigstringIndex( const char *name, int start, int max, qboolean create ) {
54 	int		i;
55 	char	s[MAX_STRING_CHARS];
56 
57 	if ( !name || !name[0] ) {
58 		return 0;
59 	}
60 
61 	for ( i=1 ; i<max ; i++ ) {
62 		gi.GetConfigstring( start + i, s, sizeof( s ) );
63 		if ( !s[0] ) {
64 			break;
65 		}
66 		if ( !Q_stricmp( s, name ) ) {
67 			return i;
68 		}
69 	}
70 
71 	if ( !create ) {
72 		return 0;
73 	}
74 
75 	if ( i == max ) {
76 		G_Error( "G_FindConfigstringIndex: overflow adding %s to set %d-%d", name, start, max );
77 	}
78 
79 	gi.SetConfigstring( start + i, name );
80 
81 	return i;
82 }
83 /*
84 Ghoul2 Insert Start
85 */
86 
G_SkinIndex(const char * name)87 int G_SkinIndex( const char *name ) {
88 	return G_FindConfigstringIndex (name, CS_CHARSKINS, MAX_CHARSKINS, qtrue);
89 }
90 
91 /*
92 Ghoul2 Insert End
93 */
94 
G_ModelIndex(const char * name)95 int G_ModelIndex( const char *name ) {
96 	return G_FindConfigstringIndex (name, CS_MODELS, MAX_MODELS, qtrue);
97 }
98 
G_SoundIndex(const char * name)99 int G_SoundIndex( const char *name ) {
100 	assert( name && name[0] );
101 	char stripped[MAX_QPATH];
102 	COM_StripExtension(name, stripped, sizeof(stripped));
103 
104 	return G_FindConfigstringIndex (stripped, CS_SOUNDS, MAX_SOUNDS, qtrue);
105 }
106 
G_EffectIndex(const char * name)107 int G_EffectIndex( const char *name )
108 {
109 	char temp[MAX_QPATH];
110 
111 	// We just don't want extensions on the things we are registering
112 	COM_StripExtension( name, temp, sizeof(temp) );
113 
114 	return G_FindConfigstringIndex( temp, CS_EFFECTS, MAX_FX, qtrue );
115 }
116 
G_BSPIndex(char * name)117 int G_BSPIndex( char *name )
118 {
119 	return G_FindConfigstringIndex (name, CS_BSP_MODELS, MAX_SUB_BSP, qtrue);
120 }
121 
122 #define FX_ENT_RADIUS 32
123 
124 //-----------------------------
125 // Effect playing utilities
126 //-----------------------------
127 
128 //-----------------------------
G_PlayEffect(int fxID,const vec3_t origin,const vec3_t fwd)129 void G_PlayEffect( int fxID, const vec3_t origin, const vec3_t fwd )
130 {
131 	gentity_t	*tent;
132 	vec3_t	temp;
133 
134 	tent = G_TempEntity( origin, EV_PLAY_EFFECT );
135 	tent->s.eventParm = fxID;
136 
137 	VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
138 	VectorScale( tent->maxs, -1, tent->mins );
139 
140 	VectorCopy( fwd, tent->pos3 );
141 
142 	// Assume angles, we'll do a cross product on the other end to finish up
143 	MakeNormalVectors( fwd, tent->pos4, temp );
144 	gi.linkentity( tent );
145 }
146 
147 // Play an effect at the origin of the specified entity
148 //----------------------------
G_PlayEffect(int fxID,int entNum,const vec3_t fwd)149 void G_PlayEffect( int fxID, int entNum, const vec3_t fwd )
150 {
151 	gentity_t	*tent;
152 	vec3_t		temp;
153 
154 	tent = G_TempEntity( g_entities[entNum].currentOrigin, EV_PLAY_EFFECT );
155 	tent->s.eventParm = fxID;
156 	tent->s.otherEntityNum = entNum;
157 	VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
158 	VectorScale( tent->maxs, -1, tent->mins );
159 	VectorCopy( fwd, tent->pos3 );
160 
161 	// Assume angles, we'll do a cross product on the other end to finish up
162 	MakeNormalVectors( fwd, tent->pos4, temp );
163 }
164 
165 // Play an effect bolted onto the muzzle of the specified client
166 //----------------------------
G_PlayEffect(const char * name,int clientNum)167 void G_PlayEffect( const char *name, int clientNum )
168 {
169 	gentity_t	*tent;
170 
171 	tent = G_TempEntity( g_entities[clientNum].currentOrigin, EV_PLAY_MUZZLE_EFFECT );
172 	tent->s.eventParm = G_EffectIndex( name );
173 	tent->s.otherEntityNum = clientNum;
174 	VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
175 	VectorScale( tent->maxs, -1, tent->mins );
176 }
177 
178 //-----------------------------
G_PlayEffect(int fxID,const vec3_t origin,const vec3_t axis[3])179 void G_PlayEffect( int fxID, const vec3_t origin, const vec3_t axis[3] )
180 {
181 	gentity_t	*tent;
182 
183 	tent = G_TempEntity( origin, EV_PLAY_EFFECT );
184 	tent->s.eventParm = fxID;
185 
186 	VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
187 	VectorScale( tent->maxs, -1, tent->mins );
188 
189 	// We can just assume axis[2] from doing a cross product on these.
190 	VectorCopy( axis[0], tent->pos3 );
191 	VectorCopy( axis[1], tent->pos4 );
192 }
193 
194 // Effect playing utilities	- bolt an effect to a ghoul2 models bolton point
195 //-----------------------------
G_PlayEffect(int fxID,const int modelIndex,const int boltIndex,const int entNum,const vec3_t origin,int iLoopTime,qboolean isRelative)196 void G_PlayEffect( int fxID, const int modelIndex, const int boltIndex, const int entNum, const vec3_t origin, int iLoopTime, qboolean isRelative )//iLoopTime 0 = not looping, 1 for infinite, else duration
197 {
198 	gentity_t	*tent;
199 
200 	tent = G_TempEntity( origin, EV_PLAY_EFFECT );
201 	tent->s.eventParm = fxID;
202 
203 	tent->s.loopSound = iLoopTime;
204 	tent->s.weapon = isRelative;
205 
206 	tent->svFlags |=SVF_BROADCAST;
207 	gi.G2API_AttachEnt(&tent->s.boltInfo, &g_entities[entNum].ghoul2[modelIndex], boltIndex, entNum, modelIndex);
208 }
209 
210 //-----------------------------
G_PlayEffect(const char * name,const vec3_t origin)211 void G_PlayEffect( const char *name, const vec3_t origin )
212 {
213 	vec3_t	up = {0, 0, 1};
214 
215 	G_PlayEffect( G_EffectIndex( name ), origin, up );
216 }
217 
G_PlayEffect(int fxID,const vec3_t origin)218 void G_PlayEffect( int fxID, const vec3_t origin )
219 {
220 	vec3_t	up = {0, 0, 1};
221 
222 	G_PlayEffect( fxID, origin, up );
223 }
224 
225 //-----------------------------
G_PlayEffect(const char * name,const vec3_t origin,const vec3_t fwd)226 void G_PlayEffect( const char *name, const vec3_t origin, const vec3_t fwd )
227 {
228 	G_PlayEffect( G_EffectIndex( name ), origin, fwd );
229 }
230 
231 //-----------------------------
G_PlayEffect(const char * name,const vec3_t origin,const vec3_t axis[3])232 void G_PlayEffect( const char *name, const vec3_t origin, const vec3_t axis[3] )
233 {
234 	G_PlayEffect( G_EffectIndex( name ), origin, axis );
235 }
236 
G_StopEffect(int fxID,const int modelIndex,const int boltIndex,const int entNum)237 void G_StopEffect( int fxID, const int modelIndex, const int boltIndex, const int entNum )
238 {
239 	gentity_t	*tent;
240 
241 	tent = G_TempEntity( g_entities[entNum].currentOrigin, EV_STOP_EFFECT );
242 	tent->s.eventParm = fxID;
243 	tent->svFlags |= SVF_BROADCAST;
244 	gi.G2API_AttachEnt( &tent->s.boltInfo, &g_entities[entNum].ghoul2[modelIndex], boltIndex, entNum, modelIndex );
245 }
246 
G_StopEffect(const char * name,const int modelIndex,const int boltIndex,const int entNum)247 void G_StopEffect(const char *name, const int modelIndex, const int boltIndex, const int entNum )
248 {
249 	G_StopEffect( G_EffectIndex( name ), modelIndex, boltIndex, entNum );
250 }
251 
252 //===Bypass network for sounds on specific channels====================
253 
254 extern void cgi_S_StartSound( const vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx );
255 #include "../cgame/cg_media.h"	//access to cgs
256 extern qboolean CG_TryPlayCustomSound( vec3_t origin, int entityNum, soundChannel_t channel, const char *soundName, int customSoundSet );
257 extern cvar_t *g_timescale;
258 //NOTE: Do NOT Try to use this before the cgame DLL is valid, it will NOT work!
G_SoundOnEnt(gentity_t * ent,soundChannel_t channel,const char * soundPath)259 void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath )
260 {
261 	int	index = G_SoundIndex( (char *)soundPath );
262 
263 	if ( !ent )
264 	{
265 		return;
266 	}
267 	if ( g_timescale->integer > 50 )
268 	{//Skip the sound!
269 		return;
270 	}
271 
272 	cgi_S_UpdateEntityPosition( ent->s.number, ent->currentOrigin );
273 	if ( cgs.sound_precache[ index ] )
274 	{
275 		cgi_S_StartSound( NULL, ent->s.number, channel, cgs.sound_precache[ index ] );
276 	}
277 	else
278 	{
279 		CG_TryPlayCustomSound( NULL, ent->s.number, channel, soundPath, -1 );
280 	}
281 }
282 
G_SoundIndexOnEnt(gentity_t * ent,soundChannel_t channel,int index)283 void G_SoundIndexOnEnt( gentity_t *ent, soundChannel_t channel, int index )
284 {
285 	if ( !ent )
286 	{
287 		return;
288 	}
289 
290 	cgi_S_UpdateEntityPosition( ent->s.number, ent->currentOrigin );
291 	if ( cgs.sound_precache[ index ] )
292 	{
293 		cgi_S_StartSound( NULL, ent->s.number, channel, cgs.sound_precache[ index ] );
294 	}
295 }
296 
297 extern cvar_t	*g_skippingcin;
G_SpeechEvent(gentity_t * self,int event)298 void G_SpeechEvent( gentity_t *self, int event )
299 {
300 	if ( in_camera
301 		&& g_skippingcin
302 		&& g_skippingcin->integer )
303 	{//Skipping a cinematic, so skip NPC voice sounds...
304 		return;
305 	}
306 	//update entity pos, too
307 	cgi_S_UpdateEntityPosition( self->s.number, self->currentOrigin );
308 	switch ( event )
309 	{
310 	case EV_ANGER1:	//Say when acquire an enemy when didn't have one before
311 	case EV_ANGER2:
312 	case EV_ANGER3:
313 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*anger%i.wav", event - EV_ANGER1 + 1), CS_COMBAT );
314 		break;
315 	case EV_VICTORY1:	//Say when killed an enemy
316 	case EV_VICTORY2:
317 	case EV_VICTORY3:
318 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*victory%i.wav", event - EV_VICTORY1 + 1), CS_COMBAT );
319 		break;
320 	case EV_CONFUSE1:	//Say when confused
321 	case EV_CONFUSE2:
322 	case EV_CONFUSE3:
323 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*confuse%i.wav", event - EV_CONFUSE1 + 1), CS_COMBAT );
324 		break;
325 	case EV_PUSHED1:	//Say when pushed
326 	case EV_PUSHED2:
327 	case EV_PUSHED3:
328 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*pushed%i.wav", event - EV_PUSHED1 + 1), CS_COMBAT );
329 		break;
330 	case EV_CHOKE1:	//Say when choking
331 	case EV_CHOKE2:
332 	case EV_CHOKE3:
333 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*choke%i.wav", event - EV_CHOKE1 + 1), CS_COMBAT );
334 		break;
335 	case EV_FFWARN:	//Warn ally to stop shooting you
336 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*ffwarn.wav", CS_COMBAT );
337 		break;
338 	case EV_FFTURN:	//Turn on ally after being shot by them
339 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*ffturn.wav", CS_COMBAT );
340 		break;
341 	//extra sounds for ST
342 	case EV_CHASE1:
343 	case EV_CHASE2:
344 	case EV_CHASE3:
345 		if ( !CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*chase%i.wav", event - EV_CHASE1 + 1), CS_EXTRA ) )
346 		{
347 			CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*anger%i.wav", Q_irand(1,3)), CS_COMBAT );
348 		}
349 		break;
350 	case EV_COVER1:
351 	case EV_COVER2:
352 	case EV_COVER3:
353 	case EV_COVER4:
354 	case EV_COVER5:
355 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*cover%i.wav", event - EV_COVER1 + 1), CS_EXTRA );
356 		break;
357 	case EV_DETECTED1:
358 	case EV_DETECTED2:
359 	case EV_DETECTED3:
360 	case EV_DETECTED4:
361 	case EV_DETECTED5:
362 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*detected%i.wav", event - EV_DETECTED1 + 1), CS_EXTRA );
363 		break;
364 	case EV_GIVEUP1:
365 	case EV_GIVEUP2:
366 	case EV_GIVEUP3:
367 	case EV_GIVEUP4:
368 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*giveup%i.wav", event - EV_GIVEUP1 + 1), CS_EXTRA );
369 		break;
370 	case EV_LOOK1:
371 	case EV_LOOK2:
372 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*look%i.wav", event - EV_LOOK1 + 1), CS_EXTRA );
373 		break;
374 	case EV_LOST1:
375 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*lost1.wav", CS_EXTRA );
376 		break;
377 	case EV_OUTFLANK1:
378 	case EV_OUTFLANK2:
379 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*outflank%i.wav", event - EV_OUTFLANK1 + 1), CS_EXTRA );
380 		break;
381 	case EV_ESCAPING1:
382 	case EV_ESCAPING2:
383 	case EV_ESCAPING3:
384 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*escaping%i.wav", event - EV_ESCAPING1 + 1), CS_EXTRA );
385 		break;
386 	case EV_SIGHT1:
387 	case EV_SIGHT2:
388 	case EV_SIGHT3:
389 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*sight%i.wav", event - EV_SIGHT1 + 1), CS_EXTRA );
390 		break;
391 	case EV_SOUND1:
392 	case EV_SOUND2:
393 	case EV_SOUND3:
394 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*sound%i.wav", event - EV_SOUND1 + 1), CS_EXTRA );
395 		break;
396 	case EV_SUSPICIOUS1:
397 	case EV_SUSPICIOUS2:
398 	case EV_SUSPICIOUS3:
399 	case EV_SUSPICIOUS4:
400 	case EV_SUSPICIOUS5:
401 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*suspicious%i.wav", event - EV_SUSPICIOUS1 + 1), CS_EXTRA );
402 		break;
403 	//extra sounds for Jedi
404 	case EV_COMBAT1:
405 	case EV_COMBAT2:
406 	case EV_COMBAT3:
407 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*combat%i.wav", event - EV_COMBAT1 + 1), CS_JEDI );
408 		break;
409 	case EV_JDETECTED1:
410 	case EV_JDETECTED2:
411 	case EV_JDETECTED3:
412 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*jdetected%i.wav", event - EV_JDETECTED1 + 1), CS_JEDI );
413 		break;
414 	case EV_TAUNT1:
415 	case EV_TAUNT2:
416 	case EV_TAUNT3:
417 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*taunt%i.wav", event - EV_TAUNT1 + 1), CS_JEDI );
418 		break;
419 	case EV_JCHASE1:
420 	case EV_JCHASE2:
421 	case EV_JCHASE3:
422 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*jchase%i.wav", event - EV_JCHASE1 + 1), CS_JEDI );
423 		break;
424 	case EV_JLOST1:
425 	case EV_JLOST2:
426 	case EV_JLOST3:
427 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*jlost%i.wav", event - EV_JLOST1 + 1), CS_JEDI );
428 		break;
429 	case EV_DEFLECT1:
430 	case EV_DEFLECT2:
431 	case EV_DEFLECT3:
432 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*deflect%i.wav", event - EV_DEFLECT1 + 1), CS_JEDI );
433 		break;
434 	case EV_GLOAT1:
435 	case EV_GLOAT2:
436 	case EV_GLOAT3:
437 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*gloat%i.wav", event - EV_GLOAT1 + 1), CS_JEDI );
438 		break;
439 	case EV_PUSHFAIL:
440 		CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*pushfail.wav", CS_JEDI );
441 		break;
442 	}
443 }
444 //=====================================================================
445 
446 
447 
448 /*
449 =============
450 G_Find
451 
452 Searches all active entities for the next one that holds
453 the matching string at fieldofs (use the FOFS() macro) in the structure.
454 
455 Searches beginning at the entity after from, or the beginning if NULL
456 NULL will be returned if the end of the list is reached.
457 
458 =============
459 */
G_Find(gentity_t * from,int fieldofs,const char * match)460 gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match)
461 {
462 	char	*s;
463 
464 	if(!match || !match[0])
465 	{
466 		return NULL;
467 	}
468 
469 	if (!from)
470 		from = g_entities;
471 	else
472 		from++;
473 
474 //	for ( ; from < &g_entities[globals.num_entities] ; from++)
475 	int i=from-g_entities;
476 	for ( ; i < globals.num_entities ; i++)
477 	{
478 //		if (!from->inuse)
479 		if(!PInUse(i))
480 			continue;
481 
482 		from=&g_entities[i];
483 		s = *(char **) ((byte *)from + fieldofs);
484 		if (!s)
485 			continue;
486 		if (!Q_stricmp (s, match))
487 			return from;
488 	}
489 
490 	return NULL;
491 }
492 
493 
494 /*
495 ============
496 G_RadiusList - given an origin and a radius, return all entities that are in use that are within the list
497 ============
498 */
G_RadiusList(vec3_t origin,float radius,gentity_t * ignore,qboolean takeDamage,gentity_t * ent_list[MAX_GENTITIES])499 int G_RadiusList ( vec3_t origin, float radius,	gentity_t *ignore, qboolean takeDamage, gentity_t *ent_list[MAX_GENTITIES])
500 {
501 	float		dist;
502 	gentity_t	*ent;
503 	gentity_t	*entityList[MAX_GENTITIES];
504 	int			numListedEntities;
505 	vec3_t		mins, maxs;
506 	vec3_t		v;
507 	int			i, e;
508 	int			ent_count = 0;
509 
510 	if ( radius < 1 )
511 	{
512 		radius = 1;
513 	}
514 
515 	for ( i = 0 ; i < 3 ; i++ )
516 	{
517 		mins[i] = origin[i] - radius;
518 		maxs[i] = origin[i] + radius;
519 	}
520 
521 	numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
522 	radius *= radius; //square for the length squared below
523 	for ( e = 0 ; e < numListedEntities ; e++ )
524 	{
525 		ent = entityList[ e ];
526 
527 		if ((ent == ignore) || !(ent->inuse) || ent->takedamage != takeDamage)
528 			continue;
529 
530 		// find the distance from the edge of the bounding box
531 		for ( i = 0 ; i < 3 ; i++ )
532 		{
533 			if ( origin[i] < ent->absmin[i] )
534 			{
535 				v[i] = ent->absmin[i] - origin[i];
536 			} else if ( origin[i] > ent->absmax[i] )
537 			{
538 				v[i] = origin[i] - ent->absmax[i];
539 			} else
540 			{
541 				v[i] = 0;
542 			}
543 		}
544 
545 		dist = VectorLengthSquared( v );
546 		if ( dist >= radius )
547 		{
548 			continue;
549 		}
550 
551 		// ok, we are within the radius, add us to the incoming list
552 		ent_list[ent_count] = ent;
553 		ent_count++;
554 
555 	}
556 	// we are done, return how many we found
557 	return(ent_count);
558 }
559 
560 
561 /*
562 =============
563 G_PickTarget
564 
565 Selects a random entity from among the targets
566 =============
567 */
568 #define MAXCHOICES	32
569 
G_PickTarget(char * targetname)570 gentity_t *G_PickTarget (char *targetname)
571 {
572 	gentity_t	*ent = NULL;
573 	int		num_choices = 0;
574 	gentity_t	*choice[MAXCHOICES];
575 
576 	if (!targetname)
577 	{
578 		gi.Printf("G_PickTarget called with NULL targetname\n");
579 		return NULL;
580 	}
581 
582 	while(1)
583 	{
584 		ent = G_Find (ent, FOFS(targetname), targetname);
585 		if (!ent)
586 			break;
587 		choice[num_choices++] = ent;
588 		if (num_choices == MAXCHOICES)
589 			break;
590 	}
591 
592 	if (!num_choices)
593 	{
594 		gi.Printf("G_PickTarget: target %s not found\n", targetname);
595 		return NULL;
596 	}
597 
598 	return choice[rand() % num_choices];
599 }
600 
G_UseTargets2(gentity_t * ent,gentity_t * activator,const char * string)601 void G_UseTargets2 (gentity_t *ent, gentity_t *activator, const char *string)
602 {
603 	gentity_t		*t;
604 
605 //
606 // fire targets
607 //
608 	if (string)
609 	{
610 		if( !Q_stricmp( string, "self") )
611 		{
612 			t = ent;
613 			if (t->e_UseFunc != useF_NULL)	// check can be omitted
614 			{
615 				GEntity_UseFunc(t, ent, activator);
616 			}
617 
618 			if (!ent->inuse)
619 			{
620 				gi.Printf("entity was removed while using targets\n");
621 				return;
622 			}
623 		}
624 		else
625 		{
626 			t = NULL;
627 			while ( (t = G_Find (t, FOFS(targetname), (char *) string)) != NULL )
628 			{
629 				if (t == ent)
630 				{
631 	//				gi.Printf ("WARNING: Entity used itself.\n");
632 				}
633 				if (t->e_UseFunc != useF_NULL)	// check can be omitted
634 				{
635 					GEntity_UseFunc(t, ent, activator);
636 				}
637 
638 				if (!ent->inuse)
639 				{
640 					gi.Printf("entity was removed while using targets\n");
641 					return;
642 				}
643 			}
644 		}
645 	}
646 }
647 
648 /*
649 ==============================
650 G_UseTargets
651 
652 "activator" should be set to the entity that initiated the firing.
653 
654 Search for (string)targetname in all entities that
655 match (string)self.target and call their .use function
656 
657 ==============================
658 */
G_UseTargets(gentity_t * ent,gentity_t * activator)659 void G_UseTargets (gentity_t *ent, gentity_t *activator)
660 {
661 //
662 // fire targets
663 //
664 	G_UseTargets2 (ent, activator, ent->target);
665 }
666 
667 /*
668 =============
669 VectorToString
670 
671 This is just a convenience function
672 for printing vectors
673 =============
674 */
vtos(const vec3_t v)675 char	*vtos( const vec3_t v ) {
676 	static	int		index;
677 	static	char	str[8][32];
678 	char	*s;
679 
680 	// use an array so that multiple vtos won't collide
681 	s = str[index];
682 	index = (index + 1)&7;
683 
684 	Com_sprintf (s, 32, "(%4.2f %4.2f %4.2f)", v[0], v[1], v[2]);
685 
686 	return s;
687 }
688 
689 
690 /*
691 ===============
692 G_SetMovedir
693 
694 The editor only specifies a single value for angles (yaw),
695 but we have special constants to generate an up or down direction.
696 Angles will be cleared, because it is being used to represent a direction
697 instead of an orientation.
698 ===============
699 */
G_SetMovedir(vec3_t angles,vec3_t movedir)700 void G_SetMovedir( vec3_t angles, vec3_t movedir ) {
701 	static vec3_t VEC_UP		= {0, -1, 0};
702 	static vec3_t MOVEDIR_UP	= {0, 0, 1};
703 	static vec3_t VEC_DOWN		= {0, -2, 0};
704 	static vec3_t MOVEDIR_DOWN	= {0, 0, -1};
705 
706 	if ( VectorCompare (angles, VEC_UP) ) {
707 		VectorCopy (MOVEDIR_UP, movedir);
708 	} else if ( VectorCompare (angles, VEC_DOWN) ) {
709 		VectorCopy (MOVEDIR_DOWN, movedir);
710 	} else {
711 		AngleVectors (angles, movedir, NULL, NULL);
712 	}
713 	VectorClear( angles );
714 }
715 
716 
vectoyaw(const vec3_t vec)717 float vectoyaw( const vec3_t vec ) {
718 	float	yaw;
719 
720 	if (vec[YAW] == 0 && vec[PITCH] == 0) {
721 		yaw = 0;
722 	} else {
723 		if (vec[PITCH]) {
724 			yaw = ( atan2( vec[YAW], vec[PITCH]) * 180 / M_PI );
725 		} else if (vec[YAW] > 0) {
726 			yaw = 90;
727 		} else {
728 			yaw = 270;
729 		}
730 		if (yaw < 0) {
731 			yaw += 360;
732 		}
733 	}
734 
735 	return yaw;
736 }
737 
738 
G_InitGentity(gentity_t * e,qboolean bFreeG2)739 void G_InitGentity( gentity_t *e, qboolean bFreeG2 )
740 {
741 	e->inuse = qtrue;
742 	SetInUse(e);
743 	e->m_iIcarusID = IIcarusInterface::ICARUS_INVALID;
744 	e->classname = "noclass";
745 	e->s.number = e - g_entities;
746 
747 	// remove any ghoul2 models here in case we're reusing
748 	if (bFreeG2 && e->ghoul2.IsValid())
749 	{
750 		gi.G2API_CleanGhoul2Models(e->ghoul2);
751 	}
752 	//Navigational setups
753 	e->waypoint				= WAYPOINT_NONE;
754 	e->lastWaypoint			= WAYPOINT_NONE;
755 }
756 
757 /*
758 =================
759 G_Spawn
760 
761 Either finds a free entity, or allocates a new one.
762 
763   The slots from 0 to MAX_CLIENTS-1 are always reserved for clients, and will
764 never be used by anything else.
765 
766 Try to avoid reusing an entity that was recently freed, because it
767 can cause the client to think the entity morphed into something else
768 instead of being removed and recreated, which can cause interpolated
769 angles and bad trails.
770 =================
771 */
G_Spawn(void)772 gentity_t *G_Spawn( void )
773 {
774 	int			i, force;
775 	gentity_t	*e;
776 
777 	e = NULL;	// shut up warning
778 	i = 0;		// shut up warning
779 	for ( force = 0 ; force < 2 ; force++ )
780 	{
781 		// if we go through all entities and can't find one to free,
782 		// override the normal minimum times before use
783 		e = &g_entities[MAX_CLIENTS];
784 //		for ( i = MAX_CLIENTS ; i<globals.num_entities ; i++, e++)
785 //		{
786 //			if ( e->inuse )
787 //			{
788 //				continue;
789 //			}
790 		for ( i = MAX_CLIENTS ; i<globals.num_entities ; i++)
791 		{
792 			if(PInUse(i))
793 			{
794 				continue;
795 			}
796 			e=&g_entities[i];
797 
798 			// the first couple seconds of server time can involve a lot of
799 			// freeing and allocating, so relax the replacement policy
800 			if ( !force && e->freetime > 2000 && level.time - e->freetime < 1000 ) {
801 				continue;
802 			}
803 
804 			// reuse this slot
805 			G_InitGentity( e, qtrue );
806 			return e;
807 		}
808 		e=&g_entities[i];
809 		if ( i != ENTITYNUM_MAX_NORMAL )
810 		{
811 			break;
812 		}
813 	}
814 	if ( i == ENTITYNUM_MAX_NORMAL )
815 	{
816 
817 //#ifndef FINAL_BUILD
818 		e = &g_entities[0];
819 
820 //--------------Use this to dump directly to a file
821 		char buff[256];
822 		FILE *fp;
823 
824 		fp = fopen( "c:/nofreeentities.txt", "w" );
825 		for ( i = 0 ; i<globals.num_entities ; i++, e++)
826 		{
827 			if ( e->classname )
828 			{
829 				sprintf( buff, "%d: %s\n", i, e->classname );
830 			}
831 
832 			fputs( buff, fp );
833 		}
834 		fclose( fp );
835 /*
836 //---------------Or use this to dump to the console -- beware though, the console will fill quickly and you probably won't see the full list
837 		for ( i = 0 ; i<globals.num_entities ; i++, e++)
838 		{
839 			if ( e->classname )
840 			{
841 				Com_Printf( "%d: %s\n", i, e->classname );
842 
843 			}
844 		}
845 */
846 //FINAL_BUILD
847 		G_Error( "G_Spawn: no free entities" );
848 	}
849 
850 	// open up a new slot
851 	globals.num_entities++;
852 	G_InitGentity( e, qtrue );
853 	return e;
854 }
855 
856 extern	void	Vehicle_Remove(gentity_t *ent);
857 
858 /*
859 =================
860 G_FreeEntity
861 
862 Marks the entity as free
863 =================
864 */
G_FreeEntity(gentity_t * ed)865 void G_FreeEntity( gentity_t *ed ) {
866 	gi.unlinkentity (ed);		// unlink from world
867 
868 	// Free the Game Element (the entity) and delete the Icarus ID.
869 	Quake3Game()->FreeEntity( ed );
870 
871 	/*if ( ed->neverFree ) {
872 		return;
873 	}*/
874 
875 	if (ed->wayedge!=0)
876 	{
877 		NAV::WayEdgesNowClear(ed);
878 	}
879 
880 
881 	// remove any ghoul2 models here
882 	gi.G2API_CleanGhoul2Models(ed->ghoul2);
883 
884 	if (ed->client && ed->client->NPC_class == CLASS_VEHICLE)
885 	{
886 		Vehicle_Remove(ed);
887 
888 		if ( ed->m_pVehicle )
889 		{
890 			gi.Free( ed->m_pVehicle );
891 		}
892 		//CVehicleNPC *pVeh = static_cast< CVehicleNPC * >( ed->NPC );
893 		//delete pVeh;
894 		//gi.Free((char*)ed->NPC-4);//crazy hack for class vtables
895 	}
896 
897 	//free this stuff now, rather than waiting until the level ends.
898 	if (ed->NPC)
899 	{
900 		gi.Free(ed->NPC);
901 
902 		if(ed->client->clientInfo.customBasicSoundDir && gi.bIsFromZone(ed->client->clientInfo.customBasicSoundDir, TAG_G_ALLOC)) {
903 			gi.Free(ed->client->clientInfo.customBasicSoundDir);
904 		}
905 		if(ed->client->clientInfo.customCombatSoundDir) {
906 			gi.Free(ed->client->clientInfo.customCombatSoundDir);
907 		}
908 		if(ed->client->clientInfo.customExtraSoundDir) {
909 			gi.Free(ed->client->clientInfo.customExtraSoundDir);
910 		}
911 		if(ed->client->clientInfo.customJediSoundDir) {
912 			gi.Free(ed->client->clientInfo.customJediSoundDir);
913 		}
914 		if(ed->client->ps.saber[0].name && gi.bIsFromZone(ed->client->ps.saber[0].name, TAG_G_ALLOC) ) {
915 			gi.Free(ed->client->ps.saber[0].name);
916 		}
917 		if(ed->client->ps.saber[0].model && gi.bIsFromZone(ed->client->ps.saber[0].model, TAG_G_ALLOC) ) {
918 			gi.Free(ed->client->ps.saber[0].model);
919 		}
920 		if(ed->client->ps.saber[1].name && gi.bIsFromZone(ed->client->ps.saber[1].name, TAG_G_ALLOC) ) {
921 			gi.Free(ed->client->ps.saber[1].name);
922 		}
923 		if(ed->client->ps.saber[1].model && gi.bIsFromZone(ed->client->ps.saber[1].model, TAG_G_ALLOC) ) {
924  			gi.Free(ed->client->ps.saber[1].model);
925 		}
926 
927 		gi.Free(ed->client);
928 	}
929 
930 	if (ed->soundSet && gi.bIsFromZone(ed->soundSet, TAG_G_ALLOC)) {
931 		gi.Free(ed->soundSet);
932 	}
933 	if (ed->targetname && gi.bIsFromZone(ed->targetname, TAG_G_ALLOC)) {
934 		gi.Free(ed->targetname);
935 	}
936 	if (ed->NPC_targetname && gi.bIsFromZone(ed->NPC_targetname, TAG_G_ALLOC)) {
937 		gi.Free(ed->NPC_targetname);
938 	}
939 	if (ed->NPC_type && gi.bIsFromZone(ed->NPC_type, TAG_G_ALLOC)) {
940 		gi.Free(ed->NPC_type);
941 	}
942 	if (ed->classname && gi.bIsFromZone(ed->classname, TAG_G_ALLOC)) {
943 		gi.Free(ed->classname );
944 	}
945 	if (ed->message && gi.bIsFromZone(ed->message, TAG_G_ALLOC)) {
946 		gi.Free(ed->message);
947 	}
948 	if (ed->model && gi.bIsFromZone(ed->model, TAG_G_ALLOC)) {
949 		gi.Free(ed->model);
950 	}
951 
952 //scripting
953 	if (ed->script_targetname && gi.bIsFromZone(ed->script_targetname, TAG_G_ALLOC)) {
954 		gi.Free(ed->script_targetname);
955 	}
956 	if (ed->cameraGroup && gi.bIsFromZone(ed->cameraGroup, TAG_G_ALLOC)) {
957 		gi.Free(ed->cameraGroup);
958 	}
959 	if (ed->paintarget && gi.bIsFromZone(ed->paintarget, TAG_G_ALLOC)) {
960 		gi.Free(ed->paintarget);
961 	}
962 	if(ed->parms) {
963 		gi.Free(ed->parms);
964 	}
965 
966 //Limbs
967 	if (ed->target && gi.bIsFromZone(ed->target , TAG_G_ALLOC)) {
968 		gi.Free(ed->target);
969 	}
970 	if (ed->target2 && gi.bIsFromZone(ed->target2 , TAG_G_ALLOC)) {
971 		gi.Free(ed->target2);
972 	}
973 	if (ed->target3 && gi.bIsFromZone(ed->target3 , TAG_G_ALLOC)) {
974 		gi.Free(ed->target3);
975 	}
976 	if (ed->target4 && gi.bIsFromZone(ed->target4 , TAG_G_ALLOC)) {
977 		gi.Free(ed->target4);
978 	}
979 	if (ed->opentarget) {
980 		gi.Free(ed->opentarget);
981 	}
982 	if (ed->closetarget) {
983 		gi.Free(ed->closetarget);
984 	}
985 	// Free any associated timers
986 	TIMER_Clear(ed->s.number);
987 
988 	memset (ed, 0, sizeof(*ed));
989 	ed->s.number = ENTITYNUM_NONE;
990 	ed->classname = "freed";
991 	ed->freetime = level.time;
992 	ed->inuse = qfalse;
993 	ClearInUse(ed);
994 }
995 
996 /*
997 =================
998 G_TempEntity
999 
1000 Spawns an event entity that will be auto-removed
1001 The origin will be snapped to save net bandwidth, so care
1002 must be taken if the origin is right on a surface (snap towards start vector first)
1003 =================
1004 */
G_TempEntity(const vec3_t origin,int event)1005 gentity_t *G_TempEntity( const vec3_t origin, int event ) {
1006 	gentity_t		*e;
1007 	vec3_t		snapped;
1008 
1009 	e = G_Spawn();
1010 	e->s.eType = ET_EVENTS + event;
1011 
1012 	e->classname = "tempEntity";
1013 	e->eventTime = level.time;
1014 	e->freeAfterEvent = qtrue;
1015 
1016 	VectorCopy( origin, snapped );
1017 	SnapVector( snapped );		// save network bandwidth
1018 	G_SetOrigin( e, snapped );
1019 
1020 	// find cluster for PVS
1021 	gi.linkentity( e );
1022 
1023 	return e;
1024 }
1025 
1026 
1027 
1028 /*
1029 ==============================================================================
1030 
1031 Kill box
1032 
1033 ==============================================================================
1034 */
1035 
1036 /*
1037 =================
1038 G_KillBox
1039 
1040 Kills all entities that would touch the proposed new positioning
1041 of ent.  Ent should be unlinked before calling this!
1042 =================
1043 */
G_KillBox(gentity_t * ent)1044 void G_KillBox (gentity_t *ent) {
1045 	int			i, num;
1046 	gentity_t	*touch[MAX_GENTITIES], *hit;
1047 	vec3_t		mins, maxs;
1048 
1049 	VectorAdd( ent->client->ps.origin, ent->mins, mins );
1050 	VectorAdd( ent->client->ps.origin, ent->maxs, maxs );
1051 	num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
1052 
1053 	for (i=0 ; i<num ; i++) {
1054 		hit = touch[i];
1055 		if ( !hit->client ) {
1056 			continue;
1057 		}
1058 		if ( hit == ent ) {
1059 			continue;
1060 		}
1061 		if ( ent->s.number && hit->client->ps.stats[STAT_HEALTH] <= 0 )
1062 		{//NPC
1063 			continue;
1064 		}
1065 		if ( ent->s.number )
1066 		{//NPC
1067 			if ( !(hit->contents&CONTENTS_BODY) )
1068 			{
1069 				continue;
1070 			}
1071 		}
1072 		else
1073 		{//player
1074 			if ( !(hit->contents & ent->contents) )
1075 			{
1076 				continue;
1077 			}
1078 		}
1079 
1080 		// nail it
1081 		G_Damage ( hit, ent, ent, NULL, NULL,
1082 			100000, DAMAGE_NO_PROTECTION, MOD_UNKNOWN);
1083 	}
1084 
1085 }
1086 
1087 //==============================================================================
1088 
1089 
1090 /*
1091 ===============
1092 G_AddEvent
1093 
1094 Adds an event+parm and twiddles the event counter
1095 ===============
1096 */
G_AddEvent(gentity_t * ent,int event,int eventParm)1097 void G_AddEvent( gentity_t *ent, int event, int eventParm ) {
1098 	int		bits;
1099 
1100 	if ( !event ) {
1101 		gi.Printf( "G_AddEvent: zero event added for entity %i\n", ent->s.number );
1102 		return;
1103 	}
1104 
1105 #if 0 // FIXME: allow multiple events on an entity
1106 	// if the entity has an event that hasn't expired yet, don't overwrite
1107 	// it unless it is identical (repeated footsteps / muzzleflashes / etc )
1108 	if ( ent->s.event && ent->s.event != event ) {
1109 		gentity_t	*temp;
1110 
1111 		// generate a temp entity that references the original entity
1112 		gi.Printf( "eventPush\n" );
1113 
1114 		temp = G_Spawn();
1115 		temp->s.eType = ET_EVENT_ONLY;
1116 		temp->s.otherEntityNum = ent->s.number;
1117 		G_SetOrigin( temp, ent->s.origin );
1118 		G_AddEvent( temp, event, eventParm );
1119 		temp->freeAfterEvent = qtrue;
1120 		gi.linkentity( temp );
1121 		return;
1122 	}
1123 #endif
1124 
1125 	// clients need to add the event in playerState_t instead of entityState_t
1126 	if ( !ent->s.number ) //only one client
1127 	{
1128 #if 0
1129 		bits = ent->client->ps.externalEvent & EV_EVENT_BITS;
1130 		bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
1131 		ent->client->ps.externalEvent = event | bits;
1132 		ent->client->ps.externalEventParm = eventParm;
1133 		ent->client->ps.externalEventTime = level.time;
1134 #endif
1135 		if ( eventParm > 255 )
1136 		{
1137 			if ( event == EV_PAIN )
1138 			{//must have cheated, in undying?
1139 				eventParm = 255;
1140 			}
1141 			else
1142 			{
1143 				assert( eventParm < 256 );
1144 			}
1145 		}
1146 		AddEventToPlayerstate( event, eventParm, &ent->client->ps );
1147 	} else {
1148 		bits = ent->s.event & EV_EVENT_BITS;
1149 		bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
1150 		ent->s.event = event | bits;
1151 		ent->s.eventParm = eventParm;
1152 	}
1153 	ent->eventTime = level.time;
1154 }
1155 
1156 
1157 /*
1158 =============
1159 G_Sound
1160 =============
1161 */
G_Sound(gentity_t * ent,int soundIndex)1162 void G_Sound( gentity_t *ent, int soundIndex )
1163 {
1164 	gentity_t	*te;
1165 
1166 	te = G_TempEntity( ent->currentOrigin, EV_GENERAL_SOUND );
1167 	te->s.eventParm = soundIndex;
1168 }
1169 
1170 /*
1171 =============
1172 G_Sound
1173 =============
1174 */
G_SoundAtSpot(vec3_t org,int soundIndex,qboolean broadcast)1175 void G_SoundAtSpot( vec3_t org, int soundIndex, qboolean broadcast )
1176 {
1177 	gentity_t	*te;
1178 
1179 	te = G_TempEntity( org, EV_GENERAL_SOUND );
1180 	te->s.eventParm = soundIndex;
1181 	if ( broadcast )
1182 	{
1183 		te->svFlags |= SVF_BROADCAST;
1184 	}
1185 }
1186 
1187 /*
1188 =============
1189 G_SoundBroadcast
1190 
1191   Plays sound that can permeate PVS blockage
1192 =============
1193 */
G_SoundBroadcast(gentity_t * ent,int soundIndex)1194 void G_SoundBroadcast( gentity_t *ent, int soundIndex )
1195 {
1196 	gentity_t	*te;
1197 
1198 	te = G_TempEntity( ent->currentOrigin, EV_GLOBAL_SOUND );	//full volume
1199 	te->s.eventParm = soundIndex;
1200 	te->svFlags |= SVF_BROADCAST;
1201 }
1202 
1203 //==============================================================================
1204 
1205 /*
1206 ================
1207 G_SetOrigin
1208 
1209 Sets the pos trajectory for a fixed position
1210 ================
1211 */
G_SetOrigin(gentity_t * ent,const vec3_t origin)1212 void G_SetOrigin( gentity_t *ent, const vec3_t origin )
1213 {
1214 	VectorCopy( origin, ent->s.pos.trBase );
1215 	if(ent->client)
1216 	{
1217 		VectorCopy( origin, ent->client->ps.origin );
1218 		VectorCopy( origin, ent->s.origin );
1219 	}
1220 	else
1221 	{
1222 		ent->s.pos.trType = TR_STATIONARY;
1223 	}
1224 	ent->s.pos.trTime = 0;
1225 	ent->s.pos.trDuration = 0;
1226 	VectorClear( ent->s.pos.trDelta );
1227 
1228 	VectorCopy( origin, ent->currentOrigin );
1229 
1230 	// clear waypoints
1231 	if( ent->client && ent->NPC )
1232 	{
1233 		ent->waypoint = 0;
1234 		ent->lastWaypoint = 0;
1235 		if( NAV::HasPath( ent ) )
1236 		{
1237 			NAV::ClearPath( ent );
1238 		}
1239 	}
1240 
1241 }
1242 
G_CheckInSolidTeleport(const vec3_t & teleportPos,gentity_t * self)1243 qboolean G_CheckInSolidTeleport (const vec3_t& teleportPos, gentity_t *self)
1244 {
1245 	trace_t	trace;
1246 	vec3_t	end, mins;
1247 
1248 	VectorCopy(teleportPos, end);
1249 	end[2] += self->mins[2];
1250 	VectorCopy(self->mins, mins);
1251 	mins[2] = 0;
1252 
1253 	gi.trace(&trace, teleportPos, mins, self->maxs, end, self->s.number, self->clipmask, (EG2_Collision)0, 0);
1254 	if(trace.allsolid || trace.startsolid)
1255 	{
1256 		return qtrue;
1257 	}
1258 	return qfalse;
1259 }
1260 
1261 //===============================================================================
G_CheckInSolid(gentity_t * self,qboolean fix)1262 qboolean G_CheckInSolid (gentity_t *self, qboolean fix)
1263 {
1264 	trace_t	trace;
1265 	vec3_t	end, mins;
1266 
1267 	VectorCopy(self->currentOrigin, end);
1268 	end[2] += self->mins[2];
1269 	VectorCopy(self->mins, mins);
1270 	mins[2] = 0;
1271 
1272 	gi.trace(&trace, self->currentOrigin, mins, self->maxs, end, self->s.number, self->clipmask, (EG2_Collision)0, 0);
1273 	if(trace.allsolid || trace.startsolid)
1274 	{
1275 		return qtrue;
1276 	}
1277 
1278 #ifdef _DEBUG
1279 	if(trace.fraction < 0.99999713)
1280 #else
1281 	if(trace.fraction < 1.0)
1282 #endif
1283 	{
1284 		if(fix)
1285 		{//Put them at end of trace and check again
1286 			vec3_t	neworg;
1287 
1288 			VectorCopy(trace.endpos, neworg);
1289 			neworg[2] -= self->mins[2];
1290 			G_SetOrigin(self, neworg);
1291 			gi.linkentity(self);
1292 
1293 			return G_CheckInSolid(self, qfalse);
1294 		}
1295 		else
1296 		{
1297 			return qtrue;
1298 		}
1299 	}
1300 
1301 	return qfalse;
1302 }
1303 
infront(gentity_t * from,gentity_t * to)1304 qboolean infront(gentity_t *from, gentity_t *to)
1305 {
1306 	vec3_t	angles, dir, forward;
1307 	float	dot;
1308 
1309 	angles[PITCH] = angles[ROLL] = 0;
1310 	angles[YAW] = from->s.angles[YAW];
1311 	AngleVectors(angles, forward, NULL, NULL);
1312 
1313 	VectorSubtract(to->s.origin, from->s.origin, dir);
1314 	VectorNormalize(dir);
1315 
1316 	dot = DotProduct(forward, dir);
1317 	if(dot < 0.0f)
1318 	{
1319 		return qfalse;
1320 	}
1321 
1322 	return qtrue;
1323 }
1324 
Svcmd_Use_f(void)1325 void Svcmd_Use_f( void )
1326 {
1327 	const char	*cmd1 = gi.argv(1);
1328 
1329 	if ( !cmd1 || !cmd1[0] )
1330 	{
1331 		//FIXME: warning message
1332 		gi.Printf( "'use' takes targetname of ent or 'list' (lists all usable ents)\n" );
1333 		return;
1334 	}
1335 	else if ( !Q_stricmp("list", cmd1) )
1336 	{
1337 		gentity_t	*ent;
1338 
1339 		gi.Printf("Listing all usable entities:\n");
1340 
1341 		for ( int i = 1; i < ENTITYNUM_WORLD; i++ )
1342 		{
1343 			 ent = &g_entities[i];
1344 			 if ( ent )
1345 			 {
1346 				 if ( ent->targetname && ent->targetname[0] )
1347 				 {
1348 					 if ( ent->e_UseFunc != useF_NULL )
1349 					 {
1350 						 if ( ent->NPC )
1351 						 {
1352 							gi.Printf( "%s (NPC)\n", ent->targetname );
1353 						 }
1354 						 else
1355 						 {
1356 							gi.Printf( "%s\n", ent->targetname );
1357 						 }
1358 					 }
1359 				 }
1360 			 }
1361 		}
1362 
1363 		gi.Printf("End of list.\n");
1364 	}
1365 	else
1366 	{
1367 		G_UseTargets2( &g_entities[0], &g_entities[0], cmd1 );
1368 	}
1369 }
1370 
1371 //======================================================
1372 
G_SetActiveState(char * targetstring,qboolean actState)1373 void G_SetActiveState(char *targetstring, qboolean actState)
1374 {
1375 	gentity_t	*target = NULL;
1376 	while( NULL != (target = G_Find(target, FOFS(targetname), targetstring)) )
1377 	{
1378 		target->svFlags = actState ? (target->svFlags&~SVF_INACTIVE) : (target->svFlags|SVF_INACTIVE);
1379 	}
1380 }
1381 
target_activate_use(gentity_t * self,gentity_t * other,gentity_t * activator)1382 void target_activate_use(gentity_t *self, gentity_t *other, gentity_t *activator)
1383 {
1384 	G_ActivateBehavior(self,BSET_USE);
1385 
1386 	G_SetActiveState(self->target, ACT_ACTIVE);
1387 }
1388 
target_deactivate_use(gentity_t * self,gentity_t * other,gentity_t * activator)1389 void target_deactivate_use(gentity_t *self, gentity_t *other, gentity_t *activator)
1390 {
1391 	G_ActivateBehavior(self,BSET_USE);
1392 
1393 	G_SetActiveState(self->target, ACT_INACTIVE);
1394 }
1395 
1396 //FIXME: make these apply to doors, etc too?
1397 /*QUAKED target_activate (1 0 0) (-4 -4 -4) (4 4 4)
1398 Will set the target(s) to be usable/triggerable
1399 */
SP_target_activate(gentity_t * self)1400 void SP_target_activate( gentity_t *self )
1401 {
1402 	G_SetOrigin( self, self->s.origin );
1403 	self->e_UseFunc = useF_target_activate_use;
1404 }
1405 
1406 /*QUAKED target_deactivate (1 0 0) (-4 -4 -4) (4 4 4)
1407 Will set the target(s) to be non-usable/triggerable
1408 */
SP_target_deactivate(gentity_t * self)1409 void SP_target_deactivate( gentity_t *self )
1410 {
1411 	G_SetOrigin( self, self->s.origin );
1412 	self->e_UseFunc = useF_target_deactivate_use;
1413 }
1414 
1415 
1416 //======================================================
1417 
1418 /*
1419 ==============
1420 ValidUseTarget
1421 
1422 Returns whether or not the targeted entity is useable
1423 ==============
1424 */
ValidUseTarget(gentity_t * ent)1425 qboolean ValidUseTarget( gentity_t *ent )
1426 {
1427 	if ( ent->e_UseFunc == useF_NULL )
1428 	{
1429 		return qfalse;
1430 	}
1431 
1432 	if ( ent->svFlags & SVF_INACTIVE )
1433 	{//set by target_deactivate
1434 		return qfalse;
1435 	}
1436 
1437 	if ( !(ent->svFlags & SVF_PLAYER_USABLE) )
1438 	{//Check for flag that denotes BUTTON_USE useability
1439 		return qfalse;
1440 	}
1441 
1442 	//FIXME: This is only a temp fix..
1443 	if ( !Q_strncmp( ent->classname, "trigger", 7) )
1444 	{
1445 		return qfalse;
1446 	}
1447 
1448 	return qtrue;
1449 }
1450 
DebugTraceForNPC(gentity_t * ent)1451 static void DebugTraceForNPC(gentity_t *ent)
1452 {
1453 	trace_t		trace;
1454 	vec3_t		src, dest, vf;
1455 
1456 	VectorCopy( ent->client->renderInfo.eyePoint, src );
1457 
1458 	AngleVectors( ent->client->ps.viewangles, vf, NULL, NULL );//ent->client->renderInfo.eyeAngles was cg.refdef.viewangles, basically
1459 	//extend to find end of use trace
1460 	VectorMA( src, 4096, vf, dest );
1461 
1462 	//Trace ahead to find a valid target
1463 	gi.trace( &trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE|CONTENTS_SOLID|CONTENTS_TERRAIN|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE, (EG2_Collision)0, 0 );
1464 
1465 	if (trace.fraction < 0.99f)
1466 	{
1467 		gentity_t	*found = &g_entities[trace.entityNum];
1468 
1469 		if (found)
1470 		{
1471 			const char *targetName = found->targetname;
1472 			const char *className = found->classname;
1473 
1474 			if (targetName == 0)
1475 			{
1476 				targetName = "<NULL>";
1477 			}
1478 			if (className == 0)
1479 			{
1480 				className = "<NULL>";
1481 			}
1482 			Com_Printf("found targetname '%s', classname '%s'\n", targetName, className);
1483 		}
1484 	}
1485 }
1486 
G_ValidActivateBehavior(gentity_t * self,int bset)1487 static qboolean G_ValidActivateBehavior (gentity_t* self, int bset)
1488 {
1489 	if ( !self )
1490 	{
1491 		return qfalse;
1492 	}
1493 
1494 	const char *bs_name = self->behaviorSet[bset];
1495 
1496 	if( !(VALIDSTRING( bs_name )) )
1497 	{
1498 		return qfalse;
1499 	}
1500 
1501 	return qtrue;
1502 }
1503 
G_IsTriggerUsable(gentity_t * self,gentity_t * other)1504 static qboolean G_IsTriggerUsable(gentity_t* self, gentity_t* other)
1505 {
1506 	if ( self->svFlags & SVF_INACTIVE )
1507 	{//set by target_deactivate
1508 		return qfalse;
1509 	}
1510 
1511 	if( self->noDamageTeam )
1512 	{
1513 		if ( other->client->playerTeam != self->noDamageTeam )
1514 		{
1515 			return qfalse;
1516 		}
1517 	}
1518 
1519 
1520 	if ( self->spawnflags & 4 )
1521 	{//USE_BUTTON
1522 		if ( !other->client )
1523 		{
1524 			return qfalse;
1525 		}
1526 	}
1527 	else
1528 	{
1529 		return qfalse;
1530 	}
1531 
1532 	if ( self->spawnflags & 2 )
1533 	{//FACING
1534 		vec3_t	forward;
1535 
1536 		if ( other->client )
1537 		{
1538 			AngleVectors( other->client->ps.viewangles, forward, NULL, NULL );
1539 		}
1540 		else
1541 		{
1542 			AngleVectors( other->currentAngles, forward, NULL, NULL );
1543 		}
1544 
1545 		if ( DotProduct( self->movedir, forward ) < 0.5 )
1546 		{//Not Within 45 degrees
1547 			return qfalse;
1548 		}
1549 	}
1550 
1551 	if ((!G_ValidActivateBehavior (self, BSET_USE) && !self->target) ||
1552 		(self->target &&
1553 		(Q_stricmp(self->target, "n") == 0 ||
1554 		(Q_stricmp(self->target, "neveropen") == 0 ||
1555 		(Q_stricmp(self->target, "run_gran_drop") == 0) ||
1556 		(Q_stricmp(self->target, "speaker") == 0) ||
1557 		(Q_stricmp(self->target, "locked") == 0)
1558 		))))
1559 	{
1560 		return qfalse;
1561 	}
1562 
1563 
1564 	/*
1565 	//NOTE: This doesn't stop you from using it, just delays the use action!
1566 	if(self->delay && self->painDebounceTime < (level.time + self->delay) )
1567 	{
1568 		return qfalse;
1569 	}
1570 	*/
1571 
1572 	return qtrue;
1573 }
1574 
CanUseInfrontOfPartOfLevel(gentity_t * ent)1575 static qboolean CanUseInfrontOfPartOfLevel(gentity_t* ent )	//originally from VV
1576 {
1577 	int			i, num;
1578 	gentity_t	*touch[MAX_GENTITIES], *hit;
1579 	vec3_t		mins, maxs;
1580 	const vec3_t	range = { 40, 40, 52 };
1581 
1582 	if ( !ent->client ) {
1583 		return qfalse;
1584 	}
1585 
1586 	VectorSubtract( ent->client->ps.origin, range, mins );
1587 	VectorAdd( ent->client->ps.origin, range, maxs );
1588 
1589 	num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
1590 
1591 	// can't use ent->absmin, because that has a one unit pad
1592 	VectorAdd( ent->client->ps.origin, ent->mins, mins );
1593 	VectorAdd( ent->client->ps.origin, ent->maxs, maxs );
1594 
1595 	for ( i=0 ; i<num ; i++ ) {
1596 		hit = touch[i];
1597 
1598 		if ( (hit->e_TouchFunc == touchF_NULL) && (ent->e_TouchFunc == touchF_NULL) ) {
1599 			continue;
1600 		}
1601 		if ( !( hit->contents & CONTENTS_TRIGGER ) ) {
1602 			continue;
1603 		}
1604 
1605 		if ( !gi.EntityContact( mins, maxs, hit ) ) {
1606 			continue;
1607 		}
1608 
1609 		if ( hit->e_TouchFunc != touchF_NULL ) {
1610 			switch (hit->e_TouchFunc )
1611 			{
1612 			case touchF_Touch_Multi:
1613 				if (G_IsTriggerUsable(hit, ent))
1614 				{
1615 					return qtrue;
1616 				}
1617 				continue;
1618 				break;
1619 			default:
1620 				continue;
1621 			}
1622 		}
1623 	}
1624 	return qfalse;
1625 }
1626 
1627 #define USE_DISTANCE	64.0f
1628 extern qboolean eweb_can_be_used( gentity_t *self, gentity_t *other, gentity_t *activator );
CanUseInfrontOf(gentity_t * ent)1629 qboolean CanUseInfrontOf(gentity_t *ent)
1630 {
1631 	gentity_t	*target;
1632 	trace_t		trace;
1633 	vec3_t		src, dest, vf;
1634 
1635 	if ( ent->s.number && ent->client->NPC_class == CLASS_ATST )
1636 	{//a player trying to get out of his ATST
1637 //		GEntity_UseFunc( ent->activator, ent, ent );
1638 		return qfalse;
1639 	}
1640 
1641 	if (ent->client->ps.viewEntity != ent->s.number)
1642 	{
1643 		ent = &g_entities[ent->client->ps.viewEntity];
1644 
1645 		if ( !Q_stricmp( "misc_camera", ent->classname ) )
1646 		{	// we are in a camera
1647 			gentity_t *next = 0;
1648 			if ( ent->target2 != NULL )
1649 			{
1650 				next = G_Find( NULL, FOFS(targetname), ent->target2 );
1651 			}
1652 			if ( next )
1653 			{//found another one
1654 				if ( !Q_stricmp( "misc_camera", next->classname ) )
1655 				{//make sure it's another camera
1656 					return qtrue;
1657 				}
1658 			}
1659 			else //if ( ent->health > 0 )
1660 			{//I was the last (only?) one, clear out the viewentity
1661 				return qfalse;
1662 			}
1663 		}
1664 	}
1665 
1666 	if ( !ent->client ) {
1667 		return qfalse;
1668 	}
1669 
1670 
1671 	//FIXME: this does not match where the new accurate crosshair aims...
1672 	//cg.refdef.vieworg, basically
1673 	VectorCopy( ent->client->renderInfo.eyePoint, src );
1674 
1675 	AngleVectors( ent->client->ps.viewangles, vf, NULL, NULL );
1676 	//extend to find end of use trace
1677 	VectorMA( src, USE_DISTANCE, vf, dest );
1678 
1679 	//Trace ahead to find a valid target
1680 	gi.trace( &trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE|CONTENTS_SOLID|CONTENTS_TERRAIN|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE , G2_NOCOLLIDE, 10);
1681 
1682 	if ( trace.fraction == 1.0f || trace.entityNum >= ENTITYNUM_WORLD )
1683 	{
1684 		return (CanUseInfrontOfPartOfLevel(ent));
1685 	}
1686 
1687 	target = &g_entities[trace.entityNum];
1688 
1689 	if ( target && target->client && target->client->NPC_class == CLASS_VEHICLE )
1690 	{
1691 		// Attempt to board this vehicle.
1692 		return qtrue;
1693 	}
1694 	//Check for a use command
1695 	if (ValidUseTarget( target )) {
1696 		if ( target->s.eType == ET_ITEM )
1697 		{//item, see if we could actually pick it up
1698 			if ( (target->spawnflags&128/*ITMSF_USEPICKUP*/) )
1699 			{//player has to be touching me and hit use to pick it up, so don't allow this
1700 				if ( !G_BoundsOverlap( target->absmin, target->absmax, ent->absmin, ent->absmax ) )
1701 				{//not touching
1702 					return qfalse;
1703 				}
1704 			}
1705 			if ( !BG_CanItemBeGrabbed( &target->s, &ent->client->ps ) )
1706 			{//nope, so don't indicate that we can use it
1707 				return qfalse;
1708 			}
1709 		}
1710 		else if ( target->e_UseFunc == useF_misc_atst_use )
1711 		{//drivable AT-ST from JK2
1712 			if ( ent->client->ps.groundEntityNum != target->s.number )
1713 			{//must be standing on it to use it
1714 				return qfalse;
1715 			}
1716 		}
1717 		else if ( target->NPC!=NULL && target->health<=0 )
1718 		{
1719 			return qfalse;
1720 		}
1721 		else if ( target->e_UseFunc == useF_eweb_use )
1722 		{
1723 			if ( !eweb_can_be_used( target, ent, ent ) )
1724 			{
1725 				return qfalse;
1726 			}
1727 		}
1728 		return qtrue;
1729 	}
1730 
1731 	if ( target->client
1732 		&& target->client->ps.pm_type < PM_DEAD
1733 		&& target->NPC!=NULL
1734 		&& target->client->playerTeam
1735 		&& (target->client->playerTeam == ent->client->playerTeam || target->client->playerTeam == TEAM_NEUTRAL)
1736 		&& !(target->NPC->scriptFlags&SCF_NO_RESPONSE)
1737 		&& G_ValidActivateBehavior (target, BSET_USE))
1738 	{
1739 		return qtrue;
1740 	}
1741 
1742 	if (CanUseInfrontOfPartOfLevel(ent)) {
1743 		return qtrue;
1744 	}
1745 
1746 	return qfalse;
1747 }
1748 
1749 /*
1750 ==============
1751 TryUse
1752 
1753 Try and use an entity in the world, directly ahead of us
1754 ==============
1755 */
1756 
1757 
TryUse(gentity_t * ent)1758 void TryUse( gentity_t *ent )
1759 {
1760 	gentity_t	*target;
1761 	trace_t		trace;
1762 	vec3_t		src, dest, vf;
1763 
1764 	if (ent->s.number == 0 && g_npcdebug->integer == 1)
1765 	{
1766 		DebugTraceForNPC(ent);
1767 	}
1768 
1769 	if ( ent->s.number == 0 && ent->client->NPC_class == CLASS_ATST )
1770 	{//a player trying to get out of his ATST
1771 		GEntity_UseFunc( ent->activator, ent, ent );
1772 		return;
1773 	}
1774 
1775 	// TODO: turo-boost.
1776 /*	if ( ent->client->ps.vehicleIndex != VEHICLE_NONE )
1777 	{//in a vehicle, use key makes you turbo-boost
1778 		return;
1779 	}*/
1780 
1781 	//FIXME: this does not match where the new accurate crosshair aims...
1782 	//cg.refdef.vieworg, basically
1783 	VectorCopy( ent->client->renderInfo.eyePoint, src );
1784 
1785 	AngleVectors( ent->client->ps.viewangles, vf, NULL, NULL );//ent->client->renderInfo.eyeAngles was cg.refdef.viewangles, basically
1786 	//extend to find end of use trace
1787 	VectorMA( src, USE_DISTANCE, vf, dest );
1788 
1789 	//Trace ahead to find a valid target
1790 	gi.trace( &trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE|CONTENTS_SOLID|CONTENTS_TERRAIN|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE , G2_NOCOLLIDE, 10);
1791 
1792 	if ( trace.fraction == 1.0f || trace.entityNum  >= ENTITYNUM_WORLD )
1793 	{
1794 		//TODO: Play a failure sound
1795 		/*
1796 		if ( ent->s.number == 0 )
1797 		{//if nothing else, try the force telepathy power
1798 			ForceTelepathy( ent );
1799 		}
1800 		*/
1801 		return;
1802 	}
1803 
1804 	target = &g_entities[trace.entityNum];
1805 
1806 	if ( target && target->client && target->client->NPC_class == CLASS_VEHICLE )
1807 	{
1808 		// Attempt to board this vehicle.
1809 		target->m_pVehicle->m_pVehicleInfo->Board( target->m_pVehicle, ent );
1810 
1811 		return;
1812 	}
1813 
1814 	//Check for a use command
1815 	if ( ValidUseTarget( target ) )
1816 	{
1817 		NPC_SetAnim( ent, SETANIM_TORSO, BOTH_BUTTON_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1818 		/*
1819 		if ( !VectorLengthSquared( ent->client->ps.velocity ) && !PM_CrouchAnim( ent->client->ps.legsAnim ) )
1820 		{
1821 			NPC_SetAnim( ent, SETANIM_LEGS, BOTH_BUTTON_HOLD, SETANIM_FLAG_NORMAL|SETANIM_FLAG_HOLD );
1822 		}
1823 		*/
1824 		//ent->client->ps.weaponTime = ent->client->ps.torsoAnimTimer;
1825 		GEntity_UseFunc( target, ent, ent );
1826 		return;
1827 	}
1828 	else if ( target->client
1829 		&& target->client->ps.pm_type < PM_DEAD
1830 		&& target->NPC!=NULL
1831 		&& target->client->playerTeam
1832 		&& (target->client->playerTeam == ent->client->playerTeam || target->client->playerTeam == TEAM_NEUTRAL)
1833 		&& !(target->NPC->scriptFlags&SCF_NO_RESPONSE) )
1834 	{
1835 		NPC_UseResponse ( target, ent, qfalse );
1836 		return;
1837 	}
1838 	/*
1839 	if ( ent->s.number == 0 )
1840 	{//if nothing else, try the force telepathy power
1841 		ForceTelepathy( ent );
1842 	}
1843 	*/
1844 }
1845 
1846 extern int killPlayerTimer;
G_ChangeMap(const char * mapname,const char * spawntarget,qboolean hub)1847 void G_ChangeMap (const char *mapname, const char *spawntarget, qboolean hub)
1848 {
1849 //	gi.Printf("Loading...");
1850 	//ignore if player is dead
1851 	if (g_entities[0].client->ps.pm_type == PM_DEAD)
1852 		return;
1853 	if ( killPlayerTimer )
1854 	{//can't go to next map if your allies have turned on you
1855 		return;
1856 	}
1857 
1858 	if (mapname[0] == '+')	//fire up the menu instead
1859 	{
1860 		gi.SendConsoleCommand( va("uimenu %s\n", mapname+1) );
1861 		gi.cvar_set("skippingCinematic", "0");
1862 		gi.cvar_set("timescale", "1");
1863 		return;
1864 	}
1865 
1866 	if ( spawntarget == NULL ) {
1867 		spawntarget = "";	//prevent it from becoming "(null)"
1868 	}
1869 	if ( hub == qtrue )
1870 	{
1871 		gi.SendConsoleCommand( va("loadtransition %s %s\n", mapname, spawntarget) );
1872 	}
1873 	else
1874 	{
1875 		gi.SendConsoleCommand( va("maptransition %s %s\n", mapname, spawntarget) );
1876 	}
1877 }
1878 
G_PointInBounds(const vec3_t point,const vec3_t mins,const vec3_t maxs)1879 qboolean G_PointInBounds( const vec3_t point, const vec3_t mins, const vec3_t maxs )
1880 {
1881 	for(int i = 0; i < 3; i++ )
1882 	{
1883 		if ( point[i] < mins[i] )
1884 		{
1885 			return qfalse;
1886 		}
1887 		if ( point[i] > maxs[i] )
1888 		{
1889 			return qfalse;
1890 		}
1891 	}
1892 
1893 	return qtrue;
1894 }
1895 
G_BoxInBounds(const vec3_t point,const vec3_t mins,const vec3_t maxs,const vec3_t boundsMins,const vec3_t boundsMaxs)1896 qboolean G_BoxInBounds( const vec3_t point, const vec3_t mins, const vec3_t maxs, const vec3_t boundsMins, const vec3_t boundsMaxs )
1897 {
1898 	vec3_t boxMins;
1899 	vec3_t boxMaxs;
1900 
1901 	VectorAdd( point, mins, boxMins );
1902 	VectorAdd( point, maxs, boxMaxs );
1903 
1904 	if(boxMaxs[0]>boundsMaxs[0])
1905 		return qfalse;
1906 
1907 	if(boxMaxs[1]>boundsMaxs[1])
1908 		return qfalse;
1909 
1910 	if(boxMaxs[2]>boundsMaxs[2])
1911 		return qfalse;
1912 
1913 	if(boxMins[0]<boundsMins[0])
1914 		return qfalse;
1915 
1916 	if(boxMins[1]<boundsMins[1])
1917 		return qfalse;
1918 
1919 	if(boxMins[2]<boundsMins[2])
1920 		return qfalse;
1921 
1922 	//box is completely contained within bounds
1923 	return qtrue;
1924 }
1925 
1926 
G_SetAngles(gentity_t * ent,const vec3_t angles)1927 void G_SetAngles( gentity_t *ent, const vec3_t angles )
1928 {
1929 	VectorCopy( angles, ent->currentAngles );
1930 	VectorCopy( angles, ent->s.angles );
1931 	VectorCopy( angles, ent->s.apos.trBase );
1932 }
1933 
G_ClearTrace(const vec3_t start,const vec3_t mins,const vec3_t maxs,const vec3_t end,int ignore,int clipmask)1934 qboolean G_ClearTrace( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int ignore, int clipmask )
1935 {
1936 	static	trace_t	tr;
1937 
1938 	gi.trace( &tr, start, mins, maxs, end, ignore, clipmask, (EG2_Collision)0, 0 );
1939 
1940 	if ( tr.allsolid || tr.startsolid || tr.fraction < 1.0 )
1941 	{
1942 		return qfalse;
1943 	}
1944 
1945 	return qtrue;
1946 }
1947 
1948 extern void CG_TestLine( vec3_t start, vec3_t end, int time, unsigned int color, int radius);
G_DebugLine(vec3_t A,vec3_t B,int duration,int color,qboolean deleteornot)1949 void	G_DebugLine(vec3_t A, vec3_t B, int duration, int color, qboolean deleteornot)
1950 {
1951 	/*
1952 	gentity_t *tent = G_TempEntity( A, EV_DEBUG_LINE );
1953 	VectorCopy(B, tent->s.origin2 );
1954 	tent->s.time = duration;		// Pause
1955 	tent->s.time2 = color;			// Color
1956 	tent->s.weapon = 1;				// Dimater
1957 	tent->freeAfterEvent = deleteornot;
1958 	*/
1959 
1960 	CG_TestLine( A, B, duration, color, 1 );
1961 }
1962 
G_ExpandPointToBBox(vec3_t point,const vec3_t mins,const vec3_t maxs,int ignore,int clipmask)1963 qboolean G_ExpandPointToBBox( vec3_t point, const vec3_t mins, const vec3_t maxs, int ignore, int clipmask )
1964 {
1965 	trace_t	tr;
1966 	vec3_t	start, end;
1967 
1968 	VectorCopy( point, start );
1969 
1970 	for ( int i = 0; i < 3; i++ )
1971 	{
1972 		VectorCopy( start, end );
1973 		end[i] += mins[i];
1974 		gi.trace( &tr, start, vec3_origin, vec3_origin, end, ignore, clipmask, (EG2_Collision)0, 0 );
1975 		if ( tr.allsolid || tr.startsolid )
1976 		{
1977 			return qfalse;
1978 		}
1979 		if ( tr.fraction < 1.0 )
1980 		{
1981 			VectorCopy( start, end );
1982 			end[i] += maxs[i]-(mins[i]*tr.fraction);
1983 			gi.trace( &tr, start, vec3_origin, vec3_origin, end, ignore, clipmask, (EG2_Collision)0, 0 );
1984 			if ( tr.allsolid || tr.startsolid )
1985 			{
1986 				return qfalse;
1987 			}
1988 			if ( tr.fraction < 1.0 )
1989 			{
1990 				return qfalse;
1991 			}
1992 			VectorCopy( end, start );
1993 		}
1994 	}
1995 	//expanded it, now see if it's all clear
1996 	gi.trace( &tr, start, mins, maxs, start, ignore, clipmask, (EG2_Collision)0, 0 );
1997 	if ( tr.allsolid || tr.startsolid )
1998 	{
1999 		return qfalse;
2000 	}
2001 	VectorCopy( start, point );
2002 	return qtrue;
2003 }
2004 /*
2005 Ghoul2 Insert Start
2006 */
2007 
removeBoltSurface(gentity_t * ent)2008 void removeBoltSurface( gentity_t *ent)
2009 {
2010 	gentity_t	*hitEnt = &g_entities[ent->cantHitEnemyCounter];
2011 
2012 	// check first to be sure the bolt is still there on the model
2013 	if ((hitEnt->ghoul2.size() > ent->damage) &&
2014 		(hitEnt->ghoul2[ent->damage].mModelindex != -1) &&
2015 		(hitEnt->ghoul2[ent->damage].mSlist.size() > (unsigned int)ent->aimDebounceTime) &&
2016 		(hitEnt->ghoul2[ent->damage].mSlist[ent->aimDebounceTime].surface != -1) &&
2017 		(hitEnt->ghoul2[ent->damage].mSlist[ent->aimDebounceTime].offFlags == G2SURFACEFLAG_GENERATED))
2018 	{
2019 		// remove the bolt
2020 		gi.G2API_RemoveBolt(&hitEnt->ghoul2[ent->damage], ent->attackDebounceTime);
2021 		// now remove a surface if there is one
2022 		if (ent->aimDebounceTime != -1)
2023 		{
2024 			gi.G2API_RemoveSurface(&hitEnt->ghoul2[ent->damage], ent->aimDebounceTime);
2025 		}
2026 	}
2027 	// we are done with this entity.
2028 	G_FreeEntity(ent);
2029 }
2030 
G_SetBoltSurfaceRemoval(const int entNum,const int modelIndex,const int boltIndex,const int surfaceIndex,float duration)2031 void G_SetBoltSurfaceRemoval( const int entNum, const int modelIndex, const int boltIndex, const int surfaceIndex , float duration ) {
2032 	gentity_t		*e;
2033 	vec3_t		snapped = {0,0,0};
2034 
2035 	e = G_Spawn();
2036 
2037 	e->classname = "BoltRemoval";
2038 	e->cantHitEnemyCounter = entNum;
2039 	e->damage = modelIndex;
2040 	e->attackDebounceTime = boltIndex;
2041 	e->aimDebounceTime = surfaceIndex;
2042 
2043 	G_SetOrigin( e, snapped );
2044 
2045 	// find cluster for PVS
2046 	gi.linkentity( e );
2047 
2048 	e->nextthink = level.time + duration;
2049 	e->e_ThinkFunc = thinkF_removeBoltSurface;
2050 
2051 }
2052 
2053 /*
2054 Ghoul2 Insert End
2055 */
2056 
2057