Ejemplo n.º 1
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;
	}
}
Ejemplo n.º 2
0
//==========================================
// BOT_DMclass_FireWeapon
// Fire if needed
//==========================================
static bool BOT_DMclass_FireWeapon( edict_t *self, usercmd_t *ucmd )
{
#define WFAC_GENERIC_PROJECTILE 300.0
#define WFAC_GENERIC_INSTANT 150.0
	float firedelay;
	vec3_t target;
	int weapon, i;
	float wfac;
	vec3_t fire_origin;
	trace_t	trace;
	bool continuous_fire = false;
	firedef_t *firedef = GS_FiredefForPlayerState( &self->r.client->ps, self->r.client->ps.stats[STAT_WEAPON] );

	if( !self->enemy )
		return false;

	weapon = self->s.weapon;
	if( weapon < 0 || weapon >= WEAP_TOTAL )
		weapon = 0;

	if( !firedef )
		return false;

	// Aim to center of the box
	for( i = 0; i < 3; i++ )
		target[i] = self->enemy->s.origin[i] + ( 0.5f * ( self->enemy->r.maxs[i] + self->enemy->r.mins[i] ) );
	fire_origin[0] = self->s.origin[0];
	fire_origin[1] = self->s.origin[1];
	fire_origin[2] = self->s.origin[2] + self->viewheight;

	if( self->s.weapon == WEAP_LASERGUN || self->s.weapon == WEAP_PLASMAGUN )
		continuous_fire = true;

	if( !continuous_fire && !BOT_DMclass_CheckShot( self, target ) )
		return false;

	// find out our weapon AIM style
	if( AIWeapons[weapon].aimType == AI_AIMSTYLE_PREDICTION_EXPLOSIVE )
	{
		// in the lowest skill level, don't predict projectiles
		if( self->ai->pers.skillLevel >= 0.33f )
			BOT_DMclass_PredictProjectileShot( self, fire_origin, firedef->speed, target, self->enemy->velocity );

		wfac = WFAC_GENERIC_PROJECTILE * 1.3;

		// aim to the feet when enemy isn't higher
		if( fire_origin[2] > ( target[2] + ( self->enemy->r.mins[2] * 0.8 ) ) )
		{
			vec3_t checktarget;
			VectorSet( checktarget,
				self->enemy->s.origin[0],
				self->enemy->s.origin[1],
				self->enemy->s.origin[2] + self->enemy->r.mins[2] + 4 );

			G_Trace( &trace, fire_origin, vec3_origin, vec3_origin, checktarget, self, MASK_SHOT );
			if( trace.fraction == 1.0f || ( trace.ent > 0 && game.edicts[trace.ent].takedamage ) )
				VectorCopy( checktarget, target );
		}
		else if( !AI_IsStep( self->enemy ) )
			wfac *= 2.5; // more imprecise for air rockets
	}
	else if( AIWeapons[weapon].aimType == AI_AIMSTYLE_PREDICTION )
	{
		if( self->s.weapon == WEAP_PLASMAGUN )
			wfac = WFAC_GENERIC_PROJECTILE * 0.5;
		else
			wfac = WFAC_GENERIC_PROJECTILE;

		// in the lowest skill level, don't predict projectiles
		if( self->ai->pers.skillLevel >= 0.33f )
			BOT_DMclass_PredictProjectileShot( self, fire_origin, firedef->speed, target, self->enemy->velocity );
	}
	else if( AIWeapons[weapon].aimType == AI_AIMSTYLE_DROP )
	{
		//jalToDo
		wfac = WFAC_GENERIC_PROJECTILE;
		// in the lowest skill level, don't predict projectiles
		if( self->ai->pers.skillLevel >= 0.33f )
			BOT_DMclass_PredictProjectileShot( self, fire_origin, firedef->speed, target, self->enemy->velocity );

	}
	else // AI_AIMSTYLE_INSTANTHIT
	{
		if( self->s.weapon == WEAP_ELECTROBOLT )
			wfac = WFAC_GENERIC_INSTANT;
		else if( self->s.weapon == WEAP_LASERGUN )
			wfac = WFAC_GENERIC_INSTANT * 1.5;
		else
			wfac = WFAC_GENERIC_INSTANT;
	}

	wfac = 25 + wfac * ( 1.0f - self->ai->pers.skillLevel );

	// look to target
	VectorSubtract( target, fire_origin, self->ai->move_vector );

	if( self->r.client->ps.weaponState == WEAPON_STATE_READY ||
		self->r.client->ps.weaponState == WEAPON_STATE_REFIRE ||
		self->r.client->ps.weaponState == WEAPON_STATE_REFIRESTRONG )
	{
		// in continuous fire weapons don't add delays
		if( self->s.weapon == WEAP_LASERGUN || self->s.weapon == WEAP_PLASMAGUN )
			firedelay = 1.0f;
		else
			firedelay = ( 1.0f - self->ai->pers.skillLevel ) - ( random()-0.25f );

		if( firedelay > 0.0f )
		{
			if( G_InFront( self, self->enemy ) ) {
				ucmd->buttons |= BUTTON_ATTACK; // could fire, but wants to?
			}
			// mess up angles only in the attacking frames
			if( self->r.client->ps.weaponState == WEAPON_STATE_READY ||
				self->r.client->ps.weaponState == WEAPON_STATE_REFIRE ||
				self->r.client->ps.weaponState == WEAPON_STATE_REFIRESTRONG )
			{
				if( (self->s.weapon == WEAP_LASERGUN) || (self->s.weapon == WEAP_PLASMAGUN) ) {
					target[0] += sinf( (float)level.time/100.0) * wfac;
					target[1] += cosf( (float)level.time/100.0) * wfac;
				}
				else
				{
					target[0] += ( random()-0.5f ) * wfac;
					target[1] += ( random()-0.5f ) * wfac;
				}
			}
		}
	}

	//update angles
	VectorSubtract( target, fire_origin, self->ai->move_vector );
	AI_ChangeAngle( self );

	if( nav.debugMode && bot_showcombat->integer )
		G_PrintChasersf( self, "%s: attacking %s\n", self->ai->pers.netname, self->enemy->r.client ? self->enemy->r.client->netname : self->classname );

	return true;
}
Ejemplo n.º 3
0
/*
=============
ai_checkattack

Decides if we're going to attack or do something else
used by ai_run and ai_stand
=============
*/
bool ai_checkattack (edict_t *self, float dist)
{
	vec3_t		temp;
	bool	hesDeadJim;

// this causes monsters to run blindly to the combat point w/o firing
	if (self->goalentity)
	{
		if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
			return false;

		if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
		{
			if (level.time - self->enemy->teleport_time > 5000)
			{
				if (self->goalentity == self->enemy) {
					if (self->movetarget)
						self->goalentity = self->movetarget;
					else
						self->goalentity = NULL;
				}
				self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
				if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
					self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
			}
			else
			{
				self->show_hostile = level.time + 1000;
				return false;
			}
		}
	}

	enemy_vis = false;

// see if the enemy is dead
	hesDeadJim = false;
	if ((!self->enemy) || (!self->enemy->r.inuse))
	{
		hesDeadJim = true;
	}
	else if (self->monsterinfo.aiflags & AI_MEDIC)
	{
		if (self->enemy->health > 0)
		{
			hesDeadJim = true;
			self->monsterinfo.aiflags &= ~AI_MEDIC;
		}
	}
	else
	{
		if (self->monsterinfo.aiflags & AI_BRUTAL)
		{
			if (self->enemy->health <= -80)
				hesDeadJim = true;
		}
		else
		{
			if (self->enemy->health <= 0)
				hesDeadJim = true;
		}
	}

	if (hesDeadJim)
	{
		self->enemy = NULL;
	// FIXME: look all around for other targets
		if (self->oldenemy && self->oldenemy->health > 0)
		{
			self->enemy = self->oldenemy;
			self->oldenemy = NULL;
			HuntTarget (self);
		}
		else
		{
			if (self->movetarget)
			{
				self->goalentity = self->movetarget;
				self->monsterinfo.walk (self);
			}
			else
			{
				// we need the pausetime otherwise the stand code
				// will just revert to walking with no target and
				// the monsters will wonder around aimlessly trying
				// to hunt the world entity
				self->monsterinfo.pausetime = level.time + 100000000;
				self->monsterinfo.stand (self);
			}
			return true;
		}
	}

	self->show_hostile = level.time + 1000;		// wake up other monsters

// check knowledge of enemy
	enemy_vis = G_Visible(self, self->enemy);
	if (enemy_vis)
	{
		self->monsterinfo.search_time = level.time + 5000;
		VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
	}

// look for other coop players here
//	if (coop && self->monsterinfo.search_time < level.time)
//	{
//		if (FindTarget (self))
//			return true;
//	}

	enemy_infront = G_InFront(self, self->enemy);
	enemy_range = range(self, self->enemy);
	VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
	enemy_yaw = vectoyaw(temp);


	// JDC self->ideal_yaw = enemy_yaw;

	if (self->monsterinfo.attack_state == AS_MISSILE)
	{
		ai_run_missile (self);
		return true;
	}
	if (self->monsterinfo.attack_state == AS_MELEE)
	{
		ai_run_melee (self);
		return true;
	}

	// if enemy is not currently visible, we will never attack
	if (!enemy_vis)
		return false;

	return self->monsterinfo.checkattack (self);
}
Ejemplo n.º 4
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;

	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 )
			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
}
Ejemplo n.º 5
0
/*
===========
FindTarget

Self is currently not attacking anything, so try to find a target

Returns TRUE if an enemy was sighted

When a player fires a missile, the point of impact becomes a fakeplayer so
that monsters that see the impact will respond as if they had seen the
player.

To avoid spending too much time, only a single client (or fakeclient) is
checked each frame.  This means multi player games will have slightly
slower noticing monsters.
============
*/
bool FindTarget (edict_t *self)
{
	edict_t		*client;
	bool	heardit;
	bool    noise;
	int			r;

	if (self->monsterinfo.aiflags & AI_GOOD_GUY)
	{
		if (self->goalentity && self->goalentity->r.inuse && self->goalentity->classname)
		{
			if (strcmp(self->goalentity->classname, "target_actor") == 0)
				return false;
		}

		//FIXME look for monsters?
		return false;
	}

	// if we're going to a combat point, just proceed
	if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
		return false;

// if the first spawnflag bit is set, the monster will only wake up on
// really seeing the player, not another monster getting angry or hearing
// something

// revised behavior so they will wake up if they "see" a player make a noise
// but not weapon impact/explosion noises

	heardit = false;
	if ((level.sight_entity_framenum+1 >= level.framenum) && !(self->spawnflags & 1) )
	{
		client = level.sight_entity;
		if (client->enemy == self->enemy)
		{
			return false;
		}
	}
	else if (level.sound_entity_framenum+1 >= level.framenum)
	{
		client = level.sound_entity;
		heardit = true;
	}
	else if (!(self->enemy) && (level.sound2_entity_framenum+1 >= level.framenum) && !(self->spawnflags & 1) )
	{
		client = level.sound2_entity;
		heardit = true;
	}
	else
	{
		client = level.sight_client;
		if (!client)
			return false;	// no clients to get mad at
	}

	// if the entity went away, forget it
	if (!client->r.inuse)
		return false;
	if (client == self->enemy)
		return true;	// JDC false;

	noise = strcmp(client->classname, "player_noise") == 0;
	if (!noise && (client->r.svflags & SVF_NOCLIENT))
		return false;

	if (client->r.client)
	{
		if (client->flags & FL_NOTARGET)
			return false;
	}
	else if (client->r.svflags & SVF_MONSTER)
	{
		if (!client->enemy)
			return false;
		if (client->enemy->flags & FL_NOTARGET)
			return false;
	}
	else if (heardit)
	{
		if (client->r.owner->flags & FL_NOTARGET)
			return false;
	}
	else
		return false;

	if (!heardit)
	{
		r = range (self, client);

		if (r == RANGE_FAR)
			return false;

// this is where we would check invisibility

		// is client in an spot too dark to be seen?
//		if (client->light_level <= 5)
//			return false;

		if (!G_Visible (self, client))
		{
			return false;
		}

		if (r == RANGE_NEAR)
		{
			if (client->show_hostile < level.time && !G_InFront (self, client))
			{
				return false;
			}
		}
		else if (r == RANGE_MID)
		{
			if (!G_InFront (self, client))
			{
				return false;
			}
		}

		self->enemy = client;

		if (!noise)
		{
			self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;

			if (!self->enemy->r.client)
			{
				self->enemy = self->enemy->enemy;
				if (!self->enemy->r.client)
				{
					self->enemy = NULL;
					return false;
				}
			}
		}
	}
	else	// heardit
	{
		vec3_t	temp;

		if (self->spawnflags & 1)
		{
			if (!G_Visible (self, client))
				return false;
		}

		VectorSubtract (client->s.origin, self->s.origin, temp);

		if (VectorLength(temp) > 1000)	// too far to hear
		{
			return false;
		}

		// check area portals - if they are different and not connected then we can't hear it
		if (client->r.areanum != self->r.areanum)
			if (!trap_CM_AreasConnected(self->r.areanum, client->r.areanum))
				return false;

		self->ideal_yaw = vectoyaw(temp);
		M_ChangeYaw (self);

		// hunt the sound for a bit; hopefully find the real player
		self->monsterinfo.aiflags |= AI_SOUND_TARGET;
		self->enemy = client;
	}

//
// got one
//
	FoundTarget (self);

	if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight))
		self->monsterinfo.sight (self, self->enemy);

	return true;
}