diff --git a/megamek/src/megamek/common/BipedMek.java b/megamek/src/megamek/common/BipedMek.java index 0a96ace6701..f21ff66ffd3 100644 --- a/megamek/src/megamek/common/BipedMek.java +++ b/megamek/src/megamek/common/BipedMek.java @@ -119,6 +119,10 @@ public int getWalkMP(MPCalculationSetting mpCalculationSetting) { mp--; } + if (!mpCalculationSetting.ignoreChainDrape && hasChainDrape()) { + mp--; + } + if (!mpCalculationSetting.ignoreHeat) { // factor in heat if ((game != null) && game.getOptions().booleanOption(OptionsConstants.ADVCOMBAT_TACOPS_HEAT)) { diff --git a/megamek/src/megamek/common/MPCalculationSetting.java b/megamek/src/megamek/common/MPCalculationSetting.java index 7e6f3cd288d..bf73ba0428c 100644 --- a/megamek/src/megamek/common/MPCalculationSetting.java +++ b/megamek/src/megamek/common/MPCalculationSetting.java @@ -63,7 +63,7 @@ public class MPCalculationSetting { * A setting for checking if a unit moved too fast under the effects of low * gravity. */ - public static final MPCalculationSetting SAFE_MOVE = new Builder().noGravity().noHeat().noModularArmor().build(); + public static final MPCalculationSetting SAFE_MOVE = new Builder().noGravity().noHeat().noModularArmor().noChainDrape().build(); /** * A setting for testing if a unit is permanently immobilized. It excludes @@ -86,7 +86,7 @@ public class MPCalculationSetting { * well as myomer boosters, DWP, BA burden and modular armor. */ public static final MPCalculationSetting AS_CONVERSION = new Builder().noGravity().noWeather() - .noHeat().noCargo().baNoBurden().noMyomerBooster().noDWP().noModularArmor().noGrounded() + .noHeat().noCargo().baNoBurden().noMyomerBooster().noDWP().noModularArmor().noChainDrape().noGrounded() .noConversion().noOptionalRules().build(); /** @@ -110,6 +110,7 @@ public class MPCalculationSetting { public final boolean ignoreGravity; public final boolean ignoreHeat; public final boolean ignoreModularArmor; + public final boolean ignoreChainDrape; public final boolean ignoreDWP; public final boolean ignoreMyomerBooster; public final boolean ignoreMASC; @@ -123,7 +124,7 @@ public class MPCalculationSetting { public final boolean ignoreSubmergedJumpJets; public final boolean forceTSM; - private MPCalculationSetting(boolean ignoreGravity, boolean ignoreHeat, boolean ignoreModularArmor, + private MPCalculationSetting(boolean ignoreGravity, boolean ignoreHeat, boolean ignoreModularArmor, boolean ignoreChainDrape, boolean ignoreMASC, boolean ignoreMyomerBooster, boolean ignoreDWP, boolean ignoreBurden, boolean ignoreCargo, boolean ignoreWeather, boolean singleMASC, boolean ignoreSubmergedJumpJets, boolean ignoreGrounded, @@ -131,6 +132,7 @@ private MPCalculationSetting(boolean ignoreGravity, boolean ignoreHeat, boolean this.ignoreGravity = ignoreGravity; this.ignoreHeat = ignoreHeat; this.ignoreModularArmor = ignoreModularArmor; + this.ignoreChainDrape = ignoreChainDrape; this.ignoreMASC = ignoreMASC; this.ignoreMyomerBooster = ignoreMyomerBooster; this.ignoreDWP = ignoreDWP; @@ -150,6 +152,7 @@ private static class Builder { private boolean ignoreGravity = false; private boolean ignoreHeat = false; private boolean ignoreModularArmor = false; + private boolean ignoreChainDrape = false; private boolean ignoreBADWP = false; private boolean ignoreMyomerBooster = false; private boolean ignoreMASC = false; @@ -164,7 +167,7 @@ private static class Builder { private boolean forceTSM = false; MPCalculationSetting build() { - return new MPCalculationSetting(ignoreGravity, ignoreHeat, ignoreModularArmor, ignoreMASC, + return new MPCalculationSetting(ignoreGravity, ignoreHeat, ignoreModularArmor, ignoreChainDrape, ignoreMASC, ignoreMyomerBooster, ignoreBADWP, ignoreBABurden, ignoreCargo, ignoreWeather, singleMASC, ignoreSubmergedJumpJets, ignoreGrounded, ignoreOptionalRules, ignoreConversion, forceTSM); } @@ -233,6 +236,12 @@ private Builder noModularArmor() { return this; } + /** Disregards the effects of a chain drape */ + private Builder noChainDrape() { + ignoreChainDrape = true; + return this; + } + /** Allows the effects of at most one MASC system if the unit has any MASC. */ private Builder singleMASC() { singleMASC = true; diff --git a/megamek/src/megamek/common/Mek.java b/megamek/src/megamek/common/Mek.java index 6c64f2b30b1..5f5a62e1ff7 100644 --- a/megamek/src/megamek/common/Mek.java +++ b/megamek/src/megamek/common/Mek.java @@ -1113,6 +1113,16 @@ public int getJumpMP(MPCalculationSetting mpCalculationSetting) { return Math.max(mp, 0); } + public boolean hasChainDrape() { + for (MiscMounted mount : getMisc()) { + if (!mount.isDestroyed() && mount.getType().hasFlag(MiscType.F_CHAIN_DRAPE)) { + return true; + } + } + + return false; + } + public int getPartialWingJumpWeightClassBonus() { return (getWeightClass() <= EntityWeightClass.WEIGHT_MEDIUM) ? 2 : 1; } diff --git a/megamek/src/megamek/common/MiscType.java b/megamek/src/megamek/common/MiscType.java index a1337ff9867..00454a31958 100644 --- a/megamek/src/megamek/common/MiscType.java +++ b/megamek/src/megamek/common/MiscType.java @@ -285,6 +285,11 @@ public class MiscType extends EquipmentType { public static final MiscTypeFlag F_TRENCH_CAPABLE = MiscTypeFlag.F_TRENCH_CAPABLE; public static final MiscTypeFlag F_SUPPORT_VEE_BAR_ARMOR = MiscTypeFlag.F_SUPPORT_VEE_BAR_ARMOR; + public static final MiscTypeFlag F_CHAIN_DRAPE = MiscTypeFlag.F_CHAIN_DRAPE; + public static final MiscTypeFlag F_CHAIN_DRAPE_CAPE = MiscTypeFlag.F_CHAIN_DRAPE_CAPE; + public static final MiscTypeFlag F_CHAIN_DRAPE_APRON = MiscTypeFlag.F_CHAIN_DRAPE_APRON; + public static final MiscTypeFlag F_CHAIN_DRAPE_PONCHO = MiscTypeFlag.F_CHAIN_DRAPE_PONCHO; + // Secondary Flags for Physical Weapons public static final long S_CLUB = 1L << 0; // BMR - Indicates an Improvised Club public static final long S_TREE_CLUB = 1L << 1;// BMR @@ -521,6 +526,8 @@ public double getTonnage(Entity entity, int location, double size, RoundWeight d } } else if (hasFlag(F_PARTIAL_WING) && hasFlag(F_PROTOMEK_EQUIPMENT)) { return RoundWeight.nearestKg(entity.getWeight() / 5.0); + } else if (hasFlag(F_CHAIN_DRAPE)) { + return RoundWeight.nextHalfTon(entity.getWeight() / 10.0); } else if (hasFlag(F_CLUB) && hasSubType(S_HATCHET)) { return RoundWeight.nextTon(entity.getWeight() / 15.0); } else if (hasFlag(F_CLUB) && hasSubType(S_LANCE)) { @@ -1839,6 +1846,9 @@ public static void initializeTypes() { EquipmentType.addType(MiscType.createISSneakIRECMInfArmor()); EquipmentType.addType(MiscType.createISSneakThreeSystemInfArmor()); + EquipmentType.addType(MiscType.createChainDrape("Cape", F_CHAIN_DRAPE_CAPE)); + EquipmentType.addType(MiscType.createChainDrape("Apron", F_CHAIN_DRAPE_APRON)); + EquipmentType.addType(MiscType.createChainDrape("Poncho", F_CHAIN_DRAPE_PONCHO)); } // Advanced Mek/ProtoMek/Vehicular Motive Systems @@ -2117,6 +2127,28 @@ public static MiscType createJumpBooster() { return misc; } + public static MiscType createChainDrape(String configurationName, MiscTypeFlag configurationFlag) { + MiscType misc = new MiscType(); + + misc.name = "Chain Drape (%s)".formatted(configurationName); + misc.setInternalName("ChainDrape%s".formatted(configurationName)); + misc.addLookupName("ChainDrape %s".formatted(configurationName)); + misc.addLookupName("Chain Drape %s".formatted(configurationName)); + misc.shortName = "Chain Drape %s".formatted(configurationName); + misc.tonnage = TONNAGE_VARIABLE; + misc.criticals = 6; + misc.spreadable = true; + misc.flags = misc.flags.or(F_CHAIN_DRAPE).or(configurationFlag).or(F_MEK_EQUIPMENT); + // Arcade Ops: UrbanFest + misc.rulesRefs = "16, UF"; + // No information about this is provided so we fill in essentially "blank" data + misc.techAdvancement.setTechBase(TECH_BASE_IS).setStaticTechLevel(SimpleTechLevel.EXPERIMENTAL).setTechRating(RATING_X) + .setAvailability(RATING_X, RATING_X, RATING_X, RATING_X) + .setISAdvancement(DATE_ES).setISApproximate(true); + + return misc; + } + public static MiscType createISPartialWing() { MiscType misc = new MiscType(); diff --git a/megamek/src/megamek/common/MiscTypeFlag.java b/megamek/src/megamek/common/MiscTypeFlag.java index 69122394078..c5d96b9f0f0 100644 --- a/megamek/src/megamek/common/MiscTypeFlag.java +++ b/megamek/src/megamek/common/MiscTypeFlag.java @@ -298,4 +298,11 @@ public enum MiscTypeFlag implements EquipmentFlag { F_HYPERSPECTRAL_IMAGER, // TODO: add game rules for the following imagers/radars, construction data only F_INFRARED_IMAGER, // TODO: add game rules for the following imagers/radars, construction data only F_LOOKDOWN_RADAR, // TODO: add game rules for the following imagers/radars, construction data only + + // UrbanFest Chain Drape + F_CHAIN_DRAPE, + F_CHAIN_DRAPE_APRON, + F_CHAIN_DRAPE_CAPE, + F_CHAIN_DRAPE_PONCHO, + } diff --git a/megamek/src/megamek/common/QuadMek.java b/megamek/src/megamek/common/QuadMek.java index 95f2e5daf25..7274eca473d 100644 --- a/megamek/src/megamek/common/QuadMek.java +++ b/megamek/src/megamek/common/QuadMek.java @@ -150,6 +150,10 @@ public int getWalkMP(MPCalculationSetting mpCalculationSetting) { mp--; } + if (!mpCalculationSetting.ignoreChainDrape && hasChainDrape()) { + mp--; + } + if (!mpCalculationSetting.ignoreHeat) { // factor in heat if ((game != null) && game.getOptions().booleanOption(OptionsConstants.ADVCOMBAT_TACOPS_HEAT)) { diff --git a/megamek/src/megamek/common/TripodMek.java b/megamek/src/megamek/common/TripodMek.java index c8ab83cd968..6e1b53fbf68 100644 --- a/megamek/src/megamek/common/TripodMek.java +++ b/megamek/src/megamek/common/TripodMek.java @@ -155,6 +155,10 @@ public int getWalkMP(MPCalculationSetting mpCalculationSetting) { mp--; } + if (!mpCalculationSetting.ignoreChainDrape && hasChainDrape()) { + mp--; + } + if (!mpCalculationSetting.ignoreHeat) { // factor in heat if ((game != null) diff --git a/megamek/src/megamek/common/verifier/TestMek.java b/megamek/src/megamek/common/verifier/TestMek.java index 66fc6b3cb02..5c58114f908 100755 --- a/megamek/src/megamek/common/verifier/TestMek.java +++ b/megamek/src/megamek/common/verifier/TestMek.java @@ -440,6 +440,16 @@ public boolean checkMiscSpreadAllocation(Entity entity, Mounted mounted, return false; } } + if (mt.hasFlag(MiscType.F_CHAIN_DRAPE)) { + if (countCriticalSlotsFromEquipInLocation(entity, mounted, Mek.LOC_LT) != 3) { + buff.append("incorrect number of chain draop crits in left torso\n"); + return false; + } + if (countCriticalSlotsFromEquipInLocation(entity, mounted, Mek.LOC_RT) != 3) { + buff.append("incorrect number of chain draop crits in right torso\n"); + return false; + } + } return true; } @@ -1063,6 +1073,13 @@ && countCriticalSlotsFromEquipInLocation(mek, m, loc) != 1) { buff.append("LAMs may not mount ").append(misc.getName()).append("\n"); illegal = true; } + + if ((misc.hasFlag(MiscType.F_CHAIN_DRAPE_APRON) || misc.hasFlag(MiscType.F_CHAIN_DRAPE_PONCHO)) + && (mek.isQuadMek() || mek.getCockpitType() == Mek.COCKPIT_TORSO_MOUNTED) + ) { + buff.append("Quad meks and meks with torso cockpits may only mount a chain drape as a Cape"); + illegal = true; + } } if (mek.isSuperHeavy()) {