public CCMyRotateToState(CCMyRotateTo action, CCNode target) : base(action, target) { FinalAngle = action.FinalAngle; StartAngle = ((IGameObject)target).MyRotation; // Now we work out how far we actually have to rotate DiffAngle = Constants.AngleFromToDeg(StartAngle, FinalAngle); }
public void RotateTowards(float angle, float maxRotationAngle) { float currentRotation = MyRotation; float difference = Constants.AngleFromToDeg(currentRotation, angle); if ((float)Math.Abs(difference) <= maxRotationAngle) { MyRotation = angle; } else { MyRotation += maxRotationAngle * Math.Sign(difference); } }
/// <summary> /// Returns the (relative) rotation angle (for MyRotation) indicating how far into which direction the weapon /// should be turned to be able to hit the target perfectly. /// </summary> /// <returns></returns> internal float AngleToAimFor() { // as there is no exact solution the solution is here approched by iteration CCPoint TargetToMyPart = MyPart.PositionWorldspace - TargetPart.PositionWorldspace; // rotate the vector, so that the movement direction of the target is identical to the x axis float transformationRotation = -Constants.DxDyToRadians(TargetAircraft.VelocityVector.X, TargetAircraft.VelocityVector.Y); TargetToMyPart = CCPoint.RotateByAngle(TargetToMyPart, CCPoint.Zero, transformationRotation); // now the cordinate system is simpler and we can compute the difference between // a) the intersection of the flight path and the bullet path // and b) the point where the target actually is going to be by then // of course we want to minimize this error, for this we will iterate // ITERATION USING THE BISECTION METHOD double epsilon = 1; double angle = 0; // define the interval double startAngle = Constants.DxDyToRadians(-TargetToMyPart.X, -TargetToMyPart.Y); double endAngle = 0; float totalBulletVelocity = ProjectileBlueprint.Velocity + AircraftVelocityBoost(); double deltaStart = TargetToMyPart.X - TargetToMyPart.Y / Math.Tan(startAngle) + (TargetToMyPart.Y * TargetAircraft.VelocityVector.Length) / (Math.Sin(startAngle) * totalBulletVelocity); for (int i = 0; i < 6; i++) // iterate 6 times at max { angle = (startAngle + endAngle) / 2; double delta = TargetToMyPart.X - TargetToMyPart.Y / Math.Tan(angle) + (TargetToMyPart.Y * TargetAircraft.VelocityVector.Length) / (Math.Sin(angle) * totalBulletVelocity); if (Math.Abs(delta) < epsilon) // you're close enough so break already break; // delta is negative when the bullet hits behind the part -> angle needs to be closer to 0 if (Math.Sign(deltaStart) == Math.Sign(delta)) { startAngle = angle; deltaStart = delta; } else { endAngle = angle; } } // subtract the transformation rotation to get the total angle float totalAngle = Constants.RadiansToCCDegrees((float)angle - transformationRotation); // for the final angle transform the total angle to a relative angle return Constants.AngleFromToDeg(((IGameObject)MyPart.Parent).TotalRotation, totalAngle); }
internal void PartsInRange(out List<Part> partsInRange, out List<float> anglesFromTo, out List<float> distances) { // collect aircrafts that are near enough to have parts which could be targets List<Aircraft> aircraftsInRange = new List<Aircraft>(); foreach (var aircraft in (MyPart.Aircraft.Team == Team.PlayerTeam ? ((PlayLayer)MyPart.Layer).ActiveAircrafts : ((PlayLayer)MyPart.Layer).PlayerAircrafts)) // a bit dirty since technically NPC allies to the player could exist (someday), which would not be contained in PlayerAircrafts, but which would still (maybe) be part of the player team { // check if it is considered an enemy if (!MyPart.Aircraft.Team.IsEnemy(aircraft.Team) || aircraft.MyState.Equals(Aircraft.State.SHOT_DOWN)) continue; // check if in attention arc if (Collisions.CollideArcBoundingBox(MyPart.PositionWorldspace, AttentionRange, MyPart.TotalNullRotation, AttentionAngle, aircraft.BoundingBoxTransformedToWorld)) aircraftsInRange.Add(aircraft); } partsInRange = new List<Part>(); anglesFromTo = new List<float>(); distances = new List<float>(); foreach (var aircraft in aircraftsInRange) { foreach (var part in aircraft.TotalParts) { if (part.MyState == Part.State.DESTROYED) continue; // using the position as criterium is a bit dirty and could be improved on by using the bounding box instead (at the cost of performance) CCPoint vectorMyPartPart = part.PositionWorldspace - MyPart.PositionWorldspace; float distance = vectorMyPartPart.Length; if (distance <= AttentionRange) { var angleFromTo = Constants.AngleFromToDeg(MyPart.TotalRotation - MyPart.RotationFromNull, Constants.DxDyToCCDegrees(vectorMyPartPart.X, vectorMyPartPart.Y)); if ((float)Math.Abs(angleFromTo) <= AttentionAngle) { partsInRange.Add(part); anglesFromTo.Add(Constants.AngleFromToDeg(MyPart.TotalRotation, Constants.DxDyToCCDegrees(vectorMyPartPart.X, vectorMyPartPart.Y))); distances.Add(distance); } } } } }
/// <summary> /// called by the Aircraft owning the part owning this WeaponAbility each frame /// </summary> /// <param name="dt">time since the previous frame</param> internal void ExecuteOrders(float dt) { // cool down CooldownUntilNextShot -= dt; CooldownUntilNextTargetUpdate -= dt; if (CooldownUntilNextShot < 0) CooldownUntilNextShot = 0; // if you have a target check if it is still in range if (TargetPart != null) { CCPoint vectorMyPartTarget = TargetPart.PositionWorldspace - MyPart.PositionWorldspace; if (TargetPart.MyState == Part.State.DESTROYED || (TargetAircraft != null && TargetAircraft.MyState == Aircraft.State.SHOT_DOWN) || CooldownUntilNextTargetUpdate <= 0 || CCPoint.Distance(MyPart.PositionWorldspace, TargetPart.PositionWorldspace) > AttentionRange || Constants.AbsAngleDifferenceDeg(MyPart.TotalRotation - MyPart.RotationFromNull, Constants.DxDyToCCDegrees(vectorMyPartTarget.X, vectorMyPartTarget.Y)) > AttentionAngle) TargetPart = null; } if (TargetPart == null && CooldownUntilNextTargetUpdate <= 0) // if you currently do not aim at anything search for a target { CooldownUntilNextTargetUpdate = UpdateTargetDelay; // collect aircrafts that are near enough to have parts which could be targets // go through the parts of all of these planes and collect those that are in the attention angle PartsInRange(out List<Part> partsInRange, out List<float> anglesFromTo, out List<float> distances); // try to choose a part that is in reach // choose the part that is closest anglewise // but prioritize aircraft bodies: // this means that you should only change target from a body to another part if the part you would choose instead (because it's closer) // belongs to another plane float minAngle = float.PositiveInfinity; for (int i=0; i<partsInRange.Count(); i++) { if (distances[i] <= ShootingRange) // first only try parts that are already in reach { float absAngle = (float)Math.Abs(anglesFromTo[i]); var part = partsInRange[i]; if (absAngle < minAngle) //&& //(TargetPart == null || !(part.Parent == TargetPart.Parent && TargetPart == TargetAircraft.Body))) // don't switch from a body to a different part of the same aircraft { TargetPart = part; minAngle = absAngle; } } } if (TargetPart == null) // if you found no target this way check the rest { minAngle = float.PositiveInfinity; for (int i = 0; i < partsInRange.Count(); i++) { float absAngle = (float)Math.Abs(anglesFromTo[i]); var part = partsInRange[i]; // now also try parts that are not already in reach if (absAngle < minAngle) //&& //(TargetPart == null || !(part.Parent == TargetPart.Parent && TargetPart == TargetAircraft.Body))) // don't switch from a body to a different part of the same aircraft { TargetPart = part; minAngle = absAngle; } } } } // calculate the perfect point to aim for in order to hit the target if (TargetPart != null) { float angleToAimFor = AngleToAimFor(); if (!(MyPart is WingJet)) // a dirty fix to make sure jet wings don't rotate since rotation screws up the flip state; for other weapons this effect can be neglected since it is only visual { float angleToTurnTo = angleToAimFor; float angleTurn = Constants.AngleFromToDeg(MyPart.NullRotation, angleToTurnTo); // make sure you don't rotate further than your MountPoint allows if (angleTurn > MyPart.MountPoint.MaxTurningAngle) angleToTurnTo = MyPart.NullRotation + MyPart.MountPoint.MaxTurningAngle; else if (angleTurn < -MyPart.MountPoint.MaxTurningAngle) angleToTurnTo = MyPart.NullRotation - MyPart.MountPoint.MaxTurningAngle; // make sure you don't rotate further than this weapons MaxTurningAngle allows if (MaxTurningAngle < MyPart.MountPoint.MaxTurningAngle) { if (angleTurn > MaxTurningAngle) angleToTurnTo = MyPart.NullRotation + MaxTurningAngle; else if (angleTurn < -MaxTurningAngle) angleToTurnTo = MyPart.NullRotation - MaxTurningAngle; } MyPart.RotateTowards(angleToTurnTo, TurningAnglePerSecond * dt); } // if you're now close enough to the perfect angle (and in range) start shooting if (CanShoot() && CCPoint.Distance(MyPart.PositionWorldspace, TargetPart.PositionWorldspace) <= ShootingRange && (Constants.AbsAngleDifferenceDeg(angleToAimFor, MyPart.MyRotation) <= ToleratedError || WouldHit())) { TryShoot(); } } // and if you have no target try to get back to NullRotation else if (!(MyPart is WingJet)) { MyPart.RotateTowards(MyPart.NullRotation, TurningAnglePerSecond * dt); } }
private float AircraftVelocityBoost() { return MyPart.Aircraft.VelocityVector.Length * (float)Math.Cos(Constants.CCDegreesToMathRadians(Constants.AngleFromToDeg(MyPart.TotalRotation, MyPart.Aircraft.TotalRotation))); }