示例#1
0
文件: NPC_utils.c 项目: jwginge/ojpa
//rww - cheap check to see if an armed client is looking in our general direction
qboolean NPC_SomeoneLookingAtMe(gentity_t *ent)
{
	int i = 0;
	gentity_t *pEnt;

	while (i < MAX_CLIENTS)
	{
		pEnt = &g_entities[i];

		if (pEnt && pEnt->inuse && pEnt->client && pEnt->client->sess.sessionTeam != TEAM_SPECTATOR &&
			!(pEnt->client->ps.pm_flags & PMF_FOLLOW) && pEnt->s.weapon != WP_NONE)
		{
			if (trap_InPVS(ent->r.currentOrigin, pEnt->r.currentOrigin))
			{
				if (InFOV( ent, pEnt, 30, 30 ))
				{ //I'm in a 30 fov or so cone from this player.. that's enough I guess.
					return qtrue;
				}
			}
		}

		i++;
	}

	return qfalse;
}
示例#2
0
/*
===========
Team_GetLocation

Report a location for the player. Uses placed nearby target_location entities
============
*/
gentity_t *Team_GetLocation(gentity_t *ent)
{
	gentity_t		*eloc, *best;
	float			bestlen, len;
	vec3_t			origin;

	best = NULL;
	bestlen = 3*8192.0*8192.0;

	VectorCopy( ent->r.currentOrigin, origin );

	for (eloc = level.locationHead; eloc; eloc = eloc->nextTrain) {
		len = ( origin[0] - eloc->r.currentOrigin[0] ) * ( origin[0] - eloc->r.currentOrigin[0] )
			+ ( origin[1] - eloc->r.currentOrigin[1] ) * ( origin[1] - eloc->r.currentOrigin[1] )
			+ ( origin[2] - eloc->r.currentOrigin[2] ) * ( origin[2] - eloc->r.currentOrigin[2] );

		if ( len > bestlen ) {
			continue;
		}

		if ( !trap_InPVS( origin, eloc->r.currentOrigin ) ) {
			continue;
		}

		bestlen = len;
		best = eloc;
	}

	return best;
}
bool TurretComponent::TargetValid(Entity& target, bool newTarget) {
	if (!target.Get<ClientComponent>() ||
	    target.Get<SpectatorComponent>() ||
	    Entities::IsDead(target) ||
	    (target.oldEnt->flags & FL_NOTARGET) ||
	    !Entities::OnOpposingTeams(entity, target) ||
	    G_Distance(entity.oldEnt, target.oldEnt) > range ||
	    !trap_InPVS(entity.oldEnt->s.origin, target.oldEnt->s.origin)) {

		if (!newTarget) {
			turretLogger.Verbose("Target lost: Out of range or eliminated.");
		}

		return false;
	}

	// New targets require a line of sight.
	if (G_LineOfFire(entity.oldEnt, target.oldEnt)) {
		lastLineOfSightToTarget = level.time;
	} else if (newTarget) {
		return false;
	}

	// Give up on an existing target if there was no line of sight for a while.
	if (lastLineOfSightToTarget + GIVEUP_TARGET_TIME <= level.time) {
		turretLogger.Verbose("Giving up on target: No line of sight for %d ms.",
			level.time - lastLineOfSightToTarget
		);

		return false;
	}

	return true;
}
示例#4
0
/**
 * @todo Move out of sg_team.c as it is not team-specific.
 */
gentity_t *GetCloseLocationEntity( gentity_t *ent )
{
	gentity_t *eloc, *best;
	float     bestlen, len;

	best = nullptr;
	bestlen = 3.0f * 8192.0f * 8192.0f;

	for ( eloc = level.locationHead; eloc; eloc = eloc->nextPathSegment )
	{
		len = DistanceSquared( ent->r.currentOrigin, eloc->r.currentOrigin );

		if ( len > bestlen )
		{
			continue;
		}

		if ( !trap_InPVS( ent->r.currentOrigin, eloc->r.currentOrigin ) )
		{
			continue;
		}

		bestlen = len;
		best = eloc;
	}

	return best;
}
示例#5
0
/*
===========
Team_GetLocation

Report a location for the player. Uses placed nearby target_location entities
============
*/
gentity_t *Team_GetLocation( gentity_t *ent )
{
	gentity_t *eloc, *best;
	float     bestlen, len;

	best = NULL;
	bestlen = 3.0f * 8192.0f * 8192.0f;

	for ( eloc = level.locationHead; eloc; eloc = eloc->nextTrain )
	{
		len = DistanceSquared( ent->r.currentOrigin, eloc->r.currentOrigin );

		if ( len > bestlen )
		{
			continue;
		}

		if ( !trap_InPVS( ent->r.currentOrigin, eloc->r.currentOrigin ) )
		{
			continue;
		}

		bestlen = len;
		best = eloc;
	}

	return best;
}
qboolean G_VisibleFromBinoculars ( gentity_t* viewer, gentity_t* ent, vec3_t origin ) {
	vec3_t vieworg;
	trace_t trace;

	VectorCopy(viewer->client->ps.origin, vieworg);
	vieworg[2] += viewer->client->ps.viewheight;

	if(!G_CullPointAndRadius( origin, 0 )) {
		return qfalse;
	}

	if(!trap_InPVS( vieworg, origin )) {
		return qfalse;
	}

	trap_Trace( &trace, vieworg, NULL, NULL, origin, viewer->s.number, MASK_SHOT );

/*	if( ent && trace.entityNum != ent-g_entities ) {
		return qfalse;
	}*/

	if( trace.fraction != 1.f ) {
		if( ent ) {
			if( trace.entityNum != ent->s.number ) {
				return qfalse;
			} else {
				return qtrue;
			}
		} else {
			return qfalse;
		}
	}

	return qtrue;
}
示例#7
0
/*
-------------------------
NAVNEW_ClearPathBetweenPoints
-------------------------
*/
int NAVNEW_ClearPathBetweenPoints(vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int ignore, int clipmask)
{
	trace_t	trace;

	//Test if they're even conceivably close to one another
	if ( !trap_InPVS( start, end ) )
	{
		return ENTITYNUM_WORLD;
	}

	trap_Trace( &trace, start, mins, maxs, end, ignore, clipmask );

		return trace.entityNum;

}
示例#8
0
	/**
	 * @brief Find a beacon matching a pattern.
	 * @return An ET_BEACON entity or nullptr.
	 */
	gentity_t *FindSimilar( const vec3_t origin, beaconType_t type, int data, int team, int owner,
	                        float radius, int eFlags, int eFlagsRelevant )
	{
		int flags = BG_Beacon( type )->flags;

		for ( gentity_t *ent = nullptr; (ent = G_IterateEntities(ent)); )
		{
			if ( ent->s.eType != ET_BEACON )
				continue;

			if ( ent->s.bc_type != type )
				continue;

			if ( ( ent->s.eFlags & eFlagsRelevant ) != ( eFlags & eFlagsRelevant ) )
				continue;

			if( ent->s.bc_team != team )
				continue;

			if ( ( flags & BCF_DATA_UNIQUE ) && ent->s.bc_data != data )
				continue;

			if ( ent->s.eFlags & EF_BC_DYING )
				continue;

			if     ( flags & BCF_PER_TEAM )
			{}
			else if( flags & BCF_PER_PLAYER )
			{
				if( ent->s.bc_owner != owner )
					continue;
			}
			else
			{
				if ( Distance( ent->s.origin, origin ) > radius )
					continue;

				if ( !trap_InPVS( ent->s.origin, origin ) )
					continue;
			}

			return ent;
		}

		return nullptr;
	}
示例#9
0
void NPC_ShowDebugInfo (void)
{
	if ( showBBoxes )
	{
		gentity_t	*found = NULL;
		vec3_t		mins, maxs;

		while( (found = G_Find( found, FOFS(classname), "NPC" ) ) != NULL )
		{
			if ( trap_InPVS( found->r.currentOrigin, g_entities[0].r.currentOrigin ) )
			{
				VectorAdd( found->r.currentOrigin, found->r.mins, mins );
				VectorAdd( found->r.currentOrigin, found->r.maxs, maxs );
				G_Cube( mins, maxs, NPCDEBUG_RED, 0.25 );
			}
		}
	}
}
示例#10
0
/* IsVisible:
 * Is player #1 visible by player #2 ? 
 */
qboolean IsVisible(gentity_t * player1, gentity_t * player2, float maxrange)
{
	vec3_t length;
	float distance;
	trace_t trace;

	// check for looking through non-transparent water
	if (!trap_InPVS(player1->client->ps.origin, player2->client->ps.origin))
		return qfalse;

	trap_Trace(&trace,
		   player1->client->ps.origin,
		   NULL, NULL, player2->client->ps.origin, player1->s.clientNum, MASK_SOLID);

	VectorSubtract(player1->client->ps.origin, player2->client->ps.origin, length);
	distance = VectorLength(length);

	return ((maxrange == 0 || distance < maxrange) && trace.fraction == 1.0f);
}
示例#11
0
/**
 * @brief G_VisibleFromBinoculars_Box
 * @param[in] viewer
 * @param[in] ent
 * @param[in,out] origin
 * @param[in] mins
 * @param[in] maxs
 * @return
 */
