// dirty, because only the center of the bounding box + all points of the box are used to check the angle; // this function is using CCDegrees internal static bool CollideArcBoundingBox(CCPoint posCircle, float radius, float angleArcCenter, float angleHalfArcWidth, CCRect box) { if (CollideBoundingBoxCircle(box, posCircle, radius)) { // check whether the center of the box is inside the arc CCPoint vectorCirclePoint = box.Center - posCircle; float anglePoint = Constants.DxDyToCCDegrees(vectorCirclePoint.X, vectorCirclePoint.Y); float angleDifference = Constants.AbsAngleDifferenceDeg(angleArcCenter, anglePoint); if (angleDifference <= angleHalfArcWidth) { return(true); } // check whether a point of the box is inside the arc foreach (CCPoint point in Constants.CCRectPoints(box)) { vectorCirclePoint = point - posCircle; anglePoint = Constants.DxDyToCCDegrees(vectorCirclePoint.X, vectorCirclePoint.Y); angleDifference = Constants.AbsAngleDifferenceDeg(angleArcCenter, anglePoint); if (angleDifference <= angleHalfArcWidth) { return(true); } } } return(false); }
/// <summary> /// Returns the Direction (in CCDegrees) of the segment /// starting at pathIndex and ending at pathIndex +1. /// For the Endpoint the same value is returned as for endIndex-1. /// </summary> /// <param name="pathIndex"></param> /// <returns></returns> internal float DirectionAt(int pathIndex) { if (pathIndex == Path.Length - 1) { pathIndex--; } return(Constants.DxDyToCCDegrees(Path[pathIndex + 1].X - Path[pathIndex].X, Path[pathIndex + 1].Y - Path[pathIndex].Y)); }
/// <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); } }
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); } } } } }