// // Created by vlad on 11/6/23. // #include "uml/ProjectilePredictor.h" #include "uml/Vector3.h" #include "uml/angles.h" #include #include namespace uml::prediction { ProjectilePredictor::ProjectilePredictor(float gravityValue, float maxTimeToTravel, float timeStep) : m_gravity(gravityValue), m_maxTravelTime(maxTimeToTravel), m_timeStepSize(timeStep) { } std::optional ProjectilePredictor::PredictPointToAim( const Target &target, const Projectile &projectile) const { for (float time = 0.0f; time <= m_maxTravelTime; time += m_timeStepSize) { auto predictedTargetPosition = LinearPrediction(target, time); const auto projectilePitch = MaybeCalculateProjectileLaunchPitchAngle(projectile, predictedTargetPosition); if (!projectilePitch.has_value()) [[unlikely]] return std::nullopt; if (!IsTargetWasHit(predictedTargetPosition, projectile, projectilePitch.value(), time)) continue; const auto delta2d = (predictedTargetPosition - projectile.m_origin).Length2D(); const auto height = delta2d * std::tan(angles::DegToRad(projectilePitch.value())); predictedTargetPosition.z = projectile.m_origin.z + height; return predictedTargetPosition; } return std::nullopt; } Vector3 ProjectilePredictor::LinearPrediction(const Target &target, float time) const { auto predicted = target.m_origin + target.m_vecVelocity * time; if (target.m_IsAirborne) predicted.z -= m_gravity * std::pow(time, 2.f) * 0.5f; return predicted; } std::optional ProjectilePredictor::MaybeCalculateProjectileLaunchPitchAngle(const Projectile &projectile, const Vector3 &targetPosition) const { const auto bulletGravity = m_gravity * projectile.m_gravityMultiplier; const auto delta = targetPosition - projectile.m_origin;; const auto distance2d = delta.Length2D(); float root = std::pow(projectile.m_velocity, 4.f) - bulletGravity * (bulletGravity * std::pow(distance2d, 2.f) + 2.0f * delta.z * std::pow(projectile.m_velocity, 2.f)); if (root < 0.0f) [[unlikely]] return std::nullopt; root = std::sqrt(root); const float angle = std::atan((std::pow(projectile.m_velocity, 2.f) - root) / (bulletGravity * distance2d)); return angles::RadToDeg(angle); } std::optional ProjectilePredictor::ProjectileTravelTime(const Vector3 &end, const Projectile &projectile, const float angle) const { auto launchAngles = projectile.m_origin.ViewAngleTo(end); launchAngles.x = angle; const auto velocity = Vector3::CreateVelocity(launchAngles, projectile.m_velocity); for (float time = 0.0f; time <= m_maxTravelTime; time += m_timeStepSize) { auto currentPos = projectile.m_origin + velocity * time; currentPos.z -= m_gravity * projectile.m_gravityMultiplier * std::pow(time, 2.f) * 0.5f; if (currentPos.DistTo(end) <= 10.f) return time; } return std::nullopt; } bool ProjectilePredictor::IsTargetWasHit(const Vector3 &end, const Projectile &projectile, const float angle, const float time) const { auto launchAngles = projectile.m_origin.ViewAngleTo(end); launchAngles.x = angle; const auto velocity = Vector3::CreateVelocity(launchAngles, projectile.m_velocity); auto currentPos = projectile.m_origin + velocity * time; currentPos.z -= m_gravity * projectile.m_gravityMultiplier * std::pow(time, 2.f) * 0.5f; return currentPos.DistTo(end) <= 10.f; } }