public static ToHitData toHit(Game game, int attackerId, Targetable target, int leg) { final Entity ae = game.getEntity(attackerId); int targetId = Entity.NONE; Entity te = null; if ( target.getTargetType() == Targetable.TYPE_ENTITY ) { te = (Entity) target; targetId = target.getTargetId(); } final int attackerElevation = ae.getElevation(); final int targetHeight = target.absHeight(); final int targetElevation = target.getElevation(); final boolean targetInBuilding = Compute.isInBuilding( game, te ); Building bldg = null; if ( targetInBuilding ) { bldg = game.board.getBuildingAt( te.getPosition() ); }  int[] kickLegs = new int[2]; if ( ae.entityIsQuad() ) { kickLegs[0] = Mech.LOC_RARM; kickLegs[1] = Mech.LOC_LARM; } else { kickLegs[0] = Mech.LOC_RLEG; kickLegs[1] = Mech.LOC_LLEG; } final int legLoc = (leg == KickAttackAction.RIGHT) ? kickLegs[0] : kickLegs[1]; final int nightModifier = (game.getOptions().booleanOption("night_battle")) ? +2 : 0;  ToHitData toHit;   if (leg != KickAttackAction.RIGHT && leg != KickAttackAction.LEFT) { throw new IllegalArgumentException("Leg must be LEFT or RIGHT"); } if (ae == null || target == null) { throw new IllegalArgumentException("Attacker or target not valid"); }   if (ae.equals(te)) { return new ToHitData(ToHitData.IMPOSSIBLE, "You can't target yourself"); }   if (!(ae instanceof Mech)) { return new ToHitData(ToHitData.IMPOSSIBLE, "Non-mechs can't kick"); }   if (ae.isSpotting()) { return new ToHitData(ToHitData.IMPOSSIBLE, "Attacker is spotting this turn"); }   if ( te != null && Entity.NONE != te.getTransportId() ) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target is a passenger."); }   if ( te != null && Entity.NONE != te.getSwarmTargetId() ) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target is swarming a Mek."); }   if (ae.isLocationBad(kickLegs[0]) || ae.isLocationBad(kickLegs[1])) { return new ToHitData(ToHitData.IMPOSSIBLE, "Leg missing"); }   if (!ae.hasWorkingSystem(Mech.ACTUATOR_HIP, kickLegs[0]) || !ae.hasWorkingSystem(Mech.ACTUATOR_HIP, kickLegs[1])) { return new ToHitData(ToHitData.IMPOSSIBLE, "Hip destroyed"); }   for (Enumeration i = ae.getWeapons(); i.hasMoreElements();) { Mounted mounted = (Mounted)i.nextElement(); if (mounted.isUsedThisRound() && mounted.getLocation() == legLoc) { return new ToHitData(ToHitData.IMPOSSIBLE, "Weapons fired from leg this turn"); } }   final int range = ae.getPosition().distance(target.getPosition()); if ( range > 1 ) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target not in range"); } else if ( te instanceof Infantry && 1 == range ) {  return new ToHitData(ToHitData.IMPOSSIBLE, "Can only stomp Infantry in same hex"); }   if (attackerElevation < targetElevation || attackerElevation > targetHeight) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target elevation not in range"); }   if ( te != null && te.isMakingDfa() ) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target is making a DFA attack"); }    if (0 != range && !Compute.isInArc(ae.getPosition(), ae.getFacing(), target.getPosition(), Compute.ARC_FORWARD)) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target not in arc"); }   if (ae.isProne()) { return new ToHitData(ToHitData.IMPOSSIBLE, "Attacker is prone"); }   if ( 0 != range && targetInBuilding ) { if ( !Compute.isInBuilding(game, ae) ) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target is inside building" ); } else if ( !game.board.getBuildingAt( ae.getPosition() ) .equals( bldg ) ) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target is inside differnt building" ); } }   if ( target.getTargetType() == Targetable.TYPE_BUILDING ) { return new ToHitData( ToHitData.AUTOMATIC_SUCCESS, "Targeting adjacent building." ); }   if ( target.getTargetType() == Targetable.TYPE_BLDG_IGNITE || target.getTargetType() == Targetable.TYPE_HEX_CLEAR || target.getTargetType() == Targetable.TYPE_HEX_IGNITE ) { return new ToHitData( ToHitData.IMPOSSIBLE, "Invalid attack"); }   int base = 3;   if ( game.getOptions().booleanOption("maxtech_physical_BTH") ) { base = ae.getCrew().getPiloting() - 2; }   toHit = new ToHitData(base, "base");   if ( te instanceof Infantry ) { toHit.addModifier( 3, "Stomping Infantry" ); }   if ( te instanceof BattleArmor ) { toHit.addModifier( 1, "battle armor target" ); }   toHit.append(Compute.getAttackerMovementModifier(game, attackerId));   toHit.append(Compute.getTargetMovementModifier(game, targetId));   toHit.append(Compute.getAttackerTerrainModifier(game, attackerId));   toHit.append(Compute.getTargetTerrainModifier(game, te));   if (!ae.hasWorkingSystem(Mech.ACTUATOR_UPPER_LEG, legLoc)) { toHit.addModifier(2, "Upper leg actuator destroyed"); } if (!ae.hasWorkingSystem(Mech.ACTUATOR_LOWER_LEG, legLoc)) { toHit.addModifier(2, "Lower leg actuator destroyed"); } if (!ae.hasWorkingSystem(Mech.ACTUATOR_FOOT, legLoc)) { toHit.addModifier(1, "Foot actuator destroyed"); }   if (te.isProne()) { toHit.addModifier(-2, "target prone and adjacent"); }   Hex targHex = game.board.getHex(te.getPosition()); if (te.height() > 0 && targHex.levelOf(Terrain.WATER) == te.height()) { toHit.addModifier(3, "target has partial cover"); }  if (nightModifier>0) { toHit.addModifier(nightModifier, "Night Battle, no Spotlight"); }   toHit.append(Compute.getImmobileMod(te));  Compute.modifyPhysicalBTHForAdvantages(ae, te, toHit, game);   if (attackerElevation < targetHeight) { toHit.setHitTable(ToHitData.HIT_KICK); } else if (te.height() > 0) { toHit.setHitTable(ToHitData.HIT_PUNCH); } else { toHit.setHitTable(ToHitData.HIT_NORMAL); }   toHit.setSideTable(Compute.targetSideTable(ae,te));    if ( 0 == range && te instanceof Tank ) { toHit.setSideTable( ToHitData.SIDE_RANDOM ); }   return toHit;
public ToHitData toHit(Game game) { return toHit(game, getEntityId(), game.getTarget(getTargetType(), getTargetId()), getLeg());
    public static ToHitData toHit(Game game, int attackerId,                                      Targetable target, int leg) {        final Entity ae = game.getEntity(attackerId);        int targetId = Entity.NONE;        Entity te = null;        if ( target.getTargetType() == Targetable.TYPE_ENTITY ) {            te = (Entity) target;            targetId = target.getTargetId();        }        final int attackerElevation = ae.getElevation();        final int targetHeight = target.absHeight();        final int targetElevation = target.getElevation();        final boolean targetInBuilding = Compute.isInBuilding( game, te );        Building bldg = null;        if ( targetInBuilding ) {            bldg = game.board.getBuildingAt( te.getPosition() );        }        int[] kickLegs = new int[2];        if ( ae.entityIsQuad() ) {          kickLegs[0] = Mech.LOC_RARM;          kickLegs[1] = Mech.LOC_LARM;        } else {          kickLegs[0] = Mech.LOC_RLEG;          kickLegs[1] = Mech.LOC_LLEG;        }        final int legLoc =            (leg == KickAttackAction.RIGHT) ? kickLegs[0] : kickLegs[1];        final int nightModifier = (game.getOptions().booleanOption("night_battle")) ? +2 : 0;        ToHitData toHit;        // arguments legal?        if (leg != KickAttackAction.RIGHT && leg != KickAttackAction.LEFT) {            throw new IllegalArgumentException("Leg must be LEFT or RIGHT");        }        if (ae == null || target == null) {            throw new IllegalArgumentException("Attacker or target not valid");        }        // can't target yourself        if (ae.equals(te)) {            return new ToHitData(ToHitData.IMPOSSIBLE, "You can't target yourself");        }        // non-mechs can't kick        if (!(ae instanceof Mech)) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Non-mechs can't kick");        }        // can't make physical attacks while spotting        if (ae.isSpotting()) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Attacker is spotting this turn");        }        // Can't target a transported entity.        if ( te != null && Entity.NONE != te.getTransportId() ) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Target is a passenger.");        }        // Can't target a entity conducting a swarm attack.        if ( te != null && Entity.NONE != te.getSwarmTargetId() ) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Target is swarming a Mek.");        }        // check if both legs are present & working        if (ae.isLocationBad(kickLegs[0])            || ae.isLocationBad(kickLegs[1])) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Leg missing");        }        // check if both hips are operational        if (!ae.hasWorkingSystem(Mech.ACTUATOR_HIP, kickLegs[0])            || !ae.hasWorkingSystem(Mech.ACTUATOR_HIP, kickLegs[1])) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Hip destroyed");        }        // check if attacker has fired leg-mounted weapons        for (Enumeration i = ae.getWeapons(); i.hasMoreElements();) {            Mounted mounted = (Mounted)i.nextElement();            if (mounted.isUsedThisRound() && mounted.getLocation() == legLoc) {                return new ToHitData(ToHitData.IMPOSSIBLE, "Weapons fired from leg this turn");            }        }        // check range        final int range = ae.getPosition().distance(target.getPosition());        if ( range > 1 ) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Target not in range");        }        else if ( te instanceof Infantry && 1 == range ) {            // As per Randall in the post, http://www.classicbattletech.com/w3t/showflat.php?Cat=&Board=ask&Number=626894&page=1&view=collapsed&sb=5&o=0&fpart=            return new ToHitData(ToHitData.IMPOSSIBLE,                                 "Can only stomp Infantry in same hex");        }        // check elevation        if (attackerElevation < targetElevation || attackerElevation > targetHeight) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Target elevation not in range");        }        // can't physically attack mechs making dfa attacks        if ( te != null && te.isMakingDfa() ) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Target is making a DFA attack");        }        // check facing        // Don't check arc for stomping infantry or tanks.        if (0 != range &&            !Compute.isInArc(ae.getPosition(), ae.getFacing(),                     target.getPosition(), Compute.ARC_FORWARD)) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Target not in arc");        }        // can't kick while prone        if (ae.isProne()) {            return new ToHitData(ToHitData.IMPOSSIBLE, "Attacker is prone");        }        // Can't target units in buildings (from the outside).        if ( 0 != range && targetInBuilding ) {            if ( !Compute.isInBuilding(game, ae) ) {                return new ToHitData(ToHitData.IMPOSSIBLE, "Target is inside building" );            }            else if ( !game.board.getBuildingAt( ae.getPosition() )                      .equals( bldg ) ) {                return new ToHitData(ToHitData.IMPOSSIBLE, "Target is inside differnt building" );            }        }        // Attacks against adjacent buildings automatically hit.        if ( target.getTargetType() == Targetable.TYPE_BUILDING ) {            return new ToHitData( ToHitData.AUTOMATIC_SUCCESS,                                  "Targeting adjacent building." );        }        // Can't target woods or ignite a building with a physical.        if ( target.getTargetType() == Targetable.TYPE_BLDG_IGNITE ||             target.getTargetType() == Targetable.TYPE_HEX_CLEAR ||             target.getTargetType() == Targetable.TYPE_HEX_IGNITE ) {            return new ToHitData( ToHitData.IMPOSSIBLE, "Invalid attack");        }        //Set the base BTH        int base = 3;        // Level 3 rule: the BTH is PSR - 2        if ( game.getOptions().booleanOption("maxtech_physical_BTH") ) {            base = ae.getCrew().getPiloting() - 2;        }        // Start the To-Hit        toHit = new ToHitData(base, "base");        // BMR(r), page 33. +3 modifier for kicking infantry.        if ( te instanceof Infantry ) {            toHit.addModifier( 3, "Stomping Infantry" );        }        // Battle Armor targets are hard for Meks and Tanks to hit.        if ( te instanceof BattleArmor ) {            toHit.addModifier( 1, "battle armor target" );        }        // attacker movement        toHit.append(Compute.getAttackerMovementModifier(game, attackerId));        // target movement        toHit.append(Compute.getTargetMovementModifier(game, targetId));        // attacker terrain        toHit.append(Compute.getAttackerTerrainModifier(game, attackerId));        // target terrain        toHit.append(Compute.getTargetTerrainModifier(game, te));        // damaged or missing actuators        if (!ae.hasWorkingSystem(Mech.ACTUATOR_UPPER_LEG, legLoc)) {            toHit.addModifier(2, "Upper leg actuator destroyed");        }        if (!ae.hasWorkingSystem(Mech.ACTUATOR_LOWER_LEG, legLoc)) {            toHit.addModifier(2, "Lower leg actuator destroyed");        }        if (!ae.hasWorkingSystem(Mech.ACTUATOR_FOOT, legLoc)) {            toHit.addModifier(1, "Foot actuator destroyed");        }        // target prone        if (te.isProne()) {            toHit.addModifier(-2, "target prone and adjacent");        }        // water partial cover?        Hex targHex = game.board.getHex(te.getPosition());        if (te.height() > 0 && targHex.levelOf(Terrain.WATER) == te.height()) {            toHit.addModifier(3, "target has partial cover");        }        if (nightModifier>0) {            toHit.addModifier(nightModifier, "Night Battle, no Spotlight");        }                // target immobile        toHit.append(Compute.getImmobileMod(te));        Compute.modifyPhysicalBTHForAdvantages(ae, te, toHit, game);        // elevation        if (attackerElevation < targetHeight) {            toHit.setHitTable(ToHitData.HIT_KICK);        } else if (te.height() > 0) {            toHit.setHitTable(ToHitData.HIT_PUNCH);        } else {            toHit.setHitTable(ToHitData.HIT_NORMAL);        }        // factor in target side        toHit.setSideTable(Compute.targetSideTable(ae,te));        // BMRr pg. 42, "The side on which a vehicle takes damage is determined        // randomly if the BattleMech is attacking from the same hex."        if ( 0 == range && te instanceof Tank ) {            toHit.setSideTable( ToHitData.SIDE_RANDOM );        }        // done!        return toHit;    }