qboolean G_VisibleFromBinoculars_Box(gentity_t *viewer, gentity_t *ent, vec3_t origin, vec3_t mins, vec3_t maxs)
{
	vec3_t  vieworg;
	trace_t trace;

	VectorCopy(viewer->client->ps.origin, vieworg);
	vieworg[2] += viewer->client->ps.viewheight;

	// check if head is visible
	if (ent->methodOfDeath != MOD_LANDMINE)
	{
		origin[2] += ent->client->ps.viewheight;
	}

	if (!G_CullPointAndRadius(origin, 0))
	{
		return qfalse;
	}

	if (!trap_InPVS(vieworg, origin))
	{
		return qfalse;
	}

	trap_Trace(&trace, vieworg, mins, maxs, origin, viewer->s.number, MASK_SHOT);

	if (trace.fraction != 1.f)
	{
		if (trace.entityNum != ent->s.number)
		{
			return qfalse;
		}
		else
		{
			return qtrue;
		}
	}

	return qtrue;
}
示例#12
0
文件: NPC_utils.c 项目: jwginge/ojpa
//[CoOp]
qboolean InPlayersPVS(vec3_t point)
{//checks to see if this point is visible to all the players in the game.
	int Counter = 0;
	gentity_t *checkEnt = NULL;

	for(; Counter < level.maxclients ; Counter++ ) 
	{
		checkEnt = &g_entities[Counter];
		if ( !checkEnt->inuse || !checkEnt->client
			|| checkEnt->client->pers.connected == CON_DISCONNECTED
			|| checkEnt->client->sess.sessionTeam == TEAM_SPECTATOR )
		{//this entity isn't going to be seeing anything
			continue;
		}
		
		if(trap_InPVS(point, checkEnt->client->ps.origin))
		{//can be seen
			return qtrue;
		}
	}

	return qfalse;
}
示例#13
0
int G_FindLocalInterestPoint( gentity_t *self )
{
	int		i, bestPoint = ENTITYNUM_NONE;
	float	dist, bestDist = Q3_INFINITE;
	vec3_t	diffVec, eyes;

	CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes );
	for ( i = 0; i < level.numInterestPoints; i++ )
	{
		//Don't ignore portals?  If through a portal, need to look at portal!
		if ( trap_InPVS( level.interestPoints[i].origin, eyes ) )
		{
			VectorSubtract( level.interestPoints[i].origin, eyes, diffVec );
			if ( (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 < 48 &&
				fabs(diffVec[2]) > (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 )
			{//Too close to look so far up or down
				continue;
			}
			dist = VectorLengthSquared( diffVec );
			//Some priority to more interesting points
			//dist -= ((int)level.interestPoints[i].lookMode * 5) * ((int)level.interestPoints[i].lookMode * 5);
			if ( dist < MAX_INTEREST_DIST && dist < bestDist )
			{
				if ( G_ClearLineOfSight( eyes, level.interestPoints[i].origin, self->s.number, MASK_OPAQUE ) )
				{
					bestDist = dist;
					bestPoint = i;
				}
			}
		}
	}
	if ( bestPoint != ENTITYNUM_NONE && level.interestPoints[bestPoint].target )
	{
		G_UseTargets2( self, self, level.interestPoints[bestPoint].target );
	}
	return bestPoint;
}
示例#14
0
文件: team.c 项目: icanhas/yantar
/*
 * Team_GetLocation
 *
 * Report a location for the player. Uses placed nearby target_location entities
 */
Gentity *
Team_GetLocation(Gentity *ent)
{
	Gentity	*eloc, *best;
	float bestlen, len;
	Vec3		origin;

	best = NULL;
	bestlen = 3*8192.0*8192.0;

	copyv3(ent->r.currentOrigin, origin);

	for(eloc = level.locationHead; eloc; eloc = eloc->nextTrain){
		len =
			(origin[0] -
			 eloc->r.currentOrigin[0]) *
			(origin[0] - eloc->r.currentOrigin[0])
			+ (origin[1] -
			   eloc->r.currentOrigin[1]) *
			(origin[1] - eloc->r.currentOrigin[1])
			+ (origin[2] -
			   eloc->r.currentOrigin[2]) *
			(origin[2] - eloc->r.currentOrigin[2]);

		if(len > bestlen)
			continue;

		if(!trap_InPVS(origin, eloc->r.currentOrigin))
			continue;

		bestlen = len;
		best = eloc;
	}

	return best;
}
示例#15
0
//ported from SP
void G_DynamicMusicUpdate( void )
{
	int			battle = 0;
	vec3_t		center;
	qboolean	clearLOS = qfalse;
	int			distSq, radius = 2048;
	int			i, e, x;
	gentity_t	*ent;
	int entityList[MAX_GENTITIES];
	int			entTeam;
	vec3_t		mins, maxs;
	int			numListedEntities;
	gentity_t	*player;

	if( DMSData.dmDebounceTime >= 0 && DMSData.dmDebounceTime < level.time )
	{//debounce over, reset to default music
		DMSData.dmDebounceTime = -1;
		DMSData.dmState = DM_AUTO;
		DMSData.olddmState = DM_AUTO;
	}

	if ( DMSData.dmState == DM_DEATH)
	{//Play the death music
		if(DMSData.olddmState != DM_DEATH)
		{//haven't set the state yet
			trap_SetConfigstring( CS_MUSIC, DMS_DEATH_MUSIC );
			DMSData.olddmState = DM_DEATH;
			DMSData.dmDebounceTime = level.time + DMS_DEATH_MUSIC_TIME;
		}
		return;
	}

	if ( DMSData.dmState == DM_BOSS )
	{
		if(DMSData.olddmState != DM_BOSS)
		{
			trap_SetConfigstring( CS_MUSIC, DMSData.bossMusic.fileName );
			DMSData.olddmState = DM_BOSS;
		}
		return;
	}

	if ( DMSData.dmState == DM_SILENCE )
	{//turn off the music
		if(DMSData.olddmState != DM_SILENCE)
		{
			trap_SetConfigstring( CS_MUSIC, "" );
			DMSData.olddmState = DM_SILENCE;
		}
		return;
	}

	if ( DMSData.dmBeatTime > level.time )
	{//not on a beat
		return;
	}

	DMSData.dmBeatTime = level.time + 1000;//1 second beats

	for(i = 0; i < MAX_CLIENTS; i++)
	{
		player = &g_entities[i];

		//check to make sure this player is valid
		if(!player || !player->inuse 
			|| player->client->pers.connected == CON_DISCONNECTED
			|| player->client->sess.sessionTeam == TEAM_SPECTATOR)
		{
			continue;
		}

		//enemy-based
		VectorCopy( player->r.currentOrigin, center );
		for ( x = 0 ; x < 3 ; x++ ) 
		{
			mins[x] = center[x] - radius;
			maxs[x] = center[x] + radius;
		}
	
		numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
		for ( e = 0 ; e < numListedEntities ; e++ ) 
		{
			ent = &g_entities[entityList[e]];
			if ( !ent || !ent->inuse )
			{
				continue;
			}

			if ( !ent->client || !ent->NPC )
			{
				if ( ent->classname && (!Q_stricmp( "PAS", ent->classname )||!Q_stricmp( "misc_turret", ent->classname )) )
				{//a turret
					entTeam = ent->teamnodmg;
					//entTeam = ent->noDamageTeam;
				}
				else
				{
					continue;
				}
			}
			else
			{//an NPC
				entTeam = ent->client->playerTeam;
			}

			if ( entTeam == player->client->playerTeam )
			{//ally
				continue;
			}

			if ( entTeam == TEAM_FREE && (!ent->enemy || !ent->enemy->client || ent->enemy->client->playerTeam != player->client->playerTeam) )
			{//a droid that is not mad at me or my allies
				continue;
			}

			if ( !trap_InPVS( player->r.currentOrigin, ent->r.currentOrigin ) )
			{//not potentially visible
				continue;
			}

			if ( ent->client && ent->s.weapon == WP_NONE )
			{//they don't have a weapon... FIXME: only do this for droids?
				continue;
			}

			clearLOS = qfalse;
			if ( (ent->enemy==player&&(!ent->NPC||ent->NPC->confusionTime<level.time)) || (ent->client&&ent->client->ps.weaponTime) || (!ent->client&&ent->attackDebounceTime>level.time))
			{//mad
				if ( ent->health > 0 )
				{//alive
					//FIXME: do I really need this check?
					if ( ent->s.weapon == WP_SABER && ent->client && ent->client->ps.saberHolstered == 2 && ent->enemy != player )
					{//a Jedi who has not yet gotten mad at me
						continue;
					}
					if ( ent->NPC && ent->NPC->behaviorState == BS_CINEMATIC )
					{//they're not actually going to do anything about being mad at me...
						continue;
					}
					//okay, they're in my PVS, but how close are they?  Are they actively attacking me?
					if ( !ent->client && ent->s.weapon == WP_TURRET && ent->fly_sound_debounce_time && ent->fly_sound_debounce_time - level.time < 10000 )
					{//a turret that shot at me less than ten seconds ago
					}
					else if( ent->NPC && level.time < ent->NPC->shotTime )
					{//npc that fired recently
					}
					/* changed from SP
					else if ( ent->client && ent->client->ps.lastShotTime && ent->client->ps.lastShotTime - level.time < 10000 )
					{//an NPC that shot at me less than ten seconds ago
					}
					*/
					else
					{//not actively attacking me lately, see how far away they are
						distSq = DistanceSquared( ent->r.currentOrigin, player->r.currentOrigin );
						if ( distSq > 4194304 )
						{//> 2048 away
							continue;
						}
						else if ( distSq > 1048576 )
						{//> 1024 away
							clearLOS = G_ClearLOS3( player, player->client->renderInfo.eyePoint, ent );
							if ( clearLOS == qfalse )
							{//No LOS
								continue;
							}
						}
					}
					battle++;
				}
			}
		}

		if ( !battle )
		{//no active enemies, but look for missiles, shot impacts, etc...
			//[CoOp]
			int alert = G_CheckAlertEvents( player, qtrue, qtrue, 1024, 1024, -1, qfalse, AEL_SUSPICIOUS, qfalse );
			//int alert = G_CheckAlertEvents( player, qtrue, qtrue, 1024, 1024, -1, qfalse, AEL_SUSPICIOUS );
			//[/CoOp]
			if ( alert != -1 )
			{//FIXME: maybe tripwires and other FIXED things need their own sound, some kind of danger/caution theme
				if ( G_CheckForDanger( player, alert ) )
				{//found danger near by
					battle = 1;
				}
			}
		}
	}

	if ( battle )
	{
		SetDMSState(DM_ACTION);
	}
	else 
	{//switch to explore
		SetDMSState(DM_EXPLORE);
	}

	if(DMSData.dmState != DMSData.olddmState)
	{//switching between action and explore modes
		TransitionBetweenState();
	}
}
示例#16
0
文件: g_turret.c 项目: jwginge/ojpa
//-----------------------------------------------------
void turret_base_think( gentity_t *self )
//-----------------------------------------------------
{
	qboolean	turnOff = qtrue;
	float		enemyDist;
	vec3_t		enemyDir, org, org2;

	if ( self->spawnflags & 1 )
	{
		// not turned on
		turret_turnoff( self );

		// No target
		self->flags |= FL_NOTARGET;
		self->nextthink = -1;//never think again
		return;
	}
	else
	{
		// I'm all hot and bothered
		self->flags &= ~FL_NOTARGET;
		//remember to keep thinking!
		self->nextthink = level.time + FRAMETIME;
	}

	if ( !self->enemy )
	{
		if ( turret_find_enemies( self ))
		{
			turnOff = qfalse;
		}
	}
	else if ( self->enemy->client && self->enemy->client->sess.sessionTeam == TEAM_SPECTATOR )
	{//don't keep going after spectators
		self->enemy = NULL;
	}
	else
	{//FIXME: remain single-minded or look for a new enemy every now and then?
		if ( self->enemy->health > 0 )
		{
			// enemy is alive
			VectorSubtract( self->enemy->r.currentOrigin, self->r.currentOrigin, enemyDir );
			enemyDist = VectorLengthSquared( enemyDir );

			if ( enemyDist < (self->radius * self->radius) )
			{
				// was in valid radius
				if ( trap_InPVS( self->r.currentOrigin, self->enemy->r.currentOrigin ) )
				{
					// Every now and again, check to see if we can even trace to the enemy
					trace_t tr;

					if ( self->enemy->client )
					{
						VectorCopy( self->enemy->client->renderInfo.eyePoint, org );
					}
					else
					{
						VectorCopy( self->enemy->r.currentOrigin, org );
					}
					VectorCopy( self->r.currentOrigin, org2 );
					if ( self->spawnflags & 2 )
					{
						org2[2] += 10;
					}
					else
					{
						org2[2] -= 10;
					}
					trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT );

					if ( !tr.allsolid && !tr.startsolid && tr.entityNum == self->enemy->s.number )
					{
						turnOff = qfalse;	// Can see our enemy
					}
				}
			}
		}

		turret_head_think( self );
	}

	if ( turnOff )
	{
		if ( self->bounceCount < level.time ) // bounceCount is used to keep the thing from ping-ponging from on to off
		{
			turret_sleep( self );
		}
	}
	else
	{
		// keep our enemy for a minimum of 2 seconds from now
		self->bounceCount = level.time + 2000 + random() * 150;
	}

	turret_aim( self );
}
示例#17
0
文件: g_turret.c 项目: jwginge/ojpa
//-----------------------------------------------------
static qboolean turret_find_enemies( gentity_t *self )
//-----------------------------------------------------
{
	qboolean	found = qfalse;
	int			i, count;
	float		bestDist = self->radius * self->radius;
	float		enemyDist;
	vec3_t		enemyDir, org, org2;
	gentity_t	*entity_list[MAX_GENTITIES], *target, *bestTarget = NULL;
	trace_t		tr;
	gentity_t *top = &g_entities[self->r.ownerNum];
	if ( !top )
	{
		return qfalse;
	}

	if ( self->aimDebounceTime > level.time ) // time since we've been shut off
	{
		// We were active and alert, i.e. had an enemy in the last 3 secs
		if ( self->timestamp < level.time )
		{
			//G_Sound(self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/ping.wav" ));
			self->timestamp = level.time + 1000;
		}
	}

	VectorCopy( top->r.currentOrigin, org2 );

	count = G_RadiusList( org2, self->radius, self, qtrue, entity_list );

	for ( i = 0; i < count; i++ )
	{
		target = entity_list[i];

		if ( !target->client )
		{
			// only attack clients
			continue;
		}
		if ( target == self || !target->takedamage || target->health <= 0 || ( target->flags & FL_NOTARGET ))
		{
			continue;
		}
		if ( target->client->sess.sessionTeam == TEAM_SPECTATOR )
		{
			continue;
		}
		if ( self->alliedTeam )
		{
			if ( target->client )
			{
				if ( target->client->sess.sessionTeam == self->alliedTeam )
				{ 
					// A bot/client/NPC we don't want to shoot
					continue;
				}
			}
			else if ( target->teamnodmg == self->alliedTeam )
			{ 
				// An ent we don't want to shoot
				continue;
			}
		}
		if ( !trap_InPVS( org2, target->r.currentOrigin ))
		{
			continue;
		}

		VectorCopy( target->r.currentOrigin, org );
		org[2] += target->r.maxs[2]*0.5f;

		trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT );

		if ( !tr.allsolid && !tr.startsolid && ( tr.fraction == 1.0 || tr.entityNum == target->s.number ))
		{
			// Only acquire if have a clear shot, Is it in range and closer than our best?
			VectorSubtract( target->r.currentOrigin, top->r.currentOrigin, enemyDir );
			enemyDist = VectorLengthSquared( enemyDir );

			if ( enemyDist < bestDist // all things equal, keep current
				|| (!Q_stricmp( "atst_vehicle", target->NPC_type ) && bestTarget && Q_stricmp( "atst_vehicle", bestTarget->NPC_type ) ) )//target AT-STs over non-AT-STs... FIXME: must be a better, easier way to tell this, no?
			{
				if ( self->attackDebounceTime < level.time )
				{
					// We haven't fired or acquired an enemy in the last 2 seconds-start-up sound
					//G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/startup.wav" ));

					// Wind up turrets for a bit
					self->attackDebounceTime = level.time + 1400;
				}

				bestTarget = target;
				bestDist = enemyDist;
				found = qtrue;
			}
		}
	}

	if ( found )
	{
		G_SetEnemy( self, bestTarget );
		if ( VALIDSTRING( self->target2 ))
		{
			G_UseTargets2( self, self, self->target2 );
		}
	}

	return found;
}
示例#18
0
visibility_t NPC_CheckVisibility ( gentity_t *ent, int flags ) 
{
	// flags should never be 0
	if ( !flags ) 
	{
		return VIS_NOT;
	}

	// check PVS
	if ( flags & CHECK_PVS ) 
	{
		if ( !trap_InPVS ( ent->r.currentOrigin, NPC->r.currentOrigin ) ) 
		{
			return VIS_NOT;
		}
	}
	if ( !(flags & (CHECK_360|CHECK_FOV|CHECK_SHOOT)) ) 
	{
		return VIS_PVS;
	}

	// check within visrange
	if (flags & CHECK_VISRANGE)
	{
		if( !InVisrange ( ent ) ) 
		{
			return VIS_PVS;
		}
	}

	// check 360 degree visibility
	//Meaning has to be a direct line of site
	if ( flags & CHECK_360 ) 
	{
		if ( !CanSee ( ent ) ) 
		{
			return VIS_PVS;
		}
	}
	if ( !(flags & (CHECK_FOV|CHECK_SHOOT)) ) 
	{
		return VIS_360;
	}

	// check FOV
	if ( flags & CHECK_FOV ) 
	{
		if ( !InFOV ( ent, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov) ) 
		{
			return VIS_360;
		}
	}

	if ( !(flags & CHECK_SHOOT) ) 
	{
		return VIS_FOV;
	}

	// check shootability
	if ( flags & CHECK_SHOOT ) 
	{
		if ( !CanShoot ( ent, NPC ) ) 
		{
			return VIS_FOV;
		}
	}

	return VIS_SHOOT;
}
示例#19
0
/*
============
AICast_StartServerFrame

  Do movements, sighting, etc
============
*/
void AICast_StartServerFrame( int time ) {
	int i, elapsed, count, clCount;
	cast_state_t    *cs;
	int castcount;
	static int lasttime;
	static vmCvar_t aicast_disable;
	gentity_t *ent;
	cast_state_t *pcs;
//	int		oldLegsTimer;

	if ( trap_Cvar_VariableIntegerValue( "savegame_loading" ) ) {
		return;
	}

	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
		return;
	}

	if ( saveGamePending ) {
		return;
	}

	// if waiting at intermission, don't think
	if ( strlen( g_missionStats.string ) > 1 ) {
		return;
	}

	if ( !aicast_disable.handle ) {
		trap_Cvar_Register( &aicast_disable, "aicast_disable", "0", CVAR_CHEAT );
	} else
	{
		trap_Cvar_Update( &aicast_disable );
		if ( aicast_disable.integer ) {
			return;
		}
	}

	trap_Cvar_Update( &aicast_debug );

	// no need to think during the intermission
	if ( level.intermissiontime ) {
		return;
	}
	//
	// make sure the AAS gets updated
	trap_BotLibStartFrame( (float) time / 1000 );
	//
	//
	elapsed = time - lasttime;
	if ( elapsed == 0 ) {
		return;         // no time has elapsed
	}

	pcs = AICast_GetCastState( 0 );

