From e471e43761bced4907f9c2318f91ba2218ed0b3e Mon Sep 17 00:00:00 2001 From: sleet01 Date: Fri, 11 Aug 2023 20:23:30 -0700 Subject: [PATCH 01/10] Fix MML #1182: mixed tech Dropship saves BA type Initial commit to fix MML issue #1182. Adds more fields to bay representation in strings and objects; normalizes string representations so all bays use the same fields in the same ways - no more bay #, _or_ infantry type, _or_ facing in one field. --- megamek/src/megamek/common/ASFBay.java | 10 +- megamek/src/megamek/common/Aero.java | 87 +-- .../src/megamek/common/BattleArmorBay.java | 15 +- megamek/src/megamek/common/Bay.java | 22 +- megamek/src/megamek/common/CargoBay.java | 10 +- .../megamek/common/CrewQuartersCargoBay.java | 7 +- .../src/megamek/common/DropshuttleBay.java | 14 +- .../megamek/common/EjectionSeatCargoBay.java | 7 +- .../common/FirstClassQuartersCargoBay.java | 7 +- .../src/megamek/common/HeavyVehicleBay.java | 10 +- megamek/src/megamek/common/InfantryBay.java | 12 +- .../src/megamek/common/InsulatedCargoBay.java | 8 +- .../src/megamek/common/LightVehicleBay.java | 10 +- .../src/megamek/common/LiquidCargoBay.java | 8 +- .../src/megamek/common/LivestockCargoBay.java | 8 +- megamek/src/megamek/common/MechBay.java | 9 +- .../megamek/common/PillionSeatCargoBay.java | 7 +- megamek/src/megamek/common/ProtomechBay.java | 8 +- .../megamek/common/RefrigeratedCargoBay.java | 8 +- .../common/SecondClassQuartersCargoBay.java | 7 +- megamek/src/megamek/common/SmallCraftBay.java | 10 +- .../megamek/common/StandardSeatCargoBay.java | 7 +- .../common/SteerageQuartersCargoBay.java | 7 +- .../megamek/common/SuperHeavyVehicleBay.java | 10 +- .../common/loaders/BLKDropshipFile.java | 2 + .../src/megamek/common/loaders/BLKFile.java | 502 +++++++++++------- .../megamek/common/loaders/BLKFileTest.java | 169 ++++-- 27 files changed, 649 insertions(+), 332 deletions(-) diff --git a/megamek/src/megamek/common/ASFBay.java b/megamek/src/megamek/common/ASFBay.java index efb1399d48a..bae86e8dcb7 100644 --- a/megamek/src/megamek/common/ASFBay.java +++ b/megamek/src/megamek/common/ASFBay.java @@ -97,7 +97,13 @@ public int getPersonnel(boolean clan) { @Override public String toString() { - return (hasARTS() ? "artsasfbay:" : "asfbay:") + totalSpace + ":" + doors + ":" + bayNumber; + String bayType = (hasARTS() ? "artsasfbay" : "asfbay"); + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } public static TechAdvancement techAdvancement() { @@ -117,4 +123,4 @@ public TechAdvancement getTechAdvancement() { public long getCost() { return 20000L * (long) totalSpace + (hasARTS() ? 1000000L : 0); } -} \ No newline at end of file +} diff --git a/megamek/src/megamek/common/Aero.java b/megamek/src/megamek/common/Aero.java index 42051f40401..6a96d60d14d 100644 --- a/megamek/src/megamek/common/Aero.java +++ b/megamek/src/megamek/common/Aero.java @@ -53,9 +53,9 @@ public class Aero extends Entity implements IAero, IBomber { public static final int COCKPIT_SMALL = 1; public static final int COCKPIT_COMMAND_CONSOLE = 2; public static final int COCKPIT_PRIMITIVE = 3; - public static final String[] COCKPIT_STRING = { "Standard Cockpit", "Small Cockpit", "Command Console", - "Primitive Cockpit" }; - public static final String[] COCKPIT_SHORT_STRING = { "Standard", "Small", "Command Console", "Primitive" }; + public static final String[] COCKPIT_STRING = {"Standard Cockpit", "Small Cockpit", "Command Console", + "Primitive Cockpit"}; + public static final String[] COCKPIT_SHORT_STRING = {"Standard", "Small", "Command Console", "Primitive"}; // critical hits public static final int CRIT_NONE = -1; @@ -86,10 +86,10 @@ public class Aero extends Entity implements IAero, IBomber { // this needs to be larger, it is too easy to go over when you get to // warships // and bombs and such - private static final int[] NUM_OF_SLOTS = { 100, 100, 100, 100, 100, 100, 100 }; + private static final int[] NUM_OF_SLOTS = {100, 100, 100, 100, 100, 100, 100}; - private static String[] LOCATION_ABBRS = { "NOS", "LWG", "RWG", "AFT", "WNG", "FSLG" }; - private static String[] LOCATION_NAMES = { "Nose", "Left Wing", "Right Wing", "Aft", "Wings", "Fuselage" }; + private static String[] LOCATION_ABBRS = {"NOS", "LWG", "RWG", "AFT", "WNG", "FSLG"}; + private static String[] LOCATION_NAMES = {"Nose", "Left Wing", "Right Wing", "Aft", "Wings", "Fuselage"}; @Override public String[] getLocationAbbrs() { @@ -111,7 +111,7 @@ public String[] getLocationNames() { private int structIntegrity; private int orig_structIntegrity; // set up damage threshold - protected int[] damThresh = { 0, 0, 0, 0, 0, 0 }; + protected int[] damThresh = {0, 0, 0, 0, 0, 0}; // set up an int for what the critical effect would be private int potCrit = CRIT_NONE; @@ -212,7 +212,7 @@ public String[] getLocationNames() { private Set escapeCraftList = new HashSet<>(); //Maps unique id of each assigned marine to marine point value - private Map marines; + private Map marines; public Aero() { super(); @@ -230,7 +230,7 @@ public int getUnitType() { .setTechRating(RATING_D).setAvailability(RATING_C, RATING_E, RATING_D, RATING_C) .setStaticTechLevel(SimpleTechLevel.STANDARD); protected static final TechAdvancement TA_ASF_PRIMITIVE = new TechAdvancement(TECH_BASE_IS) - //Per MUL team and per availability codes should exist to around 2781 + //Per MUL team and per availability codes should exist to around 2781 .setISAdvancement(DATE_ES, 2200, DATE_NONE, 2781, DATE_NONE) .setISApproximate(false, true, false, true, false).setProductionFactions(F_TA) .setTechRating(RATING_D).setAvailability(RATING_D, RATING_X, RATING_F, RATING_F) @@ -247,28 +247,28 @@ public TechAdvancement getConstructionTechAdvancement() { protected static final TechAdvancement[] COCKPIT_TA = { new TechAdvancement(TECH_BASE_ALL).setAdvancement(2460, 2470, 2491) - .setApproximate(true, false, false).setPrototypeFactions(F_TH) - .setPrototypeFactions(F_TH).setTechRating(RATING_C) - .setAvailability(RATING_C, RATING_C, RATING_C, RATING_C) - .setStaticTechLevel(SimpleTechLevel.STANDARD), //Standard + .setApproximate(true, false, false).setPrototypeFactions(F_TH) + .setPrototypeFactions(F_TH).setTechRating(RATING_C) + .setAvailability(RATING_C, RATING_C, RATING_C, RATING_C) + .setStaticTechLevel(SimpleTechLevel.STANDARD), //Standard new TechAdvancement(TECH_BASE_IS).setISAdvancement(3065, 3070, 3080) - .setClanAdvancement(DATE_NONE, DATE_NONE, 3080) - .setISApproximate(true, false, false).setPrototypeFactions(F_WB) - .setPrototypeFactions(F_WB, F_CSR).setTechRating(RATING_E) - .setAvailability(RATING_X, RATING_X, RATING_E, RATING_D) - .setStaticTechLevel(SimpleTechLevel.STANDARD), //Small + .setClanAdvancement(DATE_NONE, DATE_NONE, 3080) + .setISApproximate(true, false, false).setPrototypeFactions(F_WB) + .setPrototypeFactions(F_WB, F_CSR).setTechRating(RATING_E) + .setAvailability(RATING_X, RATING_X, RATING_E, RATING_D) + .setStaticTechLevel(SimpleTechLevel.STANDARD), //Small new TechAdvancement(TECH_BASE_ALL).setISAdvancement(2625, 2631, DATE_NONE, 2850, 3030) - .setISApproximate(true, false, false, true, true) - .setClanAdvancement(2625, 2631).setClanApproximate(true, false) - .setClanApproximate(true, false).setPrototypeFactions(F_TH) - .setPrototypeFactions(F_TH).setReintroductionFactions(F_FS).setTechRating(RATING_D) - .setAvailability(RATING_C, RATING_F, RATING_E, RATING_D) - .setStaticTechLevel(SimpleTechLevel.ADVANCED), //Cockpit command console + .setISApproximate(true, false, false, true, true) + .setClanAdvancement(2625, 2631).setClanApproximate(true, false) + .setClanApproximate(true, false).setPrototypeFactions(F_TH) + .setPrototypeFactions(F_TH).setReintroductionFactions(F_FS).setTechRating(RATING_D) + .setAvailability(RATING_C, RATING_F, RATING_E, RATING_D) + .setStaticTechLevel(SimpleTechLevel.ADVANCED), //Cockpit command console new TechAdvancement(TECH_BASE_ALL).setAdvancement(DATE_ES, 2300, DATE_NONE, 2520) - .setISApproximate(false, true, false, false) - .setPrototypeFactions(F_TA).setTechRating(RATING_C) - .setAvailability(RATING_D, RATING_X, RATING_X, RATING_F) - .setStaticTechLevel(SimpleTechLevel.STANDARD), //Primitive + .setISApproximate(false, true, false, false) + .setPrototypeFactions(F_TA).setTechRating(RATING_C) + .setAvailability(RATING_D, RATING_X, RATING_X, RATING_F) + .setStaticTechLevel(SimpleTechLevel.STANDARD), //Primitive }; public static TechAdvancement getCockpitTechAdvancement(int cockpitType) { @@ -747,9 +747,9 @@ public int getLandingGearPartialRepairs() { @Override public int getAvionicsMisreplaced() { if (getPartialRepairs().booleanOption("aero_avionics_replace")) { - return 1; + return 1; } else { - return 0; + return 0; } } @@ -2496,15 +2496,15 @@ public String getCritDamageString() { if (!first) { toReturn.append(", "); } - toReturn.append(String.format(Messages.getString("Aero.bayDamageString"), next.getType(), next.getBayNumber())); - first = false; + toReturn.append(String.format(Messages.getString("Aero.bayDamageString"), next.getType(), next.getBayNumber())); + first = false; } if (next.getCurrentDoors() < next.getDoors()) { if (!first) { toReturn.append(", "); } - toReturn.append(String.format(Messages.getString("Aero.bayDoorDamageString"), next.getType(), next.getBayNumber(), (next.getDoors() - next.getCurrentDoors()))); - first = false; + toReturn.append(String.format(Messages.getString("Aero.bayDoorDamageString"), next.getType(), next.getBayNumber(), (next.getDoors() - next.getCurrentDoors()))); + first = false; } } return toReturn.toString(); @@ -2662,8 +2662,8 @@ public boolean canSpot() { if (!isAirborne() || hasWorkingMisc(MiscType.F_RECON_CAMERA) || hasWorkingMisc(MiscType.F_INFRARED_IMAGER) || hasWorkingMisc(MiscType.F_HYPERSPECTRAL_IMAGER) || (hasWorkingMisc(MiscType.F_HIRES_IMAGER) - && ((game.getPlanetaryConditions().getLight() == PlanetaryConditions.L_DAY) - || (game.getPlanetaryConditions().getLight() == PlanetaryConditions.L_DUSK)))) { + && ((game.getPlanetaryConditions().getLight() == PlanetaryConditions.L_DAY) + || (game.getPlanetaryConditions().getLight() == PlanetaryConditions.L_DUSK)))) { return true; } else { return false; @@ -2842,7 +2842,7 @@ public void setNMarines(int marines) { * Returns our list of unique individuals being transported as marines * @return */ - public Map getMarines() { + public Map getMarines() { return marines; } @@ -3000,7 +3000,7 @@ public boolean isBomber() { @Override /** - * Returns true if this is an aerospace or conventional fighter + * Returns true if this is an aerospace or conventional fighter * but not a larger craft (i.e. "SmallCraft" or "Dropship" and bigger */ public boolean isFighter() { @@ -3044,7 +3044,7 @@ public List getActiveAMS() { // Skip anything that's not an AMS, AMS Bay or Point Defense Bay if (!weapon.getType().hasFlag(WeaponType.F_AMS) && !weapon.getType().hasFlag(WeaponType.F_AMSBAY) - && !weapon.getType().hasFlag(WeaponType.F_PDBAY)) { + && !weapon.getType().hasFlag(WeaponType.F_PDBAY)) { continue; } @@ -3067,7 +3067,7 @@ public List getActiveAMS() { Mounted bayWAmmo = bayW.getLinked(); if (!(weapon.getType().hasFlag(WeaponType.F_ENERGY)) && ((bayWAmmo == null) || (bayWAmmo.getUsableShotsLeft() == 0) - || bayWAmmo.isDumping())) { + || bayWAmmo.isDumping())) { loadWeapon(weapon); bayWAmmo = weapon.getLinked(); } @@ -3075,7 +3075,7 @@ public List getActiveAMS() { // try again if (!(weapon.getType().hasFlag(WeaponType.F_ENERGY)) && ((bayWAmmo == null) || (bayWAmmo.getUsableShotsLeft() == 0) - || bayWAmmo.isDumping())) { + || bayWAmmo.isDumping())) { // No ammo for this AMS. continue; } @@ -3132,7 +3132,7 @@ public void updateSensorOptions() { } getSensors().removeAll(sensorsToRemove); if (sensorsToRemove.size() >= 1) { - setNextSensor(getSensors().firstElement()); + setNextSensor(getSensors().firstElement()); } } //If we are in space, add them back... @@ -3154,7 +3154,7 @@ public void updateSensorOptions() { } } } else if (hasETypeFlag(Entity.ETYPE_AERO) - || hasETypeFlag(Entity.ETYPE_SMALL_CRAFT)) { + || hasETypeFlag(Entity.ETYPE_SMALL_CRAFT)) { //ASFs and small craft get thermal/optical sensors if (!hasAeroThermal) { getSensors().add(new Sensor(Sensor.TYPE_AERO_THERMAL)); @@ -3165,6 +3165,7 @@ public void updateSensorOptions() { } // autoejection methods + /** * @return unit has an ejection seat */ diff --git a/megamek/src/megamek/common/BattleArmorBay.java b/megamek/src/megamek/common/BattleArmorBay.java index 6891f7f7b24..fdb9b116a7c 100644 --- a/megamek/src/megamek/common/BattleArmorBay.java +++ b/megamek/src/megamek/common/BattleArmorBay.java @@ -113,7 +113,20 @@ public int getPersonnel(boolean clan) { @Override public String toString() { - return "battlearmorbay:" + totalSpace + ":" + doors + ":"+bayNumber+(isComStar?":C*":""); + // See BLKFile.java:BLKFile constants + int bitmap = 0; + bitmap |= (isComStar? 1 : 0); + bitmap |= (isClan? 1 << 2 : 0); + String bayType = "battlearmorbay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber, + "", + Entity.LOC_NONE, + bitmap + ); } public static TechAdvancement techAdvancement() { diff --git a/megamek/src/megamek/common/Bay.java b/megamek/src/megamek/common/Bay.java index a8e117a966f..2c5e4774683 100644 --- a/megamek/src/megamek/common/Bay.java +++ b/megamek/src/megamek/common/Bay.java @@ -363,9 +363,27 @@ public int getPersonnel(boolean clan) { return 0; } + /** + * Updated toString() and helpers to normalize bay string output + * To match new 6-field format: + * type:space(current or total):doors:bayNumber:infantryType:facing:status bitmap + * See BLKFile.java:BLKFile constants + */ @Override public String toString() { - return "bay:" + totalSpace + ":" + doors + ":"+ bayNumber; + return this.bayString("bay", totalSpace, doors, bayNumber, "", Entity.LOC_NONE, 0); + } + + public String bayString(String bayType, double space, int doors, int bayNumber, String infType, int facing, int bitmap){ + return String.format("%s:%s:%s:%s:%s:%s:%s", bayType, space, doors, bayNumber, infType, facing, bitmap); + } + + public String bayString(String bayType, double space, int doors, int bayNumber){ + return String.format("%s:%s:%s:%s:%s:%s:%s", bayType, space, doors, bayNumber, "", Entity.LOC_NONE, 0); + } + + public String bayString(String bayType, double space, int doors){ + return String.format("%s:%s:%s:%s:%s:%s:%s", bayType, space, doors, -1, "", Entity.LOC_NONE, 0); } /** @@ -535,4 +553,4 @@ public long getCost() { public int getSafeLaunchRate() { return getCurrentDoors() * 2; } -} \ No newline at end of file +} diff --git a/megamek/src/megamek/common/CargoBay.java b/megamek/src/megamek/common/CargoBay.java index 3d0b2179a1b..754404aaa7e 100644 --- a/megamek/src/megamek/common/CargoBay.java +++ b/megamek/src/megamek/common/CargoBay.java @@ -80,9 +80,15 @@ public String getType() { @Override public String toString() { - return "cargobay:" + totalSpace + ":" + doors + ":" + bayNumber; + String bayType = "cargobay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } - + @Override public boolean isCargo() { return true; diff --git a/megamek/src/megamek/common/CrewQuartersCargoBay.java b/megamek/src/megamek/common/CrewQuartersCargoBay.java index 6162a4412ec..2de41adc6a6 100644 --- a/megamek/src/megamek/common/CrewQuartersCargoBay.java +++ b/megamek/src/megamek/common/CrewQuartersCargoBay.java @@ -94,7 +94,12 @@ public double getWeight() { @Override public String toString() { - return "crewquarters:" + weight + ":" + doors; + String bayType = "crewquarters"; + return this.bayString( + bayType, + totalSpace, + doors + ); } @Override diff --git a/megamek/src/megamek/common/DropshuttleBay.java b/megamek/src/megamek/common/DropshuttleBay.java index ec974605a77..792b93bb968 100644 --- a/megamek/src/megamek/common/DropshuttleBay.java +++ b/megamek/src/megamek/common/DropshuttleBay.java @@ -92,10 +92,16 @@ public void setFacing(int facing) { @Override public String toString() { - return "dropshuttlebay:" + totalSpace + FIELD_SEPARATOR - + doors + FIELD_SEPARATOR - + bayNumber + FIELD_SEPARATOR - + FACING_PREFIX + facing; + String bayType = "dropshuttlebay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber, + "", + facing, + 0 + ); } @Override diff --git a/megamek/src/megamek/common/EjectionSeatCargoBay.java b/megamek/src/megamek/common/EjectionSeatCargoBay.java index 6d22066c17f..78191a95cf7 100644 --- a/megamek/src/megamek/common/EjectionSeatCargoBay.java +++ b/megamek/src/megamek/common/EjectionSeatCargoBay.java @@ -49,7 +49,12 @@ public String getType() { @Override public String toString() { - return "ejectionseats:" + currentSpace + ":" + doors; + String bayType = "ejectionseats"; + return this.bayString( + bayType, + currentSpace, + doors + ); } @Override diff --git a/megamek/src/megamek/common/FirstClassQuartersCargoBay.java b/megamek/src/megamek/common/FirstClassQuartersCargoBay.java index d958c55b091..889706e7841 100644 --- a/megamek/src/megamek/common/FirstClassQuartersCargoBay.java +++ b/megamek/src/megamek/common/FirstClassQuartersCargoBay.java @@ -94,7 +94,12 @@ public boolean isQuarters() { @Override public String toString() { - return "1stclassquarters:" + weight + ":" + doors; + String bayType = "1stclassquarters"; + return this.bayString( + bayType, + totalSpace, + doors + ); } @Override diff --git a/megamek/src/megamek/common/HeavyVehicleBay.java b/megamek/src/megamek/common/HeavyVehicleBay.java index 2695125f07a..67ffeb278c6 100644 --- a/megamek/src/megamek/common/HeavyVehicleBay.java +++ b/megamek/src/megamek/common/HeavyVehicleBay.java @@ -72,7 +72,13 @@ public int getPersonnel(boolean clan) { @Override public String toString() { - return "heavyvehiclebay:" + totalSpace + ":" + doors + ":"+ bayNumber; + String bayType = "heavyvehiclebay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } public static TechAdvancement techAdvancement() { @@ -92,4 +98,4 @@ public TechAdvancement getTechAdvancement() { public long getCost() { return 10000L * (long) totalSpace; } -} \ No newline at end of file +} diff --git a/megamek/src/megamek/common/InfantryBay.java b/megamek/src/megamek/common/InfantryBay.java index 04c3235161c..4dbf2e06f5f 100644 --- a/megamek/src/megamek/common/InfantryBay.java +++ b/megamek/src/megamek/common/InfantryBay.java @@ -188,8 +188,16 @@ public String getType() { @Override public String toString() { - return "infantrybay:" + (totalSpace / platoonType.getWeight()) + ":" + doors + ":" - + bayNumber + ":" + platoonType; + String bayType = "infantrybay"; + return this.bayString( + bayType, + (totalSpace / platoonType.getWeight()), + doors, + bayNumber, + platoonType.toString(), + Entity.LOC_NONE, + 0 + ); } public PlatoonType getPlatoonType() { diff --git a/megamek/src/megamek/common/InsulatedCargoBay.java b/megamek/src/megamek/common/InsulatedCargoBay.java index 8cdd03b5372..0ed1ffdfc6c 100644 --- a/megamek/src/megamek/common/InsulatedCargoBay.java +++ b/megamek/src/megamek/common/InsulatedCargoBay.java @@ -84,7 +84,13 @@ public double getWeight() { @Override public String toString() { - return "insulatedcargobay:" + totalSpace + ":" + doors + ":"+ bayNumber; + String bayType = "insulatedcargobay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } @Override diff --git a/megamek/src/megamek/common/LightVehicleBay.java b/megamek/src/megamek/common/LightVehicleBay.java index e1b3799dca3..0986319a76b 100644 --- a/megamek/src/megamek/common/LightVehicleBay.java +++ b/megamek/src/megamek/common/LightVehicleBay.java @@ -72,7 +72,13 @@ public int getPersonnel(boolean clan) { @Override public String toString() { - return "lightvehiclebay:" + totalSpace + ":" + doors + ":"+ bayNumber; + String bayType = "lightvehiclebay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } public static TechAdvancement techAdvancement() { @@ -92,4 +98,4 @@ public TechAdvancement getTechAdvancement() { public long getCost() { return 10000L * (long) totalSpace; } -} \ No newline at end of file +} diff --git a/megamek/src/megamek/common/LiquidCargoBay.java b/megamek/src/megamek/common/LiquidCargoBay.java index d1c7e0af55d..75dd4b24924 100644 --- a/megamek/src/megamek/common/LiquidCargoBay.java +++ b/megamek/src/megamek/common/LiquidCargoBay.java @@ -89,7 +89,13 @@ public double getWeight() { @Override public String toString() { - return "LiquidCargoBay: " + totalSpace + ":" + doors + ":" + bayNumber; + String bayType = "LiquidCargoBay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } @Override diff --git a/megamek/src/megamek/common/LivestockCargoBay.java b/megamek/src/megamek/common/LivestockCargoBay.java index 29c6d92aa14..1e136df2e8b 100644 --- a/megamek/src/megamek/common/LivestockCargoBay.java +++ b/megamek/src/megamek/common/LivestockCargoBay.java @@ -87,7 +87,13 @@ public double getWeight() { @Override public String toString() { - return "livestockcargobay:" + totalSpace + ":" + doors + ":"+ bayNumber; + String bayType = "livestockcargobay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } diff --git a/megamek/src/megamek/common/MechBay.java b/megamek/src/megamek/common/MechBay.java index 4ef5d3c77ab..6dfadfc0188 100644 --- a/megamek/src/megamek/common/MechBay.java +++ b/megamek/src/megamek/common/MechBay.java @@ -72,7 +72,12 @@ public int getPersonnel(boolean clan) { @Override public String toString() { - return "mechbay:" + totalSpace + ":" + doors; + String bayType = "mechbay"; + return this.bayString( + bayType, + totalSpace, + doors + ); } public static TechAdvancement techAdvancement() { @@ -92,4 +97,4 @@ public TechAdvancement getTechAdvancement() { public long getCost() { return 20000L * (long) totalSpace; } -} \ No newline at end of file +} diff --git a/megamek/src/megamek/common/PillionSeatCargoBay.java b/megamek/src/megamek/common/PillionSeatCargoBay.java index 04d8ed428e0..bf3583d16f2 100644 --- a/megamek/src/megamek/common/PillionSeatCargoBay.java +++ b/megamek/src/megamek/common/PillionSeatCargoBay.java @@ -49,7 +49,12 @@ public String getType() { @Override public String toString() { - return "pillionseats:" + currentSpace + ":" + doors; + String bayType = "pillionseats"; + return this.bayString( + bayType, + currentSpace, + doors + ); } @Override diff --git a/megamek/src/megamek/common/ProtomechBay.java b/megamek/src/megamek/common/ProtomechBay.java index 39ad214e299..d7e9139ff03 100644 --- a/megamek/src/megamek/common/ProtomechBay.java +++ b/megamek/src/megamek/common/ProtomechBay.java @@ -96,7 +96,13 @@ public int getPersonnel(boolean clan) { @Override public String toString() { - return "ProtoMekBay:" + totalSpace + ":" + doors + ":"+ bayNumber; + String bayType = "ProtoMekBay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } public static TechAdvancement techAdvancement() { diff --git a/megamek/src/megamek/common/RefrigeratedCargoBay.java b/megamek/src/megamek/common/RefrigeratedCargoBay.java index 3b1ba0129b4..f5a52e6904b 100644 --- a/megamek/src/megamek/common/RefrigeratedCargoBay.java +++ b/megamek/src/megamek/common/RefrigeratedCargoBay.java @@ -86,7 +86,13 @@ public double getWeight() { @Override public String toString() { - return "refrigeratedcargobay:" + totalSpace + ":" + doors + ":"+ bayNumber; + String bayType = "refrigeratedcargobay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } @Override diff --git a/megamek/src/megamek/common/SecondClassQuartersCargoBay.java b/megamek/src/megamek/common/SecondClassQuartersCargoBay.java index e0bd59cabdc..e5d5e25bc6a 100644 --- a/megamek/src/megamek/common/SecondClassQuartersCargoBay.java +++ b/megamek/src/megamek/common/SecondClassQuartersCargoBay.java @@ -92,7 +92,12 @@ public boolean isQuarters() { @Override public String toString() { - return "2ndclassquarters:" + weight + ":" + doors; + String bayType = "2ndclassquarters"; + return this.bayString( + bayType, + totalSpace, + doors + ); } @Override diff --git a/megamek/src/megamek/common/SmallCraftBay.java b/megamek/src/megamek/common/SmallCraftBay.java index 6c10b859baf..2b46d88d411 100644 --- a/megamek/src/megamek/common/SmallCraftBay.java +++ b/megamek/src/megamek/common/SmallCraftBay.java @@ -94,7 +94,13 @@ public int getPersonnel(boolean clan) { @Override public String toString() { - return (hasARTS() ? "artssmallcraftbay:" : "smallcraftbay:") + totalSpace + ":" + doors + ":" + bayNumber; + String bayType = (hasARTS() ? "artssmallcraftbay" : "smallcraftbay"); + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } public static TechAdvancement techAdvancement() { @@ -114,4 +120,4 @@ public TechAdvancement getTechAdvancement() { public long getCost() { return 20000L * (long) totalSpace; } -} \ No newline at end of file +} diff --git a/megamek/src/megamek/common/StandardSeatCargoBay.java b/megamek/src/megamek/common/StandardSeatCargoBay.java index 3e53eb6ee0a..fd8f8c271a7 100644 --- a/megamek/src/megamek/common/StandardSeatCargoBay.java +++ b/megamek/src/megamek/common/StandardSeatCargoBay.java @@ -75,7 +75,12 @@ public boolean isQuarters() { @Override public String toString() { - return "standardseats:" + currentSpace + ":" + doors; + String bayType = "standardseats"; + return this.bayString( + bayType, + currentSpace, + doors + ); } @Override diff --git a/megamek/src/megamek/common/SteerageQuartersCargoBay.java b/megamek/src/megamek/common/SteerageQuartersCargoBay.java index e4343e629a3..4e801087242 100644 --- a/megamek/src/megamek/common/SteerageQuartersCargoBay.java +++ b/megamek/src/megamek/common/SteerageQuartersCargoBay.java @@ -91,7 +91,12 @@ public boolean isQuarters() { @Override public String toString() { - return "steeragequarters:" + weight + ":" + doors; + String bayType = "steeragequarters"; + return this.bayString( + bayType, + totalSpace, + doors + ); } @Override diff --git a/megamek/src/megamek/common/SuperHeavyVehicleBay.java b/megamek/src/megamek/common/SuperHeavyVehicleBay.java index a230ecebe56..c70939e46c2 100644 --- a/megamek/src/megamek/common/SuperHeavyVehicleBay.java +++ b/megamek/src/megamek/common/SuperHeavyVehicleBay.java @@ -72,7 +72,13 @@ public int getPersonnel(boolean clan) { @Override public String toString() { - return "superheavyvehiclebay:" + totalSpace + ":" + doors + ":"+ bayNumber; + String bayType = "superheavyvehiclebay"; + return this.bayString( + bayType, + totalSpace, + doors, + bayNumber + ); } public static TechAdvancement techAdvancement() { @@ -92,4 +98,4 @@ public TechAdvancement getTechAdvancement() { public long getCost() { return 20000L * (long) totalSpace; } -} \ No newline at end of file +} diff --git a/megamek/src/megamek/common/loaders/BLKDropshipFile.java b/megamek/src/megamek/common/loaders/BLKDropshipFile.java index 6717bd27891..37991c54edf 100644 --- a/megamek/src/megamek/common/loaders/BLKDropshipFile.java +++ b/megamek/src/megamek/common/loaders/BLKDropshipFile.java @@ -13,6 +13,7 @@ */ package megamek.common.loaders; +import com.sun.mail.util.DecodingException; import megamek.common.*; import megamek.common.util.BuildingBlock; @@ -206,6 +207,7 @@ public Entity getEntity() throws EntityLoadingException { } addTransports(a); + a.setArmorTonnage(a.getArmorWeight()); return a; } diff --git a/megamek/src/megamek/common/loaders/BLKFile.java b/megamek/src/megamek/common/loaders/BLKFile.java index 31cd5c85755..0f3350ec8bf 100644 --- a/megamek/src/megamek/common/loaders/BLKFile.java +++ b/megamek/src/megamek/common/loaders/BLKFile.java @@ -17,6 +17,7 @@ import java.util.*; import java.util.stream.Collectors; +import com.sun.mail.util.DecodingException; import megamek.common.*; import megamek.common.InfantryBay.PlatoonType; import megamek.common.options.IOption; @@ -43,16 +44,28 @@ public class BLKFile { public static final int BATTERY = 11; public static final int SOLAR = 12; public static final int EXTERNAL = 13; - + private static final String COMSTAR_BAY = "c*"; + private static final int TRANSPORTER_FIELDS = 6; + + /** Bitmap fields; 2 of 32 used currently + * COMSTAR: bit 0 + * IS/Clan tech (for mixed tech): bit 1 + * + * Note: mutual exclusivity is _not_ enforced here. + */ + + private static final int COMSTAR_BIT = 1; + // IS = 0; CLAN = 1, mutually exclusive + private static final int TECH_CLAN_BASE = 1 << 1; static final String BLK_EXTRA_SEATS = "extra_seats"; - + /** * If a vehicular grenade launcher does not have a facing provided, assign a default facing. * For vehicles this is determined by location. For protomechs the only legal location is * the torso, but it may be mounted rear-facing. - * + * * @param location The location where the VGL is mounted. * @param rear Whether the VGL is rear-facing. * @return The facing to assign to the VGL. @@ -323,7 +336,7 @@ public void setFluff(Entity e) { if (dataFile.exists("history")) { e.getFluff().setHistory(dataFile.getDataAsString("history")[0]); } - + if (dataFile.exists("manufacturer")) { e.getFluff().setManufacturer(dataFile.getDataAsString("manufacturer")[0]); } @@ -331,7 +344,7 @@ public void setFluff(Entity e) { if (dataFile.exists("primaryFactory")) { e.getFluff().setPrimaryFactory(dataFile.getDataAsString("primaryFactory")[0]); } - + if (dataFile.exists("systemManufacturers")) { for (String line : dataFile.getDataAsString("systemManufacturers")) { String[] fields = line.split(":"); @@ -360,23 +373,23 @@ public void setFluff(Entity e) { if (dataFile.exists("notes")) { e.getFluff().setNotes(dataFile.getDataAsString("notes")[0]); } - + if (dataFile.exists("use")) { e.getFluff().setUse(dataFile.getDataAsString("use")[0]); } - + if (dataFile.exists("length")) { e.getFluff().setLength(dataFile.getDataAsString("length")[0]); } - + if (dataFile.exists("width")) { e.getFluff().setWidth(dataFile.getDataAsString("width")[0]); } - + if (dataFile.exists("height")) { e.getFluff().setHeight(dataFile.getDataAsString("height")[0]); } - + if (dataFile.exists("source")) { e.setSource(dataFile.getDataAsString("source")[0]); } @@ -689,7 +702,7 @@ public static BuildingBlock getBlock(Entity t) { if (t.isOmni()) { blk.writeBlockData("omni", 1); } - + int[] armor_array; if (t.hasETypeFlag(Entity.ETYPE_AERO)) { if (t.hasETypeFlag(Entity.ETYPE_JUMPSHIP)) { @@ -731,7 +744,7 @@ public static BuildingBlock getBlock(Entity t) { if ((m.getLinkedBy() != null) && (m.getLinkedBy().isOneShot())) { continue; } - + if (m.getType() instanceof BayWeapon) { int loc = m.getLocation(); if (loc == Entity.LOC_NONE) { @@ -783,13 +796,13 @@ public static BuildingBlock getBlock(Entity t) { if (!t.hasPatchworkArmor() && t.hasBARArmor(1)) { blk.writeBlockData("barrating", t.getBARRating(1)); } - + if (t.isSupportVehicle()) { blk.writeBlockData("structural_tech_rating", t.getStructuralTechRating()); blk.writeBlockData("engine_tech_rating", t.getEngineTechRating()); blk.writeBlockData("armor_tech_rating", t.getArmorTechRating()); } - + if (t.hasETypeFlag(Entity.ETYPE_SMALL_CRAFT) || t.hasETypeFlag(Entity.ETYPE_JUMPSHIP)) { blk.writeBlockData("structural_integrity", ((Aero) t).get0SI()); } @@ -817,7 +830,7 @@ public static BuildingBlock getBlock(Entity t) { if (!t.getFluff().getPrimaryFactory().isBlank()) { blk.writeBlockData("primaryFactory", t.getFluff().getPrimaryFactory()); } - + List list = t.getFluff().createSystemManufacturersList(); if (!list.isEmpty()) { blk.writeBlockData("systemManufacturers", list); @@ -890,11 +903,11 @@ public static BuildingBlock getBlock(Entity t) { blk.writeBlockData("Secondary", infantry.getSecondaryWeapon() .getInternalName()); } - + if (infantry.canMakeAntiMekAttacks()) { blk.writeBlockData("antimek", (infantry.getAntiMekSkill() + "")); } - + EquipmentType et = infantry.getArmorKit(); if (et != null) { blk.writeBlockData("armorKit", et.getInternalName()); @@ -982,7 +995,7 @@ public static BuildingBlock getBlock(Entity t) { blk.writeBlockData(BLK_EXTRA_SEATS, tank.getExtraCrewSeats()); } } - + if (t instanceof SmallCraft) { SmallCraft sc = (SmallCraft) t; blk.writeBlockData("designtype", sc.getDesignType()); @@ -1007,7 +1020,7 @@ public static BuildingBlock getBlock(Entity t) { && ((SpaceStation) t).isModular()) { blk.writeBlockData("modular", 1); } - + if (t instanceof Jumpship) { Jumpship js = (Jumpship) t; if (js.hasHPG()) { @@ -1091,11 +1104,11 @@ public static void encode(String fileName, Entity t) { blk.writeBlockFile(fileName); } - protected void addTransports(Entity e) { + protected void addTransports(Entity e) throws EntityLoadingException { if (dataFile.exists("transporters")) { String[] transporters = dataFile.getDataAsString("transporters"); HashSet usedBayNumbers = new HashSet<>(); - + // Walk the array of transporters. for (String transporter : transporters) { transporter = transporter.toLowerCase(); @@ -1106,129 +1119,148 @@ protected void addTransports(Entity e) { transporter = transporter.substring(4); } + String[] transporterParts = transporter.split(Bay.FIELD_SEPARATOR, 2); + String startsWith = transporterParts[0]; + String numbers = transporterParts.length > 1 ? transporterParts[1] : ""; + ParsedBayInfo pbi = null; + // TroopSpace: - if (transporter.startsWith("troopspace:")) { - // Everything after the ':' should be the space's size. - double fsize = Double.parseDouble(transporter.substring(11)); - e.addTransporter(new TroopSpace(fsize), isPod); - } else if (transporter.startsWith("cargobay:")) { - String numbers = transporter.substring(9); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new CargoBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("liquidcargobay:")) { - String numbers = transporter.substring(15); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new LiquidCargoBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("insulatedcargobay:")) { - String numbers = transporter.substring(18); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new InsulatedCargoBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("refrigeratedcargobay:")) { - String numbers = transporter.substring(21); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new RefrigeratedCargoBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("livestockcargobay:")) { - String numbers = transporter.substring(18); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new LivestockCargoBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("asfbay:")) { - String numbers = transporter.substring(7); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new ASFBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber(), hasARTS), isPod); - } else if (transporter.startsWith("smallcraftbay:")) { - String numbers = transporter.substring(14); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new SmallCraftBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber(), hasARTS), isPod); - } else if (transporter.startsWith("mechbay:")) { - String numbers = transporter.substring(8); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new MechBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("lightvehiclebay:")) { - String numbers = transporter.substring(16); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new LightVehicleBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("heavyvehiclebay:")) { - String numbers = transporter.substring(16); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new HeavyVehicleBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("superheavyvehiclebay:")) { - String numbers = transporter.substring(21); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new SuperHeavyVehicleBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("infantrybay:")) { - String numbers = transporter.substring(12); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new InfantryBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber(), pbi.getPlatoonType()), isPod); - } else if (transporter.startsWith("battlearmorbay:")) { - String numbers = transporter.substring(15); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new BattleArmorBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber(), - e.isClan(), pbi.isComstarBay()), isPod); - } else if (transporter.startsWith("bay:")) { - String numbers = transporter.substring(4); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new Bay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("protomechbay:")) { - String numbers = transporter.substring(13); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new ProtomechBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); - } else if (transporter.startsWith("dropshuttlebay:")) { - String numbers = transporter.substring("dropshuttlebay:".length()); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new DropshuttleBay(pbi.getDoors(), pbi.getBayNumber(), pbi.getFacing()), isPod); - } else if (transporter.startsWith("navalrepairpressurized:")) { - String numbers = transporter.substring("navalrepairpressurized:".length()); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new NavalRepairFacility(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber(), - pbi.getFacing(), true, hasARTS), isPod); - } else if (transporter.startsWith("navalrepairunpressurized:")) { - String numbers = transporter.substring("navalrepairunpressurized:".length()); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new NavalRepairFacility(pbi.getSize(), pbi.getDoors(), - pbi.getBayNumber(), pbi.getFacing(), false, hasARTS), isPod); - } else if (transporter.startsWith("reinforcedrepairfacility:")) { - String numbers = transporter.substring("reinforcedrepairfacility:".length()); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new ReinforcedRepairFacility(pbi.getSize(), pbi.getDoors(), - pbi.getBayNumber(), pbi.getFacing()), isPod); - } else if (transporter.startsWith("crewquarters:")) { - String numbers = transporter.substring(13); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new CrewQuartersCargoBay(pbi.getSize(), pbi.getDoors()), isPod); - } else if (transporter.startsWith("steeragequarters:")) { - String numbers = transporter.substring(17); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new SteerageQuartersCargoBay(pbi.getSize(), pbi.getDoors()), isPod); - } else if (transporter.startsWith("2ndclassquarters:")) { - String numbers = transporter.substring(17); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new SecondClassQuartersCargoBay(pbi.getSize(), pbi.getDoors()), isPod); - } else if (transporter.startsWith("1stclassquarters:")) { - String numbers = transporter.substring(17); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new FirstClassQuartersCargoBay(pbi.getSize(), pbi.getDoors()), isPod); - } else if (transporter.startsWith("pillionseats:")) { - String numbers = transporter.substring(13); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new PillionSeatCargoBay(pbi.getSize()), isPod); - } else if (transporter.startsWith("standardseats:")) { - String numbers = transporter.substring(14); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new StandardSeatCargoBay(pbi.getSize()), isPod); - } else if (transporter.startsWith("ejectionseats:")) { - String numbers = transporter.substring("ejectionseats:".length()); - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new EjectionSeatCargoBay(pbi.getSize()), isPod); - } else if (transporter.startsWith("dockingcollar")) { - //Add values for collars so they can be parsed and assigned a 'bay' number - String numbers = "1.0:0"; - ParsedBayInfo pbi = new ParsedBayInfo(numbers, usedBayNumbers); - e.addTransporter(new DockingCollar(pbi.getBayNumber())); + try { + switch (startsWith) { + case "troopspace": + // Everything after the ':' should be the space's size. + double fsize = Double.parseDouble(numbers); + e.addTransporter(new TroopSpace(fsize), isPod); + break; + case "cargobay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new CargoBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "liquidcargobay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new LiquidCargoBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "insulatedcargobay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new InsulatedCargoBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "refrigeratedcargobay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new RefrigeratedCargoBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "livestockcargobay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new LivestockCargoBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "asfbay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new ASFBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber(), hasARTS), isPod); + break; + case "smallcraftbay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new SmallCraftBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber(), hasARTS), isPod); + break; + case "mechbay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new MechBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "lightvehiclebay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new LightVehicleBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "heavyvehiclebay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new HeavyVehicleBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "superheavyvehiclebay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new SuperHeavyVehicleBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "infantrybay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new InfantryBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber(), pbi.getPlatoonType()), isPod); + break; + case "battlearmorbay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new BattleArmorBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber(), + pbi.isClan(), pbi.isComstarBay()), isPod); + break; + case "bay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new Bay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "protomechbay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new ProtomechBay(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber()), isPod); + break; + case "dropshuttlebay": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new DropshuttleBay(pbi.getDoors(), pbi.getBayNumber(), pbi.getFacing()), isPod); + break; + case "navalrepairpressurized": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new NavalRepairFacility(pbi.getSize(), pbi.getDoors(), pbi.getBayNumber(), + pbi.getFacing(), true, hasARTS), isPod); + break; + case "navalrepairunpressurized": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new NavalRepairFacility(pbi.getSize(), pbi.getDoors(), + pbi.getBayNumber(), pbi.getFacing(), false, hasARTS), isPod); + break; + case "reinforcedrepairfacility": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new ReinforcedRepairFacility(pbi.getSize(), pbi.getDoors(), + pbi.getBayNumber(), pbi.getFacing()), isPod); + break; + case "crewquarters": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new CrewQuartersCargoBay(pbi.getSize(), pbi.getDoors()), isPod); + break; + case "steeragequarters": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new SteerageQuartersCargoBay(pbi.getSize(), pbi.getDoors()), isPod); + break; + case "2ndclassquarters": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new SecondClassQuartersCargoBay(pbi.getSize(), pbi.getDoors()), isPod); + break; + case "1stclassquarters": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new FirstClassQuartersCargoBay(pbi.getSize(), pbi.getDoors()), isPod); + break; + case "pillionseats": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new PillionSeatCargoBay(pbi.getSize()), isPod); + break; + case "standardseats": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new StandardSeatCargoBay(pbi.getSize()), isPod); + break; + case "ejectionseats": + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new EjectionSeatCargoBay(pbi.getSize()), isPod); + break; + case "dockingcollar": + //Add values for collars so they can be parsed and assigned a 'bay' number + numbers = "1.0:0"; + pbi = new ParsedBayInfo(numbers, usedBayNumbers); + e.addTransporter(new DockingCollar(pbi.getBayNumber())); + break; + default: + throw new DecodingException(String.format("Could not decode transporter type '%s'", startsWith)); + } // End switch-case + } + catch(DecodingException x){ + throw new EntityLoadingException( + String.format( + "Error decoding transporter '%s' (was '%s')", transporter, x + ) + ); } } // Handle the next transportation component. - } // End has-transporters + } // End has-transporters. } /** @@ -1243,94 +1275,174 @@ public static class ParsedBayInfo { private int bayNumber = -1; private PlatoonType platoonType = InfantryBay.PlatoonType.FOOT; private boolean isComstarBay; + private boolean isClanBay; private int facing = Entity.LOC_NONE; - - public ParsedBayInfo(String numbers, HashSet usedBayNumbers) { + private int tech_base = 0; + + public ParsedBayInfo(String numbers, HashSet usedBayNumbers) throws DecodingException { // expected format of "numbers" string: - // a:b:c:d - // a is the size of the bay, in tons or # of units and is required - // b is the number of doors in the bay, and is required - // c is the bay number OR an indicator that this bay is a ComStar bay OR an indicator of the kind of infantry bay it is, and is optional - // d is like c except that it's not going to be the bay number - - String[] temp = numbers.split(Bay.FIELD_SEPARATOR); - size = Double.parseDouble(temp[0]); - doors = Integer.parseInt(temp[1]); - - // the bay type indicator will be either the third or fourth item, but the bay number always comes before it - // so we make sure to pick the last item in the array - String potentialBayTypeIndicator = ""; - boolean bayNumberPresent = false; - - if (temp.length == 3) { - potentialBayTypeIndicator = temp[2]; - } else if (temp.length == 4) { - potentialBayTypeIndicator = temp[3]; - bayNumberPresent = true; // a 4-length array indicates that the bay number is in the third element - } - - if (!potentialBayTypeIndicator.isEmpty()) { - // normally a great time for a switch statement, but we're using equalsIgnoreCase for the comparator - if (potentialBayTypeIndicator.equalsIgnoreCase(COMSTAR_BAY)) { - isComstarBay = true; - } else if (potentialBayTypeIndicator.equalsIgnoreCase("jump")) { - platoonType = InfantryBay.PlatoonType.JUMP; - } else if (potentialBayTypeIndicator.equalsIgnoreCase("foot")) { - platoonType = InfantryBay.PlatoonType.FOOT; - } else if (potentialBayTypeIndicator.equalsIgnoreCase("motorized")) { - platoonType = InfantryBay.PlatoonType.MOTORIZED; - } else if (potentialBayTypeIndicator.equalsIgnoreCase("mechanized")) { - platoonType = InfantryBay.PlatoonType.MECHANIZED; - } else if (potentialBayTypeIndicator.startsWith(Bay.FACING_PREFIX)) { - facing = Integer.parseInt(potentialBayTypeIndicator.replace(Bay.FACING_PREFIX, "")); - } else { - // if we looked at the - bayNumberPresent = temp.length == 3; - } + // 0:1:2:3:4:5 + // Field 0 is the size of the bay, in tons or # of units and is required + // Field 1 is the number of doors in the bay, and is required + // Field 2 is the bay number and is required; default value of "-1" is "unset" + // Field 3 is used to record infantry platoon type; default is an empty string + // Field 4 is an int recording facing; default is string representation of entity.LOC_NONE + // Field 5 is a bitmap recording status like tech type, ComStar bay; default is "0" + // To facilitate loading older .blk files, we first convert them to current format + + String[] fields = {}; + try { + // Turn 2-, 3-, or 4-field number lines into standardized 5-field line. + fields = this.normalizeTransporterNumbers(numbers); + + size = Double.parseDouble(fields[0]); + doors = Integer.parseInt(fields[1]); + bayNumber = Integer.parseInt(fields[2]); + platoonType = this.decodePlatoonType(fields[3]); + facing = Integer.parseInt(fields[4]); + + // Split up bitmap + int bitmap = Integer.parseInt(fields[5]); + isComstarBay = (COMSTAR_BIT & bitmap) > 0; + isClanBay = (TECH_CLAN_BASE & bitmap) > 0; + } - - // if we are looking for a bay number - // and a bay number is present, parse it - if (usedBayNumbers != null && bayNumberPresent) { - bayNumber = Integer.parseInt(temp[2]); + catch(DecodingException e){ + throw new DecodingException( + String.format( + "Failure to load '%s' (was '%s'", numbers, e.toString() + ) + ); } - // if a bay number was not specified, assign one + // if a positive bay number was not specified, assign one // if a bay number was specified but is a duplicate, assign a different one int newBay = 1; if (bayNumber == -1 || usedBayNumbers.contains(bayNumber)) { while (usedBayNumbers.contains(newBay)) { newBay++; } - + bayNumber = newBay; } - + usedBayNumbers.add(bayNumber); + } - + public double getSize() { return size; } - + public int getDoors() { return doors; } - + public int getBayNumber() { return bayNumber; } - + public PlatoonType getPlatoonType() { return platoonType; } - + public boolean isComstarBay() { return isComstarBay; } - + + public boolean isClan() { + return isClanBay; + } + public int getFacing() { return facing; } + + public String[] normalizeTransporterNumbers(String numbers) throws DecodingException { + /** In order to make all transporter bays use the same number of data fields, + * but maintain compatibility with older blk files, we will do some + * pre-processing to check what format of field we are looking at, and convert it + * to the new format. + */ + String[] numbersArray = numbers.split(Bay.FIELD_SEPARATOR); + + if (numbersArray.length == TRANSPORTER_FIELDS){ + // Already in expected format + return numbersArray; + } + else if (numbersArray.length > TRANSPORTER_FIELDS){ + throw new DecodingException(String.format("Cannot decode numbers string '%s'", numbers)); + } + + // Expand old-format to new-format size; initialize new field. + String[] temp = new String[TRANSPORTER_FIELDS]; + System.arraycopy(numbersArray,0,temp,0,2); + // Fill in other fields with default/unset values + temp[2] = String.valueOf(bayNumber); + temp[3] = ""; + temp[4] = String.valueOf(Entity.LOC_NONE); + temp[5] = String.valueOf(0); + + // If 2-field format, return with default values set. + if (numbersArray.length == 2){ + return temp; + } + + // Add bitmap field + int bitmap = 0; + + // the bay type indicator will be either the third or fourth item, but the bay number always comes before it, + // so we make sure to pick the last item in the array + String potentialBayTypeIndicator = ""; + if (numbersArray.length == 3) { + potentialBayTypeIndicator = numbersArray[2]; + } else if (numbersArray.length == 4) { + potentialBayTypeIndicator = numbersArray[3]; + } + + if (!potentialBayTypeIndicator.isEmpty()) { + if (potentialBayTypeIndicator.equalsIgnoreCase(COMSTAR_BAY)) { + bitmap |= COMSTAR_BIT; + } + else if ( + Set.of( + new String [] {"jump", "foot", "motorized", "mechanized"} + ).contains(potentialBayTypeIndicator.toLowerCase())){ + // Found an infantry type in the last field (2 or 3) + // Assign to field 3 + temp[3] = potentialBayTypeIndicator; + if (temp[2] == temp[3]){ + // We found the infantry type in the bay number field; unset bay number + temp[2] = String.valueOf(-1); + } + } else if (potentialBayTypeIndicator.startsWith(Bay.FACING_PREFIX)) { + // Strip old facing prefix, set field to remaining value. + // It's difficult to standardize this + temp[4] = potentialBayTypeIndicator.replace(Bay.FACING_PREFIX, ""); + } + } + + // save bitmap to normalize numbers + temp[5] = String.format("%s",bitmap); + return temp; + } + + public PlatoonType decodePlatoonType(String typeString) throws DecodingException { + // Handle platoon type decoding from strings of various casing + + if (typeString.equalsIgnoreCase("jump")) { + return PlatoonType.JUMP; + } else if (typeString.equalsIgnoreCase("foot")) { + return PlatoonType.FOOT; + } else if (typeString.equalsIgnoreCase("motorized")) { + return PlatoonType.MOTORIZED; + } else if (typeString.equalsIgnoreCase("mechanized")) { + return PlatoonType.MECHANIZED; + } else if (typeString == "") { + return platoonType; + } else { + throw new DecodingException(String.format("Cannot determine platoon type from '%s'", typeString)); + } + } } } diff --git a/megamek/unittests/megamek/common/loaders/BLKFileTest.java b/megamek/unittests/megamek/common/loaders/BLKFileTest.java index 141f690287c..0285e2362e0 100644 --- a/megamek/unittests/megamek/common/loaders/BLKFileTest.java +++ b/megamek/unittests/megamek/common/loaders/BLKFileTest.java @@ -18,6 +18,7 @@ */ package megamek.common.loaders; +import com.sun.mail.util.DecodingException; import megamek.common.*; import megamek.common.InfantryBay.PlatoonType; import megamek.common.loaders.BLKFile.ParsedBayInfo; @@ -25,14 +26,13 @@ import java.util.HashSet; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; public class BLKFileTest { - + /** * Strips the bay type identifier from the bay string. - * + * * @param bay The Bay being parsed * @return The part of the bay string containing the parameters (size, doors, num, etc) */ @@ -40,7 +40,7 @@ private String getBayNumbers(Bay bay) { String bayString = bay.toString(); return bayString.substring(bayString.indexOf(Bay.FIELD_SEPARATOR) + 1); } - + @Test public void parseBayDataAssignsMissingBayNumber() { final double SIZE = 2.0; @@ -49,12 +49,18 @@ public void parseBayDataAssignsMissingBayNumber() { HashSet bayNums = new HashSet<>(); bayNums.add(0); bayNums.add(1); - - ParsedBayInfo pbi = new ParsedBayInfo(bayString, bayNums); - - assertEquals(pbi.getSize(), SIZE, 0.01); - assertEquals(pbi.getDoors(), DOORS); - assertEquals(pbi.getBayNumber(), 2); + + + try { + ParsedBayInfo pbi = new ParsedBayInfo(bayString, bayNums); + assertEquals(pbi.getSize(), SIZE, 0.01); + assertEquals(pbi.getDoors(), DOORS); + assertEquals(pbi.getBayNumber(), 2); + } + catch(DecodingException e){ + fail("Unexpected exception!"); + } + } @Test @@ -65,96 +71,141 @@ public void parseBayDataFixesDuplicateBayNumber() { HashSet bayNums = new HashSet<>(); bayNums.add(0); bayNums.add(1); - - ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), bayNums); - - assertEquals(pbi.getSize(), SIZE, 0.01); - assertEquals(pbi.getDoors(), DOORS); - assertEquals(pbi.getBayNumber(), 2); + + try { + ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), bayNums); + + assertEquals(pbi.getSize(), SIZE, 0.01); + assertEquals(pbi.getDoors(), DOORS); + assertEquals(pbi.getBayNumber(), 2); + } + catch(DecodingException e) { + fail("Unexpected exception!"); + } } - + @Test public void parseBayTypeIndicatorWithBayNumber() { Bay bay = new BattleArmorBay(2.0, 1, 1, false, true); - ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); - - assertTrue(pbi.isComstarBay()); - assertEquals(pbi.getBayNumber(), 1); + try { + ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); + + assertTrue(pbi.isComstarBay()); + assertEquals(pbi.getBayNumber(), 1); + } + catch(DecodingException e) { + fail("Unexpected exception!"); + } } @Test public void parseBayTypeIndicatorWithoutBayNumber() { Bay bay = new BattleArmorBay(2.0, 1, 4, false, true); - String numbers = getBayNumbers(bay).replace(":4", ""); + String numbers = getBayNumbers(bay).replace(":4", ":-1"); HashSet bayNums = new HashSet<>(); bayNums.add(0); bayNums.add(1); - - ParsedBayInfo pbi = new ParsedBayInfo(numbers, bayNums); - - assertTrue(pbi.isComstarBay()); - assertEquals(pbi.getBayNumber(), 2); + + try { + ParsedBayInfo pbi = new ParsedBayInfo(numbers, bayNums); + + assertTrue(pbi.isComstarBay()); + assertEquals(pbi.getBayNumber(), 2); + } + catch(DecodingException e) { + fail("Unexpected exception!"); + } } @Test public void parseFootInfantryBay() { Bay bay = new InfantryBay(2.0, 1, 0, PlatoonType.FOOT); - - ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); - - assertEquals(pbi.getPlatoonType(), PlatoonType.FOOT); + + try { + ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); + + assertEquals(pbi.getPlatoonType(), PlatoonType.FOOT); + } + catch(DecodingException e) { + fail("Unexpected exception!"); + } } @Test public void parseJumpInfantryBay() { Bay bay = new InfantryBay(2.0, 1, 0, PlatoonType.JUMP); - - ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); - - assertEquals(pbi.getPlatoonType(), PlatoonType.JUMP); + + try { + ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); + + assertEquals(pbi.getPlatoonType(), PlatoonType.JUMP); + } + catch(DecodingException e) { + fail("Unexpected exception!"); + } } @Test public void parseMotorizedInfantryBay() { Bay bay = new InfantryBay(2.0, 1, 0, PlatoonType.MOTORIZED); - - ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); - - assertEquals(pbi.getPlatoonType(), PlatoonType.MOTORIZED); + + try { + ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); + + assertEquals(pbi.getPlatoonType(), PlatoonType.MOTORIZED); + } + catch(DecodingException e) { + fail("Unexpected exception!"); + } } @Test public void parseMechanizedInfantryBay() { Bay bay = new InfantryBay(2.0, 1, 0, PlatoonType.MECHANIZED); - - ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); - - assertEquals(pbi.getPlatoonType(), PlatoonType.MECHANIZED); + + try { + ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); + + assertEquals(pbi.getPlatoonType(), PlatoonType.MECHANIZED); + } + catch(DecodingException e) { + fail("Unexpected exception!"); + } } - + @Test public void parseDropShuttleBay() { Bay bay = new DropshuttleBay(1, -1, Jumpship.LOC_AFT); - - ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); - - assertEquals(pbi.getDoors(), 1); - assertEquals(pbi.getBayNumber(), 1); - assertEquals(pbi.getFacing(), Jumpship.LOC_AFT); + + try { + ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); + + assertEquals(pbi.getDoors(), 1); + assertEquals(pbi.getBayNumber(), 1); + assertEquals(pbi.getFacing(), Jumpship.LOC_AFT); + } + catch(DecodingException e) { + fail(String.format("Unexpected exception (%s)!", e.toString())); + } } - + @Test public void parseNavalRepairFacility() { final double SIZE = 5000.0; final int DOORS = 2; Bay bay = new NavalRepairFacility(SIZE, DOORS, -1, Jumpship.LOC_AFT, true); - - ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); - - assertEquals(pbi.getSize(), SIZE, 0.01); - assertEquals(pbi.getDoors(), DOORS); - assertEquals(pbi.getBayNumber(), 1); - assertEquals(pbi.getFacing(), Jumpship.LOC_AFT); + + try { + ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); + + assertEquals(pbi.getSize(), SIZE, 0.01); + assertEquals(pbi.getDoors(), DOORS); + assertEquals(pbi.getBayNumber(), 1); + assertEquals(pbi.getFacing(), Jumpship.LOC_AFT); + } + catch(DecodingException e) { + fail("Unexpected exception!"); + } } } From 1b14221f6dbc426751a7dafa9feb6f59d61f4038 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 15 Aug 2023 10:26:17 -0700 Subject: [PATCH 02/10] Fixed quarters weights; added unit tests; bugfixes --- .../megamek/common/CrewQuartersCargoBay.java | 2 +- .../common/FirstClassQuartersCargoBay.java | 2 +- .../common/SecondClassQuartersCargoBay.java | 2 +- .../common/SteerageQuartersCargoBay.java | 2 +- .../src/megamek/common/loaders/BLKFile.java | 26 +++- .../common/loaders/BLKDropshipFileTest.java | 70 +++++++++ .../megamek/common/loaders/BLKFileTest.java | 137 +++++++++++++++--- 7 files changed, 207 insertions(+), 34 deletions(-) create mode 100644 megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java diff --git a/megamek/src/megamek/common/CrewQuartersCargoBay.java b/megamek/src/megamek/common/CrewQuartersCargoBay.java index 2de41adc6a6..2e02ca97419 100644 --- a/megamek/src/megamek/common/CrewQuartersCargoBay.java +++ b/megamek/src/megamek/common/CrewQuartersCargoBay.java @@ -97,7 +97,7 @@ public String toString() { String bayType = "crewquarters"; return this.bayString( bayType, - totalSpace, + weight, doors ); } diff --git a/megamek/src/megamek/common/FirstClassQuartersCargoBay.java b/megamek/src/megamek/common/FirstClassQuartersCargoBay.java index 889706e7841..7172dff275a 100644 --- a/megamek/src/megamek/common/FirstClassQuartersCargoBay.java +++ b/megamek/src/megamek/common/FirstClassQuartersCargoBay.java @@ -97,7 +97,7 @@ public String toString() { String bayType = "1stclassquarters"; return this.bayString( bayType, - totalSpace, + weight, doors ); } diff --git a/megamek/src/megamek/common/SecondClassQuartersCargoBay.java b/megamek/src/megamek/common/SecondClassQuartersCargoBay.java index e5d5e25bc6a..8e427958b60 100644 --- a/megamek/src/megamek/common/SecondClassQuartersCargoBay.java +++ b/megamek/src/megamek/common/SecondClassQuartersCargoBay.java @@ -95,7 +95,7 @@ public String toString() { String bayType = "2ndclassquarters"; return this.bayString( bayType, - totalSpace, + weight, doors ); } diff --git a/megamek/src/megamek/common/SteerageQuartersCargoBay.java b/megamek/src/megamek/common/SteerageQuartersCargoBay.java index 4e801087242..a3d3e0ff4b5 100644 --- a/megamek/src/megamek/common/SteerageQuartersCargoBay.java +++ b/megamek/src/megamek/common/SteerageQuartersCargoBay.java @@ -94,7 +94,7 @@ public String toString() { String bayType = "steeragequarters"; return this.bayString( bayType, - totalSpace, + weight, doors ); } diff --git a/megamek/src/megamek/common/loaders/BLKFile.java b/megamek/src/megamek/common/loaders/BLKFile.java index 0f3350ec8bf..59b19fc5df5 100644 --- a/megamek/src/megamek/common/loaders/BLKFile.java +++ b/megamek/src/megamek/common/loaders/BLKFile.java @@ -1292,13 +1292,13 @@ public ParsedBayInfo(String numbers, HashSet usedBayNumbers) throws Dec String[] fields = {}; try { - // Turn 2-, 3-, or 4-field number lines into standardized 5-field line. - fields = this.normalizeTransporterNumbers(numbers); + // Turn 2-, 3-, or 4-field number lines into standardized 6-field line. + fields = normalizeTransporterNumbers(numbers); size = Double.parseDouble(fields[0]); doors = Integer.parseInt(fields[1]); bayNumber = Integer.parseInt(fields[2]); - platoonType = this.decodePlatoonType(fields[3]); + platoonType = decodePlatoonType(fields[3]); facing = Integer.parseInt(fields[4]); // Split up bitmap @@ -1358,7 +1358,7 @@ public int getFacing() { return facing; } - public String[] normalizeTransporterNumbers(String numbers) throws DecodingException { + public static String[] normalizeTransporterNumbers(String numbers) throws DecodingException { /** In order to make all transporter bays use the same number of data fields, * but maintain compatibility with older blk files, we will do some * pre-processing to check what format of field we are looking at, and convert it @@ -1376,9 +1376,10 @@ else if (numbersArray.length > TRANSPORTER_FIELDS){ // Expand old-format to new-format size; initialize new field. String[] temp = new String[TRANSPORTER_FIELDS]; + // Copy initial two fields; subsequent fields get defaults or are set later System.arraycopy(numbersArray,0,temp,0,2); // Fill in other fields with default/unset values - temp[2] = String.valueOf(bayNumber); + temp[2] = String.valueOf(-1); temp[3] = ""; temp[4] = String.valueOf(Entity.LOC_NONE); temp[5] = String.valueOf(0); @@ -1387,6 +1388,14 @@ else if (numbersArray.length > TRANSPORTER_FIELDS){ if (numbersArray.length == 2){ return temp; } + else if (numbersArray.length > 2){ + // Attempt to parse index 2 as an integer bay number, otherwise leave it as default + try{ + temp[2] = String.valueOf(Integer.parseInt(numbersArray[2])); + } catch (NumberFormatException e){ + // pass + } + } // Add bitmap field int bitmap = 0; @@ -1398,6 +1407,7 @@ else if (numbersArray.length > TRANSPORTER_FIELDS){ potentialBayTypeIndicator = numbersArray[2]; } else if (numbersArray.length == 4) { potentialBayTypeIndicator = numbersArray[3]; + temp[2] = numbersArray[2]; } if (!potentialBayTypeIndicator.isEmpty()) { @@ -1427,7 +1437,7 @@ else if ( return temp; } - public PlatoonType decodePlatoonType(String typeString) throws DecodingException { + public static PlatoonType decodePlatoonType(String typeString) throws DecodingException { // Handle platoon type decoding from strings of various casing if (typeString.equalsIgnoreCase("jump")) { @@ -1438,8 +1448,8 @@ public PlatoonType decodePlatoonType(String typeString) throws DecodingException return PlatoonType.MOTORIZED; } else if (typeString.equalsIgnoreCase("mechanized")) { return PlatoonType.MECHANIZED; - } else if (typeString == "") { - return platoonType; + } else if (typeString.isEmpty()) { + return PlatoonType.FOOT; } else { throw new DecodingException(String.format("Cannot determine platoon type from '%s'", typeString)); } diff --git a/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java new file mode 100644 index 00000000000..4a27e15d11b --- /dev/null +++ b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MegaMek. + * + * MegaMek is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MegaMek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MegaMek. If not, see . + */ +package megamek.common.loaders; + +import com.sun.mail.util.DecodingException; +import megamek.common.*; +import megamek.common.loaders.BLKFile.ParsedBayInfo; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; + +import static org.junit.jupiter.api.Assertions.*; + +public class BLKDropshipFileTest { + + /** + * Strips the bay type identifier from the bay string. + * + * @param bay The Bay being parsed + * @return The part of the bay string containing the parameters (size, doors, num, etc) + */ + private String getBayNumbers(Bay bay) { + String bayString = bay.toString(); + return bayString.substring(bayString.indexOf(Bay.FIELD_SEPARATOR) + 1); + } + + @Test + public void parseMixedTechDSWithMixedTechBABays () throws DecodingException{ + + } + + @Test + public void parseBayDataAssignsMissingBayNumber() { + final double SIZE = 2.0; + final int DOORS = 1; + String bayString = SIZE + ":" + DOORS; + HashSet bayNums = new HashSet<>(); + bayNums.add(0); + bayNums.add(1); + + + try { + ParsedBayInfo pbi = new ParsedBayInfo(bayString, bayNums); + assertEquals(pbi.getSize(), SIZE, 0.01); + assertEquals(pbi.getDoors(), DOORS); + assertEquals(pbi.getBayNumber(), 2); + } + catch(DecodingException e){ + fail("Unexpected exception!"); + } + + } + +} diff --git a/megamek/unittests/megamek/common/loaders/BLKFileTest.java b/megamek/unittests/megamek/common/loaders/BLKFileTest.java index 0285e2362e0..2c49e4906be 100644 --- a/megamek/unittests/megamek/common/loaders/BLKFileTest.java +++ b/megamek/unittests/megamek/common/loaders/BLKFileTest.java @@ -23,6 +23,8 @@ import megamek.common.InfantryBay.PlatoonType; import megamek.common.loaders.BLKFile.ParsedBayInfo; import org.junit.jupiter.api.Test; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.HashSet; @@ -34,7 +36,7 @@ public class BLKFileTest { * Strips the bay type identifier from the bay string. * * @param bay The Bay being parsed - * @return The part of the bay string containing the parameters (size, doors, num, etc) + * @return The part of the bay string containing the parameters (size, doors, num, etc) */ private String getBayNumbers(Bay bay) { String bayString = bay.toString(); @@ -56,8 +58,7 @@ public void parseBayDataAssignsMissingBayNumber() { assertEquals(pbi.getSize(), SIZE, 0.01); assertEquals(pbi.getDoors(), DOORS); assertEquals(pbi.getBayNumber(), 2); - } - catch(DecodingException e){ + } catch (DecodingException e) { fail("Unexpected exception!"); } @@ -78,8 +79,7 @@ public void parseBayDataFixesDuplicateBayNumber() { assertEquals(pbi.getSize(), SIZE, 0.01); assertEquals(pbi.getDoors(), DOORS); assertEquals(pbi.getBayNumber(), 2); - } - catch(DecodingException e) { + } catch (DecodingException e) { fail("Unexpected exception!"); } } @@ -93,8 +93,7 @@ public void parseBayTypeIndicatorWithBayNumber() { assertTrue(pbi.isComstarBay()); assertEquals(pbi.getBayNumber(), 1); - } - catch(DecodingException e) { + } catch (DecodingException e) { fail("Unexpected exception!"); } } @@ -112,8 +111,7 @@ public void parseBayTypeIndicatorWithoutBayNumber() { assertTrue(pbi.isComstarBay()); assertEquals(pbi.getBayNumber(), 2); - } - catch(DecodingException e) { + } catch (DecodingException e) { fail("Unexpected exception!"); } } @@ -126,8 +124,7 @@ public void parseFootInfantryBay() { ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); assertEquals(pbi.getPlatoonType(), PlatoonType.FOOT); - } - catch(DecodingException e) { + } catch (DecodingException e) { fail("Unexpected exception!"); } } @@ -140,8 +137,7 @@ public void parseJumpInfantryBay() { ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); assertEquals(pbi.getPlatoonType(), PlatoonType.JUMP); - } - catch(DecodingException e) { + } catch (DecodingException e) { fail("Unexpected exception!"); } } @@ -154,8 +150,7 @@ public void parseMotorizedInfantryBay() { ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); assertEquals(pbi.getPlatoonType(), PlatoonType.MOTORIZED); - } - catch(DecodingException e) { + } catch (DecodingException e) { fail("Unexpected exception!"); } } @@ -168,8 +163,7 @@ public void parseMechanizedInfantryBay() { ParsedBayInfo pbi = new ParsedBayInfo(getBayNumbers(bay), new HashSet<>()); assertEquals(pbi.getPlatoonType(), PlatoonType.MECHANIZED); - } - catch(DecodingException e) { + } catch (DecodingException e) { fail("Unexpected exception!"); } } @@ -184,8 +178,7 @@ public void parseDropShuttleBay() { assertEquals(pbi.getDoors(), 1); assertEquals(pbi.getBayNumber(), 1); assertEquals(pbi.getFacing(), Jumpship.LOC_AFT); - } - catch(DecodingException e) { + } catch (DecodingException e) { fail(String.format("Unexpected exception (%s)!", e.toString())); } } @@ -203,9 +196,109 @@ public void parseNavalRepairFacility() { assertEquals(pbi.getDoors(), DOORS); assertEquals(pbi.getBayNumber(), 1); assertEquals(pbi.getFacing(), Jumpship.LOC_AFT); - } - catch(DecodingException e) { + } catch (DecodingException e) { fail("Unexpected exception!"); } } -} + + @Test + public void decodeValidPlatoonTypes() throws DecodingException { + final String[] types = {"foot", "jump", "mechanized", "motorized", ""}; + final PlatoonType[] ptypes = { + PlatoonType.FOOT, + PlatoonType.JUMP, + PlatoonType.MECHANIZED, + PlatoonType.MOTORIZED, + PlatoonType.FOOT + }; + + for (int i = 0; i < types.length; i++) { + assertEquals(ptypes[i], ParsedBayInfo.decodePlatoonType(types[i])); + } + } + + @Test + public void decodeInvalidPlatoonTypeThrows(){ + assertThrows(DecodingException.class, + () -> { + ParsedBayInfo.decodePlatoonType("FEeeTS"); + } + ); + } + + @Test + public void normalizeInvalidNumbersThrows(){ + String invalidNumbers = "10.0:0:1:c*:extra:fields:throw"; + assertThrows(DecodingException.class, + () -> { + ParsedBayInfo.normalizeTransporterNumbers(invalidNumbers); + }); + } + + public boolean confirmTransporterNumbers(String numbers, String[] expNumArray) { + // Verifies matches between array generated from numbers list and expected array + try { + String[] genNumArray = ParsedBayInfo.normalizeTransporterNumbers(numbers); + assertEquals(expNumArray.length, genNumArray.length); + for(int i=0; i < genNumArray.length; i++){ + assertEquals(expNumArray[i], genNumArray[i], + String.format("Checking index %s of '%s' failed", i, numbers) + ); + } + } catch (Exception e){ + return false; + } + return true; + } + + @Test + public void normalizeNewNumbersFormatReturnsSameValues() { + String numbers = "1000.0:1:2::-1:2"; // 1000.0 ton Clan BA bay with one door + String[] expNumArray = numbers.split(Bay.FIELD_SEPARATOR); + + assertTrue(confirmTransporterNumbers(numbers, expNumArray)); + + } + @Test + public void normalizeOldTransporterFormats(){ + // expected format of "numbers" string: + // 0:1:2:3:4:5 + // Field 0 is the size of the bay, in tons or # of units and is required + // Field 1 is the number of doors in the bay, and is required + // Field 2 is the bay number and is required; default value of "-1" is "unset" + // Field 3 is used to record infantry platoon type; default is an empty string + // Field 4 is an int recording facing; default is string representation of entity.LOC_NONE + // Field 5 is a bitmap recording status like tech type, ComStar bay; default is "0" + // numbersArray is in old format; expNumbersArray is an array of String[] in new format + String[] numbersArray = { + "1.0:0", // Size: 1.0; Doors: 0 + "2.0:1", // Size: 2.0; Doors: 1 + "3.0:1:-1", // Size: 3.0; Doors: 1; Bay#: -1 (unset) + "4.0:2:0", // Size: 4.0; Doors: 2; Bay#: 0 + "5.0:1:1", // Size: 5.0; Doors: 1; Bay#: 1 + "6.0:0:c*", // Size: 6.0; Doors: 0; ComStar type = set + "7.0:1:Foot", // Size: 7.0; Doors: 1; infantry bay type FOOT + "8.0:0:1:Jump", // Size: 8.0; Doors: 0; Bay#: 1; infantry bay type JUMP + "9.0:1:2:f1", // Size: 9.0; Doors: 1; Bay#: 2; Facing: 1 + }; + String[][] expNumbersArray = { + {"1.0", "0", "-1", "", "-1", "0"}, + {"2.0", "1", "-1", "", "-1", "0"}, + {"3.0", "1", "-1", "", "-1", "0"}, + {"4.0", "2", "0", "", "-1", "0"}, + {"5.0", "1", "1", "", "-1", "0"}, + {"6.0", "0", "-1", "", "-1", "1"}, + {"7.0", "1", "-1", "Foot", "-1", "0"}, + {"8.0", "0", "1", "Jump", "-1", "0"}, + {"9.0", "1", "2", "", "1", "0"} + }; + boolean matched = false; + for(int i=0; i Date: Tue, 15 Aug 2023 10:27:40 -0700 Subject: [PATCH 03/10] Removed extraneous test file --- .../common/loaders/BLKDropshipFileTest.java | 70 ------------------- 1 file changed, 70 deletions(-) delete mode 100644 megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java diff --git a/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java deleted file mode 100644 index 4a27e15d11b..00000000000 --- a/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. - * - * This file is part of MegaMek. - * - * MegaMek is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MegaMek is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MegaMek. If not, see . - */ -package megamek.common.loaders; - -import com.sun.mail.util.DecodingException; -import megamek.common.*; -import megamek.common.loaders.BLKFile.ParsedBayInfo; -import org.junit.jupiter.api.Test; - -import java.util.HashSet; - -import static org.junit.jupiter.api.Assertions.*; - -public class BLKDropshipFileTest { - - /** - * Strips the bay type identifier from the bay string. - * - * @param bay The Bay being parsed - * @return The part of the bay string containing the parameters (size, doors, num, etc) - */ - private String getBayNumbers(Bay bay) { - String bayString = bay.toString(); - return bayString.substring(bayString.indexOf(Bay.FIELD_SEPARATOR) + 1); - } - - @Test - public void parseMixedTechDSWithMixedTechBABays () throws DecodingException{ - - } - - @Test - public void parseBayDataAssignsMissingBayNumber() { - final double SIZE = 2.0; - final int DOORS = 1; - String bayString = SIZE + ":" + DOORS; - HashSet bayNums = new HashSet<>(); - bayNums.add(0); - bayNums.add(1); - - - try { - ParsedBayInfo pbi = new ParsedBayInfo(bayString, bayNums); - assertEquals(pbi.getSize(), SIZE, 0.01); - assertEquals(pbi.getDoors(), DOORS); - assertEquals(pbi.getBayNumber(), 2); - } - catch(DecodingException e){ - fail("Unexpected exception!"); - } - - } - -} From d943d93d4ed90568a4a3df2d24c7b48828ca433a Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 15 Aug 2023 11:43:13 -0700 Subject: [PATCH 04/10] Re-added BLKDrophshipFileTest.java due to IJ's whining. --- .../common/loaders/BLKDropshipFileTest.java | 54 +++++++++++++++++++ .../megamek/common/loaders/BLKFileTest.java | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java diff --git a/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java new file mode 100644 index 00000000000..d424b70a3ea --- /dev/null +++ b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MegaMek. + * + * MegaMek is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MegaMek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MegaMek. If not, see . + */ +package megamek.common.loaders; + +import com.sun.mail.util.DecodingException; +import megamek.common.*; +import megamek.common.InfantryBay.PlatoonType; +import megamek.common.loaders.BLKFile.ParsedBayInfo; +import org.junit.jupiter.api.Test; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashSet; + +import static org.junit.jupiter.api.Assertions.*; + +public class BLKDropshipFileTest { + + @Test + public void loadNewFormatDropshipAndConfirmFields(){ + /** + * This test verifies that a dropship file using the new bay numbers format + * produces the desired mix of tech, specifically with Clan tech and IS BA bays. + */ + String mockBLKFile = String.join( + System.getProperty("line.separator"), + "" + ); + + // Create InputStream from string + // Instantiate bb with string + // Instantiate Dropship with bb + // Get Entity + // Confirm Dropship tech + // Confirm BA Bay tech + } + +} diff --git a/megamek/unittests/megamek/common/loaders/BLKFileTest.java b/megamek/unittests/megamek/common/loaders/BLKFileTest.java index 2c49e4906be..2805ceb6e5a 100644 --- a/megamek/unittests/megamek/common/loaders/BLKFileTest.java +++ b/megamek/unittests/megamek/common/loaders/BLKFileTest.java @@ -235,7 +235,7 @@ public void normalizeInvalidNumbersThrows(){ }); } - public boolean confirmTransporterNumbers(String numbers, String[] expNumArray) { + private boolean confirmTransporterNumbers(String numbers, String[] expNumArray) { // Verifies matches between array generated from numbers list and expected array try { String[] genNumArray = ParsedBayInfo.normalizeTransporterNumbers(numbers); From 0abfe64b00cde9c858dcaf401df1ce03c7049de4 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 15 Aug 2023 14:35:53 -0700 Subject: [PATCH 05/10] Added DS Blk parsing and BA bay validation test --- .../src/megamek/common/BattleArmorBay.java | 6 +- .../common/loaders/BLKDropshipFileTest.java | 194 +++++++++++++++++- 2 files changed, 190 insertions(+), 10 deletions(-) diff --git a/megamek/src/megamek/common/BattleArmorBay.java b/megamek/src/megamek/common/BattleArmorBay.java index fdb9b116a7c..ead7ac54eac 100644 --- a/megamek/src/megamek/common/BattleArmorBay.java +++ b/megamek/src/megamek/common/BattleArmorBay.java @@ -116,7 +116,7 @@ public String toString() { // See BLKFile.java:BLKFile constants int bitmap = 0; bitmap |= (isComStar? 1 : 0); - bitmap |= (isClan? 1 << 2 : 0); + bitmap |= (isClan? 1 << 1 : 0); String bayType = "battlearmorbay"; return this.bayString( bayType, @@ -149,6 +149,10 @@ public boolean isClan() { return isClan; } + public boolean isComStar() { + return isComStar; + } + @Override public long getCost() { // Based on the weight of the equipment (not capacity), rounded up to the whole ton diff --git a/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java index d424b70a3ea..af194d8525e 100644 --- a/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java +++ b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java @@ -22,33 +22,209 @@ import megamek.common.*; import megamek.common.InfantryBay.PlatoonType; import megamek.common.loaders.BLKFile.ParsedBayInfo; +import megamek.common.util.BuildingBlock; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.util.HashSet; +import java.util.Vector; import static org.junit.jupiter.api.Assertions.*; public class BLKDropshipFileTest { - @Test - public void loadNewFormatDropshipAndConfirmFields(){ + private static String newFormatDSwithMixedBA = String.join( + System.getProperty("line.separator"), + "", + "1", + "", + "", + "MAM0", + "", + "", + "Dropship", + "", + "", + "New", + "", + "", + "Dropship", + "", + "", + "3145", + "", + "", + "3145", + "", + "", + "Mixed (Clan Chassis)", + "", + "", + "Aerodyne", + "", + "", + "battlearmorbay:1.0:1:1::-1:0", + "1stclassquarters:10.0:0:-1::-1:0", + "crewquarters:28.0:0:-1::-1:0", + "battlearmorbay:2.0:1:2::-1:2", + "battlearmorbay:3.0:1:3::-1:1", + "", + "", + "2", + "", + "", + "1", + "", + "", + "1", + "", + "", + "4280", + "", + "", + "41", + "", + "", + "2", + "", + "", + "-1", + "", + "", + "85", + "70", + "70", + "57", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "3", + "", + "", + "200.0", + "", + "", + "1", + "", + "", + "41", + "", + "", + "1", + "", + "", + "0", + "", + "", + "0", + "", + "", + "0", + "", + "", + "0", + "", + "", + "0", + "", + "", + "0", + "", + "", + "0", + "" + ); + + private Dropship loadDropshipFromString(String strOfBLK) throws Exception { /** - * This test verifies that a dropship file using the new bay numbers format + * Load a string of BLK-style blocks as an InputStream and create a new DropShip * produces the desired mix of tech, specifically with Clan tech and IS BA bays. */ - String mockBLKFile = String.join( - System.getProperty("line.separator"), - "" - ); // Create InputStream from string + InputStream is = new ByteArrayInputStream(strOfBLK.getBytes()); + // Instantiate bb with string + BuildingBlock bb = new BuildingBlock(is); + // Instantiate Dropship with bb + IMechLoader loader = new BLKDropshipFile(bb); + // Get Entity - // Confirm Dropship tech - // Confirm BA Bay tech + Entity m_entity = loader.getEntity(); + + return (Dropship) m_entity; + + } + + private boolean confirmBayTypeinBays(Vector bays, String type){ + /** + * Helper to troll through bays looking for a specific combination. + * Can be extended as needed. + */ + boolean found = false; + for(Bay b: bays){ + switch(type) { + case "BA_IS": + if(b instanceof BattleArmorBay) { + found = !b.isClan(); + } + break; + case "BA_CLAN": + if(b instanceof BattleArmorBay) { + found = b.isClan(); + } + break; + case "BA_CS": + if(b instanceof BattleArmorBay) { + found = ((BattleArmorBay) b).isComStar(); + } + break; + } + if(found){ + return true; + } + } + + return false; } + @Test + public void testLoadNewFormatDSHasMixedBATechLevels() { + boolean parsed = false; + boolean mixedTech = false; + boolean ISBACorrect = false; + boolean ClanBACorrect = false; + boolean ComStarBACorrect = false; + Vector bays = null; + + try{ + Dropship ds = loadDropshipFromString(newFormatDSwithMixedBA); + parsed = true; + mixedTech = ds.isMixedTech() & ds.isClan(); // confirm mixed-tech Clan design + bays = ds.getTransportBays(); + ISBACorrect = confirmBayTypeinBays(bays, "BA_IS"); + ClanBACorrect = confirmBayTypeinBays(bays, "BA_CLAN"); + ComStarBACorrect = confirmBayTypeinBays(bays, "BA_CS"); + } catch (Exception e){ + } + assertTrue(parsed); + assertTrue(mixedTech); + assertTrue(ISBACorrect); + assertTrue(ClanBACorrect); + assertTrue(ComStarBACorrect); + + } } From 5312bb96092baa17ef77e399891837bce67000ff Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 15 Aug 2023 14:37:15 -0700 Subject: [PATCH 06/10] Further adding test file Added a test file for BLKDropshipFile.java that reads a BLK-file string in and confirms new BattleArmor bay behavior. --- .../megamek/common/loaders/BLKDropshipFileTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java index af194d8525e..52da3c6a5e0 100644 --- a/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java +++ b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java @@ -18,18 +18,12 @@ */ package megamek.common.loaders; -import com.sun.mail.util.DecodingException; import megamek.common.*; -import megamek.common.InfantryBay.PlatoonType; -import megamek.common.loaders.BLKFile.ParsedBayInfo; import megamek.common.util.BuildingBlock; import org.junit.jupiter.api.Test; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.util.HashSet; import java.util.Vector; import static org.junit.jupiter.api.Assertions.*; From 3a6ddb86ba45c306cf3ca4468dbbf5151581230f Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 15 Aug 2023 15:08:15 -0700 Subject: [PATCH 07/10] Removed some extraneous imports I ended up not needing Mockito after all. --- megamek/unittests/megamek/common/loaders/BLKFileTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/megamek/unittests/megamek/common/loaders/BLKFileTest.java b/megamek/unittests/megamek/common/loaders/BLKFileTest.java index 2805ceb6e5a..4a450b1f515 100644 --- a/megamek/unittests/megamek/common/loaders/BLKFileTest.java +++ b/megamek/unittests/megamek/common/loaders/BLKFileTest.java @@ -23,8 +23,6 @@ import megamek.common.InfantryBay.PlatoonType; import megamek.common.loaders.BLKFile.ParsedBayInfo; import org.junit.jupiter.api.Test; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import java.util.HashSet; From 355a192eccc0fa723b4fb7697fa2d2fdb2403e58 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 15 Aug 2023 22:36:42 -0700 Subject: [PATCH 08/10] Fix warnings found by Git checks Add some extra exception handling, fix string equality check, make bit-wise operation safer. --- megamek/src/megamek/common/loaders/BLKFile.java | 6 +++--- .../megamek/common/loaders/BLKDropshipFileTest.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/megamek/src/megamek/common/loaders/BLKFile.java b/megamek/src/megamek/common/loaders/BLKFile.java index 59b19fc5df5..036674b97a4 100644 --- a/megamek/src/megamek/common/loaders/BLKFile.java +++ b/megamek/src/megamek/common/loaders/BLKFile.java @@ -1250,7 +1250,7 @@ protected void addTransports(Entity e) throws EntityLoadingException { throw new DecodingException(String.format("Could not decode transporter type '%s'", startsWith)); } // End switch-case } - catch(DecodingException x){ + catch(DecodingException|NumberFormatException x){ throw new EntityLoadingException( String.format( "Error decoding transporter '%s' (was '%s')", transporter, x @@ -1307,7 +1307,7 @@ public ParsedBayInfo(String numbers, HashSet usedBayNumbers) throws Dec isClanBay = (TECH_CLAN_BASE & bitmap) > 0; } - catch(DecodingException e){ + catch(DecodingException|NumberFormatException e){ throw new DecodingException( String.format( "Failure to load '%s' (was '%s'", numbers, e.toString() @@ -1421,7 +1421,7 @@ else if ( // Found an infantry type in the last field (2 or 3) // Assign to field 3 temp[3] = potentialBayTypeIndicator; - if (temp[2] == temp[3]){ + if (temp[2].equals(temp[3])){ // We found the infantry type in the bay number field; unset bay number temp[2] = String.valueOf(-1); } diff --git a/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java index 52da3c6a5e0..40f1bf316c4 100644 --- a/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java +++ b/megamek/unittests/megamek/common/loaders/BLKDropshipFileTest.java @@ -207,7 +207,7 @@ public void testLoadNewFormatDSHasMixedBATechLevels() { try{ Dropship ds = loadDropshipFromString(newFormatDSwithMixedBA); parsed = true; - mixedTech = ds.isMixedTech() & ds.isClan(); // confirm mixed-tech Clan design + mixedTech = ds.isMixedTech() && ds.isClan(); // confirm mixed-tech Clan design bays = ds.getTransportBays(); ISBACorrect = confirmBayTypeinBays(bays, "BA_IS"); ClanBACorrect = confirmBayTypeinBays(bays, "BA_CLAN"); From 900a5a137a1e640a74c6b32cb0861e1ae26c2a5b Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 15 Aug 2023 23:23:03 -0700 Subject: [PATCH 09/10] Fix issue caused by illegal, empty blocks. It appears that we have been writing empty blocks into every vehicle for the past 6 or so years. These blocks should not exist in, e.g., combat vehicles, but apparently went unnoticed due to loose handling of the relevant block when empty. This change adds a new test prior to processing these empty blocks on file load, a function to support this test, and a conditional around writing the block to prevent writing empty blocks in every .BLK file. --- .../src/megamek/common/loaders/BLKFile.java | 22 +++++++++++-------- .../megamek/common/util/BuildingBlock.java | 16 ++++++++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/megamek/src/megamek/common/loaders/BLKFile.java b/megamek/src/megamek/common/loaders/BLKFile.java index 036674b97a4..b2766d78039 100644 --- a/megamek/src/megamek/common/loaders/BLKFile.java +++ b/megamek/src/megamek/common/loaders/BLKFile.java @@ -597,16 +597,20 @@ public static BuildingBlock getBlock(Entity t) { blk.writeBlockData("motion_type", t.getMovementModeAsString()); - String[] transporter_array = new String[t.getTransports().size()]; - int index = 0; - for (Transporter transporter : t.getTransports()) { - transporter_array[index] = transporter.toString(); - if (t.isPodMountedTransport(transporter)) { - transporter_array[index] += ":omni"; + if(t.getTransports().size() > 0) { + // We should only write the transporters block for units that can and do + // have transporter bays. Empty Transporters blocks cause issues. + String[] transporter_array = new String[t.getTransports().size()]; + int index = 0; + for (Transporter transporter : t.getTransports()) { + transporter_array[index] = transporter.toString(); + if (t.isPodMountedTransport(transporter)) { + transporter_array[index] += ":omni"; + } + index++; } - index++; + blk.writeBlockData("transporters", transporter_array); } - blk.writeBlockData("transporters", transporter_array); if (!t.isConventionalInfantry()) { if (t instanceof Aero) { @@ -1105,7 +1109,7 @@ public static void encode(String fileName, Entity t) { } protected void addTransports(Entity e) throws EntityLoadingException { - if (dataFile.exists("transporters")) { + if (dataFile.exists("transporters") && dataFile.containsData("transporters")) { String[] transporters = dataFile.getDataAsString("transporters"); HashSet usedBayNumbers = new HashSet<>(); diff --git a/megamek/src/megamek/common/util/BuildingBlock.java b/megamek/src/megamek/common/util/BuildingBlock.java index 9d6f801eeab..26af14439ea 100644 --- a/megamek/src/megamek/common/util/BuildingBlock.java +++ b/megamek/src/megamek/common/util/BuildingBlock.java @@ -707,4 +707,20 @@ public boolean exists(String blockName) { return true; } + + /** + * Checks if a block exists and has data. + */ + public boolean containsData(String blockName) { + if(!exists(blockName)){ + return false; + } + // If the end index is the next line after the start index, + // the block is empty. + // Otherwise it contains data. + int start = findStartIndex(blockName); + int end = findEndIndex(blockName); + return (end - start) > 1; + + } } From e24813203b15d8221801580bb4fdc28f32ee71d6 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 15 Aug 2023 23:34:45 -0700 Subject: [PATCH 10/10] Remove extraneous call to "exists()" New function "containsData" encompasses this call already. --- megamek/src/megamek/common/loaders/BLKFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/megamek/src/megamek/common/loaders/BLKFile.java b/megamek/src/megamek/common/loaders/BLKFile.java index b2766d78039..72b5cc06641 100644 --- a/megamek/src/megamek/common/loaders/BLKFile.java +++ b/megamek/src/megamek/common/loaders/BLKFile.java @@ -1109,7 +1109,7 @@ public static void encode(String fileName, Entity t) { } protected void addTransports(Entity e) throws EntityLoadingException { - if (dataFile.exists("transporters") && dataFile.containsData("transporters")) { + if (dataFile.containsData("transporters")) { String[] transporters = dataFile.getDataAsString("transporters"); HashSet usedBayNumbers = new HashSet<>();