コード例 #1
0
bool TurretComponent::TargetCanBeHit() {
	if (!target) return false;

	Vec3 aimDirection = RelativeAnglesToDirection(relativeAimAngles);
	Vec3 traceStart   = Vec3::Load(entity.oldEnt->s.pos.trBase);
	Vec3 traceEnd     = traceStart + range * aimDirection;

	trace_t tr;
	trap_Trace(&tr, traceStart.Data(), nullptr, nullptr, traceEnd.Data(), entity.oldEnt->s.number,
	           MASK_SHOT, 0);

	return (tr.entityNum == target->s.number);
}
コード例 #2
0
bool BotTacticalSpotsCache::FindMiddleRangeTacticalSpot( const Vec3 &origin, const Vec3 &enemyOrigin, vec3_t result ) {
	TacticalSpotsRegistry::AdvantageProblemParams problemParams( enemyOrigin.Data() );
	problemParams.SetMinSpotDistanceToEntity( WorldState::CLOSE_RANGE_MAX );
	problemParams.SetMaxSpotDistanceToEntity( WorldState::MIDDLE_RANGE_MAX );
	problemParams.SetOriginDistanceInfluence( 0.3f );
	problemParams.SetEntityDistanceInfluence( 0.4f );
	problemParams.SetEntityWeightFalloffDistanceRatio( 0.5f );
	problemParams.SetTravelTimeInfluence( 0.7f );
	problemParams.SetMinHeightAdvantageOverOrigin( -16.0f );
	problemParams.SetMinHeightAdvantageOverEntity( -64.0f );
	problemParams.SetHeightOverOriginInfluence( 0.6f );
	problemParams.SetHeightOverEntityInfluence( 0.8f );
	problemParams.SetCheckToAndBackReachability( false );

	float searchRadius = WorldState::MIDDLE_RANGE_MAX;
	float distanceToEnemy = ( origin - enemyOrigin ).LengthFast();
	if( distanceToEnemy < WorldState::CLOSE_RANGE_MAX ) {
		searchRadius += WorldState::CLOSE_RANGE_MAX;
	} else if( distanceToEnemy > WorldState::MIDDLE_RANGE_MAX ) {
		searchRadius += distanceToEnemy - WorldState::MIDDLE_RANGE_MAX;
	} else {
		searchRadius *= 1.0f + 0.5f * Skill();
	}

	return FindForOrigin( problemParams, origin, searchRadius, result );
}
コード例 #3
0
bool BotTacticalSpotsCache::FindCloseRangeTacticalSpot( const Vec3 &origin, const Vec3 &enemyOrigin, vec3_t result ) {
	TacticalSpotsRegistry::AdvantageProblemParams problemParams( enemyOrigin.Data() );
	float meleeRange = GS_GetWeaponDef( WEAP_GUNBLADE )->firedef_weak.timeout;
	problemParams.SetMinSpotDistanceToEntity( meleeRange );
	problemParams.SetMaxSpotDistanceToEntity( WorldState::CLOSE_RANGE_MAX );
	problemParams.SetOriginDistanceInfluence( 0.0f );
	problemParams.SetEntityDistanceInfluence( 0.7f );
	problemParams.SetEntityWeightFalloffDistanceRatio( 0.8f );
	problemParams.SetTravelTimeInfluence( 0.0f );
	problemParams.SetMinHeightAdvantageOverOrigin( -16.0f );
	problemParams.SetMinHeightAdvantageOverEntity( -16.0f );
	problemParams.SetHeightOverOriginInfluence( 0.4f );
	problemParams.SetHeightOverEntityInfluence( 0.9f );
	// Bot should be able to retreat from close combat
	problemParams.SetCheckToAndBackReachability( true );

	float searchRadius = WorldState::CLOSE_RANGE_MAX * 2;
	float distanceToEnemy = ( origin - enemyOrigin ).LengthFast();
	if( distanceToEnemy > WorldState::CLOSE_RANGE_MAX ) {
		searchRadius += distanceToEnemy - WorldState::CLOSE_RANGE_MAX;
		// On this range retreating to an old position makes little sense
		if( distanceToEnemy > 0.5f * WorldState::MIDDLE_RANGE_MAX ) {
			problemParams.SetCheckToAndBackReachability( false );
		}
	}

	return FindForOrigin( problemParams, origin, searchRadius, result );
}
コード例 #4
0
ファイル: ai_base_ai.cpp プロジェクト: Picmip/qfusion
Vec3 Ai::GetNewViewAngles( const vec3_t oldAngles, const Vec3 &desiredDirection,
						   unsigned frameTime, float angularSpeedMultiplier ) const {
	// Based on turret script code

	// For those trying to learn working with angles
	// Vec3.x is the PITCH angle (up and down rotation)
	// Vec3.y is the YAW angle (left and right rotation)
	// Vec3.z is the ROLL angle (left and right inclination)

	vec3_t newAngles, desiredAngles;
	VecToAngles( desiredDirection.Data(), desiredAngles );

	// Normalize180 all angles so they can be compared
	for( int i = 0; i < 3; ++i ) {
		newAngles[i] = AngleNormalize180( oldAngles[i] );
		desiredAngles[i] = AngleNormalize180( desiredAngles[i] );
	}

	// Rotate the entity angles to the desired angles
	if( !VectorCompare( newAngles, desiredAngles ) ) {
		for( auto angleNum: { YAW, PITCH } ) {
			newAngles[angleNum] = GetChangedAngle( newAngles[angleNum], desiredAngles[angleNum],
												   frameTime, angularSpeedMultiplier, angleNum );
		}
	}

	return Vec3( newAngles );
}
コード例 #5
0
bool BotTacticalSpotsCache::FindFarRangeTacticalSpot( const Vec3 &origin, const Vec3 &enemyOrigin, vec3_t result ) {
	TacticalSpotsRegistry::AdvantageProblemParams problemParams( enemyOrigin.Data() );
	problemParams.SetMinSpotDistanceToEntity( WorldState::MIDDLE_RANGE_MAX );
	problemParams.SetMaxSpotDistanceToEntity( WorldState::FAR_RANGE_MAX );
	problemParams.SetOriginDistanceInfluence( 0.0f );
	problemParams.SetEntityDistanceInfluence( 0.3f );
	problemParams.SetEntityWeightFalloffDistanceRatio( 0.25f );
	problemParams.SetTravelTimeInfluence( 0.8f );
	problemParams.SetMinHeightAdvantageOverOrigin( -192.0f );
	problemParams.SetMinHeightAdvantageOverEntity( -512.0f );
	problemParams.SetHeightOverOriginInfluence( 0.3f );
	problemParams.SetHeightOverEntityInfluence( 0.5f );
	problemParams.SetCheckToAndBackReachability( false );

	float searchRadius = 192.0f + 768.0f * Skill();
	float distanceToEnemy = ( origin - enemyOrigin ).LengthFast();
	float minSearchDistanceToEnemy = distanceToEnemy - searchRadius;
	float maxSearchDistanceToEnemy = distanceToEnemy + searchRadius;
	if( minSearchDistanceToEnemy < WorldState::MIDDLE_RANGE_MAX ) {
		searchRadius += WorldState::MIDDLE_RANGE_MAX - minSearchDistanceToEnemy;
	} else if( maxSearchDistanceToEnemy > WorldState::FAR_RANGE_MAX ) {
		searchRadius += maxSearchDistanceToEnemy - WorldState::FAR_RANGE_MAX;
	}

	return FindForOrigin( problemParams, origin, searchRadius, result );
}
コード例 #6
0
bool BotTacticalSpotsCache::FindCoverSpot( const Vec3 &origin, const Vec3 &enemyOrigin, vec3_t result ) {
	const float searchRadius = 192.0f + 512.0f * Skill();
	TacticalSpotsRegistry::CoverProblemParams problemParams( enemyOrigin.Data(), 32.0f );
	problemParams.SetOriginDistanceInfluence( 0.0f );
	problemParams.SetTravelTimeInfluence( 0.9f );
	problemParams.SetMinHeightAdvantageOverOrigin( -searchRadius );
	problemParams.SetHeightOverOriginInfluence( 0.3f );
	problemParams.SetCheckToAndBackReachability( false );

	auto *tacticalSpotsRegistry = TacticalSpotsRegistry::Instance();
	if( BotHasAlmostSameOrigin( origin ) ) {
		TacticalSpotsRegistry::OriginParams originParams( self, searchRadius, RouteCache() );
		return tacticalSpotsRegistry->FindCoverSpots( originParams, problemParams, (vec3_t *)result, 1 ) != 0;
	}
	TacticalSpotsRegistry::OriginParams originParams( origin.Data(), searchRadius, RouteCache() );
	return tacticalSpotsRegistry->FindCoverSpots( originParams, problemParams, (vec3_t *)result, 1 ) != 0;
}
コード例 #7
0
inline bool BotTacticalSpotsCache::FindForOrigin( const ProblemParams &problemParams,
												  const Vec3 &origin, float searchRadius, vec3_t result ) {
	vec3_t *spots = (vec3_t *)result;
	const TacticalSpotsRegistry *tacticalSpotsRegistry = TacticalSpotsRegistry::Instance();
	if( BotHasAlmostSameOrigin( origin ) ) {
		// Provide a bot entity to aid trace checks
		TacticalSpotsRegistry::OriginParams originParams( self, searchRadius, RouteCache() );
		return tacticalSpotsRegistry->FindPositionalAdvantageSpots( originParams, problemParams, spots, 1 ) > 0;
	}
	TacticalSpotsRegistry::OriginParams originParams( origin.Data(), searchRadius, RouteCache() );
	return tacticalSpotsRegistry->FindPositionalAdvantageSpots( originParams, problemParams, spots, 1 ) > 0;
}
コード例 #8
0
void TurretComponent::SetBaseDirection() {
	vec3_t torsoDirectionOldVec;
	AngleVectors(entity.oldEnt->s.angles, torsoDirectionOldVec, nullptr, nullptr);

	Vec3 torsoDirection = Math::Normalize(Vec3::Load(torsoDirectionOldVec));
	Vec3 traceStart     = Vec3::Load(entity.oldEnt->s.pos.trBase);
	Vec3 traceEnd       = traceStart + MINIMUM_CLEARANCE * torsoDirection;

	trace_t tr;
	trap_Trace(&tr, traceStart.Data(), nullptr, nullptr, traceEnd.Data(), entity.oldEnt->s.number,
	           MASK_SHOT, 0);

	// TODO: Check the presence of a PhysicsComponent to decide whether the obstacle is permanent.
	if (tr.entityNum == ENTITYNUM_WORLD ||
	    g_entities[tr.entityNum].entity->Get<BuildableComponent>()) {
		baseDirection = -torsoDirection;
	} else {
		baseDirection =  torsoDirection;
	}

	turretLogger.Verbose("Base direction set to %s.", baseDirection);
}
コード例 #9
0
ファイル: ai_base_ai.cpp プロジェクト: Picmip/qfusion
int Ai::CheckTravelTimeMillis( const Vec3& from, const Vec3 &to, bool allowUnreachable ) {

	// We try to use the same checks the TacticalSpotsRegistry performs to find spots.
	// If a spot is not reachable, it is an bug,
	// because a reachability must have been checked by the spots registry first in a few preceeding calls.

	int fromAreaNum;
	constexpr float squareDistanceError = WorldState::OriginVar::MAX_ROUNDING_SQUARE_DISTANCE_ERROR;
	if( ( from - self->s.origin ).SquaredLength() < squareDistanceError ) {
		fromAreaNum = aasWorld->FindAreaNum( self );
	} else {
		fromAreaNum = aasWorld->FindAreaNum( from );
	}

	if( !fromAreaNum ) {
		if( allowUnreachable ) {
			return 0;
		}

		FailWith( "CheckTravelTimeMillis(): Can't find `from` AAS area" );
	}

	const int toAreaNum = aasWorld->FindAreaNum( to.Data() );
	if( !toAreaNum ) {
		if( allowUnreachable ) {
			return 0;
		}

		FailWith( "CheckTravelTimeMillis(): Can't find `to` AAS area" );
	}

	for( int flags: { self->ai->aiRef->PreferredTravelFlags(), self->ai->aiRef->AllowedTravelFlags() } ) {
		if( int aasTravelTime = routeCache->TravelTimeToGoalArea( fromAreaNum, toAreaNum, flags ) ) {
			return 10U * aasTravelTime;
		}
	}

	if( allowUnreachable ) {
		return 0;
	}

	FailWith( "CheckTravelTimeMillis(): Can't find travel time %d->%d\n", fromAreaNum, toAreaNum );
}
コード例 #10
0
bool BotTacticalSpotsCache::FindSniperRangeTacticalSpot( const Vec3 &origin, const Vec3 &enemyOrigin, vec3_t result ) {
	TacticalSpotsRegistry::AdvantageProblemParams problemParams( enemyOrigin.Data() );
	problemParams.SetMinSpotDistanceToEntity( WorldState::FAR_RANGE_MAX );
	problemParams.SetOriginDistanceInfluence( 0.0f );
	problemParams.SetTravelTimeInfluence( 0.8f );
	problemParams.SetMinHeightAdvantageOverOrigin( -1024.0f );
	problemParams.SetMinHeightAdvantageOverEntity( -1024.0f );
	problemParams.SetHeightOverOriginInfluence( 0.3f );
	problemParams.SetHeightOverEntityInfluence( 0.1f );
	problemParams.SetCheckToAndBackReachability( false );

	float searchRadius = 192.0f + 768.0f * Skill();
	float distanceToEnemy = ( origin - enemyOrigin ).LengthFast();
	// If bot is not on sniper range, increase search radius (otherwise a point in a sniper range can't be found).
	if( distanceToEnemy - searchRadius < WorldState::FAR_RANGE_MAX ) {
		searchRadius += WorldState::FAR_RANGE_MAX - distanceToEnemy + searchRadius;
	}

	return FindForOrigin( problemParams, origin, searchRadius, result );
}
コード例 #11
0
ファイル: SpikerComponent.cpp プロジェクト: t4im/Unvanquished
// TODO: Use a proper trajectory trace, once available, to ensure friendly buildables are never hit.
bool SpikerComponent::SafeToShoot(Vec3 direction) {
	const missileAttributes_t* ma = BG_Missile(MIS_SPIKER);
	float missileSize = (float)ma->size;
	trace_t trace;
	vec3_t mins, maxs;
	Vec3 end = Vec3::Load(entity.oldEnt->s.origin) + (SPIKE_RANGE * direction);

	// Test once with normal and once with inflated missile bounding box.
	for (float traceSize : {missileSize, missileSize * SAFETY_TRACE_INFLATION}) {
		mins[0] = mins[1] = mins[2] = -traceSize;
		maxs[0] = maxs[1] = maxs[2] =  traceSize;
		trap_Trace(&trace, entity.oldEnt->s.origin, mins, maxs, end.Data(), entity.oldEnt->s.number,
			ma->clipmask, 0);
		gentity_t* hit = &g_entities[trace.entityNum];

		if (hit && G_OnSameTeam(entity.oldEnt, hit)) {
			return false;
		}
	}

	return true;
}
コード例 #12
0
Vec3 TurretComponent::RelativeAnglesToAbsoluteAngles(const Vec3 relativeAngles) const {
	quat_t torsoRotation;
	quat_t relativeRotation;
	quat_t absoluteRotation;
	vec3_t absoluteAngles;

	AnglesToQuat(TorsoAngles().Data(), torsoRotation);
	AnglesToQuat(relativeAngles.Data(), relativeRotation);

	// Rotate by torso rotation in world space, then by relative orientation in torso space.
	// This is equivalent to rotating by relative orientation in world space, then by torso rotation
	// in world space. This then is equivalent to multiplying the torso rotation in world space on
	// the left hand side and the relative rotation in world space on the right hand side.
	QuatMultiply(torsoRotation, relativeRotation, absoluteRotation);

	QuatToAngles(absoluteRotation, absoluteAngles);

	/*turretLogger.Debug("RelativeAnglesToAbsoluteAngles: %s → %s. Torso angles: %s.",
		Utility::Print(relativeAngles), Utility::Print(Vec3::Load(absoluteAngles)), TorsoAngles()
	);*/

	return Vec3::Load(absoluteAngles);
}
コード例 #13
0
Vec3 TurretComponent::AbsoluteAnglesToRelativeAngles(const Vec3 absoluteAngles) const {
	quat_t torsoRotation;
	quat_t absoluteRotation;
	quat_t relativeRotation;
	vec3_t relativeAngles;

	AnglesToQuat(TorsoAngles().Data(), torsoRotation);
	AnglesToQuat(absoluteAngles.Data(), absoluteRotation);

	// This is the inverse of RelativeAnglesToAbsoluteAngles. See the comment there for details.
	quat_t inverseTorsoOrientation;
	QuatCopy(torsoRotation, inverseTorsoOrientation);
	QuatInverse(inverseTorsoOrientation);
	QuatMultiply(inverseTorsoOrientation, absoluteRotation, relativeRotation);

	QuatToAngles(relativeRotation, relativeAngles);

	/*turretLogger.Debug("AbsoluteAnglesToRelativeAngles: %s → %s. Torso angles: %s.",
		Utility::Print(absoluteAngles), Utility::Print(Vec3::Load(relativeAngles)), TorsoAngles()
	);*/

	return Vec3::Load(relativeAngles);
}
コード例 #14
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() );
}
コード例 #15
0
int BotTacticalSpotsCache::FindNearbyEntities( const Vec3 &origin, float radius, int **entNums ) {
	if( const auto *nearbyEntitiesCacheEntry = nearbyEntitiesCache.TryGetCachedEntities( origin, radius ) ) {
		*entNums = (int *)nearbyEntitiesCacheEntry->entNums;
		return nearbyEntitiesCacheEntry->numEntities;
	}

	auto *nearbyEntitiesCacheEntry = nearbyEntitiesCache.Alloc();
	if( !nearbyEntitiesCacheEntry ) {
		return 0;
	}

	VectorCopy( origin.Data(), nearbyEntitiesCacheEntry->botOrigin );
	nearbyEntitiesCacheEntry->radius = radius;

	constexpr int maxCachedEntities = NearbyEntitiesCache::MAX_CACHED_NEARBY_ENTITIES;
	// Find more than maxCachedEntities entities in radius (most entities will usually be filtered out)
	constexpr int maxRadiusEntities = 2 * maxCachedEntities;

	int radiusEntNums[maxRadiusEntities];
	// Note that this value might be greater than maxRadiusEntities (an actual number of entities is returned)
	int numRadiusEntities = GClip_FindInRadius( const_cast<float *>( origin.Data() ), radius, radiusEntNums, maxRadiusEntities );

	int numEntities = 0;
	// Copy to locals for faster access (a compiler might be paranoid about aliasing)
	edict_t *gameEdicts = game.edicts;
	int *triggerEntNums = nearbyEntitiesCacheEntry->entNums;

	if( numRadiusEntities <= maxCachedEntities ) {
		// In this case we can avoid buffer capacity checks on each step
		for( int i = 0, end = std::min( numRadiusEntities, maxRadiusEntities ); i < end; ++i ) {
			edict_t *ent = gameEdicts + radiusEntNums[i];
			if( !ent->r.inuse ) {
				continue;
			}
			if( !ent->classname ) {
				continue;
			}

			triggerEntNums[numEntities++] = radiusEntNums[i];
		}
	} else {
		for( int i = 0, end = std::min( numRadiusEntities, maxRadiusEntities ); i < end; ++i ) {
			edict_t *ent = game.edicts + radiusEntNums[i];
			if( !ent->r.inuse ) {
				continue;
			}
			if( !ent->classname ) {
				continue;
			}

			triggerEntNums[numEntities++] = radiusEntNums[i];
			if( numEntities == maxCachedEntities ) {
				break;
			}
		}
	}

	nearbyEntitiesCacheEntry->numEntities = numEntities;
	*entNums = (int *)nearbyEntitiesCacheEntry->entNums;
	return numEntities;
}
コード例 #16
0
Vec3 TurretComponent::DirectionToAbsoluteAngles(const Vec3 direction) const {
	vec3_t absoluteAngles;
	vectoangles(direction.Data(), absoluteAngles);
	return Vec3::Load(absoluteAngles);
}
コード例 #17
0
Vec3 TurretComponent::AbsoluteAnglesToDirection(const Vec3 absoluteAngles) const {
	vec3_t direction;
	AngleVectors(absoluteAngles.Data(), direction, nullptr, nullptr);
	return Vec3::Load(direction);
}
コード例 #18
0
bool TurretComponent::MoveHeadToTarget(int timeDelta) {
	// Note that a timeDelta of zero may happen on a first thinker execution.
	// We do not return in that case since we don't know the return value yet.
	ASSERT_GE(timeDelta, 0);

	float timeMod = (float)timeDelta / 1000.0f;

	// Compute maximum angle changes for this execution.
	Vec3 maxAngleChange;
	maxAngleChange[PITCH] = timeMod * PITCH_SPEED;
	maxAngleChange[YAW]   = timeMod * YAW_SPEED;
	maxAngleChange[ROLL]  = 0.0f;

	// Compute angles to target, relative to the turret's base.
	Vec3 relativeAnglesToTarget = DirectionToRelativeAngles(directionToTarget);

	// Compute difference between angles to target and current angles.
	Vec3 deltaAngles;
	AnglesSubtract(relativeAnglesToTarget.Data(), relativeAimAngles.Data(), deltaAngles.Data());

	// Stop if there is nothing to do.
	if (Math::Length(deltaAngles) < 0.1f) {
		return true;
	}

	bool targetReached = true;
	Vec3 oldRelativeAimAngles = relativeAimAngles;

	// Adjust aim angles towards target angles.
	for (int angle = 0; angle < 3; angle++) {
		if (angle == ROLL) continue;

		if (fabs(deltaAngles[angle]) > maxAngleChange[angle]) {
			relativeAimAngles[angle] += (deltaAngles[angle] < 0.0f)
				? -maxAngleChange[angle]
				:  maxAngleChange[angle];
			targetReached = false;
		} else {
			relativeAimAngles[angle] = relativeAnglesToTarget[angle];
		}
	}

	// Respect pitch limits.
	if (relativeAimAngles[PITCH] > PITCH_CAP) {
		relativeAimAngles[PITCH] = PITCH_CAP;
		targetReached = false;
	}

	if (Math::DistanceSq(oldRelativeAimAngles, relativeAimAngles) > 0.0f) {
		turretLogger.Debug(
			"Aiming. Elapsed: %d ms. Delta: %.2f. Max: %.2f. Old: %s. New: %s. Reached: %s.",
			timeDelta, deltaAngles, maxAngleChange, oldRelativeAimAngles, relativeAimAngles, targetReached
		);
	}

	// TODO: Move gentity_t.buildableAim to BuildableComponent.
	Vec3 absoluteAimAngles = RelativeAnglesToAbsoluteAngles(relativeAimAngles);
	absoluteAimAngles.Store(entity.oldEnt->buildableAim);

	return targetReached;
}
コード例 #19
0
inline bool BotTacticalSpotsCache::BotHasAlmostSameOrigin( const Vec3 &unpackedOrigin ) const {
	constexpr float squareDistanceError = WorldState::OriginVar::MAX_ROUNDING_SQUARE_DISTANCE_ERROR;
	return DistanceSquared( self->s.origin, unpackedOrigin.Data() ) < squareDistanceError;
}