public static ToHitData toHit(IGame game, int attackerId, Targetable target, int arm) { final Entity ae = game.getEntity(attackerId); if (ae == null) return new ToHitData(ToHitData.IMPOSSIBLE, "You can't attack from a null entity!");  String impossible = toHitIsImpossible(game, ae, target); if(impossible != null) { return new ToHitData(ToHitData.IMPOSSIBLE, "impossible"); }  IHex attHex = game.getBoard().getHex(ae.getPosition()); IHex targHex = game.getBoard().getHex(target.getPosition()); final int attackerHeight = ae.absHeight() + attHex.getElevation(); final int targetElevation = target.getElevation() + targHex.getElevation(); final int targetHeight = targetElevation + target.getHeight(); final int armLoc = (arm == PunchAttackAction.RIGHT) ? Mech.LOC_RARM : Mech.LOC_LARM; final int armArc = (arm == PunchAttackAction.RIGHT) ? Compute.ARC_RIGHTARM : Compute.ARC_LEFTARM;  ToHitData toHit;   if (arm != PunchAttackAction.RIGHT && arm != PunchAttackAction.LEFT) { throw new IllegalArgumentException("Arm must be LEFT or RIGHT"); } if (ae == null || target == null) { throw new IllegalArgumentException("Attacker or target not valid"); }   if (!(ae instanceof Mech)) { return new ToHitData(ToHitData.IMPOSSIBLE, "Non-mechs can't punch"); }   if ( ae.entityIsQuad() ) { return new ToHitData(ToHitData.IMPOSSIBLE, "Attacker is a quad"); }   if (ae.getArmsFlipped()) { return new ToHitData(ToHitData.IMPOSSIBLE, "Arms are flipped to the rear. Can not punch."); }   if (ae.isLocationBad(armLoc)) { return new ToHitData(ToHitData.IMPOSSIBLE, "Arm missing"); }   if (!ae.hasWorkingSystem(Mech.ACTUATOR_SHOULDER, armLoc)) { return new ToHitData(ToHitData.IMPOSSIBLE, "Shoulder destroyed"); }   if (ae.weaponFiredFrom(armLoc)) { return new ToHitData(ToHitData.IMPOSSIBLE, "Weapons fired from arm this turn"); }   if (attackerHeight < targetElevation || attackerHeight > targetHeight) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target elevation not in range"); }   int base = 4;  if ( game.getOptions().booleanOption("maxtech_physical_BTH") ) { base = ae.getCrew().getPiloting() - 1; }  toHit = new ToHitData(base, "base");  setCommonModifiers(toHit, game, ae, target);   if (ae.isProne()) {   if ( !ae.isLocationBad(Mech.LOC_RARM) && !ae.isLocationBad(Mech.LOC_LARM) && target instanceof Tank && ae.getPosition().distance( target.getPosition() ) == 0 ) { toHit.addModifier( 2, "attacker is prone" ); } else { return new ToHitData(ToHitData.IMPOSSIBLE,"Attacker is prone"); } }   else if ( !Compute.isInArc(ae.getPosition(), ae.getSecondaryFacing(), target.getPosition(), armArc) ) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target not in arc"); }   if (target.getTargetType() == Targetable.TYPE_BUILDING || target.getTargetType() == Targetable.TYPE_FUEL_TANK || target instanceof GunEmplacement) { return new ToHitData( ToHitData.AUTOMATIC_SUCCESS, "Targeting adjacent building." ); }   if (!ae.hasWorkingSystem(Mech.ACTUATOR_UPPER_ARM, armLoc)) { toHit.addModifier(2, "Upper arm actuator destroyed"); } if (!ae.hasWorkingSystem(Mech.ACTUATOR_LOWER_ARM, armLoc)) { toHit.addModifier(2, "Lower arm actuator missing or destroyed"); }      final boolean hasClaws = ((BipedMech)ae).hasClaw(armLoc); if (!ae.hasWorkingSystem(Mech.ACTUATOR_HAND, armLoc) && !hasClaws) { toHit.addModifier(1, "Hand actuator missing or destroyed"); } if (hasClaws) { toHit.addModifier(1, "Using Claws"); }    if (attackerHeight == targetElevation) { if (target.getHeight() == 0) { toHit.setHitTable(ToHitData.HIT_NORMAL); } else { toHit.setHitTable(ToHitData.HIT_KICK); } } else { toHit.setHitTable(ToHitData.HIT_PUNCH); }   toHit.setSideTable(Compute.targetSideTable(ae,target));   return toHit;
public ToHitData toHit(IGame game) { return toHit(game, getEntityId(), game.getTarget(getTargetType(), getTargetId() ), getArm());
    public static ToHitData toHit(IGame game, int attackerId,                                       Targetable target, int arm) {        final Entity ae = game.getEntity(attackerId);        if (ae == null)            return new ToHitData(ToHitData.IMPOSSIBLE, "You can't attack from a null entity!");                String impossible = toHitIsImpossible(game, ae, target);        if(impossible != null) {            return new ToHitData(ToHitData.IMPOSSIBLE, "impossible");        }                IHex attHex = game.getBoard().getHex(ae.getPosition());        IHex targHex = game.getBoard().getHex(target.getPosition());        final int attackerHeight = ae.absHeight() + attHex.getElevation();        final int targetElevation = target.getElevation() + targHex.getElevation();        final int targetHeight = targetElevation + target.getHeight();        final int armLoc = (arm == PunchAttackAction.RIGHT)                           ? Mech.LOC_RARM : Mech.LOC_LARM;        final int armArc = (arm == PunchAttackAction.RIGHT)                           ? Compute.ARC_RIGHTARM : Compute.ARC_LEFTARM;        ToHitData toHit;        // arguments legal?        if (arm != PunchAttackAction.RIGHT && arm != PunchAttackAction.LEFT) {            throw new IllegalArgumentException("Arm must be LEFT or RIGHT");        }        if (ae == null || target == null) {            throw new IllegalArgumentException("Attacker or target not valid");        }        // non-mechs can't punch        if (!(ae instanceof Mech)) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Non-mechs can't punch");        }        //Quads can't punch        if ( ae.entityIsQuad() ) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Attacker is a quad");        }        //Can't punch with flipped arms        if (ae.getArmsFlipped()) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Arms are flipped to the rear. Can not punch.");        }        // check if arm is present        if (ae.isLocationBad(armLoc)) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Arm missing");        }        // check if shoulder is functional        if (!ae.hasWorkingSystem(Mech.ACTUATOR_SHOULDER, armLoc)) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Shoulder destroyed");        }        // check if attacker has fired arm-mounted weapons        if (ae.weaponFiredFrom(armLoc)) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Weapons fired from arm this turn");        }        // check elevation        if (attackerHeight < targetElevation || attackerHeight > targetHeight) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Target elevation not in range");        }        //Set the base BTH        int base = 4;            if ( game.getOptions().booleanOption("maxtech_physical_BTH") ) {            base = ae.getCrew().getPiloting() - 1;        }            toHit = new ToHitData(base, "base");            setCommonModifiers(toHit, game, ae, target);                // Prone Meks can only punch vehicles in the same hex.        if (ae.isProne()) {            // The Mek must have both arms, the target must            // be a tank, and both must be in the same hex.            if ( !ae.isLocationBad(Mech.LOC_RARM) &&                 !ae.isLocationBad(Mech.LOC_LARM) &&                 target instanceof Tank &&                 ae.getPosition().distance( target.getPosition() ) == 0 ) {                toHit.addModifier( 2, "attacker is prone" );            } else {                return new ToHitData(ToHitData.IMPOSSIBLE,"Attacker is prone");            }        }        // Check facing if the Mek is not prone.        else if ( !Compute.isInArc(ae.getPosition(), ae.getSecondaryFacing(),                           target.getPosition(), armArc) ) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Target not in arc");        }        // Attacks against adjacent buildings automatically hit.        if (target.getTargetType() == Targetable.TYPE_BUILDING                || target.getTargetType() == Targetable.TYPE_FUEL_TANK                || target instanceof GunEmplacement) {            return new ToHitData( ToHitData.AUTOMATIC_SUCCESS,                                  "Targeting adjacent building." );        }        // damaged or missing actuators        if (!ae.hasWorkingSystem(Mech.ACTUATOR_UPPER_ARM, armLoc)) {            toHit.addModifier(2, "Upper arm actuator destroyed");        }        if (!ae.hasWorkingSystem(Mech.ACTUATOR_LOWER_ARM, armLoc)) {            toHit.addModifier(2, "Lower arm actuator missing or destroyed");        }        // Claws replace Actuators, but they are Equipment vs System as they        // take up multiple crits.         // Rules state +1 bth with claws and if claws are critted then you get        // the normal +1 bth for missing hand actuator.        // Damn if you do damned if you dont. --Torren.        final boolean hasClaws = ((BipedMech)ae).hasClaw(armLoc);        if (!ae.hasWorkingSystem(Mech.ACTUATOR_HAND, armLoc) && !hasClaws) {            toHit.addModifier(1, "Hand actuator missing or destroyed");        }        if (hasClaws) {            toHit.addModifier(1, "Using Claws");        }        // elevation        if (attackerHeight == targetElevation) {            if (target.getHeight() == 0) {                toHit.setHitTable(ToHitData.HIT_NORMAL);            } else {                toHit.setHitTable(ToHitData.HIT_KICK);            }        } else {            toHit.setHitTable(ToHitData.HIT_PUNCH);        }        // factor in target side        toHit.setSideTable(Compute.targetSideTable(ae,target));        // done!        return toHit;    }