void Bot::EnableAutoAlert(const AiAlertSpot &alertSpot, AlertCallback callback, void *receiver) { // First check duplicate ids. Fail on error since callers of this method are internal. for (unsigned i = 0; i < alertSpots.size(); ++i) { if (alertSpots[i].id == alertSpot.id) FailWith("Duplicated alert spot (id=%d)\n", alertSpot.id); } if (alertSpots.size() == alertSpots.capacity()) FailWith("Can't add an alert spot (id=%d)\n: too many spots", alertSpot.id); alertSpots.emplace_back(AlertSpot(alertSpot, callback, receiver)); }
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 ); }
void Bot::DisableAutoAlert(int id) { for (unsigned i = 0; i < alertSpots.size(); ++i) { if (alertSpots[i].id == id) { alertSpots.erase(alertSpots.begin() + i); return; } } FailWith("Can't find alert spot by id %d\n", id); }
bool Bot::MayKeepRunningInCombat() const { if (!HasEnemy()) FailWith("MayKeepRunningInCombat(): there is no enemy"); Vec3 enemyToBotDir = Vec3(self->s.origin) - EnemyOrigin(); bool enemyMayHit = true; if (IsEnemyAStaticSpot()) { enemyMayHit = false; } else if (EnemyFireDelay() > 300) { enemyMayHit = false; } else { Vec3 enemyLookDir = EnemyLookDir(); float squaredDistance = enemyToBotDir.SquaredLength(); if (squaredDistance > 1) { float distance = 1.0f / Q_RSqrt(squaredDistance); enemyToBotDir *= 1.0f / distance; // Compute a cosine of angle between enemy look dir and enemy to bot dir float cosPhi = enemyLookDir.Dot(enemyToBotDir); // Be aware of RL splash on this range if (distance < 150.0f) enemyMayHit = cosPhi > 0.3; else if (cosPhi <= 0.3) enemyMayHit = false; else { float cotPhi = Q_RSqrt((1.0f / (cosPhi * cosPhi)) - 1); float sideMiss = distance / cotPhi; // Use hitbox height plus a bit as a worst case float hitboxLargestSectionSide = 8.0f + playerbox_stand_maxs[2] - playerbox_stand_mins[2]; enemyMayHit = sideMiss < hitboxLargestSectionSide; } } } if (enemyMayHit) return false; vec3_t botLookDir; AngleVectors(self->s.angles, botLookDir, nullptr, nullptr); // Check whether the bot may hit while running return ((-enemyToBotDir).Dot(botLookDir) > 0.99); }