//G_Printf( "AI startserverframe: %i\n", time );

	if ( elapsed < 0 ) {
		elapsed = 0;
		lasttime = time;
	}
	// don't let the framerate drop below 10
	if ( elapsed > 100 ) {
		elapsed = 100;
	}
	//
	// process player's current script if it exists
	AICast_ScriptRun( AICast_GetCastState( 0 ), qfalse );
	//
	AICast_SightUpdate( (int)( (float)SIGHT_PER_SEC * ( (float)elapsed / 1000 ) ) );
	//
	count = 0;
	castcount = 0;
	clCount = 0;
	ent = g_entities;
	//
	//update the AI characters
	// TTimo gcc: left-hand operand of comma expression has no effect
	// initial line: for (i = 0; i < aicast_maxclients, clCount < level.numPlayingClients; i++, ent++)
	for ( i = 0; ( i < aicast_maxclients ) && ( clCount < level.numPlayingClients ) ; i++, ent++ )
	{
		if ( ent->client ) {
			clCount++;
		}
		//
		cs = AICast_GetCastState( i );
		// is this a cast AI?
		if ( cs->bs ) {
			if ( ent->aiInactive == qfalse && ent->inuse ) {
				//
				elapsed = level.time - cs->lastMoveThink;
				//
				// optimization, if they're not in the player's PVS, and they aren't trying to move, then don't bother thinking
				if (    ( ( ent->health > 0 ) && ( elapsed > 300 ) )
						||  ( g_entities[0].client && g_entities[0].client->cameraPortal )
						||  ( cs->vislist[0].visible_timestamp == cs->vislist[0].lastcheck_timestamp )
						||  ( pcs->vislist[cs->entityNum].visible_timestamp == pcs->vislist[cs->entityNum].lastcheck_timestamp )
						||  ( VectorLength( ent->client->ps.velocity ) > 0 )
						||  ( cs->bs->lastucmd.forwardmove || cs->bs->lastucmd.rightmove || cs->bs->lastucmd.upmove > 0 || cs->bs->lastucmd.buttons || cs->bs->lastucmd.wbuttons )
						||  ( trap_InPVS( cs->bs->origin, g_entities[0].s.pos.trBase ) ) ) { // do pvs check last, since it's the most expensive to call
//					oldLegsTimer = ent->client->ps.legsTimer;
					//
					// send it's movement commands
					//
					serverTime = time;
					AICast_UpdateInput( cs, elapsed );
					trap_BotUserCommand( cs->bs->client, &( cs->bs->lastucmd ) );
					cs->lastMoveThink = level.time;
					//
					// check for anim changes that may require us to stay still
					//
/*					if (oldLegsTimer != ent->client->ps.legsTimer) {
						// dont move until they are finished
						if (cs->castScriptStatus.scriptNoMoveTime < level.time + ent->client->ps.legsTimer) {
							cs->castScriptStatus.scriptNoMoveTime = level.time + ent->client->ps.legsTimer;
						}
					}
*/              }
			} else {
				trap_UnlinkEntity( ent );
			}
			//
			// see if we've checked all cast AI's
			if ( ++castcount >= numcast ) {
				break;
			}
		}
	}
	//
	lasttime = time;
}
示例#20
0
//-----------------------------------------------------
void turretG2_base_think( gentity_t *self )
//-----------------------------------------------------
{
    qboolean	turnOff = qtrue;
    float		enemyDist;
    vec3_t		enemyDir, org, org2;

    self->nextthink = level.time + FRAMETIME;

    if ( self->health <= 0 )
    {   //dead
        if (self->spawnflags & SPF_TURRETG2_CANRESPAWN)
        {   //can respawn
            if ( self->genericValue5 && self->genericValue5 < level.time )
            {   //we are dead, see if it's time to respawn
                turretG2_respawn( self );
            }
        }
        return;
    }
    else if ( self->spawnflags & 1 )
    {   // not turned on
        turretG2_turnoff( self );
        turretG2_aim( self );

        // No target
        self->flags |= FL_NOTARGET;
        return;
    }
    else
    {
        // I'm all hot and bothered
        self->flags &= ~FL_NOTARGET;
    }

    if ( self->enemy )
    {
        if ( self->enemy->health < 0
                || !self->enemy->inuse )
        {
            self->enemy = NULL;
        }
    }

    if ( self->last_move_time < level.time )
    {   //MISNOMER: used a enemy recalcing debouncer
        if ( turretG2_find_enemies( self ) )
        {   //found one
            turnOff = qfalse;
            if ( self->enemy->client )
            {   //hold on to clients for a min of 3 seconds
                self->last_move_time = level.time + 3000;
            }
            else
            {   //hold less
                self->last_move_time = level.time + 500;
            }
        }
    }

    if ( self->enemy != NULL )
    {
        if ( self->enemy->client && self->enemy->client->sess.sessionTeam == TEAM_SPECTATOR )
        {   //don't keep going after spectators
            self->enemy = NULL;
        }
        else
        {   //FIXME: remain single-minded or look for a new enemy every now and then?
            // enemy is alive
            VectorSubtract( self->enemy->r.currentOrigin, self->r.currentOrigin, enemyDir );
            enemyDist = VectorLengthSquared( enemyDir );

            if ( enemyDist < self->radius * self->radius )
            {
                // was in valid radius
                if ( trap_InPVS( self->r.currentOrigin, self->enemy->r.currentOrigin ) )
                {
                    // Every now and again, check to see if we can even trace to the enemy
                    trace_t tr;

                    if ( self->enemy->client )
                    {
                        VectorCopy( self->enemy->client->renderInfo.eyePoint, org );
                    }
                    else
                    {
                        VectorCopy( self->enemy->r.currentOrigin, org );
                    }
                    VectorCopy( self->r.currentOrigin, org2 );
                    if ( self->spawnflags & 2 )
                    {
                        org2[2] += 10;
                    }
                    else
                    {
                        org2[2] -= 10;
                    }
                    trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT );

                    if ( !tr.allsolid && !tr.startsolid && tr.entityNum == self->enemy->s.number )
                    {
                        turnOff = qfalse;	// Can see our enemy
                    }
                }
            }

        }
    }

    if ( turnOff )
    {
        if ( self->bounceCount < level.time ) // bounceCount is used to keep the thing from ping-ponging from on to off
        {
            turretG2_turnoff( self );
        }
    }
    else
    {
        // keep our enemy for a minimum of 2 seconds from now
        self->bounceCount = level.time + 2000 + random() * 150;
    }

    turretG2_aim( self );
    if ( !turnOff )
    {
        turretG2_head_think( self );
    }
}
示例#21
0
void props_me109_think( gentity_t *self ) {

	qboolean in_PVS = qfalse;

	{
		gentity_t *player;

		player = AICast_FindEntityForName( "player" );

		if ( player ) {
			in_PVS = trap_InPVS( player->r.currentOrigin, self->s.pos.trBase );

			if ( in_PVS ) {
				self->melee->s.eType = ET_GENERAL;

				{
					float len;
					vec3_t vec;
					vec3_t forward;
					vec3_t dir;
					vec3_t point;

					VectorCopy( player->r.currentOrigin, point );
					VectorSubtract( player->r.currentOrigin, self->r.currentOrigin, vec );
					len = VectorLength( vec );
					vectoangles( vec, dir );
					AngleVectors( dir, forward, NULL, NULL );
					VectorMA( point, len * 0.1, forward, point );

					G_SetOrigin( self->melee, point );
				}
			} else
			{
				self->melee->s.eType = ET_GENERAL;
			}

			trap_LinkEntity( self->melee );
		}
	}

	Plane_Attack( self, in_PVS );

	Calc_Roll( self );

	if ( self->health < 250 ) {
		gentity_t *tent;
		vec3_t point;

		VectorCopy( self->r.currentOrigin, point );
		tent = G_TempEntity( point, EV_SMOKE );
		VectorCopy( point, tent->s.origin );
		tent->s.time = 2000;
		tent->s.time2 = 1000;
		tent->s.density = 4;
		tent->s.angles2[0] = 16;
		tent->s.angles2[1] = 48;
		tent->s.angles2[2] = 10;

		self->props_frame_state = plane_choke;
		self->health--;
	}

	if ( self->health > 0 ) {
		self->nextthink = level.time + 50;

		if ( self->props_frame_state == plane_choke ) {
			self->melee->s.loopSound = self->melee->noise_index = fpchoke_snd;
		} else if ( self->props_frame_state == plane_startup )     {
			self->melee->s.loopSound = self->melee->noise_index = fpstartup_snd;
		} else if ( self->props_frame_state == plane_idle )     {
			self->melee->s.loopSound = self->melee->noise_index = fpidle_snd;
		} else if ( self->props_frame_state == plane_flyby1 )     {
			self->melee->s.loopSound = self->melee->noise_index = fpflyby1_snd;
		} else if ( self->props_frame_state == plane_flyby2 )     {
			self->melee->s.loopSound = self->melee->noise_index = fpflyby2_snd;
		}
	} else
	{
		propExplosionLarge( self );
		self->melee->s.loopSound = self->melee->noise_index = 0;

		ExplodePlaneSndFx( self );
		G_FreeEntity( self->melee );
		G_FreeEntity( self );


	}

}
示例#22
0
/*
================
Team_FragBonuses

Calculate the bonuses for flag defense, flag carrier defense, etc.
Note that bonuses are not cumulative.  You get one, they are in importance
order.
================
*/
void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker)
{
	int i;
	gentity_t *ent;
	int flag_pw, enemy_flag_pw;
	int otherteam;
	int tokens;
	gentity_t *flag, *carrier = NULL;
	char *c;
	vec3_t v1, v2;
	int team;

	// no bonus for fragging yourself or team mates
	if (!targ->client || !attacker->client || targ == attacker || OnSameTeam(targ, attacker))
		return;

	team = targ->client->sess.sessionTeam;
	otherteam = OtherTeam(targ->client->sess.sessionTeam);
	if (otherteam < 0)
		return; // whoever died isn't on a team

	// same team, if the flag at base, check to he has the enemy flag
	if (team == TEAM_RED) {
		flag_pw = PW_REDFLAG;
		enemy_flag_pw = PW_BLUEFLAG;
	} else {
		flag_pw = PW_BLUEFLAG;
		enemy_flag_pw = PW_REDFLAG;
	}

	// did the attacker frag the flag carrier?
	tokens = 0;
	if (targ->client->ps.powerups[enemy_flag_pw]) {
		attacker->client->pers.teamState.lastfraggedcarrier = level.time;
		AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS);
		attacker->client->pers.teamState.fragcarrier++;
		//PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's flag carrier!\n",
		//	attacker->client->pers.netname, TeamName(team));
		PrintCTFMessage(attacker->s.number, team, CTFMESSAGE_FRAGGED_FLAG_CARRIER);

		// the target had the flag, clear the hurt carrier
		// field on the other team
		for (i = 0; i < sv_maxclients.integer; i++) {
			ent = g_entities + i;
			if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
				ent->client->pers.teamState.lasthurtcarrier = 0;
		}
		return;
	}

	// did the attacker frag a head carrier? other->client->ps.generic1
	if (tokens) {
		attacker->client->pers.teamState.lastfraggedcarrier = level.time;
		AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS * tokens * tokens);
		attacker->client->pers.teamState.fragcarrier++;
		//PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's skull carrier!\n",
		//	attacker->client->pers.netname, TeamName(team));

		// the target had the flag, clear the hurt carrier
		// field on the other team
		for (i = 0; i < sv_maxclients.integer; i++) {
			ent = g_entities + i;
			if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
				ent->client->pers.teamState.lasthurtcarrier = 0;
		}
		return;
	}

	if (targ->client->pers.teamState.lasthurtcarrier &&
		level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
		!attacker->client->ps.powerups[flag_pw]) {
		// attacker is on the same team as the flag carrier and
		// fragged a guy who hurt our flag carrier
		AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);

		attacker->client->pers.teamState.carrierdefense++;
		targ->client->pers.teamState.lasthurtcarrier = 0;

		attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
		team = attacker->client->sess.sessionTeam;
		attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

		return;
	}

	if (targ->client->pers.teamState.lasthurtcarrier &&
		level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT) {
		// attacker is on the same team as the skull carrier and
		AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);

		attacker->client->pers.teamState.carrierdefense++;
		targ->client->pers.teamState.lasthurtcarrier = 0;

		attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
		team = attacker->client->sess.sessionTeam;
		attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

		return;
	}

	// flag and flag carrier area defense bonuses

	// we have to find the flag and carrier entities

	// find the flag
	switch (attacker->client->sess.sessionTeam) {
	case TEAM_RED:
		c = "team_CTF_redflag";
		break;
	case TEAM_BLUE:
		c = "team_CTF_blueflag";
		break;		
	default:
		return;
	}
	// find attacker's team's flag carrier
	for (i = 0; i < sv_maxclients.integer; i++) {
		carrier = g_entities + i;
		if (carrier->inuse && carrier->client->ps.powerups[flag_pw])
			break;
		carrier = NULL;
	}
	flag = NULL;
	while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) {
		if (!(flag->flags & FL_DROPPED_ITEM))
			break;
	}

	if (!flag)
		return; // can't find attacker's flag

	// ok we have the attackers flag and a pointer to the carrier

	// check to see if we are defending the base's flag
	VectorSubtract(targ->r.currentOrigin, flag->r.currentOrigin, v1);
	VectorSubtract(attacker->r.currentOrigin, flag->r.currentOrigin, v2);

	if ( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS &&
		trap_InPVS(flag->r.currentOrigin, targ->r.currentOrigin ) ) ||
		( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS &&
		trap_InPVS(flag->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
		attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {

		// we defended the base flag
		AddScore(attacker, targ->r.currentOrigin, CTF_FLAG_DEFENSE_BONUS);
		attacker->client->pers.teamState.basedefense++;

		attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
		attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

		return;
	}

	if (carrier && carrier != attacker) {
		VectorSubtract(targ->r.currentOrigin, carrier->r.currentOrigin, v1);
		VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v1);

		if ( ( ( VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS &&
			trap_InPVS(carrier->r.currentOrigin, targ->r.currentOrigin ) ) ||
			( VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS &&
				trap_InPVS(carrier->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
			attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
			AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_PROTECT_BONUS);
			attacker->client->pers.teamState.carrierdefense++;

			attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
			attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

			return;
		}
	}
}
示例#23
0
/*
==============
AICast_VisibleFromPos
==============
*/
qboolean AICast_VisibleFromPos( vec3_t srcpos, int srcnum,
								vec3_t destpos, int destnum, qboolean updateVisPos ) {
	int i, contents_mask, passent, hitent;
	trace_t trace;
	vec3_t start, end, middle, eye;
	cast_state_t        *cs = NULL;
	int srcviewheight;
	vec3_t destmins, destmaxs;
	vec3_t right, vec;
	qboolean inPVS;

	if ( g_entities[destnum].flags & FL_NOTARGET ) {
		return qfalse;
	}

	if ( srcnum < aicast_maxclients ) {
		cs = AICast_GetCastState( srcnum );
	}
	//
	if ( cs && cs->bs ) {
		srcviewheight = cs->bs->cur_ps.viewheight;
	} else if ( g_entities[srcnum].client ) {
		srcviewheight = g_entities[srcnum].client->ps.viewheight;
	} else {
		srcviewheight = 0;
	}
	//
	VectorCopy( g_entities[destnum].r.mins, destmins );
	VectorCopy( g_entities[destnum].r.maxs, destmaxs );
	//
	//calculate middle of bounding box
	VectorAdd( destmins, destmaxs, middle );
	VectorScale( middle, 0.5, middle );
	VectorAdd( destpos, middle, middle );
	// calculate eye position
	VectorCopy( srcpos, eye );
	eye[2] += srcviewheight;
	//
	// set the right vector
	VectorSubtract( middle, eye, vec );
	VectorNormalize( vec );
	right[0] = vec[1];
	right[1] = vec[0];
	right[2] = 0;
	//
	inPVS = qfalse;
	//
	for ( i = 0; i < 5; i++ )
	{
		if ( cs && updateVisPos ) {   // if it's a grenade or something, PVS checks don't work very well
			//if the point is not in potential visible sight
			if ( i < 3 ) {    // don't do PVS check for left/right checks
				if ( !trap_InPVS( eye, middle ) ) {
					continue;
				} else {
					inPVS = qtrue;
				}
			} else if ( !inPVS ) {
				break;      // wasn't in potential view in either of the previous tests
			}               // so don't bother doing left/right
		}
		//
		contents_mask = MASK_AISIGHT; //(MASK_SHOT | CONTENTS_AI_NOSIGHT) & ~(CONTENTS_BODY);	// we can see anything that a bullet can pass through
		passent = srcnum;
		hitent = destnum;
		VectorCopy( eye, start );
		VectorCopy( middle, end );
		//if the entity is in water, lava or slime
		if ( trap_PointContents( middle, destnum ) & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) {
			contents_mask |= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
		} //end if
		  //if eye is in water, lava or slime
		if ( trap_PointContents( eye, srcnum ) & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) {
			if ( !( contents_mask & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) {
				passent = destnum;
				hitent = srcnum;
				VectorCopy( middle, start );
				VectorCopy( eye, end );
			} //end if
			contents_mask ^= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
		} //end if
		  //trace from start to end
		trap_Trace( &trace, start, NULL, NULL, end, ENTITYNUM_NONE /*passent*/, contents_mask );
		//if water was hit
		if ( trace.contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) {
			//if the water surface is translucent
//			if (trace.surface.flags & (SURF_TRANS33|SURF_TRANS66))
			{
				//trace through the water
				contents_mask &= ~( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
				trap_Trace( &trace, trace.endpos, NULL, NULL, end, passent, contents_mask );
			} //end if
		} //end if
		  //if a full trace or the hitent was hit
		if ( trace.fraction >= 1 || trace.entityNum == hitent ) {
			return qtrue;
		}
		//check bottom and top of bounding box as well
		if ( i == 0 ) {
			middle[2] -= ( destmaxs[2] - destmins[2] ) * 0.5;
		} else if ( i == 1 ) {
			middle[2] += destmaxs[2] - destmins[2];
		} else if ( i == 2 )                                                          { // right side
			middle[2] -= ( destmaxs[2] - destmins[2] ) / 2.0;
			VectorMA( eye, destmaxs[0] - 0.5, right, eye );
		} else if ( i == 3 ) {    // left side
			VectorMA( eye, -2.0 * ( destmaxs[0] - 0.5 ), right, eye );
		}
	} //end for

	return qfalse;
}
示例#24
0
//perform pvs check based on rmg or not
qboolean BotPVSCheck( const vec3_t p1, const vec3_t p2 )
{
	return trap_InPVS(p1, p2);
}
示例#25
0
void NPC_BSGM_Attack( void )
{
	//Don't do anything if we're hurt
	if ( NPC->painDebounceTime > level.time )
	{
		NPC_UpdateAngles( qtrue, qtrue );
		return;
	}

	//If we don't have an enemy, just idle
	if ( NPC_CheckEnemyExt(qfalse) == qfalse || !NPC->enemy )
	{
		NPC->enemy = NULL;
		NPC_BSGM_Patrol();
		return;
	}

	enemyLOS4 = enemyCS4 = qfalse;
	move4 = qtrue;
	faceEnemy4 = qfalse;
	shoot4 = qfalse;
	hitAlly4 = qfalse;
	VectorClear( impactPos4 );
	enemyDist4 = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );

	//if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 ||
	//	NPC->client->ps.torsoAnim == BOTH_ATTACK5 )
	if (0)
	{
		shoot4 = qfalse;
		if ( TIMER_Done( NPC, "smackTime" ) && !NPCInfo->blockedDebounceTime )
		{//time to smack
			//recheck enemyDist4 and InFront
			if ( enemyDist4 < MELEE_DIST_SQUARED && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f ) )
			{
				vec3_t	smackDir;
				VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, smackDir );
				smackDir[2] += 30;
				VectorNormalize( smackDir );
				//hurt them
				G_Sound( NPC->enemy, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/skewerhit.wav" ) );
				G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->r.currentOrigin, (g_spskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); 
				if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 )
				{//smackdown
					int knockAnim = BOTH_KNOCKDOWN1;
					if ( BG_CrouchAnim( NPC->enemy->client->ps.legsAnim ) )
					{//knockdown from crouch
						knockAnim = BOTH_KNOCKDOWN4;
					}
					//throw them
					smackDir[2] = 1;
					VectorNormalize( smackDir );
					G_Throw( NPC->enemy, smackDir, 50 );
					NPC_SetAnim( NPC->enemy, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				}
				else
				{//uppercut
					//throw them
					G_Throw( NPC->enemy, smackDir, 100 );
					//make them backflip
					NPC_SetAnim( NPC->enemy, SETANIM_BOTH, BOTH_KNOCKDOWN5, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				}
				//done with the damage
				NPCInfo->blockedDebounceTime = 1;
			}
		}
	}
	else if ( NPC->lockCount ) //already shooting laser
	{//sometimes use the laser beam attack, but only after he's taken down our generator
		shoot4 = qfalse;
		if ( NPC->lockCount == 1 )
		{//charging up
			if ( TIMER_Done( NPC, "beamDelay" ) )
			{//time to start the beam
				int laserAnim;
				//if ( Q_irand( 0, 1 ) )
				if (1)
				{
					laserAnim = BOTH_ATTACK2;
				}
				/*
				else
				{
					laserAnim = BOTH_ATTACK7;
				}
				*/
				NPC_SetAnim( NPC, SETANIM_BOTH, laserAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) );
				//turn on beam effect
				NPC->lockCount = 2;
				G_PlayEffectID( G_EffectIndex("galak/trace_beam"), NPC->r.currentOrigin, vec3_origin );
				NPC->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
				if ( !NPCInfo->coverTarg )
				{//for moving looping sound at end of trace
					NPCInfo->coverTarg = G_Spawn();
					if ( NPCInfo->coverTarg )
					{
						G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint );
						NPCInfo->coverTarg->r.svFlags |= SVF_BROADCAST;
						NPCInfo->coverTarg->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
					}
				}
			}
		}
		else
		{//in the actual attack now
			if ( NPC->client->ps.torsoTimer <= 0 )
			{//attack done!
				NPC->lockCount = 0;
				G_FreeEntity( NPCInfo->coverTarg );
				NPC->s.loopSound = 0;
				TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer );
			}
			else
			{//attack still going
				//do the trace and damage
				trace_t	trace;
				vec3_t	end, mins={-3,-3,-3}, maxs={3,3,3};
				VectorMA( NPC->client->renderInfo.muzzlePoint, 1024, NPC->client->renderInfo.muzzleDir, end );
				trap_Trace( &trace, NPC->client->renderInfo.muzzlePoint, mins, maxs, end, NPC->s.number, MASK_SHOT );
				if ( trace.allsolid || trace.startsolid )
				{//oops, in a wall
					if ( NPCInfo->coverTarg )
					{
						G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint );
					}
				}
				else
				{//clear
					if ( trace.fraction < 1.0f )
					{//hit something
						gentity_t *traceEnt = &g_entities[trace.entityNum];
						if ( traceEnt && traceEnt->takedamage )
						{//damage it
							G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) );
							G_Damage( traceEnt, NPC, NPC, NPC->client->renderInfo.muzzleDir, trace.endpos, 10, 0, MOD_UNKNOWN );
						}
					}
					if ( NPCInfo->coverTarg )
					{
						G_SetOrigin( NPCInfo->coverTarg, trace.endpos );
					}
					if ( !Q_irand( 0, 5 ) )
					{
						G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) );
					}
				}
			}
		}
	}
	else 
	{//Okay, we're not in a special attack, see if we should switch weapons or start a special attack
		/*
		if ( NPC->s.weapon == WP_REPEATER 
			&& !(NPCInfo->scriptFlags & SCF_ALT_FIRE)//using rapid-fire
			&& NPC->enemy->s.weapon == WP_SABER //enemy using saber
			&& NPC->client && (NPC->client->ps.saberEventFlags&SEF_DEFLECTED)
			&& !Q_irand( 0, 50 ) )
		{//he's deflecting my shots, switch to the laser or the lob fire for a while
			TIMER_Set( NPC, "noRapid", Q_irand( 2000, 6000 ) );
			NPCInfo->scriptFlags |= SCF_ALT_FIRE;
			NPC->alt_fire = qtrue;
			if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH && (Q_irand( 0, 1 )||enemyDist4 < MAX_LOB_DIST_SQUARED) )
			{//shield down, use laser
				NPC_GM_StartLaser();
			}
		}
		else*/
		if (// !NPC->client->ps.powerups[PW_GALAK_SHIELD] 
			1 //rwwFIXMEFIXME: just act like the shield is down til the effects and stuff are done
			&& enemyDist4 < MELEE_DIST_SQUARED 
			&& InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f ) 
			&& NPC->enemy->localAnimIndex <= 1 )//within 80 and in front
		{//our shield is down, and enemy within 80, if very close, use melee attack to slap away
			if ( TIMER_Done( NPC, "attackDelay" ) )
			{
				//animate me
				int swingAnim = BOTH_ATTACK1;
				//FIXME: swing sound
				NPC_SetAnim( NPC, SETANIM_BOTH, swingAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) );
				//delay the hurt until the proper point in the anim
				TIMER_Set( NPC, "smackTime", 600 );
				NPCInfo->blockedDebounceTime = 0;
				//FIXME: say something?
			}
		}
		else if ( !NPC->lockCount && NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH
			&& TIMER_Done( NPC, "attackDelay" )
			&& InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f )
			&& ((!Q_irand( 0, 10*(2-g_spskill.integer))&& enemyDist4 > MIN_LOB_DIST_SQUARED&& enemyDist4 < MAX_LOB_DIST_SQUARED)
				||(!TIMER_Done( NPC, "noLob" )&&!TIMER_Done( NPC, "noRapid" ))) 
			&& NPC->enemy->s.weapon != WP_TURRET )
		{//sometimes use the laser beam attack, but only after he's taken down our generator
			shoot4 = qfalse;
			NPC_GM_StartLaser();
		}
		else if ( enemyDist4 < MIN_LOB_DIST_SQUARED 
			&& (NPC->enemy->s.weapon != WP_TURRET || Q_stricmp( "PAS", NPC->enemy->classname ))
			&& TIMER_Done( NPC, "noRapid" ) )//256
		{//enemy within 256
			if ( (NPC->client->ps.weapon == WP_REPEATER) && (NPCInfo->scriptFlags & SCF_ALT_FIRE) )
			{//shooting an explosive, but enemy too close, switch to primary fire
				NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
				NPC->alt_fire = qfalse;
				//FIXME: use weap raise & lower anims
				NPC_ChangeWeapon( WP_REPEATER );
			}
		}
		else if ( (enemyDist4 > MAX_LOB_DIST_SQUARED || (NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname )))
			&& TIMER_Done( NPC, "noLob" ) )//448
		{//enemy more than 448 away and we are ready to try lob fire again
			if ( (NPC->client->ps.weapon == WP_REPEATER) && !(NPCInfo->scriptFlags & SCF_ALT_FIRE) )
			{//enemy far enough away to use lobby explosives
				NPCInfo->scriptFlags |= SCF_ALT_FIRE;
				NPC->alt_fire = qtrue;
				//FIXME: use weap raise & lower anims
				NPC_ChangeWeapon( WP_REPEATER );
			}
		}
	}

	//can we see our target?
	if ( NPC_ClearLOS4( NPC->enemy ) )
	{
		NPCInfo->enemyLastSeenTime = level.time;//used here for aim debouncing, not always a clear LOS
		enemyLOS4 = qtrue;

		if ( NPC->client->ps.weapon == WP_NONE )
		{
			enemyCS4 = qfalse;//not true, but should stop us from firing
			NPC_AimAdjust( -1 );//adjust aim worse longer we have no weapon
		}
		else
		{//can we shoot our target?
			if ( ((NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist4 < MIN_LOB_DIST_SQUARED )//256
			{
				enemyCS4 = qfalse;//not true, but should stop us from firing
				hitAlly4 = qtrue;//us!
				//FIXME: if too close, run away!
			}
			else
			{
				int hit = NPC_ShotEntity( NPC->enemy, impactPos4 );
				gentity_t *hitEnt = &g_entities[hit];
				if ( hit == NPC->enemy->s.number 
					|| ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
					|| ( hitEnt && hitEnt->takedamage ) )
				{//can hit enemy or will hit glass or other breakable, so shoot anyway
					enemyCS4 = qtrue;
					NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy
					VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
				}
				else
				{//Hmm, have to get around this bastard
					NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy
					if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->playerTeam )
					{//would hit an ally, don't fire!!!
						hitAlly4 = qtrue;
					}
					else
					{//Check and see where our shot *would* hit... if it's not close to the enemy (within 256?), then don't fire
					}
				}
			}
		}
	}
	else if ( trap_InPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) )
	{
		int hit;
		gentity_t *hitEnt;

		if ( TIMER_Done( NPC, "talkDebounce" ) && !Q_irand( 0, 10 ) )
		{
			if ( NPCInfo->enemyCheckDebounceTime < 8 )
			{
				int speech = -1;
				switch( NPCInfo->enemyCheckDebounceTime )
				{
				case 0:
				case 1:
				case 2:
					speech = EV_CHASE1 + NPCInfo->enemyCheckDebounceTime;
					break;
				case 3:
				case 4:
				case 5:
					speech = EV_COVER1 + NPCInfo->enemyCheckDebounceTime-3;
					break;
				case 6:
				case 7:
					speech = EV_ESCAPING1 + NPCInfo->enemyCheckDebounceTime-6;
					break;
				}
				NPCInfo->enemyCheckDebounceTime++;
				if ( speech != -1 )
				{
					G_AddVoiceEvent( NPC, speech, Q_irand( 3000, 5000 ) );
					TIMER_Set( NPC, "talkDebounce", Q_irand( 5000, 7000 ) );
				}
			}
		}

		NPCInfo->enemyLastSeenTime = level.time;

		hit = NPC_ShotEntity( NPC->enemy, impactPos4 );
		hitEnt = &g_entities[hit];
		if ( hit == NPC->enemy->s.number 
			|| ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
			|| ( hitEnt && hitEnt->takedamage ) )
		{//can hit enemy or will hit glass or other breakable, so shoot anyway
			enemyCS4 = qtrue;
		}
		else
		{
			faceEnemy4 = qtrue;
			NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy
		}
	}

	if ( enemyLOS4 )
	{
		faceEnemy4 = qtrue;
	}
	else
	{
		if ( !NPCInfo->goalEntity )
		{
			NPCInfo->goalEntity = NPC->enemy;
		}
		if ( NPCInfo->goalEntity == NPC->enemy )
		{//for now, always chase the enemy
			move4 = qtrue;
		}
	}
	if ( enemyCS4 )
	{
		shoot4 = qtrue;
		//NPCInfo->enemyCheckDebounceTime = level.time;//actually used here as a last actual LOS
	}
	else
	{
		if ( !NPCInfo->goalEntity )
		{
			NPCInfo->goalEntity = NPC->enemy;
		}
		if ( NPCInfo->goalEntity == NPC->enemy )
		{//for now, always chase the enemy
			move4 = qtrue;
		}
	}

	//Check for movement to take care of
	GM_CheckMoveState();

	//See if we should override shooting decision with any special considerations
	GM_CheckFireState();

	if ( NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE) && shoot4 && TIMER_Done( NPC, "attackDelay" ) )
	{
		vec3_t	muzzle;
		vec3_t	angles;
		vec3_t	target;
		vec3_t velocity = {0,0,0};
		vec3_t mins = {-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE}, maxs = {REPEATER_ALT_SIZE,REPEATER_ALT_SIZE,REPEATER_ALT_SIZE};
		qboolean clearshot;

		CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
		
		VectorCopy( NPC->enemy->r.currentOrigin, target );

		target[0] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2);
		target[1] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2);
		target[2] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2);

		//Find the desired angles
		clearshot = WP_LobFire( NPC, muzzle, target, mins, maxs, MASK_SHOT|CONTENTS_LIGHTSABER, 
			velocity, qtrue, NPC->s.number, NPC->enemy->s.number,
			300, 1100, 1500, qtrue );
		if ( VectorCompare( vec3_origin, velocity ) || (!clearshot&&enemyLOS4&&enemyCS4)  )
		{//no clear lob shot and no lob shot that will hit something breakable
			if ( enemyLOS4 && enemyCS4 && TIMER_Done( NPC, "noRapid" ) )
			{//have a clear straight shot, so switch to primary
				NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
				NPC->alt_fire = qfalse;
				NPC_ChangeWeapon( WP_REPEATER );
				//keep this weap for a bit
				TIMER_Set( NPC, "noLob", Q_irand( 500, 1000 ) );
			}
			else
			{
				shoot4 = qfalse;
			}
		}
		else
		{
			vectoangles( velocity, angles );

			NPCInfo->desiredYaw		= AngleNormalize360( angles[YAW] );
			NPCInfo->desiredPitch	= AngleNormalize360( angles[PITCH] );

			VectorCopy( velocity, NPC->client->hiddenDir );
			NPC->client->hiddenDist = VectorNormalize ( NPC->client->hiddenDir );
		}
	}
	else if ( faceEnemy4 )
	{//face the enemy
		NPC_FaceEnemy( qtrue );
	}

	if ( !TIMER_Done( NPC, "standTime" ) )
	{
		move4 = qfalse;
	}
	if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )
	{//not supposed to chase my enemies
		if ( NPCInfo->goalEntity == NPC->enemy )
		{//goal is my entity, so don't move
			move4 = qfalse;
		}
	}

	if ( move4 && !NPC->lockCount )
	{//move toward goal
		if ( NPCInfo->goalEntity 
			/*&& NPC->client->ps.legsAnim != BOTH_ALERT1
			&& NPC->client->ps.legsAnim != BOTH_ATTACK2 
			&& NPC->client->ps.legsAnim != BOTH_ATTACK4
			&& NPC->client->ps.legsAnim != BOTH_ATTACK5 
			&& NPC->client->ps.legsAnim != BOTH_ATTACK7*/ )
		{
			move4 = GM_Move();
		}
		else
		{
			move4 = qfalse;
		}
	}

	if ( !TIMER_Done( NPC, "flee" ) )
	{//running away
		faceEnemy4 = qfalse;
	}

	//FIXME: check scf_face_move_dir here?

	if ( !faceEnemy4 )
	{//we want to face in the dir we're running
		if ( !move4 )
		{//if we haven't moved, we should look in the direction we last looked?
			VectorCopy( NPC->client->ps.viewangles, NPCInfo->lastPathAngles );
		}
		if ( move4 )
		{//don't run away and shoot
			NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
			NPCInfo->desiredPitch = 0;
			shoot4 = qfalse;
		}
	}
	NPC_UpdateAngles( qtrue, qtrue );

	if ( NPCInfo->scriptFlags & SCF_DONT_FIRE )
	{
		shoot4 = qfalse;
	}

	if ( NPC->enemy && NPC->enemy->enemy )
	{
		if ( NPC->enemy->s.weapon == WP_SABER && NPC->enemy->enemy->s.weapon == WP_SABER )
		{//don't shoot at an enemy jedi who is fighting another jedi, for fear of injuring one or causing rogue blaster deflections (a la Obi Wan/Vader duel at end of ANH)
			shoot4 = qfalse;
		}
	}
	//FIXME: don't shoot right away!
	if ( shoot4 )
	{//try to shoot if it's time
		if ( TIMER_Done( NPC, "attackDelay" ) )
		{
			if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
			{
				WeaponThink( qtrue );
			}
		}
	}

	//also:
	if ( NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname ) )
	{//crush turrets
		if ( G_BoundsOverlap( NPC->r.absmin, NPC->r.absmax, NPC->enemy->r.absmin, NPC->enemy->r.absmax ) )
		{//have to do this test because placed turrets are not solid to NPCs (so they don't obstruct navigation)
			//if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 )
			if (0)
			{
				NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME;
				G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->r.currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); 
			}
			else
			{
				G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->r.currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); 
			}
		}
	}
	else if ( NPCInfo->touchedByPlayer != NULL && NPCInfo->touchedByPlayer == NPC->enemy )
	{//touched enemy
		//if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 )
		if (0)
		{//zap him!
			vec3_t	smackDir;

			//animate me
			TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer );
			TIMER_Set( NPC, "standTime", NPC->client->ps.legsTimer );
			//FIXME: debounce this?
			NPCInfo->touchedByPlayer = NULL;
			//FIXME: some shield effect?
			NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME;

			VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, smackDir );
			smackDir[2] += 30;
			VectorNormalize( smackDir );
			G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->r.currentOrigin, (g_spskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); 
			//throw them
			G_Throw( NPC->enemy, smackDir, 100 );
			//NPC->enemy->s.powerups |= ( 1 << PW_SHOCKED );
			if ( NPC->enemy->client )
			{
			//	NPC->enemy->client->ps.powerups[PW_SHOCKED] = level.time + 1000;
				NPC->enemy->client->ps.electrifyTime = level.time + 1000;
			}
			//stop any attacks
			ucmd.buttons = 0;
		}
	}

	if ( NPCInfo->movementSpeech < 3 && NPCInfo->blockedSpeechDebounceTime <= level.time )
	{
		if ( NPC->enemy && NPC->enemy->health > 0 && NPC->enemy->painDebounceTime > level.time )
		{
			if ( NPC->enemy->health < 50 && NPCInfo->movementSpeech == 2 )
			{
				G_AddVoiceEvent( NPC, EV_ANGER2, Q_irand( 2000, 4000 ) );
				NPCInfo->movementSpeech = 3;
			}
			else if ( NPC->enemy->health < 75 && NPCInfo->movementSpeech == 1 )
			{
				G_AddVoiceEvent( NPC, EV_ANGER1, Q_irand( 2000, 4000 ) );
				NPCInfo->movementSpeech = 2;
			}
			else if ( NPC->enemy->health < 100 && NPCInfo->movementSpeech == 0 )
			{
				G_AddVoiceEvent( NPC, EV_ANGER3, Q_irand( 2000, 4000 ) );
				NPCInfo->movementSpeech = 1;
			}
		}
	}
}
示例#26
0
//-----------------------------------------------------
static qboolean VEH_TurretFindEnemies( Vehicle_t *pVeh, 
						 gentity_t *parent, 
						 turretStats_t *turretStats, 
						 int turretNum, int curMuzzle )
