コード例 #1
0
ファイル: g_chase.cpp プロジェクト: Picmip/qfusion
/*
* G_Chase_IsValidTarget
*/
static bool G_Chase_IsValidTarget( edict_t *ent, edict_t *target, bool teamonly ) {
	if( !ent || !target ) {
		return false;
	}

	if( !target->r.inuse || !target->r.client || trap_GetClientState( PLAYERNUM( target ) ) < CS_SPAWNED ) {
		return false;
	}

	if( target->s.team < TEAM_PLAYERS || target->s.team >= GS_MAX_TEAMS || target == ent ) {
		return false;
	}

	if( teamonly && !ent->r.client->teamstate.is_coach && G_ISGHOSTING( target ) ) {
		return false;
	}

	if( teamonly && target->s.team != ent->s.team ) {
		return false;
	}

	if( G_ISGHOSTING( target ) && !target->deadflag && target->s.team != TEAM_SPECTATOR ) {
		return false; // ghosts that are neither dead, nor speccing (probably originating from gt-specific rules)

	}
	return true;
}
コード例 #2
0
float BOT_DMclass_PlayerWeight( edict_t *self, edict_t *enemy )
{
	bool rage_mode = false;

	if( !enemy || enemy == self )
		return 0;

	if( G_ISGHOSTING( enemy ) || enemy->flags & (FL_NOTARGET|FL_BUSY) )
		return 0;

	if( self->r.client->ps.inventory[POWERUP_QUAD] || self->r.client->ps.inventory[POWERUP_SHELL] )
		rage_mode = true;

	// don't fight against powerups.
	if( enemy->r.client && ( enemy->r.client->ps.inventory[POWERUP_QUAD] || enemy->r.client->ps.inventory[POWERUP_SHELL] ) )
		return 0.2;

	//if not team based give some weight to every one
	if( GS_TeamBasedGametype() && ( enemy->s.team == self->s.team ) )
		return 0;

	// if having EF_CARRIER we can assume it's someone important
	if( enemy->s.effects & EF_CARRIER )
		return 2.0f;

	if( enemy == self->ai->last_attacker )
		return rage_mode ? 4.0f : 1.0f;

	return rage_mode ? 4.0f : 0.3f;
}
コード例 #3
0
ファイル: p_view.cpp プロジェクト: codetwister/qfusion
/*
* G_ClientEndSnapFrame
* 
* Called for each player at the end of the server frame
* and right after spawning
*/
void G_ClientEndSnapFrame( edict_t *ent )
{
	gclient_t *client;
	int i;

	if( trap_GetClientState( PLAYERNUM( ent ) ) < CS_SPAWNED )
		return;

	client = ent->r.client;

	// set fov
	if( !client->ps.pmove.stats[PM_STAT_ZOOMTIME] )
		client->ps.fov = ent->r.client->fov;
	else
	{
		float frac = (float)client->ps.pmove.stats[PM_STAT_ZOOMTIME] / (float)ZOOMTIME;
		client->ps.fov = client->fov - ( (float)( client->fov - client->zoomfov ) * frac );
	}

	// If the end of unit layout is displayed, don't give
	// the player any normal movement attributes
	if( GS_MatchState() >= MATCH_STATE_POSTMATCH )
	{
		G_SetClientStats( ent );
	}
	else
	{
		if( G_IsDead( ent ) && !level.gametype.customDeadBodyCam )
		{
			G_Client_DeadView( ent );
		}

		G_PlayerWorldEffects( ent ); // burn from lava, etc
		G_ClientDamageFeedback( ent ); // show damage taken along the snap
		G_SetClientStats( ent );
		G_SetClientEffects( ent );
		G_SetClientSound( ent );
		G_SetClientFrame( ent );

		client->ps.plrkeys = client->resp.snap.plrkeys;
	}

	G_ReleaseClientPSEvent( client );

	// set the delta angle
	for( i = 0; i < 3; i++ )
		client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i];

	// this is pretty hackish. We exploit the fact that servers *do not* transmit
	// origin2/old_origin for ET_PLAYER/ET_CORPSE entities, and we use it for sending the player velocity
	if( !G_ISGHOSTING( ent ) )
	{
		ent->r.svflags |= SVF_TRANSMITORIGIN2;
		VectorCopy( ent->velocity, ent->s.origin2 );
	}
	else
		ent->r.svflags &= ~SVF_TRANSMITORIGIN2;
}
コード例 #4
0
void G_Gametype_GENERIC_ClientRespawn( edict_t *self, int old_team, int new_team )
{
	int i;
	gclient_t *client = self->r.client;
	gs_weapon_definition_t *weapondef;

	if( G_ISGHOSTING( self ) )
		return;

	//give default items
	if( self->s.team != TEAM_SPECTATOR )
	{
		if( GS_Instagib() )
		{
			client->ps.inventory[WEAP_INSTAGUN] = 1;
			client->ps.inventory[AMMO_INSTAS] = 1;
			client->ps.inventory[AMMO_WEAK_INSTAS] = 1;
		}
		else
		{
			if( GS_MatchState() <= MATCH_STATE_WARMUP )
			{
				for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ )
				{
					if( i == WEAP_INSTAGUN )  // dont add instagun...
						continue;

					weapondef = GS_GetWeaponDef( i );
					client->ps.inventory[i] = 1;
					if( weapondef->firedef_weak.ammo_id )
						client->ps.inventory[weapondef->firedef_weak.ammo_id] = weapondef->firedef_weak.ammo_max;
					if( weapondef->firedef.ammo_id )
						client->ps.inventory[weapondef->firedef.ammo_id] = weapondef->firedef.ammo_max;
				}

				client->resp.armor = GS_Armor_MaxCountForTag( ARMOR_YA );
			}
			else
			{
				weapondef = GS_GetWeaponDef( WEAP_GUNBLADE );
				client->ps.inventory[WEAP_GUNBLADE] = 1;
				client->ps.inventory[AMMO_GUNBLADE] = weapondef->firedef.ammo_max;;
				client->ps.inventory[AMMO_WEAK_GUNBLADE] = 0;
			}
		}
	}

	// select rocket launcher if available
	if( GS_CheckAmmoInWeapon( &client->ps, WEAP_ROCKETLAUNCHER ) )
		client->ps.stats[STAT_PENDING_WEAPON] = WEAP_ROCKETLAUNCHER;
	else
		client->ps.stats[STAT_PENDING_WEAPON] = GS_SelectBestWeapon( &client->ps );

	// add a teleportation effect
	if( self->r.solid != SOLID_NOT )
		G_RespawnEffect( self );
}
コード例 #5
0
ファイル: g_items.cpp プロジェクト: Clever-Boy/qfusion
/*
* Touch_Item
*/
void Touch_Item( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	bool taken;
	const gsitem_t *item = ent->item;

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

	if( !( other->r.client->ps.pmove.stats[PM_STAT_FEATURES] & PMFEAT_ITEMPICK ) )
		return;

	if( !item || !( item->flags & ITFLAG_PICKABLE ) )
		return; // not a grabbable item

	if( !G_Gametype_CanPickUpItem( item ) )
		return;

	taken = G_PickupItem( other, item, ent->spawnflags, ent->count, ent->invpak );

	if( !( ent->spawnflags & ITEM_TARGETS_USED ) )
	{
		G_UseTargets( ent, other );
		ent->spawnflags |= ITEM_TARGETS_USED;
	}

	if( !taken )
		return;

	if( ent->spawnflags & ITEM_TIMED )
		ent->r.owner = other;

	// flash the screen
	G_AddPlayerStateEvent( other->r.client, PSEV_PICKUP, ( item->flags & IT_WEAPON ? item->tag : 0 ) );

	G_AwardPlayerPickup( other, ent );

	// for messages
	other->r.client->teamstate.last_pickup = ent;

	// show icon and name on status bar
	other->r.client->ps.stats[STAT_PICKUP_ITEM] = item->tag;
	other->r.client->resp.pickup_msg_time = level.time + 3000;

	if( ent->attenuation )
		Touch_ItemSound( other, item );

	if( !( ent->spawnflags & DROPPED_ITEM ) && G_Gametype_CanRespawnItem( item ) )
	{
		if( (item->type & IT_WEAPON ) && GS_RaceGametype() )
			return; // weapons stay in race
		SetRespawn( ent, G_Gametype_RespawnTimeForItem( item ) );
		return;
	}
	G_FreeEdict( ent );
}
コード例 #6
0
ファイル: ai_base_ai.cpp プロジェクト: Picmip/qfusion
void Ai::Frame() {
	// Call super method first
	AiFrameAwareUpdatable::Frame();

	if( !G_ISGHOSTING( self ) ) {
		entityPhysicsState->UpdateFromEntity( self );
	}

	// Call brain Update() (Frame() and, maybe Think())
	aiBaseBrain->Update();

	if( level.spawnedTimeStamp + 5000 > game.realtime || !level.canSpawnEntities ) {
		self->nextThink = level.time + game.snapFrameTime;
		return;
	}
}
コード例 #7
0
ファイル: g_items.c プロジェクト: hettoo/racesow
/*
* Touch_Item
*/
void Touch_Item( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags )
{
	qboolean taken;

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

	if( !( other->r.client->ps.pmove.stats[PM_STAT_FEATURES] & PMFEAT_ITEMPICK ) )
		return;

	if( !ent->item || !( ent->item->flags & ITFLAG_PICKABLE ) )
		return; // not a grabbable item

	if( !G_Gametype_CanPickUpItem( ent->item ) )
		return;

	taken = G_PickupItem( ent, other );

	if( !( ent->spawnflags & ITEM_TARGETS_USED ) )
	{
		G_UseTargets( ent, other );
		ent->spawnflags |= ITEM_TARGETS_USED;
	}

	if( !taken )
		return;

	// flash the screen
	G_AddPlayerStateEvent( other->r.client, PSEV_PICKUP, ( ent->item->flags & IT_WEAPON ? ent->item->tag : 0 ) );

	G_AwardPlayerPickup( other, ent );

	// for messages
	other->r.client->teamstate.last_pickup = ent;

	// show icon and name on status bar
	other->r.client->ps.stats[STAT_PICKUP_ITEM] = ent->item->tag;
	other->r.client->resp.pickup_msg_time = level.time + 3000;

	if( ent->attenuation )
		Touch_ItemSound( other, ent->item );

	if( !( ent->spawnflags & ( DROPPED_ITEM | DROPPED_PLAYER_ITEM ) ) && G_Gametype_CanRespawnItem( ent->item ) )
		SetRespawn( ent, G_Gametype_RespawnTimeForItem( ent->item ) );
	else
		G_FreeEdict( ent );
}
コード例 #8
0
ファイル: ai_main.cpp プロジェクト: MGXRace/racesow
void AI_UpdateStatus( edict_t *self )
{
	if( !G_ISGHOSTING( self ) )
	{
		AI_ResetWeights( self->ai );

		self->ai->status.moveTypesMask = self->ai->pers.moveTypesMask;

		if( !GT_asCallBotStatus( self ) )
			self->ai->pers.UpdateStatus( self );

		self->ai->statusUpdateTimeout = level.time + AI_STATUS_TIMEOUT;

		// no cheating with moveTypesMask
		self->ai->status.moveTypesMask &= self->ai->pers.moveTypesMask;
	}
}
コード例 #9
0
ファイル: g_chase.cpp プロジェクト: MGXRace/racesow
/*
* G_Chase_IsValidTarget
*/
static bool G_Chase_IsValidTarget( edict_t *ent, edict_t *target, bool teamonly )
{
	if( !ent || !target )
		return false;

	if( !target->r.inuse || !target->r.client || trap_GetClientState( PLAYERNUM( target ) ) < CS_SPAWNED )
		return false;

	if( target->s.team < TEAM_PLAYERS || target->s.team >= GS_MAX_TEAMS || target == ent )
		return false;

	if( teamonly && !ent->r.client->teamstate.is_coach && G_ISGHOSTING( target ) )
		return false;

	if( teamonly && target->s.team != ent->s.team )
		return false;

	return true;
}
コード例 #10
0
ファイル: g_items.c プロジェクト: hettoo/racesow
/*
* G_PickupItem
*/
qboolean G_PickupItem( edict_t *ent, edict_t *other )
{
	gsitem_t	*it;
	qboolean taken = qfalse;

	if( !ent || !other )
		return qfalse;

	if( other->r.client && G_ISGHOSTING( other ) )
		return qfalse;

	if( !ent->item || !( ent->item->flags & ITFLAG_PICKABLE ) )
		return qfalse;

	it = ent->item;

	if( it->type & IT_WEAPON )
	{
		taken = Pickup_Weapon( ent, other );
	}
	else if( it->type & IT_AMMO )
	{
		taken = Pickup_Ammo( ent, other );
	}
	else if( it->type & IT_ARMOR )
	{
		taken = Pickup_Armor( ent, other );
	}
	else if( it->type & IT_HEALTH )
	{
		taken = Pickup_Health( ent, other );
	}
	else if( it->type & IT_POWERUP )
	{
		taken = Pickup_Powerup( ent, other );
	}

	if( taken && other->r.client )
		G_Gametype_ScoreEvent( other->r.client, "pickup", it->classname );

	return taken;
}
コード例 #11
0
ファイル: g_weapon.cpp プロジェクト: Picmip/qfusion
/*
* G_Laser_Think
*/
static void G_Laser_Think( edict_t *ent ) {
	edict_t *owner;

	if( ent->s.ownerNum < 1 || ent->s.ownerNum > gs.maxclients ) {
		G_FreeEdict( ent );
		return;
	}

	owner = &game.edicts[ent->s.ownerNum];

	if( G_ISGHOSTING( owner ) || owner->s.weapon != WEAP_LASERGUN ||
		trap_GetClientState( PLAYERNUM( owner ) ) < CS_SPAWNED ||
		( owner->r.client->ps.weaponState != WEAPON_STATE_REFIRESTRONG
		  && owner->r.client->ps.weaponState != WEAPON_STATE_REFIRE ) ) {
		G_HideLaser( ent );
		return;
	}

	ent->nextThink = level.time + 1;
}
コード例 #12
0
ファイル: ai_base_ai.cpp プロジェクト: Picmip/qfusion
void Ai::Think() {
	if( !G_ISGHOSTING( self ) ) {
		// TODO: Check whether we are camping/holding a spot
		bool checkBlocked = true;
		if( !self->groundentity ) {
			checkBlocked = false;
		} else if( self->groundentity->use == Use_Plat && VectorLengthSquared( self->groundentity->velocity ) > 10 * 10 ) {
			checkBlocked = false;
		}

		if( checkBlocked && VectorLengthSquared( self->velocity ) > 30 * 30 ) {
			blockedTimeoutAt = level.time + BLOCKED_TIMEOUT;
		}

		// if completely stuck somewhere
		if( blockedTimeoutAt < level.time ) {
			OnBlockedTimeout();
			return;
		}
	}
}
コード例 #13
0
ファイル: g_items.cpp プロジェクト: Clever-Boy/qfusion
/*
* G_PickupItem
*/
bool G_PickupItem( edict_t *other, const gsitem_t *it, int flags, int count, const int *invpack )
{
	bool taken = false;

	if( other->r.client && G_ISGHOSTING( other ) )
		return false;

	if( !it || !( it->flags & ITFLAG_PICKABLE ) )
		return false;

	if( it->type & IT_WEAPON )
	{
		taken = Pickup_Weapon( other, it, flags, count );
	}
	else if( it->type & IT_AMMO )
	{
		taken = Pickup_Ammo( other, it, count, invpack );
	}
	else if( it->type & IT_ARMOR )
	{
		taken = Pickup_Armor( other, it );
	}
	else if( it->type & IT_HEALTH )
	{
		taken = Pickup_Health( other, it, flags );
	}
	else if( it->type & IT_POWERUP )
	{
		taken = Pickup_Powerup( other, it, flags, count );
	}

	if( taken && other->r.client )
		G_Gametype_ScoreEvent( other->r.client, "pickup", it->classname );

	return taken;
}
コード例 #14
0
ファイル: ai_main.cpp プロジェクト: MGXRace/racesow
//==========================================
// AI_Think
// think funtion for AIs
//==========================================
void AI_Think( edict_t *self )
{
	if( !self->ai || self->ai->type == AI_INACTIVE )
		return;

	if( level.spawnedTimeStamp + 5000 > game.realtime || !level.canSpawnEntities )
	{
		self->nextThink = level.time + game.snapFrameTime;
		return;
	}

	// check for being blocked
	if( !G_ISGHOSTING( self ) )
	{
		AI_CategorizePosition( self );

		if( VectorLengthFast( self->velocity ) > 37 )
			self->ai->blocked_timeout = level.time + 10000;

		// if completely stuck somewhere
		if( self->ai->blocked_timeout < level.time )
		{
			self->ai->pers.blockedTimeout( self );
			return;
		}
	}

	//update status information to feed up ai
	if( self->ai->statusUpdateTimeout <= level.time )
		AI_UpdateStatus( self );

	if( AI_NodeHasTimedOut( self ) )
		AI_ClearGoal( self );

	if( self->ai->goal_node == NODE_INVALID )
		AI_PickLongRangeGoal( self );

	//if( self == level.think_client_entity )
	AI_PickShortRangeGoal( self );

	self->ai->pers.RunFrame( self );

	// Show the path
	if( nav.debugMode && bot_showpath->integer && self->ai->goal_node != NODE_INVALID )
	{
		// only draw the path of those bots which are being chased
		edict_t *chaser;
		bool chaserFound = false;

		for( chaser = game.edicts + 1; ENTNUM( chaser ) < gs.maxclients; chaser++ )
		{
			if( chaser->r.client->resp.chase.active && chaser->r.client->resp.chase.target == ENTNUM( self ) )
			{
				AITools_DrawPath( self, self->ai->goal_node );
				chaserFound = true;
			}
		}

		if( !chaserFound && game.numBots == 1 )
			AITools_DrawPath( self, self->ai->goal_node );
	}
}
コード例 #15
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);
}
コード例 #16
0
ファイル: ai_main.cpp プロジェクト: MGXRace/racesow
//==========================================
// 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;
	}
}
コード例 #17
0
ファイル: ai_main.cpp プロジェクト: MGXRace/racesow
//==========================================
// 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
}
コード例 #18
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
}
コード例 #19
0
//==========================================
// BOT_DMclass_RunFrame
// States Machine & call client movement
//==========================================
static void BOT_DMclass_RunFrame( edict_t *self )
{
	usercmd_t ucmd;
	float weapon_quality;
	bool inhibitCombat = false;
	int i;

	if( G_ISGHOSTING( self ) )
	{
		BOT_DMclass_GhostingFrame( self );
		return;
	}

	memset( &ucmd, 0, sizeof( ucmd ) );

	//get ready if in the game
	if( GS_MatchState() <= MATCH_STATE_WARMUP && !level.ready[PLAYERNUM(self)]
	&& self->r.client->teamstate.timeStamp + 4000 < level.time )
		G_Match_Ready( self );

	if( level.gametype.dummyBots || bot_dummy->integer )
	{
		self->r.client->level.last_activity = level.time;
	}
	else
	{
		BOT_DMclass_FindEnemy( self );

		weapon_quality = BOT_DMclass_ChooseWeapon( self );

		inhibitCombat = ( AI_CurrentLinkType( self ) & (LINK_JUMPPAD|LINK_JUMP|LINK_ROCKETJUMP) ) != 0 ? true : false;

		if( self->enemy && weapon_quality >= 0.3 && !inhibitCombat ) // don't fight with bad weapons
		{
			if( BOT_DMclass_FireWeapon( self, &ucmd ) )
				self->ai->state_combat_timeout = level.time + AI_COMBATMOVE_TIMEOUT;
		}

		if( inhibitCombat )
			self->ai->state_combat_timeout = 0;

		if( self->ai->state_combat_timeout > level.time )
		{
			BOT_DMclass_CombatMovement( self, &ucmd );
		}
		else
		{
			BOT_DMclass_Move( self, &ucmd );
		}

		//set up for pmove
		for( i = 0; i < 3; i++ )
			ucmd.angles[i] = ANGLE2SHORT( self->s.angles[i] ) - self->r.client->ps.pmove.delta_angles[i];

		VectorSet( self->r.client->ps.pmove.delta_angles, 0, 0, 0 );
	}

	// set approximate ping and show values
	ucmd.msec = game.frametime;
	ucmd.serverTimeStamp = game.serverTime;

	ClientThink( self, &ucmd, 0 );
	self->nextThink = level.time + 1;

	BOT_DMclass_VSAYmessages( self );
}
コード例 #20
0
ファイル: g_frame.cpp プロジェクト: ShaitanShootout/BM
/*
* G_EdictsAddSnapEffects
* add effects based on accumulated info along the server frame
*/
static void G_SnapEntities( void )
{
	edict_t *ent;
	int i;
	vec3_t dir, origin;

	for( i = 0, ent = &game.edicts[0]; i < game.numentities; i++, ent++ )
	{
		if( !ent->r.inuse || ( ent->r.svflags & SVF_NOCLIENT ) )
			continue;

		if( ent->s.type == ET_PARTICLES ) // particles use a special configuration
		{
			ent->s.frame = ent->particlesInfo.speed;
			ent->s.modelindex = ent->particlesInfo.shaderIndex;
			ent->s.modelindex2 = ent->particlesInfo.spread;
			ent->s.counterNum = ent->particlesInfo.time;
			ent->s.weapon = ent->particlesInfo.frequency;

			ent->s.effects = ent->particlesInfo.size & 0xFF;
			if( ent->particlesInfo.spherical )
				ent->s.effects |= ( 1<<8 );
			if( ent->particlesInfo.bounce )
				ent->s.effects |= ( 1<<9 );
			if( ent->particlesInfo.gravity )
				ent->s.effects |= ( 1<<10 );
			if( ent->particlesInfo.expandEffect )
				ent->s.effects |= ( 1<<11 );
			if( ent->particlesInfo.shrinkEffect )
				ent->s.effects |= ( 1<<11 );

			GClip_LinkEntity( ent );

			continue;
		}

		if( ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE )
		{
			// this is pretty hackish. We exploit the fact that 0.5 servers *do not* transmit
			// origin2/old_origin for ET_PLAYER/ET_CORPSE entities, and we use it for sending the player velocity
			if( !G_ISGHOSTING( ent ) )
			{
				ent->r.svflags |= SVF_TRANSMITORIGIN2;
				VectorCopy( ent->velocity, ent->s.origin2 );
			}
			else
				ent->r.svflags &= ~SVF_TRANSMITORIGIN2;
		}

		if( ISEVENTENTITY( ent ) || G_ISGHOSTING( ent ) || !ent->takedamage )
			continue;

		// types which can have accumulated damage effects
		if( ( ent->s.type == ET_GENERIC || ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE ) ) // doors don't bleed
		{
			// Until we get a proper damage saved effect, we accumulate both into the blood fx
			// so, at least, we don't send 2 entities where we can send one
			ent->snap.damage_taken += ent->snap.damage_saved;
			//ent->snap.damage_saved = 0;

			//spawn accumulated damage
			if( ent->snap.damage_taken && !( ent->flags & FL_GODMODE ) && HEALTH_TO_INT( ent->health ) > 0 )
			{
				edict_t *event;
				float damage = ent->snap.damage_taken;
				if( damage > 120 )
					damage = 120;

				VectorCopy( ent->snap.damage_dir, dir );
				VectorNormalize( dir );
				VectorAdd( ent->s.origin, ent->snap.damage_at, origin );

				if( ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE )
				{
					event = G_SpawnEvent( EV_BLOOD, DirToByte( dir ), origin );
					event->s.damage = HEALTH_TO_INT( damage );
					event->s.ownerNum = i; // set owner

					// ET_PLAYERS can also spawn sound events
					if( ent->s.type == ET_PLAYER )
					{
						// play an apropriate pain sound
						if( level.time >= ent->pain_debounce_time )
						{
							// see if it should pain for a FALL or for damage received
							if( ent->snap.damage_fall )
							{
								ent->pain_debounce_time = level.time + 200;
							}
							else if( !G_IsDead( ent ) )
							{
								if( ent->r.client->ps.inventory[POWERUP_SHELL] > 0 )
									G_AddEvent( ent, EV_PAIN, PAIN_WARSHELL, true );
								else if( ent->health <= 20 )
									G_AddEvent( ent, EV_PAIN, PAIN_20, true );
								else if( ent->health <= 35 )
									G_AddEvent( ent, EV_PAIN, PAIN_30, true );
								else if( ent->health <= 60 )
									G_AddEvent( ent, EV_PAIN, PAIN_60, true );
								else
									G_AddEvent( ent, EV_PAIN, PAIN_100, true );

								ent->pain_debounce_time = level.time + 400;
							}
						}
					}
				}
				else
				{
					event = G_SpawnEvent( EV_SPARKS, DirToByte( dir ), origin );
					event->s.damage = HEALTH_TO_INT( damage );
				}
			}
		}
	}
}
コード例 #21
0
ファイル: g_gameteams.cpp プロジェクト: tenght/qfusion
static void UpdatePoint( edict_t *who )
{
	vec3_t angles, forward, diff;
	trace_t trace;
	edict_t *ent, *ent_best = NULL;
	int i, j;
	float value, value_best = 0.35f; // if nothing better is found, print nothing
	gclient_t *client = who->r.client;
	vec3_t boxpoints[8], viewpoint;

	AngleVectors( client->ps.viewangles, forward, NULL, NULL );
	VectorCopy( who->s.origin, viewpoint );
	viewpoint[2] += who->viewheight;

	for( i = 0; i < game.numentities; i++ )
	{
		ent = game.edicts + i;

		if( !ent->r.inuse || !ent->s.modelindex || ent == who )
			continue;
		if( G_ISGHOSTING( ent ) )
			continue;
		if( ent->s.type != ET_PLAYER && ent->s.type != ET_ITEM )
			continue;

		VectorSubtract( ent->s.origin, viewpoint, angles );
		VectorNormalize( angles );
		VectorSubtract( forward, angles, diff );
		for( j = 0; j < 3; j++ ) if( diff[j] < 0 ) diff[j] = -diff[j];
		value = VectorLengthFast( diff );

		if( value < value_best )
		{
			BuildBoxPoints( boxpoints, ent->s.origin, ent->r.mins, ent->r.maxs );
			for( j = 0; j < 8; j++ )
			{
				G_Trace( &trace, viewpoint, vec3_origin, vec3_origin, boxpoints[j], who, MASK_OPAQUE );
				if( trace.fraction == 1 )
				{
					value_best = value;
					ent_best = ent;
					break;
				}
			}
		}
	}

	if( ent_best != NULL )
	{
		point = ent_best;
		VectorCopy( ent_best->s.origin, point_location );
	}
	else
	{
		vec3_t dest;

		VectorMA( viewpoint, 8192, forward, dest );
		G_Trace( &trace, viewpoint, vec3_origin, vec3_origin, dest, who, MASK_OPAQUE );

		point = NULL;
		VectorCopy( trace.endpos, point_location );
	}
}
コード例 #22
0
ファイル: p_client.cpp プロジェクト: Clever-Boy/qfusion
/*
* ClientUserinfoChanged
* called whenever the player updates a userinfo variable.
* 
* The game can override any of the settings in place
* (forcing skins or names, etc) before copying it off.
*/
void ClientUserinfoChanged( edict_t *ent, char *userinfo )
{
	char *s;
	char oldname[MAX_INFO_VALUE];
	gclient_t *cl;

	int rgbcolor, i;

	assert( ent && ent->r.client );
	assert( userinfo && Info_Validate( userinfo ) );

	// check for malformed or illegal info strings
	if( !Info_Validate( userinfo ) )
	{
		trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Invalid userinfo" );
		return;
	}

	cl = ent->r.client;

	// ip
	s = Info_ValueForKey( userinfo, "ip" );
	if( !s )
	{
		trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Server didn't provide client IP" );
		return;
	}

	Q_strncpyz( cl->ip, s, sizeof( cl->ip ) );

	// socket
	s = Info_ValueForKey( userinfo, "socket" );
	if( !s )
	{
		trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Server didn't provide client socket" );
		return;
	}

	Q_strncpyz( cl->socket, s, sizeof( cl->socket ) );

	// color
	s = Info_ValueForKey( userinfo, "color" );
	if( s )
		rgbcolor = COM_ReadColorRGBString( s );
	else
		rgbcolor = -1;

	if( rgbcolor != -1 )
	{
		rgbcolor = COM_ValidatePlayerColor( rgbcolor );
		Vector4Set( cl->color, COLOR_R( rgbcolor ), COLOR_G( rgbcolor ), COLOR_B( rgbcolor ), 255 );
	}
	else
	{
		Vector4Set( cl->color, 255, 255, 255, 255 );
	}

	// set name, it's validated and possibly changed first
	Q_strncpyz( oldname, cl->netname, sizeof( oldname ) );
	G_SetName( ent, Info_ValueForKey( userinfo, "name" ) );
	if( oldname[0] && Q_stricmp( oldname, cl->netname ) && !cl->isTV && !CheckFlood( ent, false ) )
		G_PrintMsg( NULL, "%s%s is now known as %s%s\n", oldname, S_COLOR_WHITE, cl->netname, S_COLOR_WHITE );
	if( !Info_SetValueForKey( userinfo, "name", cl->netname ) )
	{
		trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (name)" );
		return;
	}

	// clan tag
	G_SetClan( ent, Info_ValueForKey( userinfo, "clan" ) );

	// handedness
	s = Info_ValueForKey( userinfo, "hand" );
	if( !s )
		cl->hand = 2;
	else
		cl->hand = bound( atoi( s ), 0, 2 );

	// handicap
	s = Info_ValueForKey( userinfo, "handicap" );
	if( s )
	{
		i = atoi( s );

		if( i > 90 || i < 0 )
		{
			G_PrintMsg( ent, "Handicap must be defined in the [0-90] range.\n" );
			cl->handicap = 0;
		}
		else
		{
			cl->handicap = i;
		}
	}

	s = Info_ValueForKey( userinfo, "cg_movementStyle" );
	if( s )
	{
		i = bound( atoi( s ), 0, GS_MAXBUNNIES - 1 );
		if( trap_GetClientState( PLAYERNUM(ent) ) < CS_SPAWNED )
		{
			if( i != cl->movestyle )
				cl->movestyle = cl->movestyle_latched = i;
		}
		else if( cl->movestyle_latched != cl->movestyle )
		{
			G_PrintMsg( ent, "A movement style change is already in progress. Please wait.\n" );
		}
		else if( i != cl->movestyle_latched )
		{
			cl->movestyle_latched = i;
			if( cl->movestyle_latched != cl->movestyle )
			{
				edict_t *switcher;

				switcher = G_Spawn();
				switcher->think = think_MoveTypeSwitcher;
				switcher->nextThink = level.time + 10000;
				switcher->s.ownerNum = ENTNUM( ent );
				G_PrintMsg( ent, "Movement style will change in 10 seconds.\n" );
			}
		}
	}

	// update the movement features depending on the movestyle
	if( !G_ISGHOSTING( ent ) && g_allow_bunny->integer )
	{
		if( cl->movestyle == GS_CLASSICBUNNY )
			cl->ps.pmove.stats[PM_STAT_FEATURES] &= ~PMFEAT_FWDBUNNY;
		else
			cl->ps.pmove.stats[PM_STAT_FEATURES] |= PMFEAT_FWDBUNNY;
	}

	s = Info_ValueForKey( userinfo, "cg_noAutohop" );
	if( s && s[0] )
	{
		if( atoi( s ) != 0 )
			cl->ps.pmove.stats[PM_STAT_FEATURES] &= ~PMFEAT_CONTINOUSJUMP;
		else
			cl->ps.pmove.stats[PM_STAT_FEATURES] |= PMFEAT_CONTINOUSJUMP;
	}

#ifdef UCMDTIMENUDGE
	s = Info_ValueForKey( userinfo, "cl_ucmdTimeNudge" );
	if( !s )
	{
		cl->ucmdTimeNudge = 0;
	}
	else
	{
		cl->ucmdTimeNudge = atoi( s );
		clamp( cl->ucmdTimeNudge, -MAX_UCMD_TIMENUDGE, MAX_UCMD_TIMENUDGE );
	}
#endif

	// mm session
	// TODO: remove the key after storing it to gclient_t !
	s = Info_ValueForKey( userinfo, "cl_mm_session" );
	cl->mm_session = ( s == NULL ) ? 0 : atoi( s );

	s = Info_ValueForKey( userinfo, "mmflags" );
	cl->mmflags = ( s == NULL ) ? 0 : strtoul( s, NULL, 10 );

	// tv
	if( cl->isTV )
	{
		s = Info_ValueForKey( userinfo, "tv_port" );
		cl->tv.port = s ? atoi( s ) : 0;

		s = Info_ValueForKey( userinfo, "tv_port6" );
		cl->tv.port6 = s ? atoi( s ) : 0;

		s = Info_ValueForKey( userinfo, "max_cl" );
		cl->tv.maxclients = s ? atoi( s ) : 0;

		s = Info_ValueForKey( userinfo, "num_cl" );
		cl->tv.numclients = s ? atoi( s ) : 0;

		s = Info_ValueForKey( userinfo, "chan" );
		cl->tv.channel = s ? atoi( s ) : 0;
	}

	if( !G_ISGHOSTING( ent ) && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED )
		G_Client_AssignTeamSkin( ent, userinfo );

	// save off the userinfo in case we want to check something later
	Q_strncpyz( cl->userinfo, userinfo, sizeof( cl->userinfo ) );

	G_UpdatePlayerInfoString( PLAYERNUM( ent ) );
	G_UpdateMMPlayerInfoString( PLAYERNUM( ent ) );

	G_Gametype_ScoreEvent( cl, "userinfochanged", oldname );
}
コード例 #23
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
}