📄 compute.java
字号:
// allow naval units on surface to be attacked from above or below Entity te = null; if (target instanceof Entity) { te = (Entity) target; if (targBottom == 0 && UnitType.determineUnitTypeCode(te) == UnitType.NAVAL) { targetInPartialWater = true; } } // allow naval units to target underwater units, // torpedo tubes are mounted underwater if ((targetUnderwater || wtype.getAmmoType() == AmmoType.T_LRM_TORPEDO || wtype.getAmmoType() == AmmoType.T_SRM_TORPEDO) && UnitType.determineUnitTypeCode(ae) == UnitType.NAVAL) { weaponUnderwater = true; weaponRanges = wtype.getWRanges(); } // allow ice to be cleared from below if(targHex.containsTerrain(Terrains.WATER) && target.getTargetType() == Targetable.TYPE_HEX_CLEAR) { targetInPartialWater = true; } if (weaponUnderwater) { weaponRanges = wtype.getWRanges(); // HACK on ranges: for those without underwater range, // long == medium; iteration in rangeBracket() allows this if (weaponRanges[RangeType.RANGE_SHORT] == 0) { return new ToHitData(ToHitData.IMPOSSIBLE, "Weapon cannot fire underwater."); } if (!targetUnderwater && !targetInPartialWater) { // target on land or over water return new ToHitData(ToHitData.IMPOSSIBLE, "Weapon underwater, but not target."); } // special case: mechs can only fire upper body weapons at surface // naval if (te != null && UnitType.determineUnitTypeCode(te) == UnitType.NAVAL && ae instanceof Mech && ae.height() > 0 && ae.getElevation() == -1) { return new ToHitData(ToHitData.IMPOSSIBLE, "Partially submerged mech cannot fire leg weapons at surface naval vessels."); } } else if (targetUnderwater) { return new ToHitData(ToHitData.IMPOSSIBLE, "Target underwater, but not weapon."); } else if (wtype.getAmmoType() == AmmoType.T_LRM_TORPEDO || wtype.getAmmoType() == AmmoType.T_SRM_TORPEDO) { // Torpedos only fire underwater. return new ToHitData(ToHitData.IMPOSSIBLE, "Weapon can only fire underwater."); } // determine base distance & range bracket int distance = effectiveDistance(game, ae, target); int range = RangeType.rangeBracket(distance, weaponRanges, useExtremeRange); // short circuit if at zero range or out of range if (range == RangeType.RANGE_OUT) { return new ToHitData(ToHitData.AUTOMATIC_FAIL, "Target out of range"); } if (distance == 0 && !isAttackerInfantry && !(ae instanceof Mech && ((Mech)ae).getGrappled() == target.getTargetId())) { return new ToHitData(ToHitData.AUTOMATIC_FAIL, "Only infantry shoot at zero range"); } // find any c3 spotters that could help Entity c3spotter = findC3Spotter(game, ae, target); if (isIndirect) { c3spotter = ae; // no c3 when using indirect fire } if (isIndirect && game.getOptions().booleanOption("indirect_fire") && !game.getOptions().booleanOption("indirect_always_possible") && LosEffects.calculateLos(game, ae.getId(), target).canSee()) { return new ToHitData(ToHitData.IMPOSSIBLE, "Indirect fire impossible with direct LOS"); } int c3dist = effectiveDistance(game, c3spotter, target); int c3range = RangeType.rangeBracket(c3dist, weaponRanges, useExtremeRange); // determine which range we're using int usingRange = Math.min(range, c3range); String targSysType = ""; // Get the targeting system type string ready, if necessary if ((ae.getTargSysType() == MiscType.T_TARGSYS_LONGRANGE) && (ae.getTargSysType() == MiscType.T_TARGSYS_SHORTRANGE)) { targSysType = " (w/" + MiscType.getTargetSysName(ae.getTargSysType()) + ")"; } // add range modifier if (usingRange == range) { // no c3 adjustment if ((range == RangeType.RANGE_SHORT || range == RangeType.RANGE_MINIMUM) && (ae.getShortRangeModifier() != 0)) { mods.addModifier(ae.getShortRangeModifier(), "short range" + targSysType); } else if (range == RangeType.RANGE_MEDIUM) { // Right now, the range-mod affecting targeting systems DON'T // affect medium range, so we won't add that here ever. mods.addModifier(ae.getMediumRangeModifier(), "medium range"); } else if (range == RangeType.RANGE_LONG) { // Protos that loose head sensors can't shoot long range. if ((ae instanceof Protomech) && (2 == ((Protomech) ae) .getCritsHit(Protomech.LOC_HEAD))) { mods .addModifier(ToHitData.IMPOSSIBLE, "No long range attacks with destroyed head sensors."); } else { mods.addModifier(ae.getLongRangeModifier(), "long range" + targSysType); } } else if (range == RangeType.RANGE_EXTREME) { // Protos that loose head sensors can't shoot extreme range. if ((ae instanceof Protomech) && (2 == ((Protomech) ae) .getCritsHit(Protomech.LOC_HEAD))) { mods .addModifier(ToHitData.IMPOSSIBLE, "No extreme range attacks with destroyed head sensors."); } else { mods.addModifier(ae.getExtremeRangeModifier(), "extreme range" + targSysType); } } } else { // report c3 adjustment if (c3range == RangeType.RANGE_SHORT || range == RangeType.RANGE_MINIMUM) { mods.addModifier(ae.getShortRangeModifier(), "short range due to C3 spotter" + targSysType); } else if (c3range == RangeType.RANGE_MEDIUM) { mods.addModifier(ae.getMediumRangeModifier(), "medium range due to C3 spotter" + targSysType); } else if (c3range == RangeType.RANGE_LONG) { mods.addModifier(ae.getLongRangeModifier(), "long range due to C3 spotter" + targSysType); } } // add infantry LRM maximum range penalty if (isLRMInfantry && distance == weaponRanges[RangeType.RANGE_LONG]) { mods.addModifier(1, "infantry LRM maximum range"); } // add infantry zero-range modifier // TODO: this is not the right place to hardcode these if (isWeaponInfantry && distance == 0) { // Infantry platoons attacking with infantry weapons can attack // in the same hex with a base of 2, except for flamers and // SRMs, which have a base of 3 and LRMs, which suffer badly. if (wtype.hasFlag(WeaponType.F_FLAMER)) { mods.addModifier(-1, "infantry flamer assault"); } else if (wtype.getAmmoType() == AmmoType.T_SRM) { mods.addModifier(-1, "infantry SRM assault"); } else if (wtype.getAmmoType() != AmmoType.T_LRM) { mods.addModifier(-2, "infantry assault"); } } // add minimum range modifier int minRange = weaponRanges[RangeType.RANGE_MINIMUM]; if (minRange > 0 && distance <= minRange) { int minPenalty = (minRange - distance) + 1; // Infantry LRMs suffer double minimum range penalties. if (isLRMInfantry) { mods.addModifier(minPenalty * 2, "infantry LRM minimum range"); } else { mods.addModifier(minPenalty, "minimum range"); } } // add any target stealth modifier if (target instanceof Entity) { TargetRoll tmpTR = ((Entity)target).getStealthModifier(usingRange); if (tmpTR.getValue() != 0) mods.append(((Entity)target).getStealthModifier(usingRange)); } return mods; } /** * Finds the effective distance between an attacker and a target. Includes * the distance bonus if the attacker and target are in the same building * and on different levels. * * @return the effective distance */ public static int effectiveDistance(IGame game, Entity attacker, Targetable target) { int distance = attacker.getPosition().distance(target.getPosition()); // If the attack is completely inside a building, add the difference // in elevations between the attacker and target to the range. // TODO: should the player be explcitly notified? if (isInSameBuilding(game, attacker, target)) { int aElev = attacker.getElevation(); int tElev = target.getElevation(); distance += Math.abs(aElev - tElev); } return distance; } /** * Attempts to find a C3 spotter that is closer to the target than the * attacker. * * @return A closer C3 spotter, or the attack if no spotters are found */ private static Entity findC3Spotter(IGame game, Entity attacker, Targetable target) { if (!attacker.hasC3() && !attacker.hasC3i()) { return attacker; } Entity c3spotter = attacker; int c3range = attacker.getPosition().distance(target.getPosition()); for (java.util.Enumeration i = game.getEntities(); i.hasMoreElements();) { Entity friend = (Entity) i.nextElement(); // TODO : can units being transported be used for C3 spotting? if (attacker.equals(friend) || !friend.isActive() || !attacker.onSameC3NetworkAs(friend) || !canSee(game, friend, target)) { continue; // useless to us... } int buddyRange = effectiveDistance(game, friend, target); if (buddyRange < c3range) { c3range = buddyRange; c3spotter = friend; } } return c3spotter; } /** * Gets the modifiers, if any, that the mech receives from being prone. * * @return any applicable modifiers due to being prone */ public static ToHitData getProneMods(IGame game, Entity attacker, int weaponId) { if (!attacker.isProne()) { return null; // no modifier } ToHitData mods = new ToHitData(); Mounted weapon = attacker.getEquipment(weaponId); if (attacker.entityIsQuad()) { int legsDead = ((Mech) attacker).countBadLegs(); if (legsDead == 0) { // No legs destroyed: no penalty and can fire all weapons return null; // no modifier } else if (legsDead >= 3) { return new ToHitData(ToHitData.IMPOSSIBLE, "Prone with three or more legs destroyed."); } // we have one or two dead legs... // Need an intact front leg if (attacker.isLocationBad(Mech.LOC_RARM) && attacker.isLocationBad(Mech.LOC_LARM)) { return new ToHitData(ToHitData.IMPOSSIBLE, "Prone with both front legs destroyed."); } // front leg-mounted weapons have addidional trouble if (weapon.getLocation() == Mech.LOC_RARM || weapon.getLocation() == Mech.LOC_LARM) { int otherArm = weapon.getLocation() == Mech.LOC_RARM ? Mech.LOC_LARM : Mech.LOC_RARM; // check previous attacks for weapons fire from the other arm if (isFiringFromArmAlready(game, weaponId, attacker, otherArm)) { return new ToHitData(ToHitData.IMPOSSIBLE, "Prone and firing from other front leg already."); } } // can't fire rear leg weapons if (weapon.getLocation() == Mech.LOC_LLEG || weapon.getLocation() == Mech.LOC_RLEG) { return new ToHitData(ToHitData.IMPOSSIBLE, "Can't fire rear leg-mounted weapons while prone with destroyed legs."); } mods.addModifier(2, "attacker prone"); } else { int l3ProneFiringArm = Entity.LOC_NONE; if (attacker.isLocationBad(Mech.LOC_RARM) || attacker.isLocationBad(Mech.LOC_LARM)) { if (game.getOptions().booleanOption("maxtech_prone_fire")) { // Can fire with only one arm if (attacker.isLocationBad(Mech.LOC_RARM) && attacker.isLocationBad(Mech.LOC_LARM)) { return new ToHitData(ToHitData.IMPOSSIBLE, "Prone with both arms destroyed."); } l3ProneFiringArm = attacker.isLocationBad(Mech.LOC_RARM) ? Mech.LOC_LARM : Mech.LOC_RARM; } else { // must have an arm intact return new ToHitData(ToHitData.IMPOSSIBLE, "Prone with one or both arms destroyed.");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -