hits = Compute.missilesHit( hits );
if (!glancing) { hits = Compute.missilesHit( hits ); } else { hits = Compute.missilesHit(hits, glancingMissileMod, glancing); }
    private void resolveWeaponAttack(WeaponResult wr, int lastEntityId) {        //      System.out.println("rwa");      final Entity ae = game.getEntity(wr.waa.getEntityId());      final Targetable target = game.getTarget(wr.waa.getTargetType(),                                               wr.waa.getTargetId());      Entity entityTarget = null;      if (target.getTargetType() == Targetable.TYPE_ENTITY) {        entityTarget = (Entity) target;      }      final Mounted weapon = ae.getEquipment(wr.waa.getWeaponId());      final WeaponType wtype = (WeaponType) weapon.getType();      final boolean isWeaponInfantry = wtype.hasFlag(WeaponType.F_INFANTRY);      // 2002-09-16 Infantry weapons have unlimited ammo.      final boolean usesAmmo = wtype.getAmmoType() != AmmoType.T_NA &&          wtype.getAmmoType() != AmmoType.T_BA_MG &&          wtype.getAmmoType() != AmmoType.T_BA_SMALL_LASER &&          !isWeaponInfantry;      final Mounted ammo = usesAmmo ? weapon.getLinked() : null;      final AmmoType atype = ammo == null ? null : (AmmoType) ammo.getType();      Infantry platoon = null;      final boolean isBattleArmorAttack = wtype.hasFlag(WeaponType.F_BATTLEARMOR);      ToHitData toHit = wr.toHit;      boolean bInferno = (usesAmmo &&                          atype.getMunitionType() == AmmoType.M_INFERNO);      boolean bFragmentation = (usesAmmo &&                                atype.getMunitionType() == AmmoType.M_FRAGMENTATION);      boolean bFlechette = (usesAmmo &&                            atype.getMunitionType() == AmmoType.M_FLECHETTE);      boolean bArtillery = target.getTargetType() == Targetable.TYPE_HEX_ARTILLERY;      if (!bInferno) {        // also check for inferno infantry        bInferno = (isWeaponInfantry && wtype.hasFlag(WeaponType.F_INFERNO));      }      final boolean targetInBuilding =          Compute.isInBuilding(game, entityTarget);      if(bArtillery && game.getPhase()==Game.PHASE_FIRING) { //if direct artillery          wr.artyAttackerCoords=ae.getPosition();      }      // Which building takes the damage?      Building bldg = game.board.getBuildingAt(target.getPosition());      if (lastEntityId != ae.getId()) {          phaseReport.append("\nWeapons fire for ").append(ae.getDisplayName()).              append("\n");      }      // Swarming infantry can stop during any weapons phase after start.      if (Infantry.STOP_SWARM.equals(wtype.getInternalName())) {          // ... but only as their *only* attack action.          if (toHit.getValue() == ToHitData.IMPOSSIBLE) {              phaseReport.append("Swarm attack can not be ended (" +                   toHit.getDesc()).append(")\n");              return;          } else {              phaseReport.append("Swarm attack ended.\n");              // Only apply the "stop swarm 'attack'" to the swarmed Mek.              if (ae.getSwarmTargetId() != target.getTargetId()) {                  Entity other = game.getEntity(ae.getSwarmTargetId());                  other.setSwarmAttackerId(Entity.NONE);              } else {                  entityTarget.setSwarmAttackerId(Entity.NONE);              }              ae.setSwarmTargetId(Entity.NONE);              return;          }      }      // Report weapon attack and its to-hit value.      phaseReport.append("    ").append(wtype.getName()).append(" at ").append(          target.getDisplayName());      if (toHit.getValue() == ToHitData.IMPOSSIBLE) {          phaseReport.append(", but the shot is impossible (").append(toHit.getDesc()).              append(")\n");          return;      } else if (toHit.getValue() == ToHitData.AUTOMATIC_FAIL) {          phaseReport.append(", the shot is an automatic miss (").append(toHit.              getDesc()).append("), ");      } else if (toHit.getValue() == ToHitData.AUTOMATIC_SUCCESS) {          phaseReport.append(", the shot is an automatic hit (").append(toHit.              getDesc()).append("), ");      } else {          phaseReport.append("; needs ").append(toHit.getValue()).append(", ");      }      // if firing an HGR unbraced, schedule a PSR      if (wtype.getAmmoType() == AmmoType.T_GAUSS_HEAVY && ae.mpUsed > 0) {          // the mod is weight-based          int nMod;          if (ae.getWeight() <= Entity.WEIGHT_LIGHT) {              nMod = 2;          } else if (ae.getWeight() <= Entity.WEIGHT_MEDIUM) {              nMod = 1;          } else if (ae.getWeight() <= Entity.WEIGHT_HEAVY) {              nMod = 0;          } else {              nMod = -1;          }          PilotingRollData psr = new PilotingRollData(ae.getId(), nMod,                                          "fired HeavyGauss unbraced");          psr.setCumulative(false);          game.addPSR(psr);      }      // dice have been rolled, thanks      phaseReport.append("rolls ").append(wr.roll).append(" : ");      // check for AC jams      int nShots = weapon.howManyShots();      if (nShots > 1) {          int jamCheck = 0;          if (wtype.getAmmoType() == AmmoType.T_AC_ULTRA &&              weapon.curMode().equals("Ultra")) {              jamCheck = 2;          } else if (wtype.getAmmoType() == AmmoType.T_AC_ROTARY) {              if (nShots == 2) {                  jamCheck = 2;              } else if (nShots == 4) {                  jamCheck = 3;              } else if (nShots == 6) {                  jamCheck = 4;              }          }          if (jamCheck > 0 && wr.roll <= jamCheck) {              phaseReport.append("misses AND THE AUTOCANNON JAMS.\n");              weapon.setJammed(true);              // ultras are destroyed by jamming              if (wtype.getAmmoType() == AmmoType.T_AC_ULTRA) {                  weapon.setHit(true);              }              return;          }      }      // Resolve roll for disengaged field inhibitors on PPCs, if needed      if (game.getOptions().booleanOption("maxtech_ppc_inhibitors")          && wtype.hasModes()          && weapon.curMode().equals("Field Inhibitor OFF") ) {          int rollTarget = 0;          int dieRoll = Compute.d6(2);          int distance = Compute.effectiveDistance(game, ae, target);          if (distance>=3) {              rollTarget = 3;          } else if (distance == 2) {              rollTarget = 6;          } else if (distance == 1) {              rollTarget = 10;          }          phaseReport.append("\n    Fired PPC without field inhibitor, checking for damage:\n");          phaseReport.append("    Needs ");          phaseReport.append(rollTarget);          phaseReport.append(" to avoid damage, rolls ");          phaseReport.append(dieRoll);          phaseReport.append(": ");          if (dieRoll<rollTarget) {              // Oops, we ruined our day...              int wlocation = weapon.getLocation();              int wid = ae.getEquipmentNum(weapon);              int slot = 0;              weapon.setDestroyed (true);              for (int i=0; i<ae.getNumberOfCriticals(wlocation); i++) {                  CriticalSlot slot1 = ae.getCritical (wlocation, i);                  if (slot1 == null || slot1.getType() != CriticalSlot.TYPE_SYSTEM) {                      continue;                  }                  Mounted mounted = ae.getEquipment(slot1.getIndex());                  if (mounted.equals(weapon)) {                      ae.hitAllCriticals(wlocation,i);                  }              }              // Bug 1066147 : damage is *not* like an ammo explosion,              //        but it *does* get applied directly to the IS.              phaseReport.append( "fails." )                  .append( damageEntity(ae, new HitData(wlocation),                                        10, false, 0, true) )                  .append( "\n    (continuing hit report):" );          } else {              phaseReport.append("Succeeds.\n    ");          }      }      // do we hit?      boolean bMissed = wr.roll < toHit.getValue();      // special case minefield delivery, no damage and scatters if misses.      if (target.getTargetType() == Targetable.TYPE_MINEFIELD_DELIVER) {        Coords coords = target.getPosition();        if (!bMissed) {          phaseReport.append("hits the intended hex ")              .append(coords.getBoardNum())              .append("\n");        }        else {          coords = Compute.scatter(coords);          if (game.board.contains(coords)) {            phaseReport.append("misses and scatters to hex ")                .append(coords.getBoardNum())                .append("\n");          }          else {            phaseReport.append("misses and scatters off the board\n");            return;          }        }        // Handle the thunder munitions.        if (atype.getMunitionType() == AmmoType.M_THUNDER_AUGMENTED) {          deliverThunderAugMinefield(coords, ae.getOwner().getId(),                                     atype.getRackSize());        }        else if (atype.getMunitionType() == AmmoType.M_THUNDER) {          deliverThunderMinefield(coords, ae.getOwner().getId(),                                  atype.getRackSize());        }        else if (atype.getMunitionType() == AmmoType.M_THUNDER_INFERNO)          deliverThunderInfernoMinefield(coords, ae.getOwner().getId(),                                         atype.getRackSize());        else if (atype.getMunitionType() == AmmoType.M_THUNDER_VIBRABOMB)          deliverThunderVibraMinefield(coords, ae.getOwner().getId(),                                       atype.getRackSize(),                                       wr.waa.getOtherAttackInfo());        else if (atype.getMunitionType() == AmmoType.M_THUNDER_ACTIVE)          deliverThunderActiveMinefield(coords, ae.getOwner().getId(),                                        atype.getRackSize());          //else          //{          //...This is an error, but I'll just ignore it for now.          //}        return;      }      // FASCAM Artillery      if (target.getTargetType() == Targetable.TYPE_HEX_FASCAM) {          Coords coords = target.getPosition();          if (!bMissed) {              phaseReport.append("hits the intended hex ")                  .append(coords.getBoardNum())                  .append("\n");          }          else {              coords = Compute.scatter(coords);              if (game.board.contains(coords)) {                  phaseReport.append("misses and scatters to hex ")                      .append(coords.getBoardNum())                      .append("\n");              }              else {                  phaseReport.append("misses and scatters off the board\n");                  return;              }          }          if (game.board.contains(coords)) {              deliverFASCAMMinefield(coords, ae.getOwner().getId());          }          return;      }      // Vibrabomb-IV Artillery      if (target.getTargetType() == Targetable.TYPE_HEX_VIBRABOMB_IV) {          Coords coords = target.getPosition();          if (!bMissed) {              phaseReport.append("hits the intended hex ")                  .append(coords.getBoardNum())                  .append("\n");          }          else {              coords = Compute.scatter(coords);              if (game.board.contains(coords)) {                  phaseReport.append("misses and scatters to hex ")                      .append(coords.getBoardNum())                      .append("\n");              }              else {                  phaseReport.append("misses and scatters off the board\n");              }          }          if (game.board.contains(coords)) {              deliverThunderVibraMinefield(coords, ae.getOwner().getId(), 20,                                           wr.waa.getOtherAttackInfo());          }          return;      }      // Inferno IV artillery      if (target.getTargetType() == Targetable.TYPE_HEX_INFERNO_IV) {          Coords coords = target.getPosition();          if (!bMissed) {              phaseReport.append("hits the intended hex ")                  .append(coords.getBoardNum())                  .append("\n");          }          else {              coords = Compute.scatter(coords);              if (game.board.contains(coords)) {                  phaseReport.append("misses and scatters to hex ")                      .append(coords.getBoardNum())                      .append("\n");              }              else {                  phaseReport.append("misses and scatters off the board\n");                  return;              }          }          Hex h = game.getBoard().getHex(coords);          //Unless there is a fire in the hex already, start one.          if ( !h.contains( Terrain.FIRE ) ) {              phaseReport.append( "       Fire started in hex " )                  .append( coords.getBoardNum() )                  .append( ".\n" );              h.addTerrain(new Terrain(Terrain.FIRE, 1));          }          game.board.addInfernoTo( coords, InfernoTracker.INFERNO_IV_ROUND, 1 );          sendChangedHex(coords);          for(Enumeration impactHexHits = game.getEntities(coords);impactHexHits.hasMoreElements();) {              Entity entity = (Entity)impactHexHits.nextElement();              entity.infernos.add( InfernoTracker.INFERNO_IV_ROUND, 1 );              phaseReport.append( entity.getDisplayName() )                  .append( " now on fire for ")                  .append( entity.infernos.getTurnsLeftToBurn() )                  .append(" turns.\n");          }          for(int dir=0;dir<=5;dir++) {              Coords tempcoords=coords.translated(dir);              if(!game.board.contains(tempcoords)) {                  continue;              }              if(coords.equals(tempcoords)) {                  continue;              }              h = game.getBoard().getHex(tempcoords);              // Unless there is a fire in the hex already, start one.              if ( !h.contains( Terrain.FIRE ) ) {                  phaseReport.append( "       Fire started in hex " )                      .append( tempcoords.getBoardNum() )                      .append( ".\n" );                  h.addTerrain(new Terrain(Terrain.FIRE, 1));              }              game.board.addInfernoTo( tempcoords, InfernoTracker.INFERNO_IV_ROUND, 1 );              sendChangedHex(tempcoords);              for(Enumeration splashHexHits = game.getEntities(tempcoords);splashHexHits.hasMoreElements();) {                  Entity entity = (Entity)splashHexHits.nextElement();                  entity.infernos.add( InfernoTracker.INFERNO_IV_ROUND, 1 );                  phaseReport.append( entity.getDisplayName() )                      .append( " now on fire for ")                      .append( entity.infernos.getTurnsLeftToBurn() )                      .append(" turns.\n");              }          }          return;      }      //special case artillery      if (target.getTargetType() == Targetable.TYPE_HEX_ARTILLERY) {        Coords coords = target.getPosition();        if (!bMissed) {          phaseReport.append("hits the intended hex ")              .append(coords.getBoardNum())              .append("\n");        }        else {          coords = Compute.scatter(coords);          if (game.board.contains(coords)) {            phaseReport.append("misses and scatters to hex ")                .append(coords.getBoardNum())                .append("\n");          }          else {            phaseReport.append("misses and scatters off the board\n");            return;          }        }        int nCluster = 5;        for(Enumeration impactHexHits = game.getEntities(coords);impactHexHits.hasMoreElements();) {            Entity entity = (Entity)impactHexHits.nextElement();            int hits = wtype.getRackSize();            while(hits>0) {                if(wr.artyAttackerCoords!=null) {                    toHit.setSideTable(Compute.targetSideTable(wr.artyAttackerCoords,entity.getPosition(),entity.getFacing(),entity instanceof Tank));                }                HitData hit = entity.rollHitLocation                    ( toHit.getHitTable(),                      toHit.getSideTable(),                      wr.waa.getAimedLocation(),                      wr.waa.getAimingMode() );                phaseReport.append(damageEntity(entity, hit, Math.min(nCluster, hits), false, 0, false, true) + "\n");                hits -= Math.min(nCluster,hits);            }        }        for(int dir=0;dir<=5;dir++) {            Coords tempcoords=coords.translated(dir);            if(!game.board.contains(tempcoords)) {                continue;            }            if(coords.equals(tempcoords)) {                continue;            }            Enumeration splashHexHits = game.getEntities(tempcoords);            if(splashHexHits.hasMoreElements()) {                phaseReport.append("in hex " + tempcoords.getBoardNum());            }            for(;splashHexHits.hasMoreElements();) {;            Entity entity = (Entity)splashHexHits.nextElement();            int hits = wtype.getRackSize()/2;            while(hits>0) {                HitData hit = entity.rollHitLocation                    ( toHit.getHitTable(),                      toHit.getSideTable(),                      wr.waa.getAimedLocation(),                      wr.waa.getAimingMode() );                phaseReport.append(damageEntity(entity, hit, Math.min(nCluster, hits)) + "\n");                hits -= Math.min(nCluster,hits);            }            }        }        return;      } // End artillery      if (bMissed) {            // Report the miss.            if ( wtype.getAmmoType() == AmmoType.T_SRM_STREAK ) {                phaseReport.append( "fails to achieve lock.\n" );            } else {                phaseReport.append("misses.\n");            }            // Report any AMS action.            for (int i=0; i < wr.amsShotDown.length; i++) {                if (wr.amsShotDown[i] > 0) {                    phaseReport.append( "\tAMS activates, firing " )                        .append( wr.amsShotDown[i] )                        .append( " shot(s).\n" );                }            }            // Figure out the maximum number of missile hits.            // TODO: handle this in a different place.            int maxMissiles = 0;            if ( usesAmmo ) {                maxMissiles = wtype.getRackSize();                if ( wtype.hasFlag(WeaponType.F_DOUBLE_HITS) ) {                    maxMissiles *= 2;                }                if ( ae instanceof BattleArmor ) {                    platoon = (Infantry) ae;                    maxMissiles *= platoon.getShootingStrength();                }            }            // If the AMS shot down *all* incoming missiles, if            // the shot is an automatic failure, or if it's from            // a Streak rack, then Infernos can't ignite the hex            // and any building is safe from damage.            if ( (usesAmmo && wr.amsShotDownTotal >= maxMissiles) ||                 toHit.getValue() == TargetRoll.AUTOMATIC_FAIL ||                 wtype.getAmmoType() == AmmoType.T_SRM_STREAK ) {                return;            }            // Shots that miss an entity can set fires.            // Infernos always set fires.  Otherwise            // Buildings can't be accidentally ignited,            // and some weapons can't ignite fires.            if ( entityTarget != null &&                 ( bInferno ||                   ( bldg == null &&                     wtype.getFireTN() != TargetRoll.IMPOSSIBLE ) ) ) {                tryIgniteHex(target.getPosition(), bInferno, 11);            }            // BMRr, pg. 51: "All shots that were aimed at a target inside            // a building and miss do full damage to the building instead."            if ( !targetInBuilding ) {                return;            }        }        // special case NARC hits.  No damage, but a beacon is appended        if (!bMissed &&            wtype.getAmmoType() == AmmoType.T_NARC &&            atype.getMunitionType() != AmmoType.M_NARC_EX) {            if (wr.amsShotDownTotal > 0) {                phaseReport.append("would hit, but...");                for (int i=0; i < wr.amsShotDown.length; i++) {                    phaseReport.append("\n\tAMS engages, firing ")                        .append(wr.amsShotDown[i]).append(" shots");                }                phaseReport.append("\nThe pod is destroyed by AMS fire.\n");            } else if (entityTarget == null) {                phaseReport.append("hits, but doesn't do anything.\n");            } else {                entityTarget.setNarcedBy(ae.getOwner().getTeam());                phaseReport.append("hits.  Pod attached.\n");            }            return;        }        // attempt to clear minefield by LRM/MRM fire.        if (!bMissed && target.getTargetType() == Targetable.TYPE_MINEFIELD_CLEAR) {            int clearAttempt = Compute.d6(2);            if (clearAttempt >= Minefield.CLEAR_NUMBER_WEAPON) {                phaseReport.append("\n\thits and clears it.\n");                Coords coords = target.getPosition();                Enumeration minefields = game.getMinefields(coords).elements();                while (minefields.hasMoreElements()) {                    Minefield mf = (Minefield) minefields.nextElement();                    removeMinefield(mf);                }            } else {                phaseReport.append("\n\thits, but fails to clear it.\n");            }            return;        }        // yeech.  handle damage. . different weapons do this in very different ways        int hits = 1, nCluster = 1, nSalvoBonus = 0;        int nDamPerHit = wtype.getDamage();        boolean bSalvo = false;        // ecm check is heavy, so only do it once        boolean bCheckedECM = false;        boolean bECMAffected = false;        boolean bMekStealthActive = false;        String sSalvoType = " shot(s) ";        boolean bAllShotsHit = false;        int nRange = ae.getPosition().distance(target.getPosition());        int nMissilesModifier = 0;        boolean maxtechmissiles = game.getOptions().booleanOption("maxtech_mslhitpen");        if (maxtechmissiles) {            if (nRange<=1) {                nMissilesModifier = +1;            } else if (nRange <= wtype.getShortRange()) {                nMissilesModifier = 0;            } else if (nRange <= wtype.getMediumRange()) {                nMissilesModifier = -1;            } else {                nMissilesModifier = -2;            }       }        // All shots fired by a Streak SRM weapon, during        // a Mech Swarm hit, or at an adjacent building.        if ( wtype.getAmmoType() == AmmoType.T_SRM_STREAK ||             wtype.getAmmoType() == AmmoType.T_NARC ||             ae.getSwarmTargetId() == wr.waa.getTargetId() ||             ( ( target.getTargetType() == Targetable.TYPE_BLDG_IGNITE ||                 target.getTargetType() == Targetable.TYPE_BUILDING ) &&               ae.getPosition().distance( target.getPosition() ) <= 1 ) ) {            bAllShotsHit = true;        }        // Mek swarms attach the attacker to the target.        if ( !bMissed && Infantry.SWARM_MEK.equals( wtype.getInternalName() ) ) {            // Is the target already swarmed?            if ( Entity.NONE != entityTarget.getSwarmAttackerId() ) {                phaseReport.append( "succeds, but the defender is " );                phaseReport.append( "already swarmed by another unit.\n" );            }            // Did the target get destroyed by weapons fire?            else if ( entityTarget.isDoomed() || entityTarget.isDestroyed() ||                      entityTarget.getCrew().isDead() ) {                phaseReport.append( "succeds, but the defender was " );                phaseReport.append( "destroyed by weapons fire.\n" );            } else {                phaseReport.append( "succeeds!  Defender swarmed.\n" );                ae.setSwarmTargetId( wr.waa.getTargetId() );                entityTarget.setSwarmAttackerId( wr.waa.getEntityId() );            }            return;        }        // Magnetic Mine Launchers roll number of hits on battle armor        // hits table but use # mines firing instead of men shooting.        else if ( wtype.getInternalName().equals(BattleArmor.MINE_LAUNCHER) ) {            hits = nShots;            if ( !bAllShotsHit ) {                hits = Compute.getBattleArmorHits( hits );            }            bSalvo = true;            sSalvoType = " mine(s) ";        }        // Other battle armor attacks use # of men firing to determine hits.        // Each hit can be in a new location. The damage per shot comes from        // the "racksize".        else if ( isBattleArmorAttack ) {            bSalvo = true;            platoon = (Infantry) ae;            nCluster = 1;            nDamPerHit = wtype.getRackSize();            hits = platoon.getShootingStrength();            // All attacks during Mek Swarms hit; all            // others use the Battle Armor hits table.            if ( !bAllShotsHit ) {                hits = Compute.getBattleArmorHits( hits );            }            // Handle Inferno SRM squads.            if (bInferno) {                nCluster = hits;                nDamPerHit = 0;                sSalvoType = " Inferno missle(s) ";                bSalvo = false;            }        }        // Infantry damage depends on # men left in platoon.        else if (isWeaponInfantry) {            bSalvo = true;            platoon = (Infantry)ae;            nCluster = 5;            nDamPerHit = 1;            hits = platoon.getDamage(platoon.getShootingStrength());            // Handle Inferno SRM infantry.            if (bInferno) {                nCluster = hits;                nDamPerHit = 0;                sSalvoType = " Inferno missle(s) ";                bSalvo = false;            }        } else if (wtype.getDamage() == WeaponType.DAMAGE_MISSILE ||                   wtype.hasFlag(WeaponType.F_MISSILE_HITS) ) {            bSalvo = true;            // Weapons with ammo type T_BA_MG or T_BA_SMALL_LASER            // don't have an atype object.            if ( wtype.getAmmoType() == AmmoType.T_BA_MG ||                 wtype.getAmmoType() == AmmoType.T_BA_SMALL_LASER ) {                nDamPerHit = Math.abs( wtype.getAmmoType() );            } else {                sSalvoType = " missile(s) ";                // Get the damage from the linked ammo.                nDamPerHit = atype.getDamagePerShot();            }            if ( wtype.getAmmoType() == AmmoType.T_LRM ||                 wtype.getAmmoType() == AmmoType.T_MRM ||                 wtype.getAmmoType() == AmmoType.T_ATM ||                 wtype.getAmmoType() == AmmoType.T_ROCKET_LAUNCHER ) {                nCluster = 5;            }            // calculate # of missiles hitting            if ( wtype.getAmmoType() == AmmoType.T_LRM ||                 wtype.getAmmoType() == AmmoType.T_SRM ||                 wtype.getAmmoType() == AmmoType.T_ATM ) {                // check for artemis, else check for narc                Mounted mLinker = weapon.getLinkedBy();                if ( wtype.getAmmoType() == AmmoType.T_ATM ||                     ( mLinker != null &&                       mLinker.getType() instanceof MiscType &&                       !mLinker.isDestroyed() && !mLinker.isMissing() && !mLinker.isBreached() &&                       mLinker.getType().hasFlag(MiscType.F_ARTEMIS) ) ) {                    // check ECM interference                    if (!bCheckedECM) {                        // Attacking Meks using stealth suffer ECM effects.                        if ( ae instanceof Mech ) {                            bMekStealthActive = ae.isStealthActive();                        }                        bECMAffected = Compute.isAffectedByECM(ae, ae.getPosition(), target.getPosition());                        bCheckedECM = true;                    }                    // also no artemis for IDF, and only use standard ammo (excepot for ATMs)                    if (!bECMAffected&& !bMekStealthActive                        && (!weapon.getType().hasModes()                            || !weapon.curMode().equals("Indirect"))                        && (atype.getMunitionType() == AmmoType.M_STANDARD                             || atype.getMunitionType() == AmmoType.M_EXTENDED_RANGE                            || atype.getMunitionType() == AmmoType.M_HIGH_EXPLOSIVE) ) {                        nSalvoBonus += 2;                    }                } else if (entityTarget != null && entityTarget.isNarcedBy(ae.getOwner().getTeam())) {                    // check ECM interference                    if (!bCheckedECM) {                        // Attacking Meks using stealth suffer ECM effects.                        if ( ae instanceof Mech ) {                            bMekStealthActive = ae.isStealthActive();                        }                        bECMAffected = Compute.isAffectedByECM(ae, ae.getPosition(), target.getPosition());                        bCheckedECM = true;                    }                    // only apply Narc bonus if we're not suffering ECM effect                    // and we are using standard ammo.                    if (!bECMAffected && !bMekStealthActive && atype.getMunitionType() == AmmoType.M_STANDARD) {                        nSalvoBonus += 2;                    }                }            }            // If dealing with Inferno rounds set damage to zero and reset            // all salvo bonuses (cannot mix with other special munitions).            if (bInferno) {                    nDamPerHit = 0;                    nSalvoBonus = 0;                    sSalvoType = " inferno missile(s) ";                    bSalvo = false;            }            // If dealing with fragmentation missiles,            // it does double damage to infantry...            if (bFragmentation) {                sSalvoType = " fragmentation missile(s) ";            }            // Large MRM missile racks roll twice.            // MRM missiles never recieve hit bonuses.            if ( wtype.getRackSize() == 30 || wtype.getRackSize() == 40 ) {                hits = Compute.missilesHit(wtype.getRackSize() / 2, nMissilesModifier, maxtechmissiles) +                    Compute.missilesHit(wtype.getRackSize() / 2, nMissilesModifier, maxtechmissiles);            }            // Battle Armor units multiply their racksize by the number            // of men shooting and they can't get missile hit bonuses.            else if ( ae instanceof BattleArmor ) {                platoon = (Infantry) ae;                int temp = wtype.getRackSize() * platoon.getShootingStrength();                // Do all shots hit?                if ( bAllShotsHit ) {                    hits = temp;                } else {                    // Account for more than 20 missles hitting.                    hits = 0;                    while ( temp > 20 ) {                        hits += Compute.missilesHit( 20, nMissilesModifier, maxtechmissiles );                        temp -= 20;                    }                    hits += Compute.missilesHit( temp, nMissilesModifier, maxtechmissiles );                } // End not-all-shots-hit            }            // If all shots hit, use the full racksize.            else if ( bAllShotsHit ) {                hits = wtype.getRackSize();            }            // In all other circumstances, roll for hits.            else {                hits = Compute.missilesHit(wtype.getRackSize(), nSalvoBonus + nMissilesModifier, maxtechmissiles);            }            // Advanced SRM's only hit with even numbers of missles.            if ( null != atype &&                 atype.getAmmoType() == AmmoType.T_SRM_ADVANCED ) {                hits = 2 * (int) Math.floor( (1.0 + (float) hits) / 2.0);            }        } else if (atype != null && atype.getMunitionType() == AmmoType.M_CLUSTER) {            // Cluster shots break into single point clusters.            bSalvo = true;            hits = wtype.getRackSize();            if ( !bAllShotsHit ) {                hits = Compute.missilesHit( hits );            }            nDamPerHit = 1;        } else if (nShots > 1) {            // this should handle multiple attacks from ultra and rotary ACs            bSalvo = true;            hits = nShots;            if ( !bAllShotsHit ) {                hits = Compute.missilesHit( hits );            }        }        else if (wtype.getAmmoType() == AmmoType.T_GAUSS_HEAVY) {            // HGR does range-dependent damage            if (nRange <= wtype.getShortRange()) {                nDamPerHit = 25;            } else if (nRange <= wtype.getMediumRange()) {                nDamPerHit = 20;            } else {                nDamPerHit = 10;            }        } else if (wtype.hasFlag(WeaponType.F_ENERGY)) {            // Check for Altered Damage from Energy Weapons (MTR, pg.22)            nDamPerHit = wtype.getDamage();            if (game.getOptions().booleanOption("maxtech_altdmg")) {                if (nRange<=1) {                    nDamPerHit++;                } else if (nRange <= wtype.getMediumRange()) {                    // Do NOthing for Short and Medium Range                } else if (nRange <= wtype.getLongRange()) {                    nDamPerHit--;                } else if (nRange <= wtype.getExtremeRange()) {                    nDamPerHit = (int)Math.floor((double)nDamPerHit/2.0);                }            }        }        // Some weapons double the number of hits scored.        if ( wtype.hasFlag(WeaponType.F_DOUBLE_HITS) ) {            hits *= 2;        }        // We've calculated how many hits.  At this point, any missed        // shots damage the building instead of the target.        if ( bMissed ) {            if ( targetInBuilding && bldg != null ) {                // Reduce the number of hits by AMS hits.                if (wr.amsShotDownTotal > 0) {                    for (int i=0; i < wr.amsShotDown.length; i++) {                        int shotDown = Math.min(wr.amsShotDown[i], hits);                        phaseReport.append("\tAMS shoots down ")                                .append(shotDown).append(" missile(s).\n");                    }                    hits -= wr.amsShotDownTotal;                }                // Is the building hit by Inferno rounds?                if ( bInferno && hits > 0 ) {                    // start a fire in the targets hex                    Coords c = target.getPosition();                    Hex h = game.getBoard().getHex(c);                    // Is there a fire in the hex already?                    if ( h.contains( Terrain.FIRE ) ) {                        phaseReport.append( "        " )                            .append( hits )                            .append( " Inferno rounds added to hex " )                            .append( c.getBoardNum() )                            .append( ".\n" );                    } else {                        phaseReport.append( "        " )                            .append( hits )                            .append( " Inferno rounds start fire in hex " )                            .append( c.getBoardNum() )                            .append( ".\n" );                        h.addTerrain(new Terrain(Terrain.FIRE, 1));                    }                    game.board.addInfernoTo                        ( c, InfernoTracker.STANDARD_ROUND, hits );                    sendChangedHex(c);                }                // Damage the building in one big lump.                else {                    // Only report if damage was done to the building.                    int toBldg = hits * nDamPerHit;                    if ( toBldg > 0 ) {                        phaseReport.append( "        " )                            .append( damageBuilding( bldg, toBldg ) )                            .append( "\n" );                    }                } // End rounds-hit            } // End missed-target-in-building            return;        } // End missed-target        // The building shields all units from a certain amount of damage.        // The amount is based upon the building's CF at the phase's start.        int bldgAbsorbs = 0;        if ( targetInBuilding && bldg != null ) {            bldgAbsorbs = (int) Math.ceil( bldg.getPhaseCF() / 10.0 );        }        // All attacks (except from infantry weapons)        // during Mek Swarms hit the same location.        if ( !isWeaponInfantry &&             ae.getSwarmTargetId() == wr.waa.getTargetId() ) {            nCluster = hits;        }        // Battle Armor MGs do one die of damage per hit to PBI.        if ( wtype.getAmmoType() == AmmoType.T_BA_MG &&             (target instanceof Infantry) &&             !(target instanceof BattleArmor) ) {            // ASSUMPTION: Building walls protect infantry from BA MGs.            if ( bldgAbsorbs > 0 ) {                int toBldg = nDamPerHit * hits;                phaseReport.append( hits )                    .append( sSalvoType )                    .append( "hit,\n        but " )                    .append( damageBuilding( bldg,                               Math.min( toBldg, bldgAbsorbs ),                               " absorbs the shots, taking " ) )                    .append( "\n" );                return;            }            nDamPerHit = Compute.d6(hits);            phaseReport.append( "riddles the target with " ).append(                nDamPerHit ).append( sSalvoType ).append( "and " );            hits = 1;        }        // Mech and Vehicle MGs do *DICE* of damage to PBI.        else if (atype != null && atype.hasFlag(AmmoType.F_MG) &&                  !isWeaponInfantry && (target instanceof Infantry) &&                  !(target instanceof BattleArmor) ) {            int dice = wtype.getDamage();            // A building may absorb the entire shot.            if ( nDamPerHit <= bldgAbsorbs ) {                int toBldg = nDamPerHit * hits;                int curCF = bldg.getCurrentCF();                curCF = Math.min( curCF, toBldg );                bldg.setCurrentCF( curCF );                if ( bSalvo ) {                    phaseReport.append( hits )                        .append( sSalvoType )                        .append( "hit,\n" );                } else{                    phaseReport.append( "hits,\n" );                }                phaseReport.append( "        but " )                    .append( damageBuilding( bldg,                               Math.min( toBldg, bldgAbsorbs ),                               " absorbs the shots, taking " ) )                    .append( "\n" );                return;            }            // If a building absorbs partial damage, reduce the dice of damage.            else if ( bldgAbsorbs > 0 ) {                dice -= bldgAbsorbs;            }            nDamPerHit = Compute.d6( dice );            phaseReport.append( "riddles the target with " ).append(                nDamPerHit ).append( sSalvoType ).append( ".\n" );            bSalvo = true;            // If a building absorbed partial damage, report it now            // instead of later and then clear the variable.            if ( bldgAbsorbs > 0 ) {                phaseReport.append( "        " )                    .append( damageBuilding( bldg, bldgAbsorbs ) );                bldgAbsorbs = 0;            }        }        // Report the number of hits.  Infernos have their own reporting        else if (bSalvo && !bInferno) {            phaseReport.append( hits ).append( sSalvoType ).append( "hit" )                .append( toHit.getTableDesc() );            if (bECMAffected) {                phaseReport.append(" (ECM prevents bonus)");            }            else if (bMekStealthActive) {                phaseReport.append(" (active Stealth prevents bonus)");            }            if (nSalvoBonus > 0) {                phaseReport.append(" (w/ +")                    .append(nSalvoBonus)                    .append(" bonus)");            }            phaseReport.append(".");            if (wr.amsShotDownTotal > 0) {                for (int i=0; i < wr.amsShotDown.length; i++) {                    int shotDown = Math.min(wr.amsShotDown[i], hits);                    phaseReport.append("\n\tAMS engages, firing ")                        .append(wr.amsShotDown[i]).append(" shots, shooting down ")                        .append(shotDown).append(" missile(s).");                }                hits -= wr.amsShotDownTotal;                phaseReport.append("\n    ");                if (hits < 1) {                    phaseReport.append("AMS shoots down all incoming missiles!");                } else {                    phaseReport.append(hits);                    if (1 == hits) {                        phaseReport.append(" missile gets through.");                    } else {                        phaseReport.append(" missiles get through.");                    };                };            }        }        // convert the ATM missile damages to LRM type 5 point cluster damage        // done here after AMS has been performed        if (wtype.getAmmoType() == AmmoType.T_ATM)        {            hits = nDamPerHit * hits;            nDamPerHit = 1;        }        // Make sure the player knows when his attack causes no damage.        if ( hits == 0 ) {            phaseReport.append( "attack deals zero damage.\n" );        }        // for each cluster of hits, do a chunk of damage        while (hits > 0) {            int nDamage;            // If the attack was with inferno rounds then            // do heat and fire instead of damage.            if ( bInferno ) {                // AMS can shoot down infernos, too.                if (wr.amsShotDownTotal > 0) {                    for (int i=0; i < wr.amsShotDown.length; i++) {                        int shotDown = Math.min(wr.amsShotDown[i], hits);                        phaseReport.append("\n\tAMS engages, firing ")                                .append(wr.amsShotDown[i]).append(" shots, shooting down ")                                .append(shotDown).append(" missile(s).");                    }                    hits -= wr.amsShotDownTotal;                    phaseReport.append("\n    ");                    if (hits < 1) {                        phaseReport.append("AMS shoots down all incoming missiles!");                    } else {                        phaseReport.append(hits);                        if (1 == hits) {                            phaseReport.append(" missile gets through.");                        } else {                            phaseReport.append(" missiles get through.");                        };                    };                    if ( hits <= 0 ) {                        continue;                    }                }                // targeting a hex for ignition                if( target.getTargetType() == Targetable.TYPE_HEX_IGNITE ||                    target.getTargetType() == Targetable.TYPE_BLDG_IGNITE ) {                    phaseReport.append( "hits with " )                        .append( hits )                        .append( " inferno missles.\n" );                    // Unless there a fire in the hex already, start one.                    Coords c = target.getPosition();                    Hex h = game.getBoard().getHex(c);                    if ( !h.contains( Terrain.FIRE ) ) {                        phaseReport.append( " Fire started in hex " )                            .append( c.getBoardNum() )                            .append( ".\n" );                        h.addTerrain(new Terrain(Terrain.FIRE, 1));                    }                    game.board.addInfernoTo                        ( c, InfernoTracker.STANDARD_ROUND, hits );                    sendChangedHex(c);                    return;                }                // Targeting an entity                if (entityTarget != null ) {                    entityTarget.infernos.add( InfernoTracker.STANDARD_ROUND,                                               hits );                    phaseReport.append( "hits with " )                        .append( hits )                        .append( " inferno missles." );                    phaseReport.append("\n        " )                        .append( target.getDisplayName() )                        .append( " now on fire for ")                        .append( entityTarget.infernos.getTurnsLeftToBurn() )                        .append(" turns.\n");                    // start a fire in the targets hex                    Coords c = target.getPosition();                    Hex h = game.getBoard().getHex(c);                    // Unless there a fire in the hex already, start one.                    if ( !h.contains( Terrain.FIRE ) ) {                        phaseReport.append( " Fire started in hex " )                            .append( c.getBoardNum() )                            .append( ".\n" );                        h.addTerrain(new Terrain(Terrain.FIRE, 1));                    }                    game.board.addInfernoTo                        ( c, InfernoTracker.STANDARD_ROUND, 1 );                    sendChangedHex(c);                    return;                }            } // End is-inferno            // targeting a hex for igniting            if( target.getTargetType() == Targetable.TYPE_HEX_IGNITE ||                target.getTargetType() == Targetable.TYPE_BLDG_IGNITE ) {                if ( !bSalvo ) {                    phaseReport.append("hits!");                }                // We handle Inferno rounds above.                int tn = wtype.getFireTN();                if (tn != TargetRoll.IMPOSSIBLE) {                    if ( bldg != null ) {                        tn += bldg.getType() - 1;                    }                    phaseReport.append( "\n" );                    tryIgniteHex( target.getPosition(), bInferno, tn, true );                }                return;            }            // targeting a hex for clearing            if (target.getTargetType() == Targetable.TYPE_HEX_CLEAR) {                nDamage = nDamPerHit * hits;                if ( !bSalvo ) {                    phaseReport.append("hits!");                }                if (ae instanceof Infantry) {                    phaseReport.append("\n    But infantry cannot try to clear hexes!\n");                    return;                }                phaseReport.append("    Terrain takes " ).append( nDamage ).append( " damage.\n");                // Any clear attempt can result in accidental ignition, even                // weapons that can't normally start fires.  that's weird.                // Buildings can't be accidentally ignited.                if ( bldg == null) {                    boolean alreadyIgnited = game.board.getHex(target.getPosition()).contains(Terrain.FIRE);                    boolean ignited = tryIgniteHex(target.getPosition(), bInferno, 9);                    if (!alreadyIgnited && ignited) return;                }                int tn = 14 - nDamage;                tryClearHex(target.getPosition(), tn);                return;            }            // Targeting a building.            if ( target.getTargetType() == Targetable.TYPE_BUILDING ) {                // The building takes the full brunt of the attack.                nDamage = nDamPerHit * hits;                if ( !bSalvo ) {                    phaseReport.append( "hits." );                }                phaseReport.append( "\n        " )                    .append( damageBuilding( bldg, nDamage ) )                    .append( "\n" );                // Damage any infantry in the hex.                this.damageInfantryIn( bldg, nDamage );                // And we're done!                return;            }            // Battle Armor squads equipped with fire protection            // gear automatically avoid flaming death.            if ( wtype.hasFlag(WeaponType.F_FLAMER) &&                 target instanceof BattleArmor ) {                for ( Enumeration iter = entityTarget.getMisc();                      iter.hasMoreElements(); ) {                    Mounted mount = (Mounted) iter.nextElement();                    EquipmentType equip = mount.getType();                    if ( BattleArmor.FIRE_PROTECTION.equals                         (equip.getInternalName()) ) {                        if ( !bSalvo ) {                            phaseReport.append( "hits." );                        }                        phaseReport.append( "\n        However, " )                            .append(target.getDisplayName() )                            .append( " has fire protection gear so no damage is done.\n" );                        // A building may be damaged, even if the squad is not.                        if ( bldgAbsorbs > 0 ) {                            int toBldg = nDamPerHit * Math.min( bldgAbsorbs,                                                                hits );                            phaseReport.append( "        " )                                .append( damageBuilding( bldg, toBldg ) )                                .append( "\n" );                        }                     return;                    }                }            } // End target-may-be-immune            // Flamers do heat to mechs instead damage if the option is            // available and the mode is set.            if ( entityTarget != null &&                 (entityTarget instanceof Mech) &&                 wtype.hasFlag(WeaponType.F_FLAMER) &&                 game.getOptions().booleanOption("flamer_heat") &&                 wtype.hasModes() &&                 weapon.curMode().equals("Heat") ) {                nDamage = nDamPerHit * hits;                if ( !bSalvo ) {                    phaseReport.append( "hits." );                }                phaseReport.append("\n        Target gains ").append(nDamage).append(" more heat during heat phase.");                entityTarget.heatBuildup += nDamage;                hits = 0;            }            else if (entityTarget != null) {                 HitData hit = entityTarget.rollHitLocation                     ( toHit.getHitTable(),                       toHit.getSideTable(),                       wr.waa.getAimedLocation(),                       wr.waa.getAimingMode() );                // If a leg attacks hit a leg that isn't                // there, then hit the other leg.                if ( wtype.getInternalName().equals("LegAttack") &&                     entityTarget.getInternal(hit) <= 0 ) {                    if ( hit.getLocation() == Mech.LOC_RLEG ) {                        hit = new HitData( Mech.LOC_LLEG );                    }                    else {                        hit = new HitData( Mech.LOC_RLEG );                    }                }                // Mine Launchers automatically hit the                // CT of a Mech or the front of a Tank.                if ( wtype.getInternalName()                     .equals(BattleArmor.MINE_LAUNCHER) ) {                    if ( target instanceof Mech ) {                        hit = new HitData( Mech.LOC_CT );                    }                    else { // te instanceof Tank                        hit = new HitData( Tank.LOC_FRONT );                    }                }                // Each hit in the salvo get's its own hit location.                if (!bSalvo) {                    phaseReport.append("hits" ).append( toHit.getTableDesc() ).append( " " ).                            append( entityTarget.getLocationAbbr(hit));                    if (hit.hitAimedLocation()) {                        phaseReport.append("(hit aimed location)");                    }                }                // Special weapons do criticals instead of damage.                if ( nDamPerHit == WeaponType.DAMAGE_SPECIAL ) {                    // Do criticals.                    String specialDamage = criticalEntity( entityTarget, hit.getLocation() );                    // Replace "no effect" results with 4 points of damage.                    if ( specialDamage.endsWith(" no effect.") ) {                        // ASSUMPTION: buildings CAN'T absorb *this* damage.                        specialDamage = damageEntity(entityTarget, hit, 4);                    }                    else {                        specialDamage = "\n" + specialDamage;                    }                    // Report the result                    phaseReport.append( specialDamage );                }                else {                    // Resolve damage normally.                    nDamage = nDamPerHit * Math.min(nCluster, hits);                    // A building may be damaged, even if the squad is not.                    if ( bldgAbsorbs > 0 ) {                        int toBldg = Math.min( bldgAbsorbs, nDamage );                        nDamage -= toBldg;                        phaseReport.append( "\n        " )                            .append( damageBuilding( bldg, toBldg ) );                    }                    // A building may absorb the entire shot.                    if ( nDamage == 0 ) {                        phaseReport.append( "\n        " )                            .append( entityTarget.getDisplayName() )                            .append( " suffers no damage." );                    } else if (bFragmentation) {                        // If it's a frag missile...                        phaseReport.append                            ( damageEntity(entityTarget, hit, nDamage, false, 1) );                    } else if (bFlechette) {                        // If it's a frag missile...                        phaseReport.append                            ( damageEntity(entityTarget, hit, nDamage, false, 2) );                    } else {                        if ((atype != null) && (atype.getMunitionType() == AmmoType.M_ARMOR_PIERCING))                            hit.makeArmorPiercing(atype);                        phaseReport.append                            ( damageEntity(entityTarget, hit, nDamage) );                    }                }                hits -= nCluster;                creditKill(entityTarget, ae);            }        } // Handle the next cluster.        phaseReport.append("\n");    }