//-----------------------------------------------------
{
	qboolean	found = qfalse;
	int			i, count;
	float		bestDist = turretStats->fAIRange * turretStats->fAIRange;
	float		enemyDist;
	vec3_t		enemyDir, org, org2;
	qboolean	foundClient = qfalse;
	gentity_t	*entity_list[MAX_GENTITIES], *target, *bestTarget = NULL;

	WP_CalcVehMuzzle( parent, curMuzzle );
	VectorCopy( pVeh->m_vMuzzlePos[curMuzzle], org2 );

	count = G_RadiusList( org2, turretStats->fAIRange, parent, qtrue, entity_list );

	for ( i = 0; i < count; i++ )
	{
		trace_t	tr;
		target = entity_list[i];

		if ( target == parent 
			|| !target->takedamage 
			|| target->health <= 0 
			|| ( target->flags & FL_NOTARGET ))
		{
			continue;
		}
		if ( !target->client )
		{// only attack clients
			if ( !(target->flags&FL_BBRUSH)//not a breakable brush
				|| !target->takedamage//is a bbrush, but invincible
				|| (target->NPC_targetname&&parent->targetname&&Q_stricmp(target->NPC_targetname,parent->targetname)!=0) )//not in invicible bbrush, but can only be broken by an NPC that is not me
			{
				if ( target->s.weapon == WP_TURRET
					&& target->classname
					&& Q_strncmp( "misc_turret", target->classname, 11 ) == 0 )
				{//these guys we want to shoot at
				}
				else
				{
					continue;
				}
			}
			//else: we will shoot at bbrushes!
		}
		else if ( target->client->sess.sessionTeam == TEAM_SPECTATOR )
		{
			continue;
		}
		else if ( target->client->tempSpectate >= level.time )
		{
			continue;
		}
		if ( target == ((gentity_t*)pVeh->m_pPilot)
			|| target->r.ownerNum == parent->s.number )
		{//don't get angry at my pilot or passengers?
			continue;
		}
		if ( parent->client
			&& parent->client->sess.sessionTeam )
		{
			if ( target->client )
			{
				if ( target->client->sess.sessionTeam == parent->client->sess.sessionTeam )
				{ 
					// A bot/client/NPC we don't want to shoot
					continue;
				}
			}
			else if ( target->teamnodmg == parent->client->sess.sessionTeam )
			{//some other entity that's allied with us
				continue;
			}
		}
		if ( !trap_InPVS( org2, target->r.currentOrigin ))
		{
			continue;
		}

		VectorCopy( target->r.currentOrigin, org );

		trap_Trace( &tr, org2, NULL, NULL, org, parent->s.number, MASK_SHOT );

		if ( tr.entityNum == target->s.number
			|| (!tr.allsolid && !tr.startsolid && tr.fraction == 1.0 ) )
		{
			// Only acquire if have a clear shot, Is it in range and closer than our best?
			VectorSubtract( target->r.currentOrigin, org2, enemyDir );
			enemyDist = VectorLengthSquared( enemyDir );

			if ( enemyDist < bestDist || (target->client && !foundClient))// all things equal, keep current
			{
				bestTarget = target;
				bestDist = enemyDist;
				found = qtrue;
				if ( target->client )
				{//prefer clients over non-clients
					foundClient = qtrue;
				}
			}
		}
	}

	if ( found )
	{
		pVeh->turretStatus[turretNum].enemyEntNum = bestTarget->s.number;
	}

	return found;
}
示例#27
0
void VEH_TurretThink( Vehicle_t *pVeh, gentity_t *parent, int turretNum )
//-----------------------------------------------------
{
	qboolean	doAim = qfalse;
	float		enemyDist, rangeSq;
	vec3_t		enemyDir;
	turretStats_t *turretStats = &pVeh->m_pVehicleInfo->turret[turretNum];
	vehWeaponInfo_t	*vehWeapon = NULL;
	gentity_t	*turretEnemy = NULL;
	int			curMuzzle = 0;//?
	

	if ( !turretStats || !turretStats->iAmmoMax )
	{//not a valid turret
		return;
	}
	
	if ( turretStats->passengerNum 
		&& pVeh->m_iNumPassengers >= turretStats->passengerNum )
	{//the passenger that has control of this turret is on the ship
		VEH_TurretObeyPassengerControl( pVeh, parent, turretNum );
		return;
	}
	else if ( !turretStats->bAI )//try AI
	{//this turret does not think on its own.
		return;
	}

	vehWeapon = &g_vehWeaponInfo[turretStats->iWeapon];
	rangeSq = (turretStats->fAIRange*turretStats->fAIRange);
	curMuzzle = pVeh->turretStatus[turretNum].nextMuzzle;

	if ( pVeh->turretStatus[turretNum].enemyEntNum < ENTITYNUM_WORLD )
	{
		turretEnemy = &g_entities[pVeh->turretStatus[turretNum].enemyEntNum];
		if ( turretEnemy->health < 0 
			|| !turretEnemy->inuse
			|| turretEnemy == ((gentity_t*)pVeh->m_pPilot)//enemy became my pilot///?
			|| turretEnemy == parent
			|| turretEnemy->r.ownerNum == parent->s.number // a passenger?
			|| ( turretEnemy->client && turretEnemy->client->sess.sessionTeam == TEAM_SPECTATOR )
			|| ( turretEnemy->client && turretEnemy->client->tempSpectate >= level.time ) )
		{//don't keep going after spectators, pilot, self, dead people, etc.
			turretEnemy = NULL;
			pVeh->turretStatus[turretNum].enemyEntNum = ENTITYNUM_NONE;
		}
	}

	if ( pVeh->turretStatus[turretNum].enemyHoldTime < level.time )
	{
		if ( VEH_TurretFindEnemies( pVeh, parent, turretStats, turretNum, curMuzzle ) )
		{
			turretEnemy = &g_entities[pVeh->turretStatus[turretNum].enemyEntNum];
			doAim = qtrue;
		}
		else if ( parent->enemy && parent->enemy->s.number < ENTITYNUM_WORLD )
		{
			turretEnemy = parent->enemy;
			doAim = qtrue;
		}
		if ( turretEnemy )
		{//found one
			if ( turretEnemy->client )
			{//hold on to clients for a min of 3 seconds
				pVeh->turretStatus[turretNum].enemyHoldTime = level.time + 3000;
			}
			else
			{//hold less
				pVeh->turretStatus[turretNum].enemyHoldTime = level.time + 500;
			}
		}
	}
	if ( turretEnemy != NULL )
	{
		if ( turretEnemy->health > 0 )
		{
			// enemy is alive
			WP_CalcVehMuzzle( parent, curMuzzle );
			VectorSubtract( turretEnemy->r.currentOrigin, pVeh->m_vMuzzlePos[curMuzzle], enemyDir );
			enemyDist = VectorLengthSquared( enemyDir );

			if ( enemyDist < rangeSq )
			{
				// was in valid radius
				if ( trap_InPVS( pVeh->m_vMuzzlePos[curMuzzle], turretEnemy->r.currentOrigin ) )
				{
					// Every now and again, check to see if we can even trace to the enemy
					trace_t tr;
					vec3_t start, end;
					VectorCopy( pVeh->m_vMuzzlePos[curMuzzle], start );

					VectorCopy( turretEnemy->r.currentOrigin, end );
					trap_Trace( &tr, start, NULL, NULL, end, parent->s.number, MASK_SHOT );

					if ( tr.entityNum == turretEnemy->s.number
						|| (!tr.allsolid && !tr.startsolid ) )
					{
						doAim = qtrue;	// Can see our enemy
					}
				}
			}
		}
	}

	if ( doAim )
	{
		vec3_t aimAngles;
		if ( VEH_TurretAim( pVeh, parent, turretEnemy, turretStats, vehWeapon, turretNum, curMuzzle, aimAngles ) )
		{
			VEH_TurretCheckFire( pVeh, parent, /*turretEnemy,*/ turretStats, vehWeapon, turretNum, curMuzzle );
		}
	}
}
示例#28
0
//-----------------------------------------------------
static qboolean turretG2_find_enemies( gentity_t *self )
//-----------------------------------------------------
{
    qboolean	found = qfalse;
    int			i, count;
    float		bestDist = self->radius * self->radius;
    float		enemyDist;
    vec3_t		enemyDir, org, org2;
    qboolean	foundClient = qfalse;
    gentity_t	*entity_list[MAX_GENTITIES], *target, *bestTarget = NULL;

    if ( self->aimDebounceTime > level.time ) // time since we've been shut off
    {
        // We were active and alert, i.e. had an enemy in the last 3 secs
        if ( self->painDebounceTime < level.time )
        {
            if ( !(self->spawnflags&SPF_TURRETG2_TURBO) )
            {
                G_Sound(self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/ping.wav" ));
            }
            self->painDebounceTime = level.time + 1000;
        }
    }

    VectorCopy( self->r.currentOrigin, org2 );
    if ( self->spawnflags & 2 )
    {
        org2[2] += 20;
    }
    else
    {
        org2[2] -= 20;
    }

    count = G_RadiusList( org2, self->radius, self, qtrue, entity_list );

    for ( i = 0; i < count; i++ )
    {
        trace_t	tr;
        target = entity_list[i];

        if ( !target->client )
        {
            // only attack clients
            if ( !(target->flags&FL_BBRUSH)//not a breakable brush
                    || !target->takedamage//is a bbrush, but invincible
                    || (target->NPC_targetname&&self->targetname&&Q_stricmp(target->NPC_targetname,self->targetname)!=0) )//not in invicible bbrush, but can only be broken by an NPC that is not me
            {
                continue;
            }
            //else: we will shoot at bbrushes!
        }
        if ( target == self || !target->takedamage || target->health <= 0 || ( target->flags & FL_NOTARGET ))
        {
            continue;
        }
        if ( target->client && target->client->sess.sessionTeam == TEAM_SPECTATOR )
        {
            continue;
        }
        if ( self->alliedTeam )
        {
            if ( target->client )
            {
                if ( target->client->sess.sessionTeam == self->alliedTeam )
                {
                    // A bot/client/NPC we don't want to shoot
                    continue;
                }
            }
            else if ( target->teamnodmg == self->alliedTeam )
            {
                // An ent we don't want to shoot
                continue;
            }
        }
        if ( !trap_InPVS( org2, target->r.currentOrigin ))
        {
            continue;
        }

        if ( target->client )
        {
            VectorCopy( target->client->renderInfo.eyePoint, org );
        }
        else
        {
            VectorCopy( target->r.currentOrigin, org );
        }

        if ( self->spawnflags & 2 )
        {
            org[2] -= 15;
        }
        else
        {
            org[2] += 5;
        }

        trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT );

        if ( !tr.allsolid && !tr.startsolid && ( tr.fraction == 1.0 || tr.entityNum == target->s.number ))
        {
            // Only acquire if have a clear shot, Is it in range and closer than our best?
            VectorSubtract( target->r.currentOrigin, self->r.currentOrigin, enemyDir );
            enemyDist = VectorLengthSquared( enemyDir );

            if ( enemyDist < bestDist || (target->client && !foundClient))// all things equal, keep current
            {
                if ( self->attackDebounceTime < level.time )
                {
                    // We haven't fired or acquired an enemy in the last 2 seconds-start-up sound
                    if ( !(self->spawnflags&SPF_TURRETG2_TURBO) )
                    {
                        G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/startup.wav" ));
                    }

                    // Wind up turrets for a bit
                    self->attackDebounceTime = level.time + 1400;
                }

                bestTarget = target;
                bestDist = enemyDist;
                found = qtrue;
                if ( target->client )
                {   //prefer clients over non-clients
                    foundClient = qtrue;
                }
            }
        }
    }

    if ( found )
    {
        /*
        if ( !self->enemy )
        {//just aquired one
        	AddSoundEvent( bestTarget, self->r.currentOrigin, 256, AEL_DISCOVERED );
        	AddSightEvent( bestTarget, self->r.currentOrigin, 512, AEL_DISCOVERED, 20 );
        }
        */
        G_SetEnemy( self, bestTarget );
        if ( VALIDSTRING( self->target2 ))
        {
            G_UseTargets2( self, self, self->target2 );
        }
    }

    return found;
}
示例#29
0
文件: team.c 项目: icanhas/yantar
/*
 * Team_FragBonuses
 *
 * Calculate the bonuses for flag defense, flag carrier defense, etc.
 * Note that bonuses are not cumulative.  You get one, they are in importance
 * order.
 */
