Class AerospaceGeometry

java.lang.Object
megamek.client.bot.princess.AerospaceGeometry

public final class AerospaceGeometry extends Object
The air-to-air geometry an aerospace unit has to satisfy before it can shoot at anything.

Every method here takes a pose - a position and an altitude the unit might occupy - rather than an Entity whose state it reads. That is deliberate. Path ranking evaluates hundreds of positions the unit is not in, and the engine's own Compute.inDeadZone reads attacker.getAltitude(), so calling it during ranking answers a question about the present rather than the hypothesis. The same mistake produced the Entity.height() bug found in the #8659 review.

The dead zone

TW p.241: two airborne units add one hex of range per level of altitude between them, and "units cannot aim into the area immediately above or below their own hexes". Quantified by the same paragraph: one altitude apart needs two hexes of horizontal separation, two altitudes needs three, and so on - a widening cone above and below every fighter, not a fixed bubble.

What makes this the decisive rule in atmospheric combat is that it is measured in low-altitude hexes on both venues. Over a ground mapsheet one altitude of separation therefore blocks fire within seventeen ground hexes and two altitudes within thirty-three - most of a normal playing area. See AerospaceVenue.

  • Field Details

    • MINIMUM_ALTITUDE

      public static final int MINIMUM_ALTITUDE
      Lowest altitude an airborne aerospace unit can hold; below this it is landing or crashing.
      See Also:
    • MAXIMUM_ALTITUDE

      public static final int MAXIMUM_ALTITUDE
      Highest altitude in atmosphere; a unit reaching it may leave vertically under the climb-out option.
      See Also:
    • THRUST_PER_ALTITUDE_GAIN

      public static final int THRUST_PER_ALTITUDE_GAIN
      Thrust points to climb one altitude level. Descending is free, which is what makes diving cheap.
      See Also:
    • SAFE_DESCENT_PER_TURN

      public static final int SAFE_DESCENT_PER_TURN
      Altitude levels a unit will willingly shed in one turn.

      Descent costs no thrust, but dropping more than two levels forces a severe control roll, so no sane pilot - and neither does AeroPathUtil.generateValidAltitudeChanges(megamek.common.moves.MovePath) - plans past it.

      See Also:
  • Method Details

    • inDeadZone

      public static boolean inDeadZone(AerospaceVenue venue, Coords attackerPosition, int attackerAltitude, Coords targetPosition, int targetAltitude)
      Whether a shot between two poses is inside the attacker's dead zone.

      Pure geometry, so it answers the same question for either side of an engagement and for a position nobody occupies yet. It does not know about spheroids, which are exempt - use deadZoneBlocksAttack(megamek.client.bot.princess.AerospaceVenue, megamek.common.board.Coords, int, boolean, megamek.common.board.Coords, int) to ask whether a particular attacker is actually barred.

      The predicate is the engine's, rearranged. Compute.inDeadZone compares the altitude difference against effectiveDistance - altitudeDifference, and since effectiveDistance is the converted horizontal range plus that same difference, the two cancel and what remains is a comparison against horizontal range alone.

      Parameters:
      venue - which set of atmospheric rules is in force
      attackerPosition - the hex the attacker would be firing from
      attackerAltitude - the altitude the attacker would be firing from
      targetPosition - the target's hex
      targetAltitude - the target's altitude
      Returns:
      true if the target sits in the cone above or below the attacker and cannot be aimed at
    • deadZoneBlocksAttack

      public static boolean deadZoneBlocksAttack(AerospaceVenue venue, Coords attackerPosition, int attackerAltitude, boolean attackerFliesAsSpheroid, Coords targetPosition, int targetAltitude)
      Whether the dead zone actually bars this attacker from shooting.

      Spheroids are the exception the rules carve out (TW p.241): they may fire nose weapons at a target above them and aft weapons at one below, so the geometry never blocks them outright. The engine gates its own block the same way, in Compute.getRangeMods.

      Parameters:
      venue - which set of atmospheric rules is in force
      attackerPosition - the hex the attacker would be firing from
      attackerAltitude - the altitude the attacker would be firing from
      attackerFliesAsSpheroid - true if the attacker is behaving as a spheroid in atmosphere
      targetPosition - the target's hex
      targetAltitude - the target's altitude
      Returns:
      true if the attacker has no legal shot from this pose
    • effectiveRange

      public static int effectiveRange(AerospaceVenue venue, Coords attackerPosition, int attackerAltitude, Coords targetPosition, int targetAltitude)
      The range an air-to-air shot is resolved at, in the low-altitude hexes that weapon brackets use.

      Two parts, both from TW p.241: the horizontal distance converted to low-altitude hexes, plus one hex for every level of altitude between the two units. The book's own example - two fighters ten hexes apart at altitudes 3 and 5 are at an effective twelve - is the low-altitude case.

      Parameters:
      venue - which set of atmospheric rules is in force
      attackerPosition - the hex the attacker would be firing from
      attackerAltitude - the altitude the attacker would be firing from
      targetPosition - the target's hex
      targetAltitude - the target's altitude
      Returns:
      the effective range in low-altitude hexes
    • hexesUntilOffBoard

      public static int hexesUntilOffBoard(Coords start, int facing, Board board, int maximum)
      How many hexes a unit can fly straight ahead from this pose before leaving the board.

      Aerospace movement is mostly committed displacement: the velocity a path ends with must be flown next turn, largely along the facing it ends on. A pose a few hexes from the edge, pointing out, is a departure already in progress whatever the pilot intends - so the ranker needs this distance to price edge headings before they become exits.

      Parameters:
      start - the hex the pose ends in
      facing - the facing the pose ends on
      board - the board being flown over
      maximum - cap on the walk, so the cost stays bounded by what the caller cares about
      Returns:
      hexes travelled before the first off-board hex, capped at maximum
    • hexesToNearestEdge

      public static int hexesToNearestEdge(Coords position, Board board)
      How far this hex sits from the nearest board edge, in hexes.

      The directional walk in hexesUntilOffBoard(megamek.common.board.Coords, int, megamek.common.board.Board, int) prices the exit a pose is pointing at; this prices the edges it is merely standing beside. A fighter parked on the westernmost column facing north reads as safe to the directional walk while one hexside of leftward drift is an instant exit - observed live as a damaged bot hugging the map edge with the directional charge barely registering.

      Parameters:
      position - the hex to measure
      board - the board being flown over
      Returns:
      hexes to the nearest edge; 0 means standing on an edge row or column
    • reachableAltitudeBand

      public static AerospaceGeometry.AltitudeBand reachableAltitudeBand(Entity entity)
      The altitudes a unit could still be at when its turn ends.

      Asymmetric, because the rules are: climbing costs 2 thrust per level and is limited by the thrust budget, while descending is free and limited only by how much control risk a pilot will accept. A fighter with six thrust can therefore reach three levels up but only two down, and it is much likelier to take the free option.

      Used to model an enemy that has not moved yet. Reading such a unit's current altitude as though it were settled is what leaves the bot committing to geometry its opponent is about to leave.

      Parameters:
      entity - the unit to bound, which need not have moved
      Returns:
      the inclusive band of altitudes it can reach, clamped to legal airborne altitudes
    • clampAltitude

      public static int clampAltitude(int altitude)
      Holds an altitude to the range an airborne aerospace unit can legally occupy.
      Parameters:
      altitude - the altitude to clamp
      Returns:
      the altitude, bounded to 1 through 10