示例#1
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
}
示例#2
0
文件: bot.cpp 项目: DenMSC/qfusion
void Bot::RegisterVisibleEnemies()
{
    if(G_ISGHOSTING(self) || GS_MatchState() == MATCH_STATE_COUNTDOWN || GS_ShootingDisabled())
        return;

    CheckIsInThinkFrame(__FUNCTION__);

    // Compute look dir before loop
    vec3_t lookDir;
    AngleVectors(self->s.angles, lookDir, nullptr, nullptr);

    float fov = 110.0f + 69.0f * Skill();
    float dotFactor = cosf((float)DEG2RAD(fov / 2));

    struct EntAndDistance
    {
        int entNum;
        float distance;

        EntAndDistance(int entNum_, float distance_): entNum(entNum_), distance(distance_) {}
        bool operator<(const EntAndDistance &that) const { return distance < that.distance; }
    };

    // Do not call inPVS() and G_Visible() for potential targets inside a loop for all clients.
    // In worst case when all bots may see each other we get N^2 traces and PVS tests
    // First, select all candidate targets along with distance to a bot.
    // Then choose not more than BotBrain::maxTrackedEnemies nearest enemies for calling OnEnemyViewed()
    // It may cause data loss (far enemies may have higher logical priority),
    // but in a common good case (when there are few visible enemies) it preserves data,
    // and in the worst case mentioned above it does not act weird from player POV and prevents server hang up.
    // Note: non-client entities also may be candidate targets.
    StaticVector<EntAndDistance, MAX_EDICTS> candidateTargets;

    for (int i = 1; i < game.numentities; ++i)
    {
        edict_t *ent = game.edicts + i;
        if (botBrain.MayNotBeFeasibleEnemy(ent))
            continue;

        // Reject targets quickly by fov
        Vec3 toTarget(ent->s.origin);
        toTarget -= self->s.origin;
        float squareDistance = toTarget.SquaredLength();
        if (squareDistance < 1)
            continue;
        float invDistance = Q_RSqrt(squareDistance);
        toTarget *= invDistance;
        if (toTarget.Dot(lookDir) < dotFactor)
            continue;

        // It seams to be more instruction cache-friendly to just add an entity to a plain array
        // and sort it once after the loop instead of pushing an entity in a heap on each iteration
        candidateTargets.emplace_back(EntAndDistance(ENTNUM(ent), 1.0f / invDistance));
    }

    std::sort(candidateTargets.begin(), candidateTargets.end());

    // Select inPVS/visible targets first to aid instruction cache, do not call callbacks in loop
    StaticVector<edict_t *, MAX_CLIENTS> targetsInPVS;
    StaticVector<edict_t *, MAX_CLIENTS> visibleTargets;

    static_assert(AiBaseEnemyPool::MAX_TRACKED_ENEMIES <= MAX_CLIENTS, "targetsInPVS capacity may be exceeded");

    for (int i = 0, end = std::min(candidateTargets.size(), botBrain.MaxTrackedEnemies()); i < end; ++i)
    {
        edict_t *ent = game.edicts + candidateTargets[i].entNum;
        if (trap_inPVS(self->s.origin, ent->s.origin))
            targetsInPVS.push_back(ent);
    }

    for (auto ent: targetsInPVS)
        if (G_Visible(self, ent))
            visibleTargets.push_back(ent);

    // Call bot brain callbacks on visible targets
    for (auto ent: visibleTargets)
        botBrain.OnEnemyViewed(ent);

    botBrain.AfterAllEnemiesViewed();

    CheckAlertSpots(visibleTargets);
}
示例#3
0
/*
* GS_ThinkPlayerWeapon
*/
int GS_ThinkPlayerWeapon( player_state_t *playerState, int buttons, int msecs, int timeDelta )
{
	firedef_t *firedef;
	qboolean refire = qfalse;

	assert( playerState->stats[STAT_PENDING_WEAPON] >= 0 && playerState->stats[STAT_PENDING_WEAPON] < WEAP_TOTAL );

	if( GS_MatchPaused() )
		return playerState->stats[STAT_WEAPON];

	if( playerState->pmove.pm_type != PM_NORMAL )
	{
		playerState->weaponState = WEAPON_STATE_READY;
		playerState->stats[STAT_PENDING_WEAPON] = playerState->stats[STAT_WEAPON] = WEAP_NONE;
		playerState->stats[STAT_WEAPON_TIME] = 0;
		return playerState->stats[STAT_WEAPON];
	}

	if( playerState->pmove.stats[PM_STAT_NOUSERCONTROL] > 0 )
		buttons = 0;

	if( !msecs )
		goto done;

	if( playerState->stats[STAT_WEAPON_TIME] > 0 )
		playerState->stats[STAT_WEAPON_TIME] -= msecs;
	else
		playerState->stats[STAT_WEAPON_TIME] = 0;

	firedef = GS_FiredefForPlayerState( playerState, playerState->stats[STAT_WEAPON] );

	// during cool-down time it can shoot again or go into reload time
	if( playerState->weaponState == WEAPON_STATE_REFIRE || playerState->weaponState == WEAPON_STATE_REFIRESTRONG )
	{
		int last_firemode;

		if( playerState->stats[STAT_WEAPON_TIME] > 0 )
			goto done;

		last_firemode = ( playerState->weaponState == WEAPON_STATE_REFIRESTRONG ) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK;
		if( last_firemode == firedef->fire_mode )
			refire = qtrue;

		playerState->weaponState = WEAPON_STATE_READY;
	}

	// nothing can be done during reload time
	if( playerState->weaponState == WEAPON_STATE_RELOADING )
	{
		if( playerState->stats[STAT_WEAPON_TIME] > 0 )
			goto done;

		playerState->weaponState = WEAPON_STATE_READY;
	}

	if( playerState->weaponState == WEAPON_STATE_NOAMMOCLICK )
	{
		if( playerState->stats[STAT_WEAPON_TIME] > 0 )
			goto done;

		if( playerState->stats[STAT_WEAPON] != playerState->stats[STAT_PENDING_WEAPON] )
			playerState->weaponState = WEAPON_STATE_READY;
	}

	// there is a weapon to be changed
	if( playerState->stats[STAT_WEAPON] != playerState->stats[STAT_PENDING_WEAPON] )
	{
		if( ( playerState->weaponState == WEAPON_STATE_READY ) ||
			( playerState->weaponState == WEAPON_STATE_DROPPING ) ||
			( playerState->weaponState == WEAPON_STATE_ACTIVATING ) )
		{
			if( playerState->weaponState != WEAPON_STATE_DROPPING )
			{
				playerState->weaponState = WEAPON_STATE_DROPPING;
				playerState->stats[STAT_WEAPON_TIME] += firedef->weapondown_time;

				if( firedef->weapondown_time )
					module_PredictedEvent( playerState->POVnum, EV_WEAPONDROP, 0 );
			}
		}
	}

	// do the change
	if( playerState->weaponState == WEAPON_STATE_DROPPING )
	{
		if( playerState->stats[STAT_WEAPON_TIME] > 0 )
			goto done;

		playerState->stats[STAT_WEAPON] = playerState->stats[STAT_PENDING_WEAPON];

		// update the firedef
		firedef = GS_FiredefForPlayerState( playerState, playerState->stats[STAT_WEAPON] );
		playerState->weaponState = WEAPON_STATE_ACTIVATING;
		playerState->stats[STAT_WEAPON_TIME] += firedef->weaponup_time;
		module_PredictedEvent( playerState->POVnum, EV_WEAPONACTIVATE, playerState->stats[STAT_WEAPON] );
	}

	if( playerState->weaponState == WEAPON_STATE_ACTIVATING )
	{
		if( playerState->stats[STAT_WEAPON_TIME] > 0 )
			goto done;

		playerState->weaponState = WEAPON_STATE_READY;
	}

	if( ( playerState->weaponState == WEAPON_STATE_READY ) || ( playerState->weaponState == WEAPON_STATE_NOAMMOCLICK ) )
	{
		if( playerState->stats[STAT_WEAPON_TIME] > 0 )
			goto done;

		if( !GS_ShootingDisabled() )
		{
			if( buttons & BUTTON_ATTACK )
			{
				if( GS_CheckAmmoInWeapon( playerState, playerState->stats[STAT_WEAPON] ) )
				{
					playerState->weaponState = WEAPON_STATE_FIRING;
				}
				else
				{
					// player has no ammo nor clips
					if( playerState->weaponState == WEAPON_STATE_NOAMMOCLICK )
					{
						playerState->weaponState = WEAPON_STATE_RELOADING;
						playerState->stats[STAT_WEAPON_TIME] += NOAMMOCLICK_AUTOSWITCH;
						if( playerState->stats[STAT_PENDING_WEAPON] == playerState->stats[STAT_WEAPON] )
							playerState->stats[STAT_PENDING_WEAPON] = GS_SelectBestWeapon( playerState );
					}
					else
					{
						playerState->weaponState = WEAPON_STATE_NOAMMOCLICK;
						playerState->stats[STAT_WEAPON_TIME] += NOAMMOCLICK_PENALTY;
						module_PredictedEvent( playerState->POVnum, EV_NOAMMOCLICK, 0 );
						goto done;
					}
				}
			}
			// gunblade auto attack is special
			else if( playerState->stats[STAT_WEAPON] == WEAP_GUNBLADE &&
				playerState->pmove.stats[PM_STAT_NOUSERCONTROL] <= 0 &&
				playerState->pmove.stats[PM_STAT_NOAUTOATTACK] <= 0 &&
				GS_CheckBladeAutoAttack( playerState, timeDelta ) )
			{
				firedef = &GS_GetWeaponDef( WEAP_GUNBLADE )->firedef_weak;
				playerState->weaponState = WEAPON_STATE_FIRING;
			}
		}
	}

	if( playerState->weaponState == WEAPON_STATE_FIRING )
	{
		int parm = playerState->stats[STAT_WEAPON];
		if( firedef->fire_mode == FIRE_MODE_STRONG )
			parm |= EV_INVERSE;

		playerState->stats[STAT_WEAPON_TIME] += firedef->reload_time;
		if( firedef->fire_mode == FIRE_MODE_STRONG )
			playerState->weaponState = WEAPON_STATE_REFIRESTRONG;
		else
			playerState->weaponState = WEAPON_STATE_REFIRE;

		if( refire && firedef->smooth_refire )
			module_PredictedEvent( playerState->POVnum, EV_SMOOTHREFIREWEAPON, parm );
		else
			module_PredictedEvent( playerState->POVnum, EV_FIREWEAPON, parm );

		// waste ammo
		if( !GS_InfiniteAmmo() )
		{
			if( firedef->ammo_id != AMMO_NONE && firedef->usage_count )
				playerState->inventory[firedef->ammo_id] -= firedef->usage_count;
		}
	}
done:
	return playerState->stats[STAT_WEAPON];
}
示例#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;

	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
}