namespace TanksServer.GameLogic; internal sealed class MoveBullets( MapEntityManager entityManager, IOptions options ) : ITickStep { private readonly double _smartBulletInertia = options.Value.SmartBulletInertia; public ValueTask TickAsync(TimeSpan delta) { foreach (var bullet in entityManager.Bullets) MoveBullet(bullet, delta); return ValueTask.CompletedTask; } private void MoveBullet(Bullet bullet, TimeSpan delta) { if (bullet.IsSmart && TryGetSmartRotation(bullet.Position, bullet.Owner, out var wantedRotation)) { var inertiaFactor = _smartBulletInertia * delta.TotalSeconds; var difference = wantedRotation - bullet.Rotation; bullet.Rotation += difference * inertiaFactor; } bullet.Speed *= 1 + (bullet.Acceleration * delta.TotalSeconds); var speed = bullet.Speed * delta.TotalSeconds; var angle = bullet.Rotation * 2 * Math.PI; bullet.Position = new FloatPosition( bullet.Position.X + Math.Sin(angle) * speed, bullet.Position.Y - Math.Cos(angle) * speed ); } private bool TryGetSmartRotation(FloatPosition position, Player bulletOwner, out double rotation) { var nearestEnemy = entityManager.Tanks .Where(t => t.Owner != bulletOwner) .MinBy(t => position.Distance(t.Position)); if (nearestEnemy == null) { rotation = double.NaN; return false; } var rotationRadians = Math.Atan2( y: nearestEnemy.Position.Y - position.Y, x: nearestEnemy.Position.X - position.X ) + (Math.PI / 2); rotation = rotationRadians / (2 * Math.PI); return true; } }