internal static bool TargetAligned(Weapon weapon, Target target, out Vector3D targetPos) { Vector3 targetLinVel = Vector3.Zero; Vector3 targetAccel = Vector3.Zero; Vector3D targetCenter; FakeWorldTargetInfo fakeTargetInfo = null; if (weapon.Comp.Data.Repo.Base.Set.Overrides.Control != GroupOverrides.ControlModes.Auto && weapon.ValidFakeTargetInfo(weapon.Comp.Data.Repo.Base.State.PlayerId, out fakeTargetInfo)) { targetCenter = fakeTargetInfo.WorldPosition; } else if (target.IsProjectile) { targetCenter = target.Projectile?.Position ?? Vector3D.Zero; } else if (!target.IsFakeTarget) { targetCenter = target.Entity?.PositionComp.WorldAABB.Center ?? Vector3D.Zero; } else { targetCenter = Vector3D.Zero; } var validEstimate = true; if (weapon.System.Prediction != Prediction.Off && (!weapon.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && weapon.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed > 0)) { if (fakeTargetInfo != null) { targetLinVel = fakeTargetInfo.LinearVelocity; targetAccel = fakeTargetInfo.Acceleration; } else { var cube = target.Entity as MyCubeBlock; var topMostEnt = cube != null ? cube.CubeGrid : target.Entity; if (target.Projectile != null) { targetLinVel = target.Projectile.Velocity; targetAccel = target.Projectile.AccelVelocity; } else if (topMostEnt?.Physics != null) { targetLinVel = topMostEnt.Physics.LinearVelocity; targetAccel = topMostEnt.Physics.LinearAcceleration; } } if (Vector3D.IsZero(targetLinVel, 5E-03)) { targetLinVel = Vector3.Zero; } if (Vector3D.IsZero(targetAccel, 5E-03)) { targetAccel = Vector3.Zero; } targetPos = TrajectoryEstimation(weapon, targetCenter, targetLinVel, targetAccel, out validEstimate); } else { targetPos = targetCenter; } var targetDir = targetPos - weapon.MyPivotPos; double rangeToTarget; Vector3D.DistanceSquared(ref targetPos, ref weapon.MyPivotPos, out rangeToTarget); var inRange = rangeToTarget <= weapon.MaxTargetDistanceSqr && rangeToTarget >= weapon.MinTargetDistanceSqr; var isAligned = validEstimate && (inRange || weapon.Comp.Data.Repo.Base.State.TrackingReticle) && MathFuncs.IsDotProductWithinTolerance(ref weapon.MyPivotFwd, ref targetDir, weapon.AimingTolerance); weapon.Target.TargetPos = targetPos; weapon.Target.IsAligned = isAligned; return(isAligned); }
internal static bool TrackingTarget(Weapon w, Target target, out bool targetLock) { Vector3D targetPos; Vector3 targetLinVel = Vector3.Zero; Vector3 targetAccel = Vector3.Zero; Vector3D targetCenter; targetLock = false; var baseData = w.Comp.Data.Repo.Base; var session = w.System.Session; var ai = w.Comp.Ai; FakeWorldTargetInfo fakeTargetInfo = null; if (baseData.Set.Overrides.Control != GroupOverrides.ControlModes.Auto && w.ValidFakeTargetInfo(baseData.State.PlayerId, out fakeTargetInfo)) { targetCenter = fakeTargetInfo.WorldPosition; } else if (target.IsProjectile) { targetCenter = target.Projectile?.Position ?? Vector3D.Zero; } else if (!target.IsFakeTarget) { targetCenter = target.Entity?.PositionComp.WorldAABB.Center ?? Vector3D.Zero; } else { targetCenter = Vector3D.Zero; } var validEstimate = true; if (w.System.Prediction != Prediction.Off && !w.ActiveAmmoDef.AmmoDef.Const.IsBeamWeapon && w.ActiveAmmoDef.AmmoDef.Const.DesiredProjectileSpeed > 0) { if (fakeTargetInfo != null) { targetLinVel = fakeTargetInfo.LinearVelocity; targetAccel = fakeTargetInfo.Acceleration; } else { var cube = target.Entity as MyCubeBlock; var topMostEnt = cube != null ? cube.CubeGrid : target.Entity; if (target.Projectile != null) { targetLinVel = target.Projectile.Velocity; targetAccel = target.Projectile.AccelVelocity; } else if (topMostEnt?.Physics != null) { targetLinVel = topMostEnt.Physics.LinearVelocity; targetAccel = topMostEnt.Physics.LinearAcceleration; } } if (Vector3D.IsZero(targetLinVel, 5E-03)) { targetLinVel = Vector3.Zero; } if (Vector3D.IsZero(targetAccel, 5E-03)) { targetAccel = Vector3.Zero; } targetPos = TrajectoryEstimation(w, targetCenter, targetLinVel, targetAccel, out validEstimate); } else { targetPos = targetCenter; } w.Target.TargetPos = targetPos; double rangeToTargetSqr; Vector3D.DistanceSquared(ref targetPos, ref w.MyPivotPos, out rangeToTargetSqr); var targetDir = targetPos - w.MyPivotPos; var readyToTrack = validEstimate && !w.Comp.ResettingSubparts && (baseData.State.TrackingReticle || rangeToTargetSqr <= w.MaxTargetDistanceSqr && rangeToTargetSqr >= w.MinTargetDistanceSqr); var locked = true; var isTracking = false; if (readyToTrack && baseData.State.Control != CompStateValues.ControlMode.Camera) { if (MathFuncs.WeaponLookAt(w, ref targetDir, rangeToTargetSqr, true, false, out isTracking)) { w.ReturingHome = false; locked = false; w.AimBarrel(); } } w.Rotating = !locked; if (baseData.State.Control == CompStateValues.ControlMode.Camera) { return(isTracking); } var isAligned = false; if (isTracking) { isAligned = locked || MathFuncs.IsDotProductWithinTolerance(ref w.MyPivotFwd, ref targetDir, w.AimingTolerance); } var wasAligned = w.Target.IsAligned; w.Target.IsAligned = isAligned; var alignedChange = wasAligned != isAligned; if (w.System.DesignatorWeapon && session.IsServer && alignedChange) { for (int i = 0; i < w.Comp.Platform.Weapons.Length; i++) { var weapon = w.Comp.Platform.Weapons[i]; if (isAligned && !weapon.System.DesignatorWeapon) { weapon.Target.Reset(session.Tick, Target.States.Designator); } else if (!isAligned && weapon.System.DesignatorWeapon) { weapon.Target.Reset(session.Tick, Target.States.Designator); } } } targetLock = isTracking && w.Target.IsAligned; if (session.IsServer && baseData.Set.Overrides.Repel && ai.TargetingInfo.DroneInRange && !target.IsDrone && (session.AwakeCount == w.Acquire.SlotId || ai.Construct.RootAi.Construct.LastDroneTick == session.Tick) && GridAi.SwitchToDrone(w)) { return(true); } var rayCheckTest = !w.Comp.Session.IsClient && targetLock && (baseData.State.Control == CompStateValues.ControlMode.None || baseData.State.Control == CompStateValues.ControlMode.Ui) && w.ActiveAmmoDef.AmmoDef.Trajectory.Guidance != GuidanceType.Smart && (!w.Casting && session.Tick - w.Comp.LastRayCastTick > 29 || w.System.Values.HardPoint.Other.MuzzleCheck && session.Tick - w.LastMuzzleCheck > 29); if (rayCheckTest && !w.RayCheckTest()) { return(false); } return(isTracking); }
internal void RunSmart() { Vector3D newVel; if (DeltaVelocityPerTick <= 0 || Vector3D.DistanceSquared(Info.Origin, Position) >= Info.AmmoDef.Const.SmartsDelayDistSqr) { var fake = Info.Target.IsFakeTarget; var gaveUpChase = !fake && Info.Age - ChaseAge > MaxChaseTime && HadTarget; var validTarget = fake || Info.Target.IsProjectile || Info.Target.Entity != null && !Info.Target.Entity.MarkedForClose; var isZombie = Info.AmmoDef.Const.CanZombie && HadTarget && !fake && !validTarget && ZombieLifeTime > 0 && (ZombieLifeTime + SmartSlot) % 30 == 0; var seekFirstTarget = !HadTarget && !validTarget && Info.Age > 120 && (Info.Age + SmartSlot) % 30 == 0; if ((PickTarget && (Info.Age + SmartSlot) % 30 == 0 || gaveUpChase && validTarget || isZombie || seekFirstTarget) && NewTarget() || validTarget) { HadTarget = true; if (ZombieLifeTime > 0) { ZombieLifeTime = 0; OffSetTarget(); } var targetPos = Vector3D.Zero; FakeWorldTargetInfo fakeTargetInfo = null; if (fake) { var fakeTarget = Info.DummyTargets.PaintedTarget.EntityId != 0 && !Info.DummyTargets.PaintedTarget.Dirty ? Info.DummyTargets.PaintedTarget : Info.DummyTargets.ManualTarget; fakeTargetInfo = fakeTarget.LastInfoTick != Info.System.Session.Tick ? fakeTarget.GetFakeTargetInfo(Info.Ai) : fakeTarget.FakeInfo; targetPos = fakeTargetInfo.WorldPosition; } else if (Info.Target.IsProjectile) { targetPos = Info.Target.Projectile.Position; } else if (Info.Target.Entity != null) { targetPos = Info.Target.Entity.PositionComp.WorldAABB.Center; } if (Info.AmmoDef.Const.TargetOffSet && WasTracking) { if (Info.Age - LastOffsetTime > 300) { double dist; Vector3D.DistanceSquared(ref Position, ref targetPos, out dist); if (dist < OffsetSqr + VelocityLengthSqr && Vector3.Dot(Info.Direction, Position - targetPos) > 0) { OffSetTarget(); } } targetPos += TargetOffSet; } PredictedTargetPos = targetPos; var physics = Info.Target.Entity?.Physics ?? Info.Target.Entity?.Parent?.Physics; if (!(Info.Target.IsProjectile || fake) && (physics == null || Vector3D.IsZero(targetPos))) { PrevTargetPos = PredictedTargetPos; } else { PrevTargetPos = targetPos; } var tVel = Vector3.Zero; if (fake && fakeTargetInfo != null) { tVel = fakeTargetInfo.LinearVelocity; } else if (Info.Target.IsProjectile) { tVel = Info.Target.Projectile.Velocity; } else if (physics != null) { tVel = physics.LinearVelocity; } if (Info.AmmoDef.Const.TargetLossDegree > 0 && Vector3D.DistanceSquared(Info.Origin, Position) >= Info.AmmoDef.Const.SmartsDelayDistSqr) { if (WasTracking && (Info.System.Session.Tick20 || Vector3.Dot(Info.Direction, Position - targetPos) > 0) || !WasTracking) { var targetDir = -Info.Direction; var refDir = Vector3D.Normalize(Position - targetPos); if (!MathFuncs.IsDotProductWithinTolerance(ref targetDir, ref refDir, Info.AmmoDef.Const.TargetLossDegree)) { if (WasTracking) { PickTarget = true; } } else if (!WasTracking) { WasTracking = true; } } } PrevTargetVel = tVel; } else { var roam = Info.AmmoDef.Trajectory.Smarts.Roam; PrevTargetPos = roam ? PredictedTargetPos : Position + (Info.Direction * Info.MaxTrajectory); if (ZombieLifeTime++ > Info.AmmoDef.Const.TargetLossTime && !Info.AmmoDef.Trajectory.Smarts.KeepAliveAfterTargetLoss && (Info.AmmoDef.Trajectory.Smarts.NoTargetExpire || HadTarget)) { DistanceToTravelSqr = Info.DistanceTraveled * Info.DistanceTraveled; EarlyEnd = true; } if (roam && Info.Age - LastOffsetTime > 300 && HadTarget) { double dist; Vector3D.DistanceSquared(ref Position, ref PrevTargetPos, out dist); if (dist < OffsetSqr + VelocityLengthSqr && Vector3.Dot(Info.Direction, Position - PrevTargetPos) > 0) { OffSetTarget(true); PrevTargetPos += TargetOffSet; PredictedTargetPos = PrevTargetPos; } } else if (MineSeeking) { ResetMine(); return; } } var missileToTarget = Vector3D.Normalize(PrevTargetPos - Position); var relativeVelocity = PrevTargetVel - Velocity; var normalMissileAcceleration = (relativeVelocity - (relativeVelocity.Dot(missileToTarget) * missileToTarget)) * Info.AmmoDef.Trajectory.Smarts.Aggressiveness; Vector3D commandedAccel; if (Vector3D.IsZero(normalMissileAcceleration)) { commandedAccel = (missileToTarget * AccelInMetersPerSec); } else { var maxLateralThrust = AccelInMetersPerSec * Math.Min(1, Math.Max(0, Info.AmmoDef.Const.MaxLateralThrust)); if (normalMissileAcceleration.LengthSquared() > maxLateralThrust * maxLateralThrust) { Vector3D.Normalize(ref normalMissileAcceleration, out normalMissileAcceleration); normalMissileAcceleration *= maxLateralThrust; } commandedAccel = Math.Sqrt(Math.Max(0, AccelInMetersPerSec * AccelInMetersPerSec - normalMissileAcceleration.LengthSquared())) * missileToTarget + normalMissileAcceleration; } var offsetTime = Info.AmmoDef.Trajectory.Smarts.OffsetTime; if (offsetTime > 0) { if ((Info.Age % offsetTime == 0)) { double angle = Info.WeaponRng.ClientProjectileRandom.NextDouble() * MathHelper.TwoPi; Info.WeaponRng.ClientProjectileCurrentCounter += 1; var up = Vector3D.Normalize(Vector3D.CalculatePerpendicularVector(Info.Direction)); var right = Vector3D.Cross(Info.Direction, up); OffsetDir = Math.Sin(angle) * up + Math.Cos(angle) * right; OffsetDir *= Info.AmmoDef.Trajectory.Smarts.OffsetRatio; } commandedAccel += AccelInMetersPerSec * OffsetDir; commandedAccel = Vector3D.Normalize(commandedAccel) * AccelInMetersPerSec; } newVel = Velocity + (commandedAccel * StepConst); var accelDir = commandedAccel / AccelInMetersPerSec; AccelDir = accelDir; Vector3D.Normalize(ref newVel, out Info.Direction); } else { newVel = Velocity += (Info.Direction * DeltaVelocityPerTick); } VelocityLengthSqr = newVel.LengthSquared(); if (VelocityLengthSqr > MaxSpeedSqr) { newVel = Info.Direction * MaxSpeed; } Velocity = newVel; }