示例#1
0
static bool AI_AttemptWalljump( edict_t *self )
{
	if( self->ai->path.numNodes >= 1 )
	{
		int n1 = self->ai->current_node;
		int n2 = self->ai->next_node;
		vec3_t n1origin, n2origin, origin;

		if( n1 == n2 )
			return false;

		// we use a wider radius in 2D, and a height range enough so they can't be jumped over
		AI_GetNodeOrigin( n1, n1origin );
		AI_GetNodeOrigin( n2, n2origin );
		VectorCopy( self->s.origin, origin );

		if( fabs( n1origin[2] - n2origin[2] ) < 32.0f && origin[2] >= n1origin[2] - 4.0f ) {
			float dist = DistanceFast( n1origin, n2origin );
			float n1d, n2d;

			n1d = DistanceFast( n1origin, origin );
			n2d = DistanceFast( n2origin, origin );

			if( dist >= 150.0f && 
				n1d >= dist*0.5f &&
				n2d < dist ) {
				return true;
			}
		}
	}

	return false;
}
示例#2
0
bool AI_NodeReached_Generic( edict_t *self )
{
	bool reached = false;
	float RADIUS = NODE_REACH_RADIUS;

	if( !( AI_GetNodeFlags( self->ai->next_node ) & (NODEFLAGS_REACHATTOUCH|NODEFLAGS_ENTITYREACH) ) )
	{
		if( self->ai->path.numNodes >= MIN_BUNNY_NODES )
		{
			int n1 = self->ai->path.nodes[self->ai->path.numNodes];
			int n2 = self->ai->path.nodes[self->ai->path.numNodes-1];
			vec3_t n1origin, n2origin, origin;

			// if falling from a jump pad use a taller cylinder
			if( !self->groundentity && !self->is_step && !self->is_swim
				&& ( AI_CurrentLinkType( self ) & LINK_JUMPPAD ) )
				RADIUS = NODE_WIDE_REACH_RADIUS;

			// we use a wider radius in 2D, and a height range enough so they can't be jumped over
			AI_GetNodeOrigin( n1, n1origin );
			AI_GetNodeOrigin( n2, n2origin );
			VectorCopy( self->s.origin, origin );
			n1origin[2] = n2origin[2] = origin[2] = 0;

			// see if reached the second
			if( n2 != NODE_INVALID &&
				( ( nodes[n2].origin[2] - 16 ) < self->s.origin[2] ) &&
				( nodes[n2].origin[2] + RADIUS > self->s.origin[2] ) &&
				( DistanceFast( n2origin, origin ) < RADIUS )
				)
			{
				AI_NodeReached( self ); // advance the first
				reached = true;		// return the second as reached
			}
			// see if reached the first
			else if( ( ( nodes[n1].origin[2] - 16 ) < self->s.origin[2] ) &&
				( nodes[n1].origin[2] + RADIUS > self->s.origin[2] ) &&
				( DistanceFast( n1origin, origin ) < RADIUS ) )
			{
				reached = true; // return the first as reached
			}
		}
		else
		{
			reached = ( DistanceFast( self->s.origin, nodes[self->ai->next_node].origin ) < RADIUS ) ? true : false;
		}
	}

	return reached;
}
示例#3
0
/*
* PlayersRangeFromSpot
* 
* Returns the distance to the nearest player from the given spot
*/
float PlayersRangeFromSpot( edict_t *spot, int ignore_team )
{
	edict_t	*player;
	float bestplayerdistance;
	int n;
	float playerdistance;

	bestplayerdistance = 9999999;

	for( n = 1; n <= gs.maxclients; n++ )
	{
		player = &game.edicts[n];

		if( !player->r.inuse )
			continue;
		if( player->r.solid == SOLID_NOT )
			continue;
		if( ( ignore_team && ignore_team == player->s.team ) || player->s.team == TEAM_SPECTATOR )
			continue;

		playerdistance = DistanceFast( spot->s.origin, player->s.origin );

		if( playerdistance < bestplayerdistance )
			bestplayerdistance = playerdistance;
	}

	return bestplayerdistance;
}
示例#4
0
/*
* CG_ImpactSmokePuff
*/
void CG_ImpactSmokePuff( vec3_t origin, vec3_t dir, float radius, float alpha, int time, int speed )
{
#define SMOKEPUFF_MAXVIEWDIST 700
	lentity_t *le;
	struct shader_s *shader = CG_MediaShader( cgs.media.shaderSmokePuff );

	if( CG_PointContents( origin ) & MASK_WATER )
	{
		return;
	}

	if( DistanceFast( origin, cg.view.origin ) * cg.view.fracDistFOV > SMOKEPUFF_MAXVIEWDIST )
		return;

	if( !VectorLength( dir ) )
	{
		VectorCopy( cg.view.axis[FORWARD], dir );
		VectorInverse( dir );
	}
	VectorNormalize( dir );
	//offset the origin by half of the radius
	VectorMA( origin, radius*0.5f, dir, origin );

	le = CG_AllocSprite( LE_SCALE_ALPHA_FADE, origin, radius + crandom(), time,
		1, 1, 1, alpha, 0, 0, 0, 0, shader );

	le->ent.rotation = rand() % 360;
	VectorScale( dir, speed, le->velocity );
}
示例#5
0
void GS_TraceCurveLaserBeam( trace_t *trace, vec3_t origin, vec3_t angles, vec3_t blendPoint, int ignore, int timeDelta, void ( *impact )( trace_t *tr, vec3_t dir ) )
{
	float frac, subdivisions = CURVELASERBEAM_SUBDIVISIONS;
	float range = (float)GS_GetWeaponDef( WEAP_LASERGUN )->firedef_weak.timeout;
	vec3_t from, dir, end;
	int passthrough = ignore;

	int i, j;
	vec3_t tmpangles, blendAngles;

	assert( trace );

	VectorCopy( origin, from );
	VectorSubtract( blendPoint, origin, dir );
	VecToAngles( dir, blendAngles );

	for( i = 1; i <= (int)subdivisions; i++ )
	{
		frac = ( ( range/subdivisions )*(float)i ) / (float)range;

		for( j = 0; j < 3; j++ )
			tmpangles[j] = LerpAngle( angles[j], blendAngles[j], frac );

		AngleVectors( tmpangles, dir, NULL, NULL );
		VectorMA( origin, range * frac, dir, end );

		GS_TraceLaserBeam( trace, from, tmpangles, DistanceFast( from, end ), passthrough, timeDelta, impact );
		if( trace->fraction != 1.0f )
			break;

		passthrough = trace->ent;
		VectorCopy( end, from );
	}
}
示例#6
0
/*
* AI_MoveToShortRangeGoalEntity
* A.K.A Item pick magnet
*/
qboolean AI_MoveToShortRangeGoalEntity( edict_t *self, usercmd_t *ucmd )
{
	if( !self->movetarget || !self->r.client )
		return qfalse;

	if( self->ai.goalEnt && ( self->ai.goalEnt->ent == self->movetarget )
		&& ( AI_GetNodeFlags( self->ai.goal_node ) & NODEFLAGS_ENTITYREACH ) )
	{
		// wait
		VectorSubtract( self->movetarget->s.origin, self->s.origin, self->ai.move_vector );
		if( VectorLength( self->ai.move_vector ) < 72 )
			ucmd->buttons |= BUTTON_WALK;

		if( BoundsIntersect( self->movetarget->r.absmin, self->movetarget->r.absmax, self->r.absmin, self->r.absmax ) )
		{
			ucmd->forwardmove = 0;
			ucmd->sidemove = 0;
			ucmd->upmove = 0;
			self->ai.node_timeout = 0;
			return qtrue;
		}
	}

	if( self->movetarget->r.solid == SOLID_NOT || DistanceFast( self->movetarget->s.origin, self->s.origin ) > AI_GOAL_SR_RADIUS + 72 )
	{
		self->movetarget = NULL;
		self->ai.shortRangeGoalTimeout = level.time;
		return qfalse;
	}

	// Force movement direction to reach the goal entity
	VectorSubtract( self->movetarget->s.origin, self->s.origin, self->ai.move_vector );

	return qtrue;
}
示例#7
0
bool AI_NodeReached_PlatformEnd( edict_t *self )
{
	bool reached = false;

	if( self->ai->next_node == NODE_INVALID )
		return true;

	if( self->groundentity && self->groundentity->use == Use_Plat )
	{
		reached = ( self->groundentity->moveinfo.state == STATE_TOP 
			|| VectorCompare( self->groundentity->s.origin, self->groundentity->moveinfo.dest ) )
			? true : false;
	}
	else
	{
		vec3_t v1, v2;

		v1[0] = self->s.origin[0];
		v1[1] = self->s.origin[1];
		v1[2] = 0;

		v2[0] = nodes[self->ai->next_node].origin[0];
		v2[1] = nodes[self->ai->next_node].origin[1];
		v2[2] = 0;

		if( DistanceFast( v1, v2 ) < NODE_REACH_RADIUS )
			reached = 
			( fabs( nodes[self->ai->next_node].origin[2] - self->s.origin[2] ) < ( AI_JUMPABLE_HEIGHT * 0.5 ) )
			? true : false;
	}

	return reached;
}
示例#8
0
int AI_FindClosestNode( vec3_t origin, float mindist, int range, unsigned int flagsmask )
{
	int i;
	float closest;
	float dist;
	int node = NODE_INVALID;

	if( mindist > range ) return -1;

	closest = range;

	for( i = 0; i < nav.num_nodes; i++ )
	{
		if( flagsmask == NODE_ALL || nodes[i].flags & flagsmask )
		{
			dist = DistanceFast( nodes[i].origin, origin );
			if( dist > mindist && dist < closest )
			{
				node = i;
				closest = dist;
			}
		}
	}
	return node;
}
示例#9
0
bool AI_NodeReached_Special( edict_t *self )
{
	bool reached = false;

	if( self->ai->next_node != NODE_INVALID && !( AI_GetNodeFlags( self->ai->next_node ) & (NODEFLAGS_REACHATTOUCH|NODEFLAGS_ENTITYREACH) ) )
	{
		if( self->ai->path.numNodes >= MIN_BUNNY_NODES )
		{
			int n1 = self->ai->path.nodes[self->ai->path.numNodes];
			int n2 = self->ai->path.nodes[self->ai->path.numNodes-1];
			vec3_t n1origin, n2origin, origin;

			// we use a wider radius in 2D, and a height range enough so they can't be jumped over
			AI_GetNodeOrigin( n1, n1origin );
			AI_GetNodeOrigin( n2, n2origin );
			VectorCopy( self->s.origin, origin );
			n1origin[2] = n2origin[2] = origin[2] = 0;

			// see if reached the second
			if( ( ( nodes[n2].origin[2] - 16 ) < self->s.origin[2] ) &&
				( nodes[n2].origin[2] + NODE_WIDE_REACH_RADIUS > self->s.origin[2] ) &&
				( DistanceFast( n2origin, origin ) < NODE_WIDE_REACH_RADIUS ) && 
				AI_ReachabilityVisible( self, nodes[n2].origin ) )
			{
				AI_NodeReached( self ); // advance the first
				reached = true;		// return the second as reached
			}
			// see if reached the first
			else if( ( ( nodes[n1].origin[2] - 16 ) < self->s.origin[2] ) &&
				( nodes[n1].origin[2] + NODE_WIDE_REACH_RADIUS > self->s.origin[2] ) &&
				( DistanceFast( n1origin, origin ) < NODE_WIDE_REACH_RADIUS ) && 
				AI_ReachabilityVisible( self, nodes[n1].origin ) )
			{
				reached = true; // return the first as reached
			}
		}
		else
			return AI_NodeReached_Generic( self );
	}

	return reached;
}
示例#10
0
/*
* AI_AddNode_Platform_FindLowerLinkableCandidate
* helper to AI_AddNode_Platform
*/
static int AI_AddNode_Platform_FindLowerLinkableCandidate( edict_t *ent )
{
	trace_t	trace;
	float plat_dist;
	float platlip;
	int numtries = 0, maxtries = 10;
	int candidate;
	vec3_t candidate_origin, virtualorigin;
	float mindist = 0;

	if( ent->flags & FL_TEAMSLAVE )
		return NODE_INVALID;

	plat_dist = ent->moveinfo.start_origin[2] - ent->moveinfo.end_origin[2];
	platlip = ( ent->r.maxs[2] - ent->r.mins[2] ) - plat_dist;

	//find a good candidate for lower
	candidate_origin[0] = ( ent->r.maxs[0] - ent->r.mins[0] ) / 2 + ent->r.mins[0];
	candidate_origin[1] = ( ent->r.maxs[1] - ent->r.mins[1] ) / 2 + ent->r.mins[1];
	candidate_origin[2] = ent->r.mins[2] + platlip;

	//try to find the closer reachable node to the bottom of the plat
	do
	{
		candidate = AI_FindClosestNode( candidate_origin, mindist, NODE_DENSITY * 2, NODE_ALL );
		if( candidate != NODE_INVALID )
		{
			mindist = DistanceFast( candidate_origin, nodes[candidate].origin );

			//check to see if it would be valid
			if( fabs( candidate_origin[2] - nodes[candidate].origin[2] )
			   < ( fabs( platlip ) + AI_JUMPABLE_HEIGHT ) )
			{
				//put at linkable candidate height
				virtualorigin[0] = candidate_origin[0];
				virtualorigin[1] = candidate_origin[1];
				virtualorigin[2] = nodes[candidate].origin[2];

				G_Trace( &trace, virtualorigin, vec3_origin, vec3_origin, nodes[candidate].origin, ent, MASK_NODESOLID );
				//trace = gi.trace( virtualorigin, vec3_origin, vec3_origin, nodes[candidate].origin, ent, MASK_NODESOLID );
				if( trace.fraction == 1.0 && !trace.startsolid )
				{
#ifdef SHOW_JUMPAD_GUESS
					AI_JumpadGuess_ShowPoint( virtualorigin, "models/objects/grenade/tris.md2" );
#endif
					return candidate;
				}
			}
		}
	}
	while( candidate != NODE_INVALID && numtries++ < maxtries );

	return NODE_INVALID;
}
示例#11
0
//==========================================
// BOT_DMclass_PredictProjectileShot
// predict target movement
//==========================================
static void BOT_DMclass_PredictProjectileShot( edict_t *self, vec3_t fire_origin, float projectile_speed, vec3_t target, vec3_t target_velocity )
{
	vec3_t predictedTarget;
	vec3_t targetMovedir;
	float targetSpeed;
	float predictionTime;
	float distance;
	trace_t	trace;
	int contents;

	if( projectile_speed <= 0.0f )
		return;

	targetSpeed = VectorNormalize2( target_velocity, targetMovedir );

	// ok, this is not going to be 100% precise, since we will find the
	// time our projectile will take to travel to enemy's CURRENT position,
	// and them find enemy's position given his CURRENT velocity and his CURRENT dir
	// after prediction time. The result will be much better if the player
	// is moving to the sides (relative to us) than in depth (relative to us).
	// And, of course, when the player moves in a curve upwards it will totally miss (ie, jumping).

	// but in general it does a great job, much better than any human player :)

	distance = DistanceFast( fire_origin, target );
	predictionTime = distance/projectile_speed;
	VectorMA( target, predictionTime*targetSpeed, targetMovedir, predictedTarget );

	// if this position is inside solid, try finding a position at half of the prediction time
	contents = G_PointContents( predictedTarget );
	if( contents & CONTENTS_SOLID && !( contents & CONTENTS_PLAYERCLIP ) )
	{
		VectorMA( target, ( predictionTime * 0.5f )*targetSpeed, targetMovedir, predictedTarget );
		contents = G_PointContents( predictedTarget );
		if( contents & CONTENTS_SOLID && !( contents & CONTENTS_PLAYERCLIP ) )
			return; // INVALID
	}

	// if we can see this point, we use it, otherwise we keep the current position
	G_Trace( &trace, fire_origin, vec3_origin, vec3_origin, predictedTarget, self, MASK_SHOT );
	if( trace.fraction == 1.0f || ( trace.ent && game.edicts[trace.ent].takedamage ) )
		VectorCopy( predictedTarget, target );
}
示例#12
0
/*
* R_AliasModelLOD
*/
static model_t *R_AliasModelLOD( entity_t *e )
{
	int lod;
	float dist;

	if( !e->model->numlods || ( e->flags & RF_FORCENOLOD ) )
		return e->model;

	dist = DistanceFast( e->origin, ri.viewOrigin );
	dist *= ri.lod_dist_scale_for_fov;

	lod = (int)( dist / e->model->radius );
	if( r_lodscale->integer )
		lod /= r_lodscale->integer;
	lod += r_lodbias->integer;

	if( lod < 1 )
		return e->model;
	return e->model->lods[min( lod, e->model->numlods )-1];
}
示例#13
0
int AI_FindClosestReachableNode( vec3_t origin, edict_t *passent, int range, unsigned int flagsmask )
{
	int i;
	float closest;
	float dist;
	int node = -1;
	trace_t	tr;
	vec3_t maxs, mins;

	VectorSet( mins, -8, -8, -8 );
	VectorSet( maxs, 8, 8, 8 );

	// For Ladders, do not worry so much about reachability
	if( flagsmask & NODEFLAGS_LADDER )
	{
		VectorCopy( vec3_origin, maxs );
		VectorCopy( vec3_origin, mins );
	}

	closest = range;

	for( i = 0; i < nav.num_nodes; i++ )
	{
		if( flagsmask == NODE_ALL || nodes[i].flags & flagsmask )
		{
			dist = DistanceFast( nodes[i].origin, origin );

			if( dist < closest )
			{
				// make sure it is visible
				G_Trace( &tr, origin, mins, maxs, nodes[i].origin, passent, MASK_NODESOLID );
				if( tr.fraction == 1.0 )
				{
					node = i;
					closest = dist;
				}
			}
		}
	}
	return node;
}
示例#14
0
bool AI_NodeReached_PlatformStart( edict_t *self )
{
	bool reached = false;

	if( self->ai->next_node == NODE_INVALID )
		return true;

	if( self->groundentity && self->groundentity->use == Use_Plat )
	{
		vec3_t v1, v2;

		v1[0] = self->s.origin[0];
		v1[1] = self->s.origin[1];
		v1[2] = 0;

		v2[0] = nodes[self->ai->next_node].origin[0];
		v2[1] = nodes[self->ai->next_node].origin[1];
		v2[2] = 0;

		reached = ( DistanceFast( v1, v2 ) < NODE_REACH_RADIUS ) ? true : false;
	}

	return reached;
}
示例#15
0
void BotTacticalSpotsCache::FindReachableClassEntities( const Vec3 &origin, float radius, const char *classname,
														BotTacticalSpotsCache::ReachableEntities &result ) {
	int *triggerEntities;
	int numEntities = FindNearbyEntities( origin, radius, &triggerEntities );

	ReachableEntities candidateEntities;
	// Copy to locals for faster access (a compiler might be paranoid about aliasing)
	edict_t *gameEdicts = game.edicts;

	if( numEntities > (int)candidateEntities.capacity() ) {
		for( int i = 0; i < numEntities; ++i ) {
			edict_t *ent = gameEdicts + triggerEntities[i];
			// Specify expected strcmp() result explicitly to avoid misinterpreting the condition
			// (Strings are equal if an strcmp() result is zero)
			if( strcmp( ent->classname, classname ) != 0 ) {
				continue;
			}
			float distance = DistanceFast( origin.Data(), ent->s.origin );
			candidateEntities.push_back( EntAndScore( triggerEntities[i], radius - distance ) );
			if( candidateEntities.size() == candidateEntities.capacity() ) {
				break;
			}
		}
	} else {
		for( int i = 0; i < numEntities; ++i ) {
			edict_t *ent = gameEdicts + triggerEntities[i];
			if( strcmp( ent->classname, classname ) != 0 ) {
				continue;
			}
			float distance = DistanceFast( origin.Data(), ent->s.origin );
			candidateEntities.push_back( EntAndScore( triggerEntities[i], radius - distance ) );
		}
	}

	const AiAasWorld *aasWorld = AiAasWorld::Instance();
	AiAasRouteCache *routeCache = self->ai->botRef->routeCache;

	bool testTwoCurrAreas = false;
	int fromAreaNum = 0;
	// If an origin matches actual bot origin
	if( ( origin - self->s.origin ).SquaredLength() < WorldState::OriginVar::MAX_ROUNDING_SQUARE_DISTANCE_ERROR ) {
		// Try testing both areas
		if( self->ai->botRef->CurrAreaNum() != self->ai->botRef->DroppedToFloorAreaNum() ) {
			testTwoCurrAreas = true;
		} else {
			fromAreaNum = self->ai->botRef->CurrAreaNum();
		}
	} else {
		fromAreaNum = aasWorld->FindAreaNum( origin );
	}

	if( testTwoCurrAreas ) {
		int fromAreaNums[2] = { self->ai->botRef->CurrAreaNum(), self->ai->botRef->DroppedToFloorAreaNum() };
		for( EntAndScore &candidate: candidateEntities ) {
			edict_t *ent = gameEdicts + candidate.entNum;

			int toAreaNum = FindMostFeasibleEntityAasArea( ent, aasWorld );
			if( !toAreaNum ) {
				continue;
			}

			int travelTime = 0;
			for( int i = 0; i < 2; ++i ) {
				travelTime = routeCache->TravelTimeToGoalArea( fromAreaNums[i], toAreaNum, Bot::ALLOWED_TRAVEL_FLAGS );
				if( travelTime ) {
					break;
				}
			}
			if( !travelTime ) {
				continue;
			}

			// AAS travel time is in seconds^-2
			float factor = 1.0f / Q_RSqrt( 1.0001f - BoundedFraction( travelTime, 200 ) );
			result.push_back( EntAndScore( candidate.entNum, candidate.score * factor ) );
		}
	} else {
		for( EntAndScore &candidate: candidateEntities ) {
			edict_t *ent = gameEdicts + candidate.entNum;

			int toAreaNum = FindMostFeasibleEntityAasArea( ent, aasWorld );
			if( !toAreaNum ) {
				continue;
			}

			int travelTime = routeCache->TravelTimeToGoalArea( fromAreaNum, toAreaNum, Bot::ALLOWED_TRAVEL_FLAGS );
			if( !travelTime ) {
				continue;
			}

			float factor = 1.0f / Q_RSqrt( 1.0001f - BoundedFraction( travelTime, 200 ) );
			result.push_back( EntAndScore( candidate.entNum, candidate.score * factor ) );
		}
	}

	// Sort entities so best entities are first
	std::sort( result.begin(), result.end() );
}
示例#16
0
//==========================================
// BOT_DMclass_FindEnemy
// Scan for enemy (simplifed for now to just pick any visible enemy)
//==========================================
void BOT_DMclass_FindEnemy( edict_t *self )
{
#define WEIGHT_MAXDISTANCE_FACTOR 15000
	nav_ents_t *goalEnt;
	edict_t *bestTarget = NULL;
	float dist, weight, bestWeight = 9999999;
	vec3_t forward, vec;
	int i;

	if( G_ISGHOSTING( self ) 
		|| GS_MatchState() == MATCH_STATE_COUNTDOWN
		|| GS_ShootingDisabled() )
	{
		self->ai->enemyReactionDelay = 0;
		self->enemy = self->ai->latched_enemy = NULL;
		return;
	}

	// we also latch NULL enemies, so the bot can loose them
	if( self->ai->enemyReactionDelay > 0 )
	{
		self->ai->enemyReactionDelay -= game.frametime;
		return;
	}

	self->enemy = self->ai->latched_enemy;

	FOREACH_GOALENT( goalEnt )
	{
		i = goalEnt->id;

		if( !goalEnt->ent || !goalEnt->ent->r.inuse )
			continue;

		if( !goalEnt->ent->r.client ) // this may be changed, there could be enemies which aren't clients
			continue;

		if( G_ISGHOSTING( goalEnt->ent ) )
			continue;

		if( self->ai->status.entityWeights[i] <= 0 || goalEnt->ent->flags & (FL_NOTARGET|FL_BUSY) )
			continue;

		if( GS_TeamBasedGametype() && goalEnt->ent->s.team == self->s.team )
			continue;

		dist = DistanceFast( self->s.origin, goalEnt->ent->s.origin );

		// ignore very soft weighted enemies unless they are in your face
		if( dist > 500 && self->ai->status.entityWeights[i] <= 0.1f )
			continue;

		//if( dist > 700 && dist > WEIGHT_MAXDISTANCE_FACTOR * self->ai->status.entityWeights[i] )
		//	continue;

		weight = dist / self->ai->status.entityWeights[i];

		if( weight < bestWeight )
		{
			if( trap_inPVS( self->s.origin, goalEnt->ent->s.origin ) && G_Visible( self, goalEnt->ent ) )
			{
				bool close = dist < 2000 || goalEnt->ent == self->ai->last_attacker;

				if( !close )
				{
					VectorSubtract( goalEnt->ent->s.origin, self->s.origin, vec );
					VectorNormalize( vec );
					close = DotProduct( vec, forward ) > 0.3;
				}

				if( close )				
				{
					bestWeight = weight;
					bestTarget = goalEnt->ent;
				}
			}
		}
	}

	AI_NewEnemyInView( self, bestTarget );
#undef WEIGHT_MAXDISTANCE_FACTOR
}
示例#17
0
//==========================================
// BOT_DMclass_CombatMovement
//
// NOTE: Very simple for now, just a basic move about avoidance.
//       Change this routine for more advanced attack movement.
//==========================================
void BOT_DMclass_CombatMovement( edict_t *self, usercmd_t *ucmd )
{
	float c;
	float dist;
	bool rocket = false;
	vec3_t away_from_rocket = { 0, 0, 0 };

	if( !self->enemy || self->ai->rush_item )
	{
		BOT_DMclass_Move( self, ucmd );
		return;
	}

	if( self->ai->pers.skillLevel >= 0.25f ) 
		rocket = BOT_DMclass_FindRocket( self, away_from_rocket );

	dist = DistanceFast( self->s.origin, self->enemy->s.origin );
	c = random();

	if( level.time > self->ai->combatmovepush_timeout )
	{
		bool canMOVELEFT, canMOVERIGHT, canMOVEFRONT, canMOVEBACK;

		canMOVELEFT = AI_CanMove( self, BOT_MOVE_LEFT );
		canMOVERIGHT = AI_CanMove( self, BOT_MOVE_RIGHT );
		canMOVEFRONT = AI_CanMove( self, BOT_MOVE_FORWARD );
		canMOVEBACK = AI_CanMove( self, BOT_MOVE_BACK );

		self->ai->combatmovepush_timeout = level.time + AI_COMBATMOVE_TIMEOUT;
		VectorClear( self->ai->combatmovepushes );

		if( rocket )
		{
			//VectorScale(away_from_rocket,1,self->ai->combatmovepushes);
			if( away_from_rocket[0] )
			{
				if( ( away_from_rocket[0] < 0 ) && canMOVEBACK )
					self->ai->combatmovepushes[0] = -1;
				else if( ( away_from_rocket[0] > 0 ) && canMOVEFRONT )
					self->ai->combatmovepushes[0] = 1;
			}
			if( away_from_rocket[1] )
			{
				if( ( away_from_rocket[1] < 0 ) && canMOVELEFT )
					self->ai->combatmovepushes[1] = -1;
				else if( ( away_from_rocket[1] > 0 ) && canMOVERIGHT )
					self->ai->combatmovepushes[1] = 1;
			}

			ucmd->buttons |= BUTTON_SPECIAL;
		}
		else
			if( dist < 150 ) // range = AIWEAP_MELEE_RANGE;
			{
				if( self->s.weapon == WEAP_GUNBLADE ) // go into him!
				{
					ucmd->buttons &= ~BUTTON_ATTACK; // remove pressing fire
					if( canMOVEFRONT )  // move to your enemy
						self->ai->combatmovepushes[0] = 1;
					else if( c <= 0.5 && canMOVELEFT )
						self->ai->combatmovepushes[1] = -1;
					else if( canMOVERIGHT )
						self->ai->combatmovepushes[1] = 1;
				}
				else
				{
					//priorize sides
					if( canMOVELEFT || canMOVERIGHT )
					{
						if( canMOVELEFT && canMOVERIGHT )
						{
							self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1;
						}
						else if( canMOVELEFT )
						{
							self->ai->combatmovepushes[1] = -1;
						}
						else
						{
							self->ai->combatmovepushes[1] = 1;
						}
					}

					if( c < 0.3 && canMOVEBACK )
						self->ai->combatmovepushes[0] = -1;
				}

			}
			else if( dist < 500 ) //AIWEAP_SHORT_RANGE limit is Grenade Laucher range
			{
				if( canMOVELEFT || canMOVERIGHT )
				{
					if( canMOVELEFT && canMOVERIGHT )
					{
						self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1;
					}
					else if( canMOVELEFT )
					{
						self->ai->combatmovepushes[1] = -1;
					}
					else
					{
						self->ai->combatmovepushes[1] = 1;
					}
				}

				if( c < 0.3 && canMOVEFRONT )
				{
					self->ai->combatmovepushes[0] = 1;
				}

			}
			else if( dist < 900 )
			{
				if( canMOVELEFT || canMOVERIGHT )
				{
					if( canMOVELEFT && canMOVERIGHT )
					{
						self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1;
					}
					else if( canMOVELEFT )
					{
						self->ai->combatmovepushes[1] = -1;
					}
					else
					{
						self->ai->combatmovepushes[1] = 1;
					}
				}
			}
			else //range = AIWEAP_LONG_RANGE;
			{
				if( c < 0.75 && ( canMOVELEFT || canMOVERIGHT ) )
				{
					if( canMOVELEFT && canMOVERIGHT )
					{
						self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1;
					}
					else if( canMOVELEFT )
					{
						self->ai->combatmovepushes[1] = -1;
					}
					else
					{
						self->ai->combatmovepushes[1] = 1;
					}
				}
			}
	}

	if( !rocket && ( self->health < 25 || ( dist >= 500 && c < 0.2 ) || ( dist >= 1000 && c < 0.5 ) ) )
	{
		BOT_DMclass_Move( self, ucmd );
	}

	if( !self->ai->camp_item )
	{
		ucmd->forwardmove = self->ai->combatmovepushes[0];
	}
	ucmd->sidemove = self->ai->combatmovepushes[1];
	ucmd->upmove = self->ai->combatmovepushes[2];
}
示例#18
0
void BOT_DMclass_Move( edict_t *self, usercmd_t *ucmd )
{
#define BOT_FORWARD_EPSILON 0.5f
	int i;
	unsigned int linkType;
	bool printLink = false;
	bool nodeReached = false;
	bool specialMovement = false;
	vec3_t v1, v2;
	vec3_t lookdir, pathdir;
	float lookDot;

	if( self->ai->next_node == NODE_INVALID || self->ai->goal_node == NODE_INVALID )
	{
		BOT_DMclass_MoveWander( self, ucmd );
		return;
	}

	linkType = AI_CurrentLinkType( self );

	specialMovement = ( self->ai->path.numNodes >= MIN_BUNNY_NODES ) ? true : false;

	if( AI_GetNodeFlags( self->ai->next_node ) & (NODEFLAGS_REACHATTOUCH|NODEFLAGS_ENTITYREACH) )
		specialMovement = false;

	if( linkType & (LINK_JUMP|LINK_JUMPPAD|LINK_CROUCH|LINK_FALL|LINK_WATER|LINK_LADDER|LINK_ROCKETJUMP) )
		specialMovement = false;

	if( self->ai->pers.skillLevel < 0.33f )
		specialMovement = false;

	if( specialMovement == false || self->groundentity )
		self->ai->is_bunnyhop = false;

	VectorSubtract( nodes[self->ai->next_node].origin, self->s.origin, self->ai->move_vector );

	// 2D, normalized versions of look and path directions
	pathdir[0] = self->ai->move_vector[0];
	pathdir[1] = self->ai->move_vector[1];
	pathdir[2] = 0.0f;
	VectorNormalize( pathdir );

	AngleVectors( self->s.angles, lookdir, NULL, NULL );
	lookdir[2] = 0.0f;
	VectorNormalize( lookdir );

	lookDot = DotProduct( lookdir, pathdir );

	// Ladder movement
	if( self->is_ladder )
	{
		ucmd->forwardmove = 0;
		ucmd->upmove = 1;
		ucmd->sidemove = 0;

		if( nav.debugMode && printLink )
			G_PrintChasersf( self, "LINK_LADDER\n" );

		nodeReached = AI_NodeReached_Generic( self );
	}
	else if( linkType & LINK_JUMPPAD )
	{
		VectorCopy( self->s.origin, v1 );
		VectorCopy( nodes[self->ai->next_node].origin, v2 );
		v1[2] = v2[2] = 0;
		if( DistanceFast( v1, v2 ) > 32 && lookDot > BOT_FORWARD_EPSILON ) {
			ucmd->forwardmove = 1; // push towards destination
			ucmd->buttons |= BUTTON_WALK;
		}
		nodeReached = self->groundentity != NULL && AI_NodeReached_Generic( self );
	}
	// Platform riding - No move, riding elevator
	else if( linkType & LINK_PLATFORM )
	{
		VectorCopy( self->s.origin, v1 );
		VectorCopy( nodes[self->ai->next_node].origin, v2 );
		v1[2] = v2[2] = 0;
		if( DistanceFast( v1, v2 ) > 32 && lookDot > BOT_FORWARD_EPSILON )
			ucmd->forwardmove = 1; // walk to center

		ucmd->buttons |= BUTTON_WALK;
		ucmd->upmove = 0;
		ucmd->sidemove = 0;

		if( nav.debugMode && printLink )
			G_PrintChasersf( self, "LINK_PLATFORM (riding)\n" );

		self->ai->move_vector[2] = 0; // put view horizontal

		nodeReached = AI_NodeReached_PlatformEnd( self );
	}
	// entering platform
	else if( AI_GetNodeFlags( self->ai->next_node ) & NODEFLAGS_PLATFORM )
	{
		ucmd->forwardmove = 1;
		ucmd->upmove = 0;
		ucmd->sidemove = 0;

		if( lookDot <= BOT_FORWARD_EPSILON )
			ucmd->buttons |= BUTTON_WALK;

		if( nav.debugMode && printLink )
			G_PrintChasersf( self, "NODEFLAGS_PLATFORM (moving to plat)\n" );

		// is lift down?
		for( i = 0; i < nav.num_navigableEnts; i++ )
		{
			if( nav.navigableEnts[i].node == self->ai->next_node )
			{
				//testing line
				//vec3_t	tPoint;
				//int		j;
				//for(j=0; j<3; j++)//center of the ent
				//	tPoint[j] = nav.ents[i].ent->s.origin[j] + 0.5*(nav.ents[i].ent->r.mins[j] + nav.ents[i].ent->r.maxs[j]);
				//tPoint[2] = nav.ents[i].ent->s.origin[2] + nav.ents[i].ent->r.maxs[2];
				//tPoint[2] += 8;
				//AITools_DrawLine( self->s.origin, tPoint );

				//if not reachable, wait for it (only height matters)
				if( ( nav.navigableEnts[i].ent->s.origin[2] + nav.navigableEnts[i].ent->r.maxs[2] ) > ( self->s.origin[2] + self->r.mins[2] + AI_JUMPABLE_HEIGHT ) &&
					nav.navigableEnts[i].ent->moveinfo.state != STATE_BOTTOM )
				{
					self->ai->blocked_timeout = level.time + 10000;
					ucmd->forwardmove = 0;
				}
			}
		}

		nodeReached = AI_NodeReached_PlatformStart( self );
	}
	// Falling off ledge or jumping
	else if( !self->groundentity && !self->is_step && !self->is_swim && !self->ai->is_bunnyhop )
	{
		ucmd->upmove = 0;
		ucmd->sidemove = 0;
		ucmd->forwardmove = 0;

		if( lookDot > BOT_FORWARD_EPSILON )
		{
			ucmd->forwardmove = 1;

			// add fake strafe accel
			if( !(linkType & LINK_FALL) || linkType & (LINK_JUMP|LINK_ROCKETJUMP) )
			{
				if( linkType & LINK_JUMP )
				{
					if( AI_AttemptWalljump( self ) ) {
						ucmd->buttons |= BUTTON_SPECIAL;
					}
					if( VectorLengthFast( tv( self->velocity[0], self->velocity[1], 0 ) ) < 600 )
						VectorMA( self->velocity, 6.0f, lookdir, self->velocity );
				}
				else
				{
					if( VectorLengthFast( tv( self->velocity[0], self->velocity[1], 0 ) ) < 450 )
						VectorMA( self->velocity, 1.0f, lookdir, self->velocity );
				}
			}
		}
		else if( lookDot < -BOT_FORWARD_EPSILON )
			ucmd->forwardmove = -1;

		if( nav.debugMode && printLink )
			G_PrintChasersf( self, "FLY MOVE\n" );

		nodeReached = AI_NodeReached_Generic( self );
	}
	else // standard movement
	{
		ucmd->forwardmove = 1;
		ucmd->upmove = 0;
		ucmd->sidemove = 0;

		// starting a jump
		if( ( linkType & LINK_JUMP ) )
		{
			if( self->groundentity )
			{
				trace_t trace;
				vec3_t v1, v2;

				if( nav.debugMode && printLink )
					G_PrintChasersf( self, "LINK_JUMP\n" );

				//check floor in front, if there's none... Jump!
				VectorCopy( self->s.origin, v1 );
				VectorNormalize2( self->ai->move_vector, v2 );
				VectorMA( v1, 18, v2, v1 );
				v1[2] += self->r.mins[2];
				VectorCopy( v1, v2 );
				v2[2] -= AI_JUMPABLE_HEIGHT;
				G_Trace( &trace, v1, vec3_origin, vec3_origin, v2, self, MASK_AISOLID );
				if( !trace.startsolid && trace.fraction == 1.0 )
				{
					//jump!

					// prevent double jumping on crates
					VectorCopy( self->s.origin, v1 );
					v1[2] += self->r.mins[2];
					G_Trace( &trace, v1, tv( -12, -12, -8 ), tv( 12, 12, 0 ), v1, self, MASK_AISOLID );
					if( trace.startsolid )
						ucmd->upmove = 1;
				}
			}

			nodeReached = AI_NodeReached_Generic( self );
		}
		// starting a rocket jump
		else if( ( linkType & LINK_ROCKETJUMP ) )
		{
			if( nav.debugMode && printLink )
				G_PrintChasersf( self, "LINK_ROCKETJUMP\n" );

			if( !self->ai->rj_triggered && self->groundentity && ( self->s.weapon == WEAP_ROCKETLAUNCHER ) )
			{
				self->s.angles[PITCH] = 170;
				ucmd->upmove = 1;
				ucmd->buttons |= BUTTON_ATTACK;
				self->ai->rj_triggered = true;
			}

			nodeReached = AI_NodeReached_Generic( self );
		}
		else
		{
			// Move To Short Range goal (not following paths)
			// plats, grapple, etc have higher priority than SR Goals, cause the bot will
			// drop from them and have to repeat the process from the beginning
			if( AI_MoveToShortRangeGoalEntity( self, ucmd ) )
			{
				nodeReached = AI_NodeReached_Generic( self );
			}
			else if( specialMovement && !self->is_swim ) // bunny-hopping movement here
			{
				BOT_DMclass_SpecialMove( self, lookdir, pathdir, ucmd );
				nodeReached = AI_NodeReached_Special( self );
			}
			else
			{
				nodeReached = AI_NodeReached_Generic( self );
			}
		}

		// if static assume blocked and try to get free
		if( VectorLengthFast( self->velocity ) < 37 && ( ucmd->forwardmove || ucmd->sidemove || ucmd->upmove ) )
		{
			if( random() > 0.1 && AI_SpecialMove( self, ucmd ) )  // jumps, crouches, turns...
				return;

			self->s.angles[YAW] += brandom( -90, 90 );
		}
	}

	// swimming
	if( self->is_swim )
	{
		if( !( G_PointContents( nodes[self->ai->next_node].origin ) & MASK_WATER ) )  // Exit water
			ucmd->upmove = 1;
	}

	AI_ChangeAngle( self );

	if( nodeReached )
		AI_NodeReached( self );

#undef BOT_FORWARD_EPSILON
}
示例#19
0
/*
* CG_SpawnDecal
*/
void CG_SpawnDecal( vec3_t origin, vec3_t dir, float orient, float radius,
				   float r, float g, float b, float a, float die, float fadetime, bool fadealpha, struct shader_s *shader )
{
	int i, j;
	cdecal_t *dl;
	poly_t *poly;
	vec3_t axis[3];
	vec3_t verts[MAX_DECAL_VERTS];
	vec3_t v;
	byte_vec4_t color;
	fragment_t *fr, fragments[MAX_DECAL_FRAGMENTS];
	int numfragments;
	float dietime, fadefreq;

	if( !cg_addDecals->integer )
		return;

	// invalid decal
	if( radius <= 0 || VectorCompare( dir, vec3_origin ) )
		return;

	// we don't spawn decals if too far away (we could move there, but players won't notice there should be a decal by then)
	if( DistanceFast( origin, cg.view.origin ) * cg.view.fracDistFOV > 2048 )
		return;

	// calculate orientation matrix
	VectorNormalize2( dir, axis[0] );
	PerpendicularVector( axis[1], axis[0] );
	RotatePointAroundVector( axis[2], axis[0], axis[1], orient );
	CrossProduct( axis[0], axis[2], axis[1] );

	numfragments = trap_R_GetClippedFragments( origin, radius, axis, // clip it
		MAX_DECAL_VERTS, verts, MAX_DECAL_FRAGMENTS, fragments );

	// no valid fragments
	if( !numfragments )
		return;

	// clamp and scale colors
	if( r < 0 ) r = 0;else if( r > 1 ) r = 255;else r *= 255;
	if( g < 0 ) g = 0;else if( g > 1 ) g = 255;else g *= 255;
	if( b < 0 ) b = 0;else if( b > 1 ) b = 255;else b *= 255;
	if( a < 0 ) a = 0;else if( a > 1 ) a = 255;else a *= 255;

	color[0] = ( qbyte )( r );
	color[1] = ( qbyte )( g );
	color[2] = ( qbyte )( b );
	color[3] = ( qbyte )( a );

	radius = 0.5f / radius;
	VectorScale( axis[1], radius, axis[1] );
	VectorScale( axis[2], radius, axis[2] );

	dietime = cg.time + die * 1000;
	fadefreq = 0.001f / min( fadetime, die );
	fadetime = cg.time + ( die - min( fadetime, die ) ) * 1000;

	for( i = 0, fr = fragments; i < numfragments; i++, fr++ )
	{
		if( fr->numverts > MAX_DECAL_VERTS )
			return;
		else if( fr->numverts <= 0 )
			continue;

		// allocate decal
		dl = CG_AllocDecal();
		dl->die = dietime;
		dl->fadetime = fadetime;
		dl->fadefreq = fadefreq;
		dl->fadealpha = fadealpha;
		dl->shader = shader;
		dl->color[0] = r;
		dl->color[1] = g;
		dl->color[2] = b;
		dl->color[3] = a;

		// setup polygon for drawing
		poly = dl->poly;
		poly->shader = shader;
		poly->numverts = fr->numverts;
		poly->fognum = fr->fognum;

		for( j = 0; j < fr->numverts; j++ )
		{
			VectorCopy( verts[fr->firstvert+j], poly->verts[j] );
			VectorCopy( fr->normal, poly->normals[j] );
			VectorSubtract( poly->verts[j], origin, v );
			poly->stcoords[j][0] = DotProduct( v, axis[1] ) + 0.5f;
			poly->stcoords[j][1] = DotProduct( v, axis[2] ) + 0.5f;
			*( int * )poly->colors[j] = *( int * )color;
		}
	}
}
示例#20
0
//==========================================
// BOT_DMclass_FindEnemy
// Scan for enemy (simplifed for now to just pick any visible enemy)
//==========================================
void BOT_DMclass_FindEnemy( edict_t *self )
{
#define WEIGHT_MAXDISTANCE_FACTOR 15000
	nav_ents_t *goalEnt;
	edict_t *bestTarget = NULL;
	float dist, weight, bestWeight = 9999999;
	int i;

	if( G_ISGHOSTING( self ) 
		|| GS_MatchState() == MATCH_STATE_COUNTDOWN
		|| GS_ShootingDisabled() )
	{
		self->ai.enemyReactionDelay = 0;
		self->enemy = self->ai.latched_enemy = NULL;
		return;
	}

	// we also latch NULL enemies, so the bot can loose them
	if( self->ai.enemyReactionDelay > 0 )
	{
		self->ai.enemyReactionDelay -= game.frametime;
		return;
	}

	self->enemy = self->ai.latched_enemy;

	for( i = 0; i < nav.num_goalEnts; i++ )
	{
		goalEnt = &nav.goalEnts[i];

		if( !goalEnt->ent || !goalEnt->ent->r.inuse )
			continue;

		if( !goalEnt->ent->r.client ) // this may be changed, there could be enemies which aren't clients
			continue;

		if( G_ISGHOSTING( goalEnt->ent ) )
			continue;

		if( self->ai.status.entityWeights[i] <= 0 || goalEnt->ent->ai.notarget )
			continue;

		if( GS_TeamBasedGametype() && goalEnt->ent->s.team == self->s.team )
			continue;

		dist = DistanceFast( self->s.origin, goalEnt->ent->s.origin );

		// ignore very soft weighted enemies unless they are in your face
		if( dist > 500 && self->ai.status.entityWeights[i] <= 0.1f )
			continue;

		if( dist > 700 && dist > WEIGHT_MAXDISTANCE_FACTOR * self->ai.status.entityWeights[i] )
			continue;

		if( trap_inPVS( self->s.origin, goalEnt->ent->s.origin ) && G_Visible( self, goalEnt->ent ) )
		{
			weight = dist / self->ai.status.entityWeights[i];

			if( ( dist < 350 ) || G_InFront( self, goalEnt->ent ) )
			{
				if( weight < bestWeight )
				{
					bestWeight = weight;
					bestTarget = goalEnt->ent;
				}
			}
		}
	}

	AI_NewEnemyInView( self, bestTarget );
#undef WEIGHT_MAXDISTANCE_FACTOR
}
示例#21
0
/*
* G_SplashFrac
*/
void G_SplashFrac( const vec3_t origin, const vec3_t mins, const vec3_t maxs, const vec3_t point, float maxradius, vec3_t pushdir, float *kickFrac, float *dmgFrac )
{
#define VERTICALBIAS 0.65f // 0...1
#define CAPSULEDISTANCE
#define SPLASH_HDIST_CLAMP 53
	vec3_t boxcenter = { 0, 0, 0 };
	vec3_t hitpoint, vec;
	float distance;
	int i;
	float innerradius;
	float outerradius;
	float refdistance;

	if( maxradius <= 0 )
	{
		if( kickFrac )
			*kickFrac = 0;
		if( dmgFrac )
			*dmgFrac = 0;
		if( pushdir )
			VectorClear( pushdir );
		return;
	}

	VectorCopy( point, hitpoint );

	innerradius = ( maxs[0] + maxs[1] - mins[0] - mins[1] ) * 0.25;
	outerradius = ( sqrt( maxs[0]*maxs[0] + maxs[1]*maxs[1] ) + sqrt( mins[0]*mins[0] + mins[1]*mins[1] ) ) * 0.5;

#ifdef CAPSULEDISTANCE
	// Find the distance to the closest point in the capsule contained in the player bbox
	// modify the origin so the inner sphere acts as a capsule
	VectorCopy( origin, boxcenter );
	boxcenter[2] = hitpoint[2];
	clamp( boxcenter[2], ( origin[2] + mins[2] ) + innerradius, ( origin[2] + maxs[2] ) - innerradius );
#else
	// find center of the box
	for( i = 0; i < 3; i++ )
		boxcenter[i] = origin[i] + ( 0.5f * ( maxs[i] + mins[i] ) );
#endif

	// find push intensity
	distance = DistanceFast( boxcenter, hitpoint );

	if( distance >= maxradius )
	{
		if( kickFrac )
			*kickFrac = 0;
		if( dmgFrac )
			*dmgFrac = 0;
		if( pushdir )
			VectorClear( pushdir );
		return;
	}

	refdistance = innerradius;
	if( refdistance >= maxradius )
	{
		if( kickFrac )
			*kickFrac = 0;
		if( dmgFrac )
			*dmgFrac = 0;
		if( pushdir )
			VectorClear( pushdir );
		return;
	}

	maxradius -= refdistance;
	distance -= refdistance;
	if( distance < 0 )
		distance = 0;

	distance = maxradius - distance;
	clamp( distance, 0, maxradius );

	if( dmgFrac )
	{
		// soft sin curve
		*dmgFrac = sin( DEG2RAD( ( distance / maxradius ) * 80 ) );
		clamp( *dmgFrac, 0.0f, 1.0f );
	}

	if( kickFrac )
	{
		// linear kick fraction
		float kick = ( distance / maxradius );

		// half linear half exponential
		//*kickFrac =  ( kick + ( kick * kick ) ) * 0.5f;

		// linear
		*kickFrac = kick;

		clamp( *kickFrac, 0.0f, 1.0f );
	}

	//if( dmgFrac && kickFrac )
	//	G_Printf( "SPLASH: dmgFrac %.2f kickFrac %.2f\n", *dmgFrac, *kickFrac );

	// find push direction

	if( pushdir )
	{
#ifdef CAPSULEDISTANCE
		// find real center of the box again
		for( i = 0; i < 3; i++ )
			boxcenter[i] = origin[i] + ( 0.5f * ( maxs[i] + mins[i] ) );
#endif

#ifdef VERTICALBIAS
		// move the center up for the push direction
		if( origin[2] + maxs[2] > boxcenter[2] )
			boxcenter[2] += VERTICALBIAS * ( ( origin[2] + maxs[2] ) - boxcenter[2] );
#endif // VERTICALBIAS

#ifdef SPLASH_HDIST_CLAMP
		// if pushed from below, hack the hitpoint to limit the side push direction
		if( hitpoint[2] < boxcenter[2] && SPLASH_HDIST_CLAMP > 0 )
		{
			// do not allow the hitpoint to be further away
			// than SPLASH_HDIST_CLAMP in the horizontal axis
			vec[0] = hitpoint[0];
			vec[1] = hitpoint[1];
			vec[2] = boxcenter[2];

			if( DistanceFast( boxcenter, vec ) > SPLASH_HDIST_CLAMP )
			{
				VectorSubtract( vec, boxcenter, pushdir );
				VectorNormalizeFast( pushdir );
				VectorMA( boxcenter, SPLASH_HDIST_CLAMP, pushdir, hitpoint );
				hitpoint[2] = point[2]; // restore the original hitpoint height
			}
		}
#endif // SPLASH_HDIST_CLAMP

		VectorSubtract( boxcenter, hitpoint, pushdir );
		VectorNormalizeFast( pushdir );
	}

#undef VERTICALBIAS
#undef CAPSULEDISTANCE
#undef SPLASH_HDIST_CLAMP
}
示例#22
0
void CG_LaserBeamEffect( centity_t *cent )
{
	struct sfx_s *sound = NULL;
	float range;
	trace_t trace;
	orientation_t projectsource;
	vec4_t color;
	vec3_t laserOrigin, laserAngles, laserPoint;
	int i, j;

	if( cent->localEffects[LOCALEFFECT_LASERBEAM] <= cg.time )
		return;

	laserOwner = cent;

	if( cg_teamColoredBeams->integer && ( cent->current.team == TEAM_ALPHA || cent->current.team == TEAM_BETA ) )
		CG_TeamColor( cent->current.team, color );
	else
		Vector4Set( color, 1, 1, 1, 1 );

	// interpolate the positions

	if( ISVIEWERENTITY( cent->current.number ) && !cg.view.thirdperson )
	{
		VectorCopy( cg.predictedPlayerState.pmove.origin, laserOrigin );
		laserOrigin[2] += cg.predictedPlayerState.viewheight;
		VectorCopy( cg.predictedPlayerState.viewangles, laserAngles );

		VectorLerp( cent->laserPointOld, cg.lerpfrac, cent->laserPoint, laserPoint );
	}
	else
	{
		VectorLerp( cent->laserOriginOld, cg.lerpfrac, cent->laserOrigin, laserOrigin );
		VectorLerp( cent->laserPointOld, cg.lerpfrac, cent->laserPoint, laserPoint );
		if( !cent->laserCurved )
		{
			vec3_t dir;

			// make up the angles from the start and end points (s->angles is not so precise)
			VectorSubtract( laserPoint, laserOrigin, dir );
			VecToAngles( dir, laserAngles );
		}
		else // use player entity angles
		{
			for( i = 0; i < 3; i++ )
				laserAngles[i] = LerpAngle( cent->prev.angles[i], cent->current.angles[i], cg.lerpfrac );
		}
	}

	if( !cent->laserCurved )
	{
		range = GS_GetWeaponDef( WEAP_LASERGUN )->firedef.timeout;

		if( cent->current.effects & EF_QUAD )
			sound = CG_MediaSfx( cgs.media.sfxLasergunStrongQuadHum );
		else
			sound = CG_MediaSfx( cgs.media.sfxLasergunStrongHum );

		// trace the beam: for tracing we use the real beam origin
		GS_TraceLaserBeam( &trace, laserOrigin, laserAngles, range, cent->current.number, 0, _LaserImpact );
		
		// draw the beam: for drawing we use the weapon projection source (already handles the case of viewer entity)
		if( !CG_PModel_GetProjectionSource( cent->current.number, &projectsource ) )
			VectorCopy( laserOrigin, projectsource.origin );

		CG_KillPolyBeamsByTag( cent->current.number );
		CG_LaserGunPolyBeam( projectsource.origin, trace.endpos, color, cent->current.number );
	}
	else
	{
		float frac, subdivisions = cg_laserBeamSubdivisions->integer;
		vec3_t from, dir, end, blendPoint;
		int passthrough = cent->current.number;
		vec3_t tmpangles, blendAngles;

		range = GS_GetWeaponDef( WEAP_LASERGUN )->firedef_weak.timeout;

		if( cent->current.effects & EF_QUAD )
			sound = CG_MediaSfx( cgs.media.sfxLasergunWeakQuadHum );
		else
			sound = CG_MediaSfx( cgs.media.sfxLasergunWeakHum );

		// trace the beam: for tracing we use the real beam origin
		GS_TraceCurveLaserBeam( &trace, laserOrigin, laserAngles, laserPoint, cent->current.number, 0, _LaserImpact );

		// draw the beam: for drawing we use the weapon projection source (already handles the case of viewer entity)
		if( !CG_PModel_GetProjectionSource( cent->current.number, &projectsource ) )
			VectorCopy( laserOrigin, projectsource.origin );

		if( subdivisions < CURVELASERBEAM_SUBDIVISIONS )
			subdivisions = CURVELASERBEAM_SUBDIVISIONS;

		CG_KillPolyBeamsByTag( cent->current.number );

		// we redraw the full beam again, and trace each segment for stop dead impact
		VectorCopy( laserPoint, blendPoint );
		VectorCopy( projectsource.origin, from );
		VectorSubtract( blendPoint, projectsource.origin, dir );
		VecToAngles( dir, blendAngles );

		for( i = 1; i <= (int)subdivisions; i++ )
		{
			frac = ( ( range/subdivisions )*(float)i ) / (float)range;

			for( j = 0; j < 3; j++ )
				tmpangles[j] = LerpAngle( laserAngles[j], blendAngles[j], frac );

			AngleVectors( tmpangles, dir, NULL, NULL );
			VectorMA( projectsource.origin, range * frac, dir, end );

			GS_TraceLaserBeam( &trace, from, tmpangles, DistanceFast( from, end ), passthrough, 0, NULL );
			CG_LaserGunPolyBeam( from, trace.endpos, color, cent->current.number );
			if( trace.fraction != 1.0f )
				break;

			passthrough = trace.ent;
			VectorCopy( trace.endpos, from );
		}
	}

	// enable continuous flash on the weapon owner
	if( cg_weaponFlashes->integer )
		cg_entPModels[cent->current.number].flash_time = cg.time + CG_GetWeaponInfo( WEAP_LASERGUN )->flashTime;

	if( sound )
	{
		if( ISVIEWERENTITY( cent->current.number ) )
			trap_S_AddLoopSound( sound, cent->current.number, 1.0, ATTN_NONE );
		else
			trap_S_AddLoopSound( sound, cent->current.number, 1.0, ATTN_STATIC );
	}

	laserOwner = NULL;
}
示例#23
0
//==========================================
// BOT_DMclass_ChooseWeapon
// Choose weapon based on range & weights
//==========================================
static float BOT_DMclass_ChooseWeapon( edict_t *self )
{
	float dist;
	int i;
	float best_weight = 0.0;
	gsitem_t *weaponItem;
	int curweapon, weapon_range = 0, best_weapon = WEAP_NONE;

	curweapon = self->r.client->ps.stats[STAT_PENDING_WEAPON];

	// if no enemy, then what are we doing here?
	if( !self->enemy )
	{
		weapon_range = AIWEAP_MEDIUM_RANGE;
		if( curweapon == WEAP_GUNBLADE || curweapon == WEAP_NONE )
			self->ai->changeweapon_timeout = level.time;
	}
	else // Base weapon selection on distance:
	{
		dist = DistanceFast( self->s.origin, self->enemy->s.origin );

		if( dist < 150 )
			weapon_range = AIWEAP_MELEE_RANGE;
		else if( dist < 500 )  // Medium range limit is Grenade launcher range
			weapon_range = AIWEAP_SHORT_RANGE;
		else if( dist < 900 )
			weapon_range = AIWEAP_MEDIUM_RANGE;
		else
			weapon_range = AIWEAP_LONG_RANGE;
	}

	if( self->ai->changeweapon_timeout > level.time )
		return AIWeapons[curweapon].RangeWeight[weapon_range];

	for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ )
	{
		float rangeWeight;

		if( ( weaponItem = GS_FindItemByTag( i ) ) == NULL )
			continue;

		if( !GS_CheckAmmoInWeapon( &self->r.client->ps, i ) )
			continue;

		rangeWeight = AIWeapons[i].RangeWeight[weapon_range] * self->ai->pers.cha.weapon_affinity[i - ( WEAP_GUNBLADE - 1 )];

		// weigh up if having strong ammo
		if( self->r.client->ps.inventory[weaponItem->ammo_tag] )
			rangeWeight *= 1.25;

		// add a small random factor (less random the more skill)
		rangeWeight += brandom( -( 1.0 - self->ai->pers.skillLevel ), 1.0 - self->ai->pers.skillLevel );

		// compare range weights
		if( rangeWeight > best_weight )
		{
			best_weight = rangeWeight;
			best_weapon = i;
		}
	}

	// do the change (same weapon, or null best_weapon is covered at ChangeWeapon)
	if( best_weapon != WEAP_NONE )
		BOT_DMClass_ChangeWeapon( self, best_weapon );

	return AIWeapons[curweapon].RangeWeight[weapon_range]; // return current
}
示例#24
0
//==========================================
// AI_PickLongRangeGoal
//
// Evaluate the best long range goal and send the bot on
// its way. This is a good time waster, so use it sparingly.
// Do not call it for every think cycle.
//
// jal: I don't think there is any problem by calling it,
// now that we have stored the costs at the nav.costs table (I don't do it anyway)
//==========================================
void AI_PickLongRangeGoal( edict_t *self )
{
#define WEIGHT_MAXDISTANCE_FACTOR 20000.0f
#define COST_INFLUENCE	0.5f
	int i;
	float weight, bestWeight = 0.0;
	int current_node;
	float cost;
	float dist;
	nav_ents_t *goalEnt, *bestGoalEnt = NULL;

	AI_ClearGoal( self );

	if( G_ISGHOSTING( self ) )
		return;

	if( self->ai->longRangeGoalTimeout > level.time )
		return;

	if( !self->r.client->ps.pmove.stats[PM_STAT_MAXSPEED] ) {
		return;
	}

	self->ai->longRangeGoalTimeout = level.time + AI_LONG_RANGE_GOAL_DELAY + brandom( 0, 1000 );

	// look for a target
	current_node = AI_FindClosestReachableNode( self->s.origin, self, ( ( 1 + self->ai->nearest_node_tries ) * NODE_DENSITY ), NODE_ALL );
	self->ai->current_node = current_node;

	if( current_node == NODE_INVALID )
	{
		if( nav.debugMode && bot_showlrgoal->integer )
			G_PrintChasersf( self, "%s: LRGOAL: Closest node not found. Tries:%i\n", self->ai->pers.netname, self->ai->nearest_node_tries );

		self->ai->nearest_node_tries++; // extend search radius with each try
		return;
	}

	self->ai->nearest_node_tries = 0;

	// Run the list of potential goal entities
	FOREACH_GOALENT( goalEnt )
	{
		i = goalEnt->id;
		if( !goalEnt->ent )
			continue;

		if( !goalEnt->ent->r.inuse )
		{
			goalEnt->node = NODE_INVALID;
			continue;
		}

		if( goalEnt->ent->r.client )
		{
			if( G_ISGHOSTING( goalEnt->ent ) || goalEnt->ent->flags & (FL_NOTARGET|FL_BUSY) )
				goalEnt->node = NODE_INVALID;
			else
				goalEnt->node = AI_FindClosestReachableNode( goalEnt->ent->s.origin, goalEnt->ent, NODE_DENSITY, NODE_ALL );
		}

		if( goalEnt->ent->item )
		{
			if( !G_Gametype_CanPickUpItem( goalEnt->ent->item ) )
				continue;
		}

		if( goalEnt->node == NODE_INVALID )
			continue;

		weight = self->ai->status.entityWeights[i];

		if( weight <= 0.0f )
			continue;

		// don't try to find cost for too far away objects
		dist = DistanceFast( self->s.origin, goalEnt->ent->s.origin );
		if( dist > WEIGHT_MAXDISTANCE_FACTOR * weight/* || dist < AI_GOAL_SR_RADIUS*/ )
			continue;

		cost = AI_FindCost( current_node, goalEnt->node, self->ai->status.moveTypesMask );
		if( cost == NODE_INVALID )
			continue;

		cost -= brandom( 0, 2000 ); // allow random variations
		clamp_low( cost, 1 );
		weight = ( 1000 * weight ) / ( cost * COST_INFLUENCE ); // Check against cost of getting there

		if( weight > bestWeight )
		{
			bestWeight = weight;
			bestGoalEnt = goalEnt;
		}
	}

	if( bestGoalEnt )
	{
		self->ai->goalEnt = bestGoalEnt;
		AI_SetGoal( self, bestGoalEnt->node );

		if( self->ai->goalEnt != NULL && nav.debugMode && bot_showlrgoal->integer )
			G_PrintChasersf( self, "%s: selected a %s at node %d for LR goal. (weight %f)\n", self->ai->pers.netname, self->ai->goalEnt->ent->classname, self->ai->goalEnt->node, bestWeight );

		return;
	}

	if( nav.debugMode && bot_showlrgoal->integer )
		G_PrintChasersf( self, "%s: did not find a LR goal.\n", self->ai->pers.netname );

#undef WEIGHT_MAXDISTANCE_FACTOR
#undef COST_INFLUENCE
}
示例#25
0
/*
* W_Fire_Electrobolt_Combined
*/
void W_Fire_Electrobolt_Combined( edict_t *self, vec3_t start, vec3_t angles, float maxdamage, float mindamage, float maxknockback, float minknockback, int stun, int range, int mod, int timeDelta )
{
	vec3_t from, end, dir;
	trace_t	tr;
	edict_t	*ignore, *event, *hit, *damaged;
	int mask;
	qboolean missed = qtrue;
	int dmgflags = 0;
	int fireMode;

#ifdef ELECTROBOLT_TEST
	fireMode = FIRE_MODE_WEAK;
#else
	fireMode = FIRE_MODE_STRONG;
#endif

	if( GS_Instagib() )
		maxdamage = mindamage = 9999;

	AngleVectors( angles, dir, NULL, NULL );
	VectorMA( start, range, dir, end );
	VectorCopy( start, from );

	ignore = self;
	hit = damaged = NULL;

	mask = MASK_SHOT;
	if( GS_RaceGametype() )
		mask = MASK_SOLID;

	clamp_high( mindamage, maxdamage );
	clamp_high( minknockback, maxknockback );

	tr.ent = -1;
	while( ignore )
	{
		G_Trace4D( &tr, from, NULL, NULL, end, ignore, mask, timeDelta );

		VectorCopy( tr.endpos, from );
		ignore = NULL;

		if( tr.ent == -1 )
			break;

		// some entity was touched
		hit = &game.edicts[tr.ent];
		if( hit == world )  // stop dead if hit the world
			break;
		if( hit->movetype == MOVETYPE_NONE || hit->movetype == MOVETYPE_PUSH )
			break;

		// allow trail to go through BBOX entities (players, gibs, etc)
		if( !ISBRUSHMODEL( hit->s.modelindex ) )
			ignore = hit;

		if( ( hit != self ) && ( hit->takedamage ) )
		{
			float frac, damage, knockback;

			frac = DistanceFast( tr.endpos, start ) / (float)range;
			clamp( frac, 0.0f, 1.0f );

			damage = maxdamage - ( ( maxdamage - mindamage ) * frac );
			knockback = maxknockback - ( ( maxknockback - minknockback ) * frac );

			G_Damage( hit, self, self, dir, dir, tr.endpos, damage, knockback, stun, dmgflags, mod );
			
			// spawn a impact event on each damaged ent
			event = G_SpawnEvent( EV_BOLT_EXPLOSION, DirToByte( tr.plane.normal ), tr.endpos );
			event->s.firemode = fireMode;
			if( hit->r.client )
				missed = qfalse;

			damaged = hit;
		}
	}

	if( missed && self->r.client )
		G_AwardPlayerMissedElectrobolt( self, mod );

	// send the weapon fire effect
	event = G_SpawnEvent( EV_ELECTROTRAIL, ENTNUM( self ), start );
	event->r.svflags = SVF_TRANSMITORIGIN2;
	VectorScale( dir, 1024, event->s.origin2 );
	event->s.firemode = fireMode;

	if( !GS_Instagib() && tr.ent == -1 )	// didn't touch anything, not even a wall
	{
		edict_t *bolt;
		gs_weapon_definition_t *weapondef = GS_GetWeaponDef( self->s.weapon );

		// fire a weak EB from the end position
		bolt = W_Fire_Electrobolt_Weak( self, end, angles, weapondef->firedef_weak.speed, mindamage, minknockback, minknockback, stun, weapondef->firedef_weak.timeout, mod, timeDelta );
		bolt->enemy = damaged;
	}
}
示例#26
0
//==========================================
// AI_PickShortRangeGoal
// Pick best goal based on importance and range. This function
// overrides the long range goal selection for items that
// are very close to the bot and are reachable.
//==========================================
static void AI_PickShortRangeGoal( edict_t *self )
{
	edict_t *bestGoal = NULL;
	float bestWeight = 0;
	nav_ents_t *goalEnt;
	const gsitem_t *item;
	bool canPickupItems;
	int i;

	if( !self->r.client || G_ISGHOSTING( self ) )
		return;

	if( self->ai->state_combat_timeout > level.time )
	{
		self->ai->shortRangeGoalTimeout = self->ai->state_combat_timeout;
		return;
	}

	if( self->ai->shortRangeGoalTimeout > level.time )
		return;

	canPickupItems = (self->r.client->ps.pmove.stats[PM_STAT_FEATURES] & PMFEAT_ITEMPICK) != 0 ? true : false;

	self->ai->shortRangeGoalTimeout = level.time + AI_SHORT_RANGE_GOAL_DELAY;

	self->movetarget = NULL;

	FOREACH_GOALENT( goalEnt )
	{
		float dist;

		i = goalEnt->id;
		if( !goalEnt->ent->r.inuse || goalEnt->ent->r.solid == SOLID_NOT )
			continue;

		if( goalEnt->ent->r.client )
			continue;

		if( self->ai->status.entityWeights[i] <= 0.0f )
			continue;

		item = goalEnt->ent->item;
		if( canPickupItems && item ) {
			if( !G_Gametype_CanPickUpItem( item ) || !( item->flags & ITFLAG_PICKABLE ) ) {
				continue;
			}
		}

		dist = DistanceFast( self->s.origin, goalEnt->ent->s.origin );
		if( goalEnt == self->ai->goalEnt ) {
			if( dist > AI_GOAL_SR_LR_RADIUS )
				continue;			
		}
		else {
			if( dist > AI_GOAL_SR_RADIUS )
				continue;
		}		

		clamp_low( dist, 0.01f );

		if( AI_ShortRangeReachable( self, goalEnt->ent->s.origin ) )
		{
			float weight;
			bool in_front = G_InFront( self, goalEnt->ent );

			// Long range goal gets top priority
			if( in_front && goalEnt == self->ai->goalEnt ) 
			{
				bestGoal = goalEnt->ent;
				break;
			}

			// get the one with the best weight
			weight = self->ai->status.entityWeights[i] / dist * (in_front ? 1.0f : 0.5f);
			if( weight > bestWeight )
			{
				bestWeight = weight;
				bestGoal = goalEnt->ent;
			}
		}
	}

	if( bestGoal )
	{
		self->movetarget = bestGoal;
		if( nav.debugMode && bot_showsrgoal->integer )
			G_PrintChasersf( self, "%i %s: selected a %s for SR goal.\n", level.framenum, self->ai->pers.netname, self->movetarget->classname );
	}
	else
	{
		// got nothing else to do so keep scanning
		self->ai->shortRangeGoalTimeout = level.time + AI_SHORT_RANGE_GOAL_DELAY_IDLE;
	}
}
示例#27
0
/*
* R_DrawShadowmaps
*/
void R_DrawShadowmaps( void ) {
	unsigned int i;
	image_t *shadowmap;
	int textureWidth, textureHeight;
	float lodScale;
	vec3_t lodOrigin;
	vec3_t viewerOrigin;
	shadowGroup_t *group;
	int shadowBits = rn.shadowBits;
	refdef_t refdef;
	int lod;
	float farClip;
	float dist;

	if( !rsc.numShadowGroups ) {
		return;
	}
	if( rn.renderFlags & RF_SHADOWMAPVIEW ) {
		return;
	}
	if( rn.refdef.rdflags & RDF_NOWORLDMODEL ) {
		return;
	}
	if( !shadowBits ) {
		return;
	}

	if( !R_PushRefInst() ) {
		return;
	}

	lodScale = rn.lod_dist_scale_for_fov;
	VectorCopy( rn.lodOrigin, lodOrigin );
	VectorCopy( rn.viewOrigin, viewerOrigin );

	refdef = rn.refdef;

	// find lighting group containing entities with same lightingOrigin as ours
	for( i = 0; i < rsc.numShadowGroups; i++ ) {
		if( !shadowBits ) {
			break;
		}

		group = rsc.shadowGroups + i;
		if( !( shadowBits & group->bit ) ) {
			continue;
		}
		shadowBits &= ~group->bit;

		// make sure we don't render the same shadowmap twice in the same scene frame
		if( rsc.renderedShadowBits & group->bit ) {
			continue;
		}

		// calculate LOD for shadowmap
		dist = DistanceFast( group->origin, lodOrigin );
		lod = (int)( dist * lodScale ) / group->projDist - SHADOWMAP_LODBIAS;
		if( lod < 0 ) {
			lod = 0;
		}

		// allocate/resize the texture if needed
		shadowmap = R_GetShadowmapTexture( i, rsc.refdef.width, rsc.refdef.height, 0 );

		assert( shadowmap && shadowmap->upload_width && shadowmap->upload_height );

		group->shadowmap = shadowmap;
		textureWidth = shadowmap->upload_width;
		textureHeight = shadowmap->upload_height;

		if( !shadowmap->fbo ) {
			continue;
		}

		farClip = R_SetupShadowmapView( group, &refdef, lod );
		if( farClip <= 0.0f ) {
			continue;
		}

		// ignore shadowmaps of very low detail level
		if( refdef.width < SHADOWMAP_MIN_VIEWPORT_SIZE || refdef.height < SHADOWMAP_MIN_VIEWPORT_SIZE ) {
			continue;
		}

		rn.renderTarget = shadowmap->fbo;
		rn.farClip = farClip;
		rn.renderFlags = RF_SHADOWMAPVIEW | RF_FLIPFRONTFACE;
		if( !( shadowmap->flags & IT_DEPTH ) ) {
			rn.renderFlags |= RF_SHADOWMAPVIEW_RGB;
		}
		rn.clipFlags |= 16; // clip by far plane too
		rn.meshlist = &r_shadowlist;
		rn.portalmasklist = NULL;
		rn.shadowGroup = group;
		rn.lod_dist_scale_for_fov = lodScale;
		VectorCopy( viewerOrigin, rn.pvsOrigin );
		VectorCopy( lodOrigin, rn.lodOrigin );

		// 3 pixels border on each side to prevent nasty stretching/bleeding of shadows,
		// also accounting for smoothing done in the fragment shader
		Vector4Set( rn.viewport, refdef.x + 3,refdef.y + textureHeight - refdef.height + 3, refdef.width - 6, refdef.height - 6 );
		Vector4Set( rn.scissor, refdef.x, refdef.y, textureWidth, textureHeight );

		R_RenderView( &refdef );

		Matrix4_Copy( rn.cameraProjectionMatrix, group->cameraProjectionMatrix );

		rsc.renderedShadowBits |= group->bit;
	}

	R_PopRefInst();
}
示例#28
0
void W_Fire_Electrobolt_FullInstant( edict_t *self, vec3_t start, vec3_t angles, float maxdamage, float mindamage, int maxknockback, int minknockback, int stun, int range, int minDamageRange, int mod, int timeDelta )
{
	vec3_t from, end, dir;
	trace_t	tr;
	edict_t	*ignore, *event, *hit, *damaged;
	int mask;
	qboolean missed = qtrue;
	int dmgflags = 0;

#define FULL_DAMAGE_RANGE g_projectile_prestep->value

	if( GS_Instagib() )
		maxdamage = mindamage = 9999;

	AngleVectors( angles, dir, NULL, NULL );
	VectorMA( start, range, dir, end );
	VectorCopy( start, from );

	ignore = self;
	hit = damaged = NULL;

	mask = MASK_SHOT;
	if( GS_RaceGametype() )
		mask = MASK_SOLID;

	clamp_high( mindamage, maxdamage );
	clamp_high( minknockback, maxknockback );
	clamp_high( minDamageRange, range );

	if( minDamageRange <= FULL_DAMAGE_RANGE )
		minDamageRange = FULL_DAMAGE_RANGE + 1;

	if( range <= FULL_DAMAGE_RANGE + 1 )
		range = FULL_DAMAGE_RANGE + 1;

	tr.ent = -1;
	while( ignore )
	{
		G_Trace4D( &tr, from, NULL, NULL, end, ignore, mask, timeDelta );

		VectorCopy( tr.endpos, from );
		ignore = NULL;

		if( tr.ent == -1 )
			break;

		// some entity was touched
		hit = &game.edicts[tr.ent];
		if( hit == world )  // stop dead if hit the world
			break;
		if( hit->movetype == MOVETYPE_NONE || hit->movetype == MOVETYPE_PUSH )
			break;

		// allow trail to go through BBOX entities (players, gibs, etc)
		if( !ISBRUSHMODEL( hit->s.modelindex ) )
			ignore = hit;

		if( ( hit != self ) && ( hit->takedamage ) )
		{
			float frac, damage, knockback, dist;

			dist = DistanceFast( tr.endpos, start );
			if( dist <= FULL_DAMAGE_RANGE )
				frac = 0.0f;
			else
			{
				frac = ( dist - FULL_DAMAGE_RANGE ) / (float)( minDamageRange - FULL_DAMAGE_RANGE );
				clamp( frac, 0.0f, 1.0f );
			}

			damage = maxdamage - ( ( maxdamage - mindamage ) * frac );
			knockback = maxknockback - ( ( maxknockback - minknockback ) * frac );

			//G_Printf( "mindamagerange %i frac %.1f damage %i\n", minDamageRange, 1.0f - frac, (int)damage );

			G_Damage( hit, self, self, dir, dir, tr.endpos, damage, knockback, stun, dmgflags, mod );
			
			// spawn a impact event on each damaged ent
			event = G_SpawnEvent( EV_BOLT_EXPLOSION, DirToByte( tr.plane.normal ), tr.endpos );
			event->s.firemode = FIRE_MODE_STRONG;
			if( hit->r.client )
				missed = qfalse;

			damaged = hit;
		}
	}

	if( missed && self->r.client )
		G_AwardPlayerMissedElectrobolt( self, mod );

	// send the weapon fire effect
	event = G_SpawnEvent( EV_ELECTROTRAIL, ENTNUM( self ), start );
	event->r.svflags = SVF_TRANSMITORIGIN2;
	VectorScale( dir, 1024, event->s.origin2 );
	event->s.firemode = FIRE_MODE_STRONG;

#undef FULL_DAMAGE_RANGE
}
示例#29
0
/*
* AI_PredictJumpadDestity
* Make a guess on where a jumpad will send the player.
*/
static qboolean AI_PredictJumpadDestity( edict_t *ent, vec3_t out )
{
	int i;
	edict_t *target;
	trace_t	trace;
	vec3_t pad_origin, v1, v2;
	float htime, vtime, tmpfloat, player_factor;
	vec3_t floor_target_origin, target_origin;
	vec3_t floor_dist_vec, floor_movedir;

	VectorClear( out );

	if( !ent->target )
		return qfalse;

	// get target entity
	target = G_Find( NULL, FOFS( targetname ), ent->target );
	if( !target )
		return qfalse;

	// find pad origin
	VectorCopy( ent->r.maxs, v1 );
	VectorCopy( ent->r.mins, v2 );
	pad_origin[0] = ( v1[0] - v2[0] ) / 2 + v2[0];
	pad_origin[1] = ( v1[1] - v2[1] ) / 2 + v2[1];
	pad_origin[2] = ent->r.maxs[2];

	//make a projection 'on floor' of target origin
	VectorCopy( target->s.origin, target_origin );
	VectorCopy( target->s.origin, floor_target_origin );
	floor_target_origin[2] = pad_origin[2]; //put at pad's height

	//make a guess on how player movement will affect the trajectory
	tmpfloat = DistanceFast( pad_origin, floor_target_origin );
	htime = sqrt( ( tmpfloat ) );
	vtime = sqrt( ( target->s.origin[2] - pad_origin[2] ) );
	if( !vtime ) return qfalse;
	htime *= 4; vtime *= 4;
	if( htime > vtime )
		htime = vtime;
	player_factor = vtime - htime;

	// find distance vector, on floor, from pad_origin to target origin.
	for( i = 0; i < 3; i++ )
		floor_dist_vec[i] = floor_target_origin[i] - pad_origin[i];

	// movement direction on floor
	VectorCopy( floor_dist_vec, floor_movedir );
	VectorNormalize( floor_movedir );

	// move both target origin and target origin on floor by player movement factor.
	VectorMA( target_origin, player_factor, floor_movedir, target_origin );
	VectorMA( floor_target_origin, player_factor, floor_movedir, floor_target_origin );

	// move target origin on floor by floor distance, and add another player factor step to it
	VectorMA( floor_target_origin, 1, floor_dist_vec, floor_target_origin );
	VectorMA( floor_target_origin, player_factor, floor_movedir, floor_target_origin );

#ifdef SHOW_JUMPAD_GUESS
	// this is our top of the curve point, and the original target
	AI_JumpadGuess_ShowPoint( target_origin, PATH_AMMO_BOX_MODEL );
	AI_JumpadGuess_ShowPoint( target->s.origin, PATH_AMMO_BOX_MODEL );
#endif

	//trace from target origin to endPoint.
	G_Trace( &trace, target_origin, tv( -15, -15, -8 ), tv( 15, 15, 8 ), floor_target_origin, NULL, MASK_NODESOLID );
	if( ( trace.fraction == 1.0 && trace.startsolid ) || ( trace.allsolid && trace.startsolid ) )
	{
		G_Printf( "JUMPAD LAND: ERROR: trace was in solid.\n" ); //started inside solid (target should never be inside solid, this is a mapper error)
		return qfalse;
	}
	else if( trace.fraction == 1.0 )
	{

		//didn't find solid. Extend Down (I have to improve this part)
		vec3_t target_origin2, extended_endpoint, extend_dist_vec;

		VectorCopy( floor_target_origin, target_origin2 );
		for( i = 0; i < 3; i++ )
			extend_dist_vec[i] = floor_target_origin[i] - target_origin[i];

		VectorMA( target_origin2, 1, extend_dist_vec, extended_endpoint );

		G_Trace( &trace, target_origin2, tv( -15, -15, -8 ), tv( 15, 15, 8 ), extended_endpoint, NULL, MASK_NODESOLID );
		if( trace.fraction == 1.0 )
			return qfalse; //still didn't find solid
	}

#ifdef SHOW_JUMPAD_GUESS
	// destiny found
	AI_JumpadGuess_ShowPoint( trace.endpos, PATH_AMMO_BOX_MODEL );
#endif

	VectorCopy( trace.endpos, out );
	return qtrue;
}