Package megamek.common

Examples of megamek.common.LosEffects


                    break; // 1st in list is OK
                if (act.getEntityId() == attackerId)
                    return false; // can only declare searchlight once!
            }
        }
        LosEffects los = LosEffects.calculateLos(game, attackerId, target);
        return los.canSee();
    }
View Full Code Here


                                                                // target
        for (Coords c : in) {
            for (Enumeration<Entity> e = game.getEntities(c); e
                    .hasMoreElements();) {
                Entity en = e.nextElement();
                LosEffects los = LosEffects.calculateLos(game, getEntityId(),
                        en);
                if (los.canSee()) {
                    en.setIlluminated(true);
                    r = new Report(3455);
                    r.subject = this.getEntityId();
                    r.newlines = 1;
                    r.add(en.getDisplayName());
View Full Code Here

                                                                // target
        for (Coords c : in) {
            for (Enumeration<Entity> e = game.getEntities(c); e
                    .hasMoreElements();) {
                Entity en = e.nextElement();
                LosEffects los = LosEffects.calculateLos(game, getEntityId(),
                        en);
                if (los.canSee() && en.equals(who))
                    return true;
            }
        }
        return false;
    }
View Full Code Here

            moveCursor(secondLOSSprite, c2);
            Entity ae = chooseEntity(c1);
            Entity te = chooseEntity(c2);

            StringBuffer message = new StringBuffer();
            LosEffects le;
            if ((ae == null) || (te == null)) {
                boolean mechInFirst = GUIPreferences.getInstance()
                .getMechInFirst();
                boolean mechInSecond = GUIPreferences.getInstance()
                .getMechInSecond();
                LosEffects.AttackInfo ai = new LosEffects.AttackInfo();
                ai.attackPos = c1;
                ai.targetPos = c2;
                ai.attackHeight = mechInFirst ? 1 : 0;
                ai.targetHeight = mechInSecond ? 1 : 0;
                ai.attackAbsHeight = game.getBoard().getHex(c1).floor()
                + ai.attackHeight;
                ai.targetAbsHeight = game.getBoard().getHex(c2).floor()
                + ai.targetHeight;
                le = LosEffects.calculateLos(game, ai);
                message
                .append(Messages
                        .getString(
                                "BoardView1.Attacker", new Object[] { //$NON-NLS-1$
                                        mechInFirst ? Messages
                                                .getString("BoardView1.Mech") : Messages.getString("BoardView1.NonMech"), //$NON-NLS-1$ //$NON-NLS-2$
                                                c1.getBoardNum() }));
                message
                .append(Messages
                        .getString(
                                "BoardView1.Target", new Object[] { //$NON-NLS-1$
                                        mechInSecond ? Messages
                                                .getString("BoardView1.Mech") : Messages.getString("BoardView1.NonMech"), //$NON-NLS-1$ //$NON-NLS-2$
                                                c2.getBoardNum() }));
            } else {
                le = LosEffects.calculateLos(game, ae.getId(), te);
                message.append(Messages.getString(
                        "BoardView1.Attacker", new Object[] { //$NON-NLS-1$
                                ae.getDisplayName(), c1.getBoardNum() }));
                message.append(Messages.getString(
                        "BoardView1.Target", new Object[] { //$NON-NLS-1$
                                te.getDisplayName(), c2.getBoardNum() }));
            }
            if (le.isBlocked()) {
                message.append(Messages.getString(
                        "BoardView1.LOSBlocked", new Object[] { //$NON-NLS-1$
                                new Integer(c1.distance(c2)) }));
            } else {
                message.append(Messages.getString(
                        "BoardView1.LOSNotBlocked", new Object[] { //$NON-NLS-1$
                                new Integer(c1.distance(c2)) }));
                if (le.getHeavyWoods() > 0) {
                    message.append(Messages.getString(
                            "BoardView1.HeavyWoods", new Object[] { //$NON-NLS-1$
                                    new Integer(le.getHeavyWoods()) }));
                }
                if (le.getLightWoods() > 0) {
                    message.append(Messages.getString(
                            "BoardView1.LightWoods", new Object[] { //$NON-NLS-1$
                                    new Integer(le.getLightWoods()) }));
                }
                if (le.getLightSmoke() > 0) {
                    message.append(Messages.getString(
                            "BoardView1.LightSmoke", new Object[] { //$NON-NLS-1$
                                    new Integer(le.getLightSmoke()) }));
                }
                if (le.getHeavySmoke() > 0) {
                    message.append(Messages.getString(
                            "BoardView1.HeavySmoke", new Object[] { //$NON-NLS-1$
                                    new Integer(le.getHeavySmoke()) }));
                }
                if (le.isTargetCover()) {
                    message.append(Messages
                            .getString("BoardView1.TargetPartialCover")); //$NON-NLS-1$
                }
                if (le.isAttackerCover()) {
                    message.append(Messages
                            .getString("BoardView1.AttackerPartialCover")); //$NON-NLS-1$
                }
            }
            AlertDialog alert = new AlertDialog((Frame)SwingUtilities.getAncestorOfClass(Frame.class, this), Messages
View Full Code Here

            // surface to fire
            ae.setElevation(0);
        }

        // check LOS (indirect LOS is from the spotter)
        LosEffects los;
        ToHitData losMods;
        if (!isIndirect || (isIndirect && (spotter == null) )) {
            los = LosEffects.calculateLos(game, attackerId, target);

            if (ae.hasActiveEiCockpit()) {
                if (los.getLightWoods() > 0) {
                    eistatus = 2;
                } else {
                    eistatus = 1;
                }
            }

            if ( (wtype instanceof MekMortarWeapon) && isIndirect ){
                los.setArcedAttack(true);
            }

            losMods = los.losModifiers(game, eistatus);
        } else {
            los = LosEffects.calculateLos(game, spotter.getId(), target);
            // do not count attacker partial cover in indirect fire
            los.setAttackerCover(LosEffects.COVER_NONE);

            if (spotter.hasActiveEiCockpit()) {
                if (los.getLightWoods() > 0) {
                    eistatus = 2;
                } else {
                    eistatus = 1;
                }
            }

            if ( (wtype instanceof MekMortarWeapon) && isIndirect ){
                los.setArcedAttack(true);
            }

            losMods = los.losModifiers(game);
        }

        if (multiPurposeelevationHack) {
            // and descend back to depth 1
            ae.setElevation(-1);
        }

        // if LOS is blocked, block the shot
        if ((losMods.getValue() == TargetRoll.IMPOSSIBLE) && !isArtilleryIndirect) {
            return losMods.getDesc();
        }

        // Weapon in arc?
        if (!Compute.isInArc(game, attackerId, weaponId, target)) {
            return "Target not in arc.";
        }

        // Protomech can fire MGA only into front arc, TW page 137
        if (!Compute.isInArc(ae.getPosition(), ae.getFacing(), target.getPosition(), Compute.ARC_FORWARD)
                && wtype.hasFlag(WeaponType.F_MGA) && (ae instanceof Protomech)) {
            return "Protomech can fire MGA only into front arc.";
        }

        //for spheroid dropships in atmosphere, nose and aft mounted weapons can only be fired
        //at units two elevations different
        //TODO: awaiting rules clarification on forums
        if((ae instanceof Aero) && ((Aero)ae).isSpheroid() && game.getBoard().inAtmosphere()) {
            int altDif = ae.getElevation() - target.getElevation();
            if((weapon.getLocation() == Aero.LOC_NOSE) && (altDif > -3)) {
                return "Target is too low";
            }
            if((weapon.getLocation() == Aero.LOC_AFT) && (altDif < 3)) {
                return "Target is too high";
            }
        }

        // Must target infantry in buildings from the inside.
        if (targetInBuilding && (te instanceof Infantry)
                && (null == los.getThruBldg())) {
            return "Attack on infantry crosses building exterior wall.";
        }

        if ((wtype.getAmmoType() == AmmoType.T_NARC)
                || (wtype.getAmmoType() == AmmoType.T_INARC)) {
            if (targetInBuilding) {
                return "Narc pods cannot be fired into or inside buildings.";
            }
            if (target instanceof Infantry) {
                return "Narc pods cannot be used to attack infantry.";
            }
        }

        //is the attack originating from underwater
        boolean underWater = (ae.getLocationStatus(weapon.getLocation()) == ILocationExposureStatus.WET) || (wtype instanceof SRTWeapon) || (wtype instanceof LRTWeapon);

        // attacker partial cover means no leg weapons
        if (los.isAttackerCover()
                && ae.locationIsLeg(weapon.getLocation())
                && !underWater) {
            return "Nearby terrain blocks leg weapons.";
        }
View Full Code Here

            Entity ae = chooseEntity(c1);
            Entity te = chooseEntity(c2);

            StringBuffer message = new StringBuffer();
            LosEffects le;
            if ((ae == null) || (te == null)) {
                boolean mechInFirst = GUIPreferences.getInstance().getMechInFirst();
                boolean mechInSecond = GUIPreferences.getInstance().getMechInSecond();
                LosEffects.AttackInfo ai = new LosEffects.AttackInfo();
                ai.attackPos = c1;
                ai.targetPos = c2;
                ai.attackHeight = mechInFirst ? 1 : 0;
                ai.targetHeight = mechInSecond ? 1 : 0;
                ai.attackAbsHeight = game.getBoard().getHex(c1).floor() + ai.attackHeight;
                ai.targetAbsHeight = game.getBoard().getHex(c2).floor() + ai.targetHeight;
                le = LosEffects.calculateLos(game, ai);
                message
                        .append(Messages
                                .getString(
                                        "BoardView1.Attacker", new Object[] { //$NON-NLS-1$
                                                mechInFirst ? Messages.getString("BoardView1.Mech") : Messages.getString("BoardView1.NonMech"), //$NON-NLS-1$ //$NON-NLS-2$
                                                c1.getBoardNum() }));
                message
                        .append(Messages
                                .getString(
                                        "BoardView1.Target", new Object[] { //$NON-NLS-1$
                                                mechInSecond ? Messages
                                                        .getString("BoardView1.Mech") : Messages.getString("BoardView1.NonMech"), //$NON-NLS-1$ //$NON-NLS-2$
                                                c2.getBoardNum() }));
            } else {
                le = LosEffects.calculateLos(game, ae.getId(), te);
                message.append(Messages.getString("BoardView1.Attacker", new Object[] { //$NON-NLS-1$
                        ae.getDisplayName(), c1.getBoardNum() }));
                message.append(Messages.getString("BoardView1.Target", new Object[] { //$NON-NLS-1$
                        te.getDisplayName(), c2.getBoardNum() }));
            }
            if (le.isBlocked()) {
                message.append(Messages.getString("BoardView1.LOSBlocked", new Object[] { //$NON-NLS-1$
                        new Integer(c1.distance(c2)) }));
            } else {
                message.append(Messages.getString("BoardView1.LOSNotBlocked", new Object[] { //$NON-NLS-1$
                        new Integer(c1.distance(c2)) }));
                if (le.getHeavyWoods() > 0) {
                    message.append(Messages.getString("BoardView1.HeavyWoods", new Object[] { //$NON-NLS-1$
                            new Integer(le.getHeavyWoods()) }));
                }
                if (le.getLightWoods() > 0) {
                    message.append(Messages.getString("BoardView1.LightWoods", new Object[] { //$NON-NLS-1$
                            new Integer(le.getLightWoods()) }));
                }
                if (le.getLightSmoke() > 0) {
                    message.append(Messages.getString("BoardView1.LightSmoke", new Object[] { //$NON-NLS-1$
                            new Integer(le.getLightSmoke()) }));
                }
                if (le.getHeavySmoke() > 0) {
                    message.append(Messages.getString("BoardView1.HeavySmoke", new Object[] { //$NON-NLS-1$
                            new Integer(le.getHeavySmoke()) }));
                }
                if (le.isTargetCover()) {
                    message.append(Messages.getString("BoardView1.TargetPartialCover")); //$NON-NLS-1$
                }
                if (le.isAttackerCover()) {
                    message.append(Messages.getString("BoardView1.AttackerPartialCover")); //$NON-NLS-1$
                }
            }
            JOptionPane.showMessageDialog(getRootPane(), message.toString(), Messages
                    .getString("BoardView1.LOSTitle"), JOptionPane.INFORMATION_MESSAGE);
View Full Code Here

            MPMelevationHack = true;
            // surface to fire
            ae.setElevation(0);
        }
        // check LOS (indirect LOS is from the spotter)
        LosEffects los;
        ToHitData losMods;

        if (!isIndirect || (isIndirect && (spotter == null))) {
            los = LosEffects.calculateLos(game, attackerId, target);

            if (ae.hasActiveEiCockpit()) {
                if (los.getLightWoods() > 0) {
                    eistatus = 2;
                } else {
                    eistatus = 1;
                }
            }

            if ((wtype instanceof MekMortarWeapon) && isIndirect) {
                los.setArcedAttack(true);
            }

            losMods = los.losModifiers(game, eistatus);
            if ((atype != null)
                    && ((atype.getAmmoType() == AmmoType.T_LRM_TORPEDO)
                            || (atype.getAmmoType() == AmmoType.T_SRM_TORPEDO) || (((atype
                            .getAmmoType() == AmmoType.T_SRM)
                            || (atype.getAmmoType() == AmmoType.T_MRM)
                            || (atype.getAmmoType() == AmmoType.T_LRM) || (atype
                            .getAmmoType() == AmmoType.T_MML)) && (munition == AmmoType.M_TORPEDO)))
                    && (los.getMinimumWaterDepth() < 1)) {
                return new ToHitData(TargetRoll.IMPOSSIBLE,
                        "Torpedos must follow water their entire LOS");
            }
        } else {
            los = LosEffects.calculateLos(game, spotter.getId(), target);
            // do not count attacker partial cover in indirect fire
            los.setAttackerCover(LosEffects.COVER_NONE);

            if (!narcSpotter && spotter.hasActiveEiCockpit()) {
                if (los.getLightWoods() > 0) {
                    eistatus = 2;
                } else {
                    eistatus = 1;
                }
            }

            if (wtype instanceof MekMortarWeapon) {
                los.setArcedAttack(true);
            }

            losMods = los.losModifiers(game);
        }
        if (MPMelevationHack) {
            // return to depth 1
            ae.setElevation(-1);
        }

        // Leg attacks, Swarm attacks, and
        // Mine Launchers don't use gunnery.
        if (Infantry.LEG_ATTACK.equals(wtype.getInternalName())) {
            toHit = Compute.getLegAttackBaseToHit(ae, te);
            if (toHit.getValue() == TargetRoll.IMPOSSIBLE) {
                return toHit;
            }

            // If the attacker has Assault claws, give a -1 modifier.
            // We can stop looking when we find our first match.
            for (Mounted mount : ae.getMisc()) {
                EquipmentType equip = mount.getType();
                if (BattleArmor.ASSAULT_CLAW.equals(equip.getInternalName())) {
                    toHit.addModifier(-1, "attacker has assault claws");
                    break;
                }
            }
        } else if (Infantry.SWARM_MEK.equals(wtype.getInternalName())) {
            toHit = Compute.getSwarmMekBaseToHit(ae, te);
            if (toHit.getValue() == TargetRoll.IMPOSSIBLE) {
                return toHit;
            }

            if (te instanceof Tank) {
                toHit.addModifier(-2, "target is vehicle");
            }

            // If the attacker has assault claws, give a -1 modifier.
            // We can stop looking when we find our first match.
            for (Mounted mount : ae.getMisc()) {
                EquipmentType equip = mount.getType();
                if (BattleArmor.ASSAULT_CLAW.equals(equip.getInternalName())) {
                    toHit.addModifier(-1, "attacker has assault claws");
                    break;
                }
            }

            //MD Infantry with grappler/magnets get bonus
            if(ae.crew.getOptions().booleanOption("grappler")) {
                toHit.addModifier(-2, "MD Grapple/Magnet");
            }

            // If the defender carries mechanized BA, they can fight off the
            // swarm
            for (Entity e : te.getExternalUnits()) {
                if (e instanceof BattleArmor) {
                    BattleArmor ba = (BattleArmor) e;
                    int def = ba.getShootingStrength();
                    int att = ((Infantry) ae).getShootingStrength();
                    if (!(ae instanceof BattleArmor)) {
                        if (att >= 28) {
                            att = 5;
                        } else if (att >= 24) {
                            att = 4;
                        } else if (att >= 21) {
                            att = 3;
                        } else if (att >= 18) {
                            att = 2;
                        } else {
                            att = 1;
                        }
                    }
                    def = def + 2 - att;
                    if (def > 0) {
                        toHit.addModifier(def, "Defending mechanized BA");
                    }
                }
            }
        } else if (Infantry.STOP_SWARM.equals(wtype.getInternalName())) {
            // Can't stop if we're not swarming, otherwise automatic.
            return new ToHitData(TargetRoll.AUTOMATIC_SUCCESS,
                    "End swarm attack.");
        } else if (BattleArmor.MINE_LAUNCHER.equals(wtype.getInternalName())) {
            // Mine launchers can not hit infantry.
            toHit = new ToHitData(8, "magnetic mine attack");
        } else if ((atype != null) && (atype.getAmmoType() == AmmoType.T_BA_MICRO_BOMB)) {
            if (ae.getPosition().equals(target.getPosition())) {
                // Micro bombs use anti-mech skill
                return new ToHitData(ae.getCrew().getPiloting(), "anti-mech skill");
            } else {
                return new ToHitData(TargetRoll.IMPOSSIBLE, "out of range");
            }
        }
        // Swarming infantry always hit their target, but
        // they can only target the Mek they're swarming.
        else if ((te != null) && (ae.getSwarmTargetId() == te.getId())) {
            int side = te instanceof Tank ? ToHitData.SIDE_RANDOM
                    : ToHitData.SIDE_FRONT;
            if (ae instanceof BattleArmor) {
                return new ToHitData(TargetRoll.AUTOMATIC_SUCCESS,
                        "Attack during swarm.", ToHitData.HIT_SWARM, side);
            } else {
                return new ToHitData(TargetRoll.AUTOMATIC_SUCCESS,
                        "Attack during swarm.",
                        ToHitData.HIT_SWARM_CONVENTIONAL, side);
            }
        } else if (isArtilleryFLAK) {
            toHit = new ToHitData(9, "artillery FLAK");
        } else {
            toHit = new ToHitData(ae.crew.getGunnery(), "gunnery skill");
            if (game.getOptions().booleanOption("rpg_gunnery")) {
                if (wtype.hasFlag(WeaponType.F_ENERGY)) {
                    toHit = new ToHitData(ae.crew.getGunneryL(),
                            "gunnery (L) skill");
                }
                if (wtype.hasFlag(WeaponType.F_MISSILE)) {
                    toHit = new ToHitData(ae.crew.getGunneryM(),
                            "gunnery (M) skill");
                }
                if (wtype.hasFlag(WeaponType.F_BALLISTIC)) {
                    toHit = new ToHitData(ae.crew.getGunneryB(),
                            "gunnery (B) skill");
                }
            }
            if (ae.getTaserFeedBackRounds() > 0) {
                toHit.addModifier(1, "Taser feedback");
            }
            if (ae.getTaserInterferenceRounds() > 0) {
                toHit.addModifier(ae.getTaserInterference(), "Taser interference");
            }
         }

        // Engineer's fire extinguisher has fixed to hit number,
        // Note that coolant trucks make a regular attack.
        if (wtype.hasFlag(WeaponType.F_EXTINGUISHER)) {
            toHit = new ToHitData(8, "fire extinguisher");
            if (((target instanceof Entity)
                    && ((Entity) target).infernos.isStillBurning())
                    || ((target instanceof Tank)
                    && ((Tank) target).isInfernoFire())) {
                toHit.addModifier(2, "inferno fire");
            }
            if ((Targetable.TYPE_HEX_EXTINGUISH == target.getTargetType())
                    && game.getBoard().isInfernoBurning(target.getPosition())) {
                toHit.addModifier(2, "inferno fire");
            }
            return toHit;
        }

        // if we're spotting for indirect fire, add +1
        if (ae.isSpotting()) {
            toHit.addModifier(+1, "attacker is spotting for indirect LRM fire");
        }

        //fatigue
        if(game.getOptions().booleanOption("tacops_fatigue") && ae.crew.isGunneryFatigued(game.getRoundCount())) {
            toHit.addModifier(1,"fatigue");
        }

        // If a unit is suffering from electromagnetic interference, they get a
        // blanket +2.
        // Sucks to be them.
        if (ae.isSufferingEMI()) {
            toHit.addModifier(+2, "electromagnetic interference");
        }

        // evading bonuses (
        if ((target.getTargetType() == Targetable.TYPE_ENTITY) && te.isEvading()) {
            toHit.addModifier(te.getEvasionBonus(), "target is evading");
        }

        // ghost target modifier
        if (game.getOptions().booleanOption("tacops_ghost_target")) {
            int ghostTargetMod = Compute.getGhostTargetNumber(ae, ae
                    .getPosition(), target.getPosition());
            if ((ghostTargetMod > -1)
                    && !((ae instanceof Infantry) && !(ae instanceof BattleArmor))) {
                int bapMod = 0;
                if (ae.hasBAP()) {
                    bapMod = 1;
                }
                int tcMod = 0;
                if (ae.hasTargComp()
                        && wtype.hasFlag(WeaponType.F_DIRECT_FIRE)
                        && (!usesAmmo || !(((atype.getAmmoType() == AmmoType.T_AC_LBX) || (atype
                                .getAmmoType() == AmmoType.T_AC_LBX_THB)) && (atype
                                .getMunitionType() == AmmoType.M_CLUSTER)))) {
                    tcMod = 2;
                }
                int ghostTargetMoF = (ae.getCrew().getSensorOps() + ghostTargetMod)
                        - (ae.getGhostTargetOverride() + bapMod + tcMod);
                if (ghostTargetMoF > 0) {
                    toHit.addModifier(Math.min(4, ghostTargetMoF / 2),
                            "ghost targets");
                }
            }
        }

        //Space ECM
        if(game.getBoard().inSpace() && game.getOptions().booleanOption("stratops_ecm")) {
            int ecm = Compute.getLargeCraftECM(ae, ae.getPosition(), target.getPosition());
            if(!ae.isLargeCraft()) {
                ecm += Compute.getSmallCraftECM(ae, ae.getPosition(), target.getPosition());
            }
            ecm = Math.min(4,ecm);
            int eccm = 0;
            if(ae.isLargeCraft()) {
                eccm = ((Aero)ae).getECCMBonus();
            }
            if(ecm > 0) {
                toHit.addModifier(ecm, "ECM");
                if(eccm > 0) {
                    toHit.addModifier(-1*Math.min(ecm, eccm), "ECCM");
                }
            }
        }

        // Aeros may suffer from criticals
        if (ae instanceof Aero) {
            Aero aero = (Aero) ae;

            // sensor hits
            int sensors = aero.getSensorHits();

            if(!aero.isCapitalFighter()) {
                if ((sensors > 0) && (sensors < 3)) {
                    toHit.addModifier(sensors, "sensor damage");
                }
                if (sensors > 2) {
                    toHit.addModifier(+5, "sensors destroyed");
                }
            }

            // FCS hits
            int fcs = aero.getFCSHits();

            if ((fcs > 0) && !aero.isCapitalFighter()) {
                toHit.addModifier(fcs * 2, "fcs damage");
            }

            // pilot hits
            int pilothits = aero.getCrew().getHits();
            if ((pilothits > 0) && !aero.isCapitalFighter()) {
                toHit.addModifier(pilothits, "pilot hits");
            }

            // out of control
            if (aero.isOutControlTotal()) {
                toHit.addModifier(+2, "out-of-control");
            }

            if (aero instanceof Jumpship) {
                Jumpship js = (Jumpship) aero;
                int cic = js.getCICHits();
                if (cic > 0) {
                    toHit.addModifier(cic * 2, "CIC damage");
                }
            }

            // targeting mods for evasive action by large craft
            if (aero.isEvading()) {
                toHit.addModifier(+2, "attacker is evading");
            }



            // check for heavy gauss rifle on fighter of small craft
            if ((weapon.getType() instanceof ISHGaussRifle) && (ae instanceof Aero)
                    && !(ae instanceof Dropship) && !(ae instanceof Jumpship)) {
                toHit.addModifier(+1, "weapon to-hit modifier");
            }

            // check for NOE
            // if the target is NOE in atmosphere
            if (game.getBoard().inAtmosphere()
                    && (1 == (ae.getElevation() - game.getBoard().getHex(
                            ae.getPosition()).ceiling()))) {
                if (ae.isOmni()) {
                    toHit.addModifier(+1, "attacker is flying at NOE (omni)");
                } else {
                    toHit.addModifier(+2, "attacker is flying at NOE");
                }
            }

            // check for particular kinds of weapons in weapon bays
            if (ae.usesWeaponBays()) {

                // any heavy lasers
                if (wtype.getAtClass() == WeaponType.CLASS_LASER) {
                    for (int wId : weapon.getBayWeapons()) {
                        Mounted bweap = ae.getEquipment(wId);
                        WeaponType bwtype = (WeaponType) bweap.getType();
                        if ((bwtype.getInternalName().indexOf("Heavy") != -1)
                                && (bwtype.getInternalName().indexOf("Laser") != -1)) {
                            toHit.addModifier(+1, "bay contains heavy laser");
                            break;
                        }
                    }
                }
                // barracuda and piranha missiles
                else if (wtype.getAtClass() == WeaponType.CLASS_CAPITAL_MISSILE) {
                    boolean onlyBarracuda = true;
                    boolean onlyPiranha = true;
                    for (int wId : weapon.getBayWeapons()) {
                        Mounted bweap = ae.getEquipment(wId);
                        Mounted bammo = bweap.getLinked();
                        if (bammo != null) {
                            AmmoType batype = (AmmoType) bammo.getType();
                            if (batype.getAmmoType() != AmmoType.T_BARRACUDA) {
                                onlyBarracuda = false;
                            }
                            if ((batype.getAmmoType() != AmmoType.T_PIRANHA) && (batype.getAmmoType() != AmmoType.T_BARRACUDA)) {
                                onlyPiranha = false;
                            }
                        }
                    }
                    if (onlyBarracuda) {
                        toHit.addModifier(-2, "barracuda missile");
                    } else if (onlyPiranha) {
                        toHit.addModifier(-1, "piranha missile");
                    }
                }
                // barracuda missiles in an AR10 launcher (must all be
                // barracuda)
                else if (wtype.getAtClass() == WeaponType.CLASS_AR10) {
                    boolean onlyBarracuda = true;
                    for (int wId : weapon.getBayWeapons()) {
                        Mounted bweap = ae.getEquipment(wId);
                        Mounted bammo = bweap.getLinked();
                        if (bammo != null) {
                            AmmoType batype = (AmmoType) bammo.getType();
                            if (!batype.hasFlag(AmmoType.F_AR10_BARRACUDA)) {
                                onlyBarracuda = false;
                            }
                        }
                    }
                    if (onlyBarracuda) {
                        toHit.addModifier(-2, "barracuda missile");
                    }
                }
                // LBX cluster
                else if (wtype.getAtClass() == WeaponType.CLASS_LBX_AC) {
                    boolean onlyCluster = true;
                    for (int wId : weapon.getBayWeapons()) {
                        Mounted bweap = ae.getEquipment(wId);
                        Mounted bammo = bweap.getLinked();
                        if (bammo != null) {
                            AmmoType batype = (AmmoType) bammo.getType();
                            if (batype.getMunitionType() != AmmoType.M_CLUSTER) {
                                onlyCluster = false;
                                break;
                            }
                        }
                    }
                    if (onlyCluster) {
                        toHit.addModifier(-1, "cluster ammo");
                    }
                }
            }
        }

        if(wtype.hasFlag(WeaponType.F_ANTI_SHIP) && (target instanceof Entity) && (te.getWeight() < 500)) {
            toHit.addModifier(4, "Anti-ship missile at a small target");
        }

        if (target instanceof Aero) {

            Aero a = (Aero) target;

            // is the target at zero velocity
            if (a.getCurrentVelocity() == 0) {
                toHit.addModifier(-2, "target is not moving");
            }

            // capital weapon (except missiles) penalties at small targets
            if (wtype.isCapital()
                    && (wtype.getAtClass() != WeaponType.CLASS_CAPITAL_MISSILE)
                    && (wtype.getAtClass() != WeaponType.CLASS_AR10)
                    && !te.isLargeCraft()) {
                //check to see if we are using AAA mode
                int aaaMod = 0;
                if(wtype.hasModes() && weapon.curMode().equals("AAA")) {
                    aaaMod = 2;
                }
                if(wtype.isSubCapital()) {
                    toHit.addModifier(3-aaaMod, "sub-capital weapon at small target");
                } else {
                    toHit.addModifier(5-aaaMod, "capital weapon at small target");
                }
            }

            //AAA mode makes targeting large craft more difficult
            if(wtype.hasModes() && weapon.curMode().equals("AAA") && te.isLargeCraft()) {
                toHit.addModifier(+1, "AAA mode at large craft");
            }

            //check for bracketing mode
            if(wtype.hasModes() && weapon.curMode().equals("Bracket 80%")) {
                toHit.addModifier(-1, "Bracketing 80%");
            }
            if(wtype.hasModes() && weapon.curMode().equals("Bracket 60%")) {
                toHit.addModifier(-2, "Bracketing 60%");
            }
            if(wtype.hasModes() && weapon.curMode().equals("Bracket 40%")) {
                toHit.addModifier(-3, "Bracketing 40%");
            }

            //sensor shadows
            if(game.getOptions().booleanOption("stratops_sensor_shadow") && game.getBoard().inSpace()) {
                for(Entity en : Compute.getAdjacentEntitiesAlongAttack(ae.getPosition(), target.getPosition(), game)) {
                    if(!en.isEnemyOf(a) && en.isLargeCraft()
                            && ((en.getWeight() - a.getWeight()) >= -100000.0)) {
                        toHit.addModifier(+1, "Sensor Shadow");
                        break;
                    }
                }
                for (Enumeration<Entity> i = game.getEntities(target.getPosition()); i.hasMoreElements();) {
                    Entity en = i.nextElement();
                    if(!en.isEnemyOf(a) && en.isLargeCraft() && !en.equals(a)
                            && ((en.getWeight() - a.getWeight()) >= -100000.0)) {
                        toHit.addModifier(+1, "Sensor Shadow");
                        break;
                    }
                }
            }

        }

        // Vehicles may suffer from criticals
        if (ae instanceof Tank) {
            Tank tank = (Tank) ae;
            if (tank.isCommanderHit()) {
                if (ae instanceof VTOL) {
                    toHit.addModifier(+1, "copilot injured");
                } else {
                    toHit.addModifier(+1, "commander injured");
                }
            }
            int sensors = tank.getSensorHits();
            if (sensors > 0) {
                toHit.addModifier(sensors, "sensor damage");
            }
            if (tank.isStabiliserHit(weapon.getLocation())) {
                toHit.addModifier(Compute.getAttackerMovementModifier(game,
                        tank.getId()).getValue(), "stabiliser damage");
            }
        }

        if (ae.hasFunctionalArmAES(weapon.getLocation()) && !weapon.isSplit()) {
            toHit.addModifier(-1, "AES modifer");
        }

        if (ae.hasShield()) {
            // active shield has already been checked as it makes shots
            // impossible
            // time to check passive defense and no defense

            if (ae.hasPassiveShield(weapon.getLocation(), weapon
                    .isRearMounted())) {
                toHit.addModifier(+2, "weapon hampered by passive shield");
            } else if (ae.hasNoDefenseShield(weapon.getLocation())) {
                toHit.addModifier(+1, "weapon hampered by shield");
            }
        }
        // if we have BAP with MaxTech rules, and there are woods in the
        // way, and we are within BAP range, we reduce the BTH by 1
        if (game.getOptions().booleanOption("tacops_bap")
                && !isIndirect
                && (te != null)
                && ae.hasBAP()
                && (ae.getBAPRange() >= Compute.effectiveDistance(game, ae, te))
                && !Compute.isAffectedByECM(ae, ae.getPosition(), te
                        .getPosition())
                && (game.getBoard().getHex(te.getPosition()).containsTerrain(
                        Terrains.WOODS)
                        || game.getBoard().getHex(te.getPosition())
                                .containsTerrain(Terrains.JUNGLE)
                        || (los.getLightWoods() > 0) || (los.getHeavyWoods() > 0) || (los
                        .getUltraWoods() > 0))) {
            toHit
                    .addModifier(-1,
                            "target in/behind woods and attacker has BAP");
        }

        // Is the pilot a weapon specialist?
        if (ae.crew.getOptions().stringOption("weapon_specialist").equals(
                wtype.getName())) {
            toHit.addModifier(-2, "weapon specialist");
        }

        // Has the pilot the appropriate gunnery skill?
        if (ae.crew.getOptions().booleanOption("gunnery_laser")
                && wtype.hasFlag(WeaponType.F_ENERGY)) {
            toHit.addModifier(-1, "Gunnery/Laser");
        }

        if (ae.crew.getOptions().booleanOption("gunnery_ballistic")
                && wtype.hasFlag(WeaponType.F_BALLISTIC)) {
            toHit.addModifier(-1, "Gunnery/Ballistic");
        }

        if (ae.crew.getOptions().booleanOption("gunnery_missile")
                && wtype.hasFlag(WeaponType.F_MISSILE)) {
            toHit.addModifier(-1, "Gunnery/Missile");
        }

        // check for VDNI
        if (ae.crew.getOptions().booleanOption("vdni")
                || ae.crew.getOptions().booleanOption("bvdni")) {
            toHit.addModifier(-1, "VDNI");
        }

        //check for pl-masc
        if (ae.crew.getOptions().booleanOption("pl_masc")
                && (ae.getMovementMode() == IEntityMovementMode.INF_LEG)) {
            toHit.addModifier(+1, "PL-MASC");
        }

        //check for cyber eye laser sighting
        if (ae.crew.getOptions().booleanOption("cyber_eye_tele")) {
            toHit.addModifier(-1, "MD laser-sighting");
        }

        // If it has a torso-mounted cockpit and two head sensor hits or three
        // sensor hits...
        // It gets a =4 penalty for being blind!
        if ((ae instanceof Mech)
                && (((Mech) ae).getCockpitType() == Mech.COCKPIT_TORSO_MOUNTED)) {
            int sensorHits = ae.getBadCriticals(CriticalSlot.TYPE_SYSTEM,
                    Mech.SYSTEM_SENSORS, Mech.LOC_HEAD);
            if (sensorHits == 2) {
                toHit.addModifier(4,
                        "Head Sensors Destroyed for Torso-Mounted Cockpit");
            }
        }

        // industrial cockpit: +1 to hit
        if ((ae instanceof Mech) && (((Mech)ae).getCockpitType() == Mech.COCKPIT_INDUSTRIAL)) {
            toHit.addModifier(1, "industrial cockpit without advanced fire control");
        }
        // primitive industrial cockpit: +2 to hit
        if ((ae instanceof Mech) && (((Mech)ae).getCockpitType() == Mech.COCKPIT_PRIMITIVE_INDUSTRIAL)) {
            toHit.addModifier(2, "primitive industrial cockpit without advanced fire control");
        }

        // primitive industrial cockpit with advanced firing control: +1 to hit
        if ((ae instanceof Mech) && (((Mech)ae).getCockpitType() == Mech.COCKPIT_PRIMITIVE) && ((Mech)ae).isIndustrial()) {
            toHit.addModifier(1, "primitive industrial cockpit with advanced fire control");
        }

        // Do we use Listen-Kill ammo from War of 3039 sourcebook?
        if (!isECMAffected
                && (atype != null)
                && ((atype.getAmmoType() == AmmoType.T_LRM)
                        || (atype.getAmmoType() == AmmoType.T_MML) || (atype
                        .getAmmoType() == AmmoType.T_SRM))
                && (atype.getMunitionType() == AmmoType.M_LISTEN_KILL)
                && !((te != null) && te.isClan())) {
            toHit.addModifier(-1, "Listen-Kill ammo");
        }

        // determine some more variables
        int aElev = ae.getElevation();
        int tElev = target.getElevation();
        int distance = Compute.effectiveDistance(game, ae, target);

        toHit.append(AbstractAttackAction.nightModifiers(game, target, atype, ae, true));

        // weather mods (not in space)
        int weatherMod = game.getPlanetaryConditions().getWeatherHitPenalty(ae);
        if ((weatherMod != 0) && !game.getBoard().inSpace()) {
            toHit.addModifier(weatherMod, game.getPlanetaryConditions()
                    .getWeatherCurrentName());
        }

        // wind mods (not in space)
        if (!game.getBoard().inSpace()) {
            int windCond = game.getPlanetaryConditions().getWindStrength();
            if (windCond == PlanetaryConditions.WI_MOD_GALE) {
                if (wtype.hasFlag(WeaponType.F_MISSILE)) {
                    toHit.addModifier(1, PlanetaryConditions
                            .getWindDisplayableName(windCond));
                }
            } else if (windCond == PlanetaryConditions.WI_STRONG_GALE) {
                if (wtype.hasFlag(WeaponType.F_BALLISTIC)) {
                    toHit.addModifier(1, PlanetaryConditions
                            .getWindDisplayableName(windCond));
                } else if (wtype.hasFlag(WeaponType.F_MISSILE)) {
                    toHit.addModifier(2, PlanetaryConditions
                            .getWindDisplayableName(windCond));
                }
            } else if (windCond == PlanetaryConditions.WI_STORM) {
                if (wtype.hasFlag(WeaponType.F_BALLISTIC)) {
                    toHit.addModifier(2, PlanetaryConditions
                            .getWindDisplayableName(windCond));
                } else if (wtype.hasFlag(WeaponType.F_MISSILE)) {
                    toHit.addModifier(3, PlanetaryConditions
                            .getWindDisplayableName(windCond));
                }
            } else if (windCond == PlanetaryConditions.WI_TORNADO_F13) {
                if (wtype.hasFlag(WeaponType.F_ENERGY)) {
                    toHit.addModifier(2, PlanetaryConditions
                            .getWindDisplayableName(windCond));
                } else {
                    toHit.addModifier(3, PlanetaryConditions
                            .getWindDisplayableName(windCond));
                }
            } else if (windCond == PlanetaryConditions.WI_TORNADO_F4) {
                toHit.addModifier(3, PlanetaryConditions
                        .getWindDisplayableName(windCond));
            }
        }

        // fog mods (not in space)
        if (wtype.hasFlag(WeaponType.F_ENERGY)
                && !game.getBoard().inSpace()
                && (game.getPlanetaryConditions().getFog() == PlanetaryConditions.FOG_HEAVY)) {
            toHit.addModifier(1, "heavy fog");
        }

        //blowind sand mods
        if(wtype.hasFlag(WeaponType.F_ENERGY) && !game.getBoard().inSpace()
                && game.getPlanetaryConditions().isSandBlowing()
                && (game.getPlanetaryConditions().getWindStrength() > PlanetaryConditions.WI_LIGHT_GALE)) {
            toHit.addModifier(1, "blowing sand");
        }

        // gravity mods (not in space)
        if (!game.getBoard().inSpace()) {
            int mod = (int) Math.ceil(Math.abs((game.getPlanetaryConditions()
                    .getGravity() - 1.0f) / 0.2f));
            if ((mod != 0)
                    && (wtype.hasFlag(WeaponType.F_BALLISTIC) || wtype
                            .hasFlag(WeaponType.F_MISSILE))) {
                toHit.addModifier(mod, "gravity");
            }
        }

        // Electro-Magnetic Interference
        if (game.getPlanetaryConditions().hasEMI()
                && !((ae instanceof Infantry) && !(ae instanceof BattleArmor))) {
            toHit.addModifier(2, "EMI");
        }

        // handle LAM speial rules

        // a temporary variable so I don't need to keep casting.
        LandAirMech lam;
        if (ae instanceof LandAirMech) {
            lam = (LandAirMech) ae;
            if (lam.isInMode(LandAirMech.MODE_AIRMECH)) {
                toHit.addModifier(2, "Attacker is a Flying Airmek");
            }
        }
        if (target instanceof LandAirMech) {
            lam = (LandAirMech) target;
            if (lam.isInMode(LandAirMech.MODE_AIRMECH) && lam.isFlying()) {
                if (ae.isFlying()) {
                    toHit.addModifier(-1, "Target is a flying Airmek"); // and
                    // we
                    // are
                    // too.
                } else {
                    toHit.addModifier(4, "Target is a flying Airmek");// and
                    // we
                    // are
                    // on
                    // the
                    // ground
                }
            }
        }

        // Handle direct artillery attacks.
        if (isArtilleryDirect) {
            if (!isArtilleryFLAK) {
                toHit.addModifier(4, "direct artillery modifer");
            }
            toHit.append(Compute.getAttackerMovementModifier(game, attackerId));
            toHit.append(losMods);
            toHit.append(Compute.getSecondaryTargetMod(game, ae, target));
            // actuator & sensor damage to attacker
            toHit.append(Compute.getDamageWeaponMods(ae, weapon));
            // heat
            if (ae.getHeatFiringModifier() != 0) {
                toHit.addModifier(ae.getHeatFiringModifier(), "heat");
            }

            // weapon to-hit modifier
            if (wtype instanceof VariableSpeedPulseLaserWeapon) {
                int nRange = ae.getPosition().distance(target.getPosition());
                int[] nRanges = wtype.getRanges(weapon);
                int modifier = wtype.getToHitModifier();

                if (nRange <= nRanges[RangeType.RANGE_SHORT]) {
                    modifier -= RangeType.RANGE_SHORT;
                } else if (nRange <= nRanges[RangeType.RANGE_MEDIUM]) {
                    modifier -= RangeType.RANGE_MEDIUM;
                } else if (nRange <= nRanges[RangeType.RANGE_LONG]) {
                    modifier -= RangeType.RANGE_LONG;
                } else {
                    modifier = 0;
                }

                toHit.addModifier(modifier, "weapon to-hit modifier");
            } else if (wtype instanceof ISBombastLaser) {
                int damage = (int) Math.ceil((Compute.dialDownDamage(weapon,
                        wtype) - 7) / 2);

                if (damage > 0) {
                    toHit.addModifier(damage, "weapon to-hit modifier");
                }
            } else if (wtype.getToHitModifier() != 0) {
                toHit.addModifier(wtype.getToHitModifier(),
                        "weapon to-hit modifier");
            }

            // ammo to-hit modifier
            if (usesAmmo && (atype.getToHitModifier() != 0)) {
                toHit.addModifier(atype.getToHitModifier(),
                        "ammunition to-hit modifier");
            }
            if (isHoming) {
                return new ToHitData(4, "Homing shot");
            }

            if (game.getEntity(attackerId).getOwner().getArtyAutoHitHexes()
                    .contains(target.getPosition())
                    && !isArtilleryFLAK) {
                return new ToHitData(TargetRoll.AUTOMATIC_SUCCESS,
                        "Artillery firing at designated artillery target.");
            }
            return toHit;
        }
        if (isArtilleryIndirect) {
            if (isHoming) {
                return new ToHitData(4, "Homing shot (will miss if TAG misses)");
            }

            if (game.getEntity(attackerId).getOwner().getArtyAutoHitHexes()
                    .contains(target.getPosition())) {
                return new ToHitData(TargetRoll.AUTOMATIC_SUCCESS,
                        "Artillery firing at designated artillery target.");
            }
            toHit.addModifier(7, "indirect artillery modifier");
            int adjust = ae.aTracker.getModifier(weapon, target.getPosition());
            if (adjust == TargetRoll.AUTOMATIC_SUCCESS) {
                return new ToHitData(TargetRoll.AUTOMATIC_SUCCESS,
                        "Artillery firing at target that's been hit before.");
            } else if (adjust != 0) {
                toHit.addModifier(adjust, "adjusted fire");
            }
            return toHit;

        }

        // Attacks against adjacent buildings automatically hit.
        if ((distance == 1)
                && ((target.getTargetType() == Targetable.TYPE_BUILDING)
                        || (target.getTargetType() == Targetable.TYPE_BLDG_IGNITE)
                        || (target.getTargetType() == Targetable.TYPE_FUEL_TANK)
                        || (target.getTargetType() == Targetable.TYPE_FUEL_TANK_IGNITE) || (target instanceof GunEmplacement))) {
            return new ToHitData(TargetRoll.AUTOMATIC_SUCCESS,
                    "Targeting adjacent building.");
        }

        // Attacks against buildings from inside automatically hit.
        if ((null != los.getThruBldg())
                && ((target.getTargetType() == Targetable.TYPE_BUILDING)
                        || (target.getTargetType() == Targetable.TYPE_BLDG_IGNITE)
                        || (target.getTargetType() == Targetable.TYPE_FUEL_TANK)
                        || (target.getTargetType() == Targetable.TYPE_FUEL_TANK_IGNITE) || (target instanceof GunEmplacement))) {
            return new ToHitData(TargetRoll.AUTOMATIC_SUCCESS,
                    "Targeting building from inside (are you SURE this is a good idea?).");
        }

        // add range mods
        toHit.append(Compute.getRangeMods(game, ae, weaponId, target));

        // If it's an anti-air system, add mods for that
        if ((ae.getTargSysType() == MiscType.T_TARGSYS_ANTI_AIR)
                && (target instanceof Entity)) {
            if (target instanceof VTOL) {
                toHit.addModifier(-2, "anti-air targetting system vs. VTOL");
            } else {
                toHit.addModifier(1,
                        "anti-air targetting system vs. non-aerial unit");
            }
        }

        // Battle Armor targets are hard for Meks and Tanks to hit.
        if (!isAttackerInfantry && (te != null) && (te instanceof BattleArmor)) {
            toHit.addModifier(1, "battle armor target");
        }

        // Ejected MechWarriors are harder to hit
        if ((te != null) && (te instanceof MechWarrior)) {
            toHit.addModifier(2, "ejected MechWarrior target");
        }

        // Indirect fire has a +1 mod
        if (isIndirect) {
            toHit.addModifier(1, "indirect fire");
        }

        if (wtype instanceof MekMortarWeapon) {
            if (isIndirect) {
                if (spotter == null) {
                    toHit.addModifier(2, "no spotter");
                }
            } else {
                toHit.addModifier(3, "direct fire");
            }
        }

        // attacker movement
        toHit.append(Compute.getAttackerMovementModifier(game, attackerId));

        // target movement
        if (te != null) {
            ToHitData thTemp = Compute.getTargetMovementModifier(game, target
                    .getTargetId());
            toHit.append(thTemp);
            toSubtract += thTemp.getValue();

            // semiguided ammo negates this modifier, if TAG succeeded
            if ((atype != null)
                    && ((atype.getAmmoType() == AmmoType.T_LRM) || (atype
                            .getAmmoType() == AmmoType.T_MML))
                    && (atype.getMunitionType() == AmmoType.M_SEMIGUIDED)
                    && (te.getTaggedBy() != -1)) {
                int nAdjust = thTemp.getValue();
                if (nAdjust > 0) {
                    toHit.append(new ToHitData(-nAdjust,
                            "Semi-guided ammo vs tagged target"));
                }
            }
            // precision ammo reduces this modifier
            else if ((atype != null)
                    && ((atype.getAmmoType() == AmmoType.T_AC) || (atype
                            .getAmmoType() == AmmoType.T_LAC))
                    && (atype.getMunitionType() == AmmoType.M_PRECISION)) {
                int nAdjust = Math.min(2, thTemp.getValue());
                if (nAdjust > 0) {
                    toHit.append(new ToHitData(-nAdjust, "Precision Ammo"));
                }
            }
        }

        // Armor Piercing ammo is a flat +1
        if ((atype != null)
                && ((atype.getAmmoType() == AmmoType.T_AC) || (atype.getAmmoType() == AmmoType.T_LAC))
                && (atype.getMunitionType() == AmmoType.M_ARMOR_PIERCING)) {
            toHit.addModifier(1, "Armor-Piercing Ammo");
        }

        // spotter movement, if applicable
        if (isIndirect) {
            // semiguided ammo negates this modifier, if TAG succeeded
            if ((atype != null)
                    && ((atype.getAmmoType() == AmmoType.T_LRM) || (atype
                            .getAmmoType() == AmmoType.T_MML))
                    && (atype.getMunitionType() == AmmoType.M_SEMIGUIDED)
                    && (te.getTaggedBy() != -1)) {
                toHit
                        .addModifier(-1,
                                "semiguided ignores spotter movement & indirect fire penalties");
            } else if (!narcSpotter && (spotter != null)) {
                toHit.append(Compute.getSpotterMovementModifier(game, spotter
                        .getId()));
                if (spotter.isAttackingThisTurn()) {
                    toHit.addModifier(1,
                            "spotter is making an attack this turn");
                }
            }
        }

        // attacker terrain
        toHit.append(Compute.getAttackerTerrainModifier(game, attackerId));

        // target terrain, not applicable when delivering minefields
        if (target.getTargetType() != Targetable.TYPE_MINEFIELD_DELIVER) {
            toHit.append(Compute.getTargetTerrainModifier(game, target,
                    eistatus, inSameBuilding));
            toSubtract += Compute.getTargetTerrainModifier(game, target,
                    eistatus, inSameBuilding).getValue();
        }

        // target in water?
        IHex targHex = game.getBoard().getHex(target.getPosition());
        if ((target.getTargetType() == Targetable.TYPE_ENTITY)
                && targHex.containsTerrain(Terrains.WATER)
                && (targHex.terrainLevel(Terrains.WATER) == 1) && (targEl == 0)
                && (te.height() > 0)) { // target in partial water
            los.setTargetCover(los.getTargetCover()
                    | LosEffects.COVER_HORIZONTAL);
            losMods = los.losModifiers(game, eistatus);
        }

        if ((target instanceof Infantry) && !wtype.hasFlag(WeaponType.F_FLAMER)) {
            if (targHex.containsTerrain(Terrains.FORTIFIED)
                    || (((Infantry) target).getDugIn() == Infantry.DUG_IN_COMPLETE)) {
                toHit.addModifier(2, "infantry dug in");
            }
        }

        // add in LOS mods that we've been keeping
        toHit.append(losMods);

        if ((te != null)
                && te.isHullDown()
                && (((te instanceof Mech)
                        && (los.getTargetCover() > LosEffects.COVER_NONE)) || ((te instanceof Tank)
                        && targHex.containsTerrain(Terrains.FORTIFIED)
                        && (te.sideTable(ae.getPosition()) == ToHitData.SIDE_FRONT)))) {
            toHit.addModifier(2, "Hull down target");
        }

        // secondary targets modifier,
        // if this is not a iNarc Nemesis confused attack
        if (!isNemesisConfused) {
            toHit.append(Compute.getSecondaryTargetMod(game, ae, target,
                    exchangeSwarmTarget));
        }

        // heat
        if (ae.getHeatFiringModifier() != 0) {
            toHit.addModifier(ae.getHeatFiringModifier(), "heat");
        }

        // actuator & sensor damage to attacker
        toHit.append(Compute.getDamageWeaponMods(ae, weapon));

        // target immobile
        ToHitData immobileMod = Compute.getImmobileMod(target, aimingAt,
                aimingMode);
        if (immobileMod != null) {
            toHit.append(immobileMod);
            toSubtract += immobileMod.getValue();
        }

        // attacker prone
        toHit.append(Compute.getProneMods(game, ae, weaponId));

        // target prone
        ToHitData proneMod = null;
        if ((te != null) && te.isProne()) {
            // easier when point-blank
            if (distance <= 1) {
                // TW, pg. 221: Swarm Mek attacks apply prone/immobile mods as
                // normal.
                proneMod = new ToHitData(-2, "target prone and adjacent");
            } else {
                // Harder at range.
                proneMod = new ToHitData(1, "target prone and at range");
            }
        }
        if (proneMod != null) {
            toHit.append(proneMod);
            toSubtract += proneMod.getValue();
        }

        // weapon to-hit modifier
        if (wtype instanceof VariableSpeedPulseLaserWeapon) {
            int nRange = ae.getPosition().distance(target.getPosition());
            int[] nRanges = wtype.getRanges(weapon);
            int modifier = wtype.getToHitModifier();

            if (nRange <= nRanges[RangeType.RANGE_SHORT]) {
                modifier += RangeType.RANGE_SHORT;
            } else if (nRange <= nRanges[RangeType.RANGE_MEDIUM]) {
                modifier += RangeType.RANGE_MEDIUM;
            } else if (nRange <= nRanges[RangeType.RANGE_LONG]) {
                modifier += RangeType.RANGE_LONG;
            } else {
                modifier = 0;
            }

            toHit.addModifier(modifier, "weapon to-hit modifier");
        } else if (wtype instanceof ISBombastLaser) {
            double damage = Compute.dialDownDamage(weapon, wtype);
            damage = Math.ceil((damage - 7) / 2);

            if (damage > 0) {
                toHit.addModifier((int) damage, "weapon to-hit modifier");
            }
        } else if (wtype.getToHitModifier() != 0) {
            toHit.addModifier(wtype.getToHitModifier(),
                    "weapon to-hit modifier");
        }

        // ammo to-hit modifier
        if ((te != null)
                && ((te.getMovementMode() == IEntityMovementMode.VTOL)
                        || (te.getMovementMode() == IEntityMovementMode.AERODYNE)
                        || (te.getMovementMode() == IEntityMovementMode.AIRMECH)
                        || (te.getMovementMode() == IEntityMovementMode.AEROSPACE)
                        || (te.getMovementMode() == IEntityMovementMode.SPHEROID) || (te
                        .getMovementMode() == IEntityMovementMode.WIGE))
                && (atype != null)
                && ((((atype.getAmmoType() == AmmoType.T_AC_LBX)
                        || (atype.getAmmoType() == AmmoType.T_AC_LBX_THB)
                        || (atype.getAmmoType() == AmmoType.T_SBGAUSS))
                        && (atype.getMunitionType() == AmmoType.M_CLUSTER))
                    || (atype.getMunitionType() == AmmoType.M_FLAK)
                    || (atype.getAmmoType() == AmmoType.T_HAG))
                   && (te.getElevation() > 0)
                && (te.getElevation() > game.getBoard().getHex(te.getPosition())
                        .terrainLevel(Terrains.BLDG_ELEV))
                && (te.getElevation() != game.getBoard()
                        .getHex(te.getPosition()).terrainLevel(
                                Terrains.BRIDGE_ELEV))) {
            toHit.addModifier(-2, "flak to-hit modifier");
        }
        if (usesAmmo && (atype.getToHitModifier() != 0)) {
            toHit.addModifier(atype.getToHitModifier(),
                    "ammunition to-hit modifier");
        }

        // add iNarc bonus
        if (isINarcGuided) {
            toHit.addModifier(-1, "iNarc homing pod");
        }

        if (isHaywireINarced) {
            toHit.addModifier(1, "iNarc Haywire pod");
        }

        // `Screen launchers hit automatically (if in range)
        if ((toHit.getValue() != TargetRoll.IMPOSSIBLE)
                && ((wtype.getAmmoType() == AmmoType.T_SCREEN_LAUNCHER) || (wtype instanceof ScreenLauncherBayWeapon))) {
            return new ToHitData(TargetRoll.AUTOMATIC_SUCCESS,
                    "Screen launchers always hit");
        }

        // Heat Seeking Missles
        if (bHeatSeeking) {
            if (te == null) {
                if ((target.getTargetType() == Targetable.TYPE_BUILDING)
                        || (target.getTargetType() == Targetable.TYPE_BLDG_IGNITE)
                        || (target.getTargetType() == Targetable.TYPE_FUEL_TANK)
                        || (target.getTargetType() == Targetable.TYPE_FUEL_TANK_IGNITE)
                        || (target instanceof GunEmplacement)) {
                    IHex hexTarget = game.getBoard().getHex(
                            target.getPosition());
                    if (hexTarget.containsTerrain(Terrains.FIRE)) {
                        toHit.addModifier(-2, "ammunition to-hit modifier");
                    }
                }
            } else if ((te instanceof Aero)
                    && (toHit.getSideTable() == ToHitData.SIDE_REAR)) {
                toHit.addModifier(-2, "ammunition to-hit modifier");
            } else if (te.heat == 0) {
                toHit.addModifier(1, "ammunition to-hit modifier");
            } else {
                toHit.addModifier(-te.getHeatMPReduction(),
                        "ammunition to-hit modifier");
            }

            if (!(ae instanceof Aero)
                    && LosEffects.hasFireBetween(ae.getPosition(), target
                            .getPosition(), game)) {
                toHit.addModifier(2, "fire between target and attacker");
            }
        }

        if (bFTL) {
            toHit.addModifier(2, "ammunition to-hit modifier");
        }

        if (bApollo) {
            toHit.addModifier(-1, "Apollo FCS");
        }

        // Heavy infantry have +1 penalty
        if ((ae instanceof Infantry)
                && ae.hasWorkingMisc(MiscType.F_TOOLS, MiscType.S_HEAVY_ARMOR)) {
            toHit.addModifier(1, "Heavy Armor");
        }

        //penalty for void sig system
        if(ae.isVoidSigActive()) {
            toHit.addModifier(1, "Void signature active");
        }

        // add targeting computer (except with LBX cluster ammo)
        if ((aimingMode == IAimingModes.AIM_MODE_TARG_COMP)
                && (aimingAt != Entity.LOC_NONE)) {
            if (ae.hasActiveEiCockpit()) {
                if (ae.hasTargComp()) {
                    toHit.addModifier(2,
                            "aiming with targeting computer & EI system");
                } else {
                    toHit.addModifier(6, "aiming with EI system");
                }
            } else {
                toHit.addModifier(3, "aiming with targeting computer");
            }
        } else {
            if (ae.hasTargComp()
                    && wtype.hasFlag(WeaponType.F_DIRECT_FIRE)
                    && (!usesAmmo || !(((atype.getAmmoType() == AmmoType.T_AC_LBX) || (atype
                            .getAmmoType() == AmmoType.T_AC_LBX_THB)) && (atype
                            .getMunitionType() == AmmoType.M_CLUSTER)))) {
                toHit.addModifier(-1, "targeting computer");
            }
        }

        // Change hit table for elevation differences inside building.
        if ((null != los.getThruBldg()) && (aElev != tElev)) {

            // Tanks get hit in a random side.
            if (target instanceof Tank) {
                toHit.setSideTable(ToHitData.SIDE_RANDOM);
            } else if (target instanceof Mech) {
                // Meks have special tables for shots from above and below.
                if (aElev > tElev) {
                    toHit.setHitTable(ToHitData.HIT_ABOVE);
                } else {
                    toHit.setHitTable(ToHitData.HIT_BELOW);
                }
            }

        }

        // Aeros in atmosphere can hit above and below
        if ((ae instanceof Aero) && (target instanceof Aero)
                && game.getBoard().inAtmosphere()) {
            if ((aElev - tElev) > 2) {
                toHit.setHitTable(ToHitData.HIT_ABOVE);
            } else if ((tElev - aElev) > 2) {
                toHit.setHitTable(ToHitData.HIT_BELOW);
            }
        }

        //is this attack originating from underwater
        //TODO: assuming that torpedoes are underwater attacks even if fired from surface vessel, awaiting rules clarification
        //http://www.classicbattletech.com/forums/index.php/topic,48744.0.html
        boolean underWater = (ae.getLocationStatus(weapon.getLocation()) == ILocationExposureStatus.WET) || (wtype instanceof SRTWeapon) || (wtype instanceof LRTWeapon);

        // Change hit table for partial cover, accomodate for partial
        // underwater(legs)
        if (los.getTargetCover() != LosEffects.COVER_NONE) {
            if (underWater
                    && (targHex.containsTerrain(Terrains.WATER) && (targEl == 0) && (te
                            .height() > 0))) {
                // weapon underwater, target in partial water
                toHit.setHitTable(ToHitData.HIT_PARTIAL_COVER);
                toHit.setCover(LosEffects.COVER_UPPER);
            } else {
                if (game.getOptions().booleanOption("tacops_partial_cover")) {
                    toHit.setHitTable(ToHitData.HIT_PARTIAL_COVER);
                    toHit.setCover(los.getTargetCover());
                } else {
                    toHit.setHitTable(ToHitData.HIT_PARTIAL_COVER);
                    toHit.setCover(LosEffects.COVER_HORIZONTAL);
                }
            }
            // XXX what to do about GunEmplacements with partial cover?
        }

        //change hit table for surface vessels hit by underwater attacks
        if(underWater && targHex.containsTerrain(Terrains.WATER) && (null != te)
                && te.isSurfaceNaval()) {
            toHit.setHitTable(ToHitData.HIT_UNDERWATER);
        }

        // factor in target side
        if (isAttackerInfantry && (0 == distance)) {
            // Infantry attacks from the same hex are resolved against the
            // front.
            toHit.setSideTable(ToHitData.SIDE_FRONT);
        } else {
            toHit.setSideTable(Compute.targetSideTable(ae, target));
        }

        if (target instanceof Aero) {

            // hit locations for spheroids in atmosphere are handled differently
            // TODO: awaiting rules clarification on forums
            // http://www.classicbattletech.com/forums/index.php/topic,29329.0.
            // html
            // Until then assume that above/below are actually nose/aft
            if (((Aero) target).isSpheroid() && game.getBoard().inAtmosphere()) {
                if (toHit.getHitTable() == ToHitData.HIT_ABOVE) {
                    toHit.setSideTable(ToHitData.SIDE_FRONT);
                    toHit.setHitTable(ToHitData.HIT_NORMAL);
                }
                if (toHit.getHitTable() == ToHitData.HIT_BELOW) {
                    toHit.setSideTable(ToHitData.SIDE_REAR);
                    toHit.setHitTable(ToHitData.HIT_NORMAL);
                }
            } else {
                // get mods for direction of attack
                int side = toHit.getSideTable();
                // if this is an aero attack using advanced movement rules then
                // determine side differently
                if ((target instanceof Aero) && game.useVectorMove()) {
                    boolean usePrior = false;
                    Coords attackPos = ae.getPosition();
                    if(game.getBoard().inSpace() && ae.getPosition().equals(target.getPosition())) {
                        if(((Aero)ae).shouldMoveBackHex((Aero)target)) {
                            attackPos = ae.getPriorPosition();
                        }
                        usePrior = ((Aero)target).shouldMoveBackHex((Aero)ae);
                    }
                    side = ((Entity) target).chooseSide(attackPos,usePrior);
                }
                if (side == ToHitData.SIDE_FRONT) {
                    toHit.addModifier(+1, "attack against nose");
                }
                if ((side == ToHitData.SIDE_LEFT) || (side == ToHitData.SIDE_RIGHT)) {
                    toHit.addModifier(+2, "attack against side");
                }
            }
        }

        // deal with grapples
        if (target instanceof Entity) {
            int grapple = ((Entity) target).getGrappled();
            if (grapple != Entity.NONE) {
                if ((grapple == ae.getId())
                        && (((Entity) target).getGrappleSide() == Entity.GRAPPLE_BOTH)) {
                    toHit.addModifier(-4, "target grappled");
                } else if ((grapple == ae.getId())
                        && (((Entity) target).getGrappleSide() != Entity.GRAPPLE_BOTH)) {
                    toHit.addModifier(-2, "target grappled (Chain Whip)");
                } else if (!exchangeSwarmTarget) {
                    toHit.addModifier(1, "CQC, possible friendly fire");
                } else {
                    // this -1 cancels the original +1
                    toHit.addModifier(-1, "friendly fire");
                    return toHit;
                }
            }
        }

        // remove old target movement and terrain mods,
        // add those for new target.
        if (exchangeSwarmTarget) {
            toHit.addModifier(-toSubtract, "original target mods");
            toHit.append(Compute
                    .getImmobileMod(oldTarget, aimingAt, aimingMode));
            toHit.append(Compute.getTargetMovementModifier(game, oldTarget.getId()));
            toHit.append(Compute.getTargetTerrainModifier(game, game
                    .getEntity(oldTarget.getId()), eistatus, inSameBuilding));
            toHit.setCover(LosEffects.COVER_NONE);
            distance = Compute.effectiveDistance(game, ae, oldTarget);
            LosEffects swarmlos = LosEffects.calculateLos(game, te.getId(), oldTarget);
         // reset cover
            if (swarmlos.getTargetCover() != LosEffects.COVER_NONE) {
                if (game.getOptions().booleanOption("tacops_partial_cover")) {
                    toHit.setHitTable(ToHitData.HIT_PARTIAL_COVER);
                    toHit.setCover(swarmlos.getTargetCover());
                } else {
                    toHit.setHitTable(ToHitData.HIT_PARTIAL_COVER);
                    toHit.setCover(LosEffects.COVER_HORIZONTAL);
                }
            }
View Full Code Here

                        "Defender in depth 2+ water");
            }
        }

        // calc & add attacker los mods
        LosEffects los = LosEffects.calculateLos(game, ae.getId(), te);
        toHita.append(los.losModifiers(game));
        // save variables
        pc = los.isTargetCover();
        apc = los.isAttackerCover();
        // reverse attacker & target partial cover & calc defender los mods
        int temp = los.getTargetCover();
        los.setTargetCover(los.getAttackerCover());
        los.setAttackerCover(temp);
        toHitd.append(los.losModifiers(game));

        // heatBuildup
        if (ae.getHeatFiringModifier() != 0) {
            toHita.addModifier(ae.getHeatFiringModifier(), "heatBuildup");
        }