void
Team_FragBonuses(Gentity *targ, Gentity *inflictor, Gentity *attacker)
{
	int	i;
	Gentity *ent;
	int	flag_pw, enemy_flag_pw;
	int	otherteam;
	int	tokens;
	Gentity	*flag, *carrier = NULL;
	char *c;
	Vec3		v1, v2;
	int team;

	/* no bonus for fragging yourself or team mates */
	if(!targ->client || !attacker->client || targ == attacker ||
	   OnSameTeam(targ, attacker))
		return;

	team = targ->client->sess.team;
	otherteam = OtherTeam(targ->client->sess.team);
	if(otherteam < 0)
		return;		/* whoever died isn't on a team */

	/* same team, if the flag at base, check to he has the enemy flag */
	if(team == TEAM_RED){
		flag_pw = PW_REDFLAG;
		enemy_flag_pw = PW_BLUEFLAG;
	}else{
		flag_pw = PW_BLUEFLAG;
		enemy_flag_pw = PW_REDFLAG;
	}

	if(g_gametype.integer == GT_1FCTF)
		enemy_flag_pw = PW_NEUTRALFLAG;

	/* did the attacker frag the flag carrier? */
	tokens = 0;
	if(targ->client->ps.powerups[enemy_flag_pw]){
		attacker->client->pers.teamState.lastfraggedcarrier = level.time;
		AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS);
		attacker->client->pers.teamState.fragcarrier++;
		PrintMsg(NULL,
			"%s" S_COLOR_WHITE " fragged %s's flag carrier!\n",
			attacker->client->pers.netname, TeamName(
				team));

		/* the target had the flag, clear the hurt carrier
		 * field on the other team */
		for(i = 0; i < g_maxclients.integer; i++){
			ent = g_entities + i;
			if(ent->inuse && ent->client->sess.team ==
			   otherteam)
				ent->client->pers.teamState.lasthurtcarrier = 0;
		}
		return;
	}

	/* did the attacker frag a head carrier? other->client->ps.generic1 */
	if(tokens){
		attacker->client->pers.teamState.lastfraggedcarrier = level.time;
		AddScore(attacker, targ->r.currentOrigin,
			CTF_FRAG_CARRIER_BONUS * tokens * tokens);
		attacker->client->pers.teamState.fragcarrier++;
		PrintMsg(NULL,
			"%s" S_COLOR_WHITE " fragged %s's skull carrier!\n",
			attacker->client->pers.netname, TeamName(
				team));

		/* the target had the flag, clear the hurt carrier
		 * field on the other team */
		for(i = 0; i < g_maxclients.integer; i++){
			ent = g_entities + i;
			if(ent->inuse && ent->client->sess.team ==
			   otherteam)
				ent->client->pers.teamState.lasthurtcarrier = 0;
		}
		return;
	}

	if(targ->client->pers.teamState.lasthurtcarrier &&
	   level.time - targ->client->pers.teamState.lasthurtcarrier <
	   CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
	   !attacker->client->ps.powerups[flag_pw]){
		/* attacker is on the same team as the flag carrier and
		 * fragged a guy who hurt our flag carrier */
		AddScore(attacker, targ->r.currentOrigin,
			CTF_CARRIER_DANGER_PROTECT_BONUS);

		attacker->client->pers.teamState.carrierdefense++;
		targ->client->pers.teamState.lasthurtcarrier = 0;

		attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
		/* add the sprite over the player's head */
		attacker->client->ps.eFlags &=
			~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT |
			  EF_AWARD_GAUNTLET |
			  EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP);
		attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
		attacker->client->rewardTime = level.time +
					       REWARD_SPRITE_TIME;

		return;
	}

	if(targ->client->pers.teamState.lasthurtcarrier &&
	   level.time - targ->client->pers.teamState.lasthurtcarrier <
	   CTF_CARRIER_DANGER_PROTECT_TIMEOUT){
		/* attacker is on the same team as the skull carrier and */
		AddScore(attacker, targ->r.currentOrigin,
			CTF_CARRIER_DANGER_PROTECT_BONUS);

		attacker->client->pers.teamState.carrierdefense++;
		targ->client->pers.teamState.lasthurtcarrier = 0;

		attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
		/* add the sprite over the player's head */
		attacker->client->ps.eFlags &=
			~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT |
			  EF_AWARD_GAUNTLET |
			  EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP);
		attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
		attacker->client->rewardTime = level.time +
					       REWARD_SPRITE_TIME;

		return;
	}

	/* flag and flag carrier area defense bonuses */

	/* we have to find the flag and carrier entities */
	/* find the flag */
	switch(attacker->client->sess.team){
	case TEAM_RED:
		c = "team_CTF_redflag";
		break;
	case TEAM_BLUE:
		c = "team_CTF_blueflag";
		break;
	default:
		return;
	}
	/* find attacker's team's flag carrier */
	for(i = 0; i < g_maxclients.integer; i++){
		carrier = g_entities + i;
		if(carrier->inuse && carrier->client->ps.powerups[flag_pw])
			break;
		carrier = NULL;
	}
	flag = NULL;
	while((flag = G_Find (flag, FOFS(classname), c)) != NULL)
		if(!(flag->flags & FL_DROPPED_ITEM))
			break;

	if(!flag)
		return;		/* can't find attacker's flag */

	/* ok we have the attackers flag and a pointer to the carrier */

	/* check to see if we are defending the base's flag */
	subv3(targ->r.currentOrigin, flag->r.currentOrigin, v1);
	subv3(attacker->r.currentOrigin, flag->r.currentOrigin, v2);

	if(((lenv3(v1) < CTF_TARGET_PROTECT_RADIUS &&
	     trap_InPVS(flag->r.currentOrigin, targ->r.currentOrigin)) ||
	    (lenv3(v2) < CTF_TARGET_PROTECT_RADIUS &&
	     trap_InPVS(flag->r.currentOrigin, attacker->r.currentOrigin))) &&
	   attacker->client->sess.team !=
	   targ->client->sess.team){

		/* we defended the base flag */
		AddScore(attacker, targ->r.currentOrigin, CTF_FLAG_DEFENSE_BONUS);
		attacker->client->pers.teamState.basedefense++;

		attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
		/* add the sprite over the player's head */
		attacker->client->ps.eFlags &=
			~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT |
			  EF_AWARD_GAUNTLET |
			  EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP);
		attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
		attacker->client->rewardTime = level.time +
					       REWARD_SPRITE_TIME;

		return;
	}

	if(carrier && carrier != attacker){
		subv3(targ->r.currentOrigin, carrier->r.currentOrigin,
			v1);
		subv3(attacker->r.currentOrigin,
			carrier->r.currentOrigin,
			v1);

		if(((lenv3(v1) < CTF_ATTACKER_PROTECT_RADIUS &&
		     trap_InPVS(carrier->r.currentOrigin,
			     targ->r.currentOrigin)) ||
		    (lenv3(v2) < CTF_ATTACKER_PROTECT_RADIUS &&
		     trap_InPVS(carrier->r.currentOrigin,
			     attacker->r.currentOrigin))) &&
		   attacker->client->sess.team !=
		   targ->client->sess.team){
			AddScore(attacker, targ->r.currentOrigin,
				CTF_CARRIER_PROTECT_BONUS);
			attacker->client->pers.teamState.carrierdefense++;

			attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
			/* add the sprite over the player's head */
			attacker->client->ps.eFlags &=
				~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT |
				  EF_AWARD_GAUNTLET | EF_AWARD_ASSIST |
				  EF_AWARD_DEFEND |
				  EF_AWARD_CAP);
			attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
			attacker->client->rewardTime = level.time +
						       REWARD_SPRITE_TIME;

			return;
		}
	}
}
示例#30
0
	/**
	 * @brief Perform an approximate trace to find a taggable entity.
	 * @param team           Team the caller belongs to.
	 * @param refreshTagged  Refresh all already tagged entities's tags and exclude these entities from further consideration.
	 */
	gentity_t *TagTrace( const vec3_t begin, const vec3_t end, int skip, int mask, team_t team, bool refreshTagged )
	{
		tagtrace_ent_t list[ MAX_GENTITIES ];
		int i, count = 0;
		gentity_t *ent, *reticleEnt = nullptr;
		vec3_t seg, delta;
		float dot;

		VectorSubtract( end, begin, seg );

		// Do a trace for bounding boxes under the reticle first, they are prefered
		{
			trace_t tr;
			trap_Trace( &tr, begin, nullptr, nullptr, end, skip, mask, 0 );
			if ( EntityTaggable( tr.entityNum, team, true ) )
			{
				reticleEnt = g_entities + tr.entityNum;
				if ( !refreshTagged || !CheckRefreshTag( reticleEnt, team ) )
					return reticleEnt;
			}
		}

		for( i = 0; i < level.num_entities; i++ )
		{
			ent = g_entities + i;

			if( ent == reticleEnt )
				continue;

			if( !ent->inuse )
				continue;

			if( !EntityTaggable( i, team, true ) )
				continue;

			VectorSubtract( ent->r.currentOrigin, begin, delta );
			dot = DotProduct( seg, delta ) / VectorLength( seg ) / VectorLength( delta );

			if( dot < 0.9 )
				continue;

			if( !trap_InPVS( ent->r.currentOrigin, begin ) )
				continue;

			// LOS
			{
				trace_t tr;
				trap_Trace( &tr, begin, nullptr, nullptr, ent->r.currentOrigin, skip, mask, 0 );
				if( tr.entityNum != i )
					continue;
			}

			if( refreshTagged && CheckRefreshTag( ent, team ) )
				continue;

			list[ count ].ent = ent;
			list[ count++ ].dot = dot;
		}

		if( !count )
			return nullptr;

		qsort( list, count, sizeof( tagtrace_ent_t ), TagTrace_EntCmp );

		return list[ 0 ].ent;
	}