View Full Code Here

                    done.put(eq.getName(), null);
                }
            }
        } else {
            // ...otherwise, use the generic LOS tool
            LosEffects le = LosEffects.calculateLos(game, ai);
            if (le.isBlocked()) {
                out.add(Messages.getString("BoardView1.LOSBlocked", new Object[]{ //$NON-NLS-1$
                    new Integer(src.distance(coords))}));
            } else {
                out.add(Messages.getString("BoardView1.LOSNotBlocked", new Object[]{ //$NON-NLS-1$
                        new Integer(src.distance(coords))}));
                if (le.getHeavyWoods() > 0) {
                    out.add(Messages.getString("BoardView1.HeavyWoods", new Object[]{ //$NON-NLS-1$
                            new Integer(le.getHeavyWoods())}));
                }
                if (le.getLightWoods() > 0) {
                    out.add(Messages.getString("BoardView1.LightWoods", new Object[]{ //$NON-NLS-1$
                            new Integer(le.getLightWoods())}));
                }
                if (le.getLightSmoke() > 0) {
                    out.add(Messages.getString("BoardView1.LightSmoke", new Object[]{ //$NON-NLS-1$
                            new Integer(le.getLightSmoke())}));
                }
                if (le.getHeavySmoke() > 0) {
                    if (game.getOptions().booleanOption("maxtech_fire")) { //$NON-NLS-1$
                        out.add(Messages.getString("BoardView1.HeavySmoke", new Object[]{ //$NON-NLS-1$
                                new Integer(le.getHeavySmoke())}));
                    }
                    else {
                        out.add(Messages.getString("BoardView1.Smoke", new Object[]{ //$NON-NLS-1$
                                new Integer(le.getHeavySmoke())}));
                    }
                }
                if (le.isTargetCover()) {
                    out.add(Messages.getString("BoardView1.TargetPartialCover")); //$NON-NLS-1$
                }
                if (le.isAttackerCover()) {
                    out.add(Messages.getString("BoardView1.AttackerPartialCover")); //$NON-NLS-1$
                }
            }
        }
View Full Code Here

            if (te instanceof Mech) {
                // Bug #1585497: Check for partial cover
                int m = missiles;
                for (int i = 0; i < m; i++) {
                    int roll = Compute.d6(2);
                    LosEffects le = LosEffects.calculateLos(game, ae.getId(), t);
                    if (te.removePartialCoverHits(roll, le.getTargetCover(), Compute.targetSideTable(ae, t))) {
                        missiles--;
                    }
                }
                if (missiles != m) {
                    r = new Report(3403);
View Full Code Here

TOP

Related Classes of megamek.common.LosEffects

Copyright © 2018 www.massapicom. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.