From 1cdfcbad45f9486255547be805a63bc774713199 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Mon, 2 Dec 2024 14:26:54 -0800 Subject: [PATCH 1/9] Initial addition of existing legallity check to ASF loadout code --- .../generator/TeamLoadOutGenerator.java | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java b/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java index 4e57a978253..5bab3dfe10d 100644 --- a/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java +++ b/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java @@ -340,6 +340,11 @@ public boolean checkLegality(AmmoType aType, String faction, String techBase, bo boolean legal = false; boolean clan = techBase.equals("CL"); + // Null-type is illegal! + if (null == aType) { + return false; + } + // Check if tech exists at all (or is explicitly allowed despite being extinct) // and whether it is available at the current tech level. legal = aType.isAvailableIn(allowedYear, showExtinct) @@ -1111,7 +1116,9 @@ public void reconfigureEntities(ArrayList entities, String faction, Muni reconfigurationParameters.groundMap || reconfigurationParameters.enemyCount > reconfigurationParameters.enemyFliers, reconfigurationParameters.friendlyQuality, - reconfigurationParameters.isPirate); + reconfigurationParameters.isPirate, + faction + ); } /** @@ -1388,11 +1395,14 @@ private static String getRandomBin(String binName, boolean trueRandom) { * @param quality IUnitRating enum for force quality (A/A* through F) * @param isPirate true to use specific pirate ordnance loadouts */ - public static void populateAeroBombs(List entityList, - int year, - boolean hasGroundTargets, - int quality, - boolean isPirate) { + public void populateAeroBombs( + List entityList, + int year, + boolean hasGroundTargets, + int quality, + boolean isPirate, + String faction + ) { // Get all valid bombers, and sort unarmed ones to the front // Ignore VTOLs for now, as they suffer extra penalties for mounting bomb @@ -1438,6 +1448,8 @@ public static void populateAeroBombs(List entityList, Entity curBomber = bomberList.get(i); boolean isUnarmed = curBomber.getIndividualWeaponList().isEmpty(); + String techBase = (curBomber.isClan()) ? "CL" : "IS"; + boolean mixedTech = curBomber.isMixedTech(); // Some fighters on ground attack may be flying air cover rather than strictly // air-to-ground @@ -1477,7 +1489,11 @@ public static void populateAeroBombs(List entityList, isCAP, isPirate, quality, - year); + year, + faction, + techBase, + mixedTech + ); // Whoops, go yell at the ordnance technician if (Arrays.stream(generatedBombs).sum() == 0) { continue; @@ -1549,11 +1565,16 @@ private static void loadBombsOntoBombers(List bomberList, Map workingBombMap = new HashMap<>(); for (int curBombType : bombMap.keySet()) { + // Make sure the bomb type is even legal for the current scenario + if ( + !checkLegality( + BombType.createBombByType(curBombType), + faction, + techBase, + mixedTech + ) + ) { + continue; + } + String typeName = BombType.getBombInternalName(curBombType); if (curBombType == BombType.B_RL || curBombType == BombType.B_HE || From d32d5db9ccbaeabf151031016565911dfd44734c Mon Sep 17 00:00:00 2001 From: sleet01 Date: Mon, 2 Dec 2024 23:51:17 -0800 Subject: [PATCH 2/9] Add first unit test of new check --- .../generator/TeamLoadOutGenerator.java | 2 +- .../generator/TeamLoadOutGeneratorTest.java | 82 +++++++++++++------ 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java b/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java index 5bab3dfe10d..6dd880333bf 100644 --- a/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java +++ b/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java @@ -1565,7 +1565,7 @@ private static void loadBombsOntoBombers(List bomberList, Map m.getSubMunitionName().contains("ADA")).findFirst().orElse(null); // Should be available by default in 3151, including to Clans (using MixTech) - assertTrue(tlg.checkLegality(mType, "CC", "IS", false)); - assertTrue(tlg.checkLegality(mType, "FS", "IS", false)); - assertTrue(tlg.checkLegality(mType, "IS", "IS", false)); + Assertions.assertTrue(tlg.checkLegality(mType, "CC", "IS", false)); + Assertions.assertTrue(tlg.checkLegality(mType, "FS", "IS", false)); + Assertions.assertTrue(tlg.checkLegality(mType, "IS", "IS", false)); // Check mixed-tech and regular Clan tech, which should match IS at this point - assertTrue(tlg.checkLegality(mType, "CLAN", "CL", true)); - assertFalse(tlg.checkLegality(mType, "CLAN", "CL", false)); + Assertions.assertTrue(tlg.checkLegality(mType, "CLAN", "CL", true)); + Assertions.assertFalse(tlg.checkLegality(mType, "CLAN", "CL", false)); // Set year back to 3025 when(mockGameOptions.intOption(OptionsConstants.ALLOWED_YEAR)).thenReturn(3025); tlg.updateOptionValues(); - assertFalse(tlg.checkLegality(mType, "CC", "IS", false)); - assertFalse(tlg.checkLegality(mType, "FS", "IS", false)); - assertFalse(tlg.checkLegality(mType, "IS", "IS", false)); - assertFalse(tlg.checkLegality(mType, "CLAN", "CL", true)); + Assertions.assertFalse(tlg.checkLegality(mType, "CC", "IS", false)); + Assertions.assertFalse(tlg.checkLegality(mType, "FS", "IS", false)); + Assertions.assertFalse(tlg.checkLegality(mType, "IS", "IS", false)); + Assertions.assertFalse(tlg.checkLegality(mType, "CLAN", "CL", true)); // Move up to 3070. Because of game settings and lack of "Common" year, ADA // becomes available // everywhere (at least in the IS) immediately after its inception. when(mockGameOptions.intOption(OptionsConstants.ALLOWED_YEAR)).thenReturn(3070); tlg.updateOptionValues(); - assertTrue(tlg.checkLegality(mType, "CC", "IS", false)); - assertFalse(tlg.checkLegality(mType, "FS", "IS", false)); - assertFalse(tlg.checkLegality(mType, "IS", "IS", false)); - assertFalse(tlg.checkLegality(mType, "CLAN", "CL", true)); + Assertions.assertTrue(tlg.checkLegality(mType, "CC", "IS", false)); + Assertions.assertFalse(tlg.checkLegality(mType, "FS", "IS", false)); + Assertions.assertFalse(tlg.checkLegality(mType, "IS", "IS", false)); + Assertions.assertFalse(tlg.checkLegality(mType, "CLAN", "CL", true)); } @Test @@ -597,4 +596,33 @@ void testClampAmmoShotsCannotExceedFull() throws LocationFullException { assertEquals(8, bin1.getUsableShotsLeft()); assertEquals(8, bin2.getUsableShotsLeft()); } + + @Test + void testGenerateExternalOrdnanceCAP3SWEraPirates() { + // Game setup + int year = 2875; + when(mockGameOptions.intOption(OptionsConstants.ALLOWED_YEAR)).thenReturn(year); + TeamLoadOutGenerator tlg = new TeamLoadOutGenerator(game); + // Bomber info + int bombUnits = 20; + boolean airOnly = true; + boolean isPirate = true; + int quality = ForceDescriptor.RATING_5; + String faction = "PIR"; + String techBase = "IS"; + boolean mixedTech = false; + int[] generatedBombs = tlg.generateExternalOrdnance( + bombUnits, + airOnly, + isPirate, + quality, + year, + faction, + techBase, + mixedTech + ); + int[] expected = new int[17]; + assertArrayEquals(expected, generatedBombs); + + } } From a41f9b5e2abdd848553b491e6aaa783172016589 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Wed, 4 Dec 2024 23:27:32 -0800 Subject: [PATCH 3/9] Fix unexpected RL addition when no bombs loaded due to ineligibility --- .../generator/TeamLoadOutGenerator.java | 54 +++++++++++-------- .../generator/TeamLoadOutGeneratorTest.java | 3 ++ 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java b/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java index 6dd880333bf..f1ac7f31a9d 100644 --- a/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java +++ b/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java @@ -1741,37 +1741,49 @@ public int[] generateExternalOrdnance( completeWeight = ordnanceRandomWeights.stream().mapToInt(curWeight -> Math.max(curWeight, 1)).asDoubleStream() .sum(); - for (int curLoad = 0; curLoad < bombUnits && loopSafety < castPropertyInt("maxBombApplicationLoopCount", 10);) { + if (!ordnanceIDs.isEmpty() && !ordnanceRandomWeights.isEmpty() && completeWeight != 0) { + for (int curLoad = 0; curLoad < bombUnits && loopSafety < castPropertyInt("maxBombApplicationLoopCount", 10); ) { - // Randomly get the ordnance type - randomThreshold = (Compute.randomInt( + // Randomly get the ordnance type + randomThreshold = (Compute.randomInt( castPropertyInt("maxBombOrdnanceWeightPercentThreshold", 100)) / 100.0) * completeWeight; - countWeight = 0.0; - for (int i = 0; i < ordnanceIDs.size(); i++) { - countWeight += Math.max(ordnanceRandomWeights.get(i), 1.0); - if (countWeight >= randomThreshold) { - selectedBombType = ordnanceIDs.get(i); - break; + countWeight = 0.0; + for (int i = 0; i < ordnanceIDs.size(); i++) { + countWeight += Math.max(ordnanceRandomWeights.get(i), 1.0); + if (countWeight >= randomThreshold) { + selectedBombType = ordnanceIDs.get(i); + break; + } } - } - // If the selected ordnance doesn't exceed the provided limit increment the - // counter, - // otherwise skip it and keep trying with some safeties to prevent infinite - // loops. - if (selectedBombType >= 0 && + // If the selected ordnance doesn't exceed the provided limit increment the + // counter, + // otherwise skip it and keep trying with some safeties to prevent infinite + // loops. + if (selectedBombType >= 0 && curLoad + BombType.getBombCost(selectedBombType) <= bombUnits) { - bombLoad[selectedBombType]++; - curLoad += BombType.getBombCost(selectedBombType); - } else { - loopSafety++; + bombLoad[selectedBombType]++; + curLoad += BombType.getBombCost(selectedBombType); + } else { + loopSafety++; + } } } // Oops, nothing left - rocket launchers are always popular + // XXX: Add prototype RLs as well. if (Arrays.stream(bombLoad).sum() == 0) { - bombLoad[BombType.B_RL] = bombUnits; - return bombLoad; + if ( + checkLegality( + BombType.createBombByType(BombType.B_RL), + faction, + techBase, + mixedTech + ) + ) { + bombLoad[BombType.B_RL] = bombUnits; + return bombLoad; + } } // Randomly replace advanced ordnance with rockets or HE, depending on force diff --git a/megamek/unittests/megamek/client/generator/TeamLoadOutGeneratorTest.java b/megamek/unittests/megamek/client/generator/TeamLoadOutGeneratorTest.java index ab8e4e29f8f..377f0fa2593 100644 --- a/megamek/unittests/megamek/client/generator/TeamLoadOutGeneratorTest.java +++ b/megamek/unittests/megamek/client/generator/TeamLoadOutGeneratorTest.java @@ -597,6 +597,9 @@ void testClampAmmoShotsCannotExceedFull() throws LocationFullException { assertEquals(8, bin2.getUsableShotsLeft()); } + /** + * We expect CAP Pirate flights in the 3SW era to mount no ordnance. + */ @Test void testGenerateExternalOrdnanceCAP3SWEraPirates() { // Game setup From c4b6ca6bd9d3233dff2573a59c07a6857259d2dd Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 10 Dec 2024 09:15:55 -0800 Subject: [PATCH 4/9] Add Prototype RL10 Bomb and weapontype --- megamek/src/megamek/common/BombType.java | 28 +++++++++ megamek/src/megamek/common/WeaponType.java | 1 + .../common/weapons/bombs/BombISPRL10.java | 63 +++++++++++++++++++ .../common/weapons/bombs/BombISRL10.java | 18 +++--- 4 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 megamek/src/megamek/common/weapons/bombs/BombISPRL10.java diff --git a/megamek/src/megamek/common/BombType.java b/megamek/src/megamek/common/BombType.java index fa22c87a823..b860df51e86 100644 --- a/megamek/src/megamek/common/BombType.java +++ b/megamek/src/megamek/common/BombType.java @@ -42,6 +42,8 @@ public class BombType extends AmmoType { public static final int B_FAE_SMALL = 15; public static final int B_FAE_LARGE = 16; public static final int B_NUM = 17; + // Prototype Rocket Launcher Pod + public static final int B_PRL = 18; public static final String[] bombNames = { "HE Bomb","Cluster Bomb","Laser-guided Bomb", "Rocket", "TAG", "AAA Missile", "AS Missile", @@ -242,6 +244,8 @@ public static BombType createBombByType(int bType) { return createSmallFuelAirBomb(); case B_FAE_LARGE: return createLargeFuelAirBomb(); + case B_PRL: + return createPrototypeRocketBomb(); default: return null; } @@ -631,6 +635,30 @@ private static BombType createRocketBomb() { return bomb; } + private static BombType createPrototypeRocketBomb() { + BombType bomb = new BombType(); + + bomb.name = "Prototype Rocket Launcher Pod"; + bomb.setInternalName("P" + BombType.getBombInternalName(BombType.B_RL)); + bomb.addLookupName("PRL 10 (Bomb)"); + bomb.damagePerShot = 1; + // This works but is fragile + bomb.flags = bomb.flags.or(AmmoType.F_OTHER_BOMB).or(WeaponType.F_PROTOTYPE); + bomb.rackSize = 10; + bomb.ammoType = AmmoType.T_RL_BOMB; + bomb.bombType = BombType.B_PRL; + bomb.shots = 1; + bomb.bv = 18; + bomb.cost = 15000; + bomb.rulesRefs = "229, TM"; + bomb.techAdvancement.setTechBase(TechAdvancement.TECH_BASE_IS); + bomb.techAdvancement.setISAdvancement(3055, 3064, 3067); + bomb.techAdvancement.setTechRating(RATING_B); + bomb.techAdvancement.setAvailability(RATING_X, RATING_X, RATING_B, RATING_B); + + return bomb; + } + private static BombType createTAGBomb() { BombType bomb = new BombType(); diff --git a/megamek/src/megamek/common/WeaponType.java b/megamek/src/megamek/common/WeaponType.java index 3d4820e2712..a623eb9e3d7 100644 --- a/megamek/src/megamek/common/WeaponType.java +++ b/megamek/src/megamek/common/WeaponType.java @@ -2061,6 +2061,7 @@ public static void initializeTypes() { EquipmentType.addType(new ISBombTAG()); EquipmentType.addType(new CLBombTAG()); EquipmentType.addType(new BombISRL10()); + EquipmentType.addType(new BombISPRL10()); EquipmentType.addType(new AlamoMissileWeapon()); EquipmentType.addType(new SpaceBombAttack()); EquipmentType.addType(new DiveBombAttack()); diff --git a/megamek/src/megamek/common/weapons/bombs/BombISPRL10.java b/megamek/src/megamek/common/weapons/bombs/BombISPRL10.java new file mode 100644 index 00000000000..275c1de978f --- /dev/null +++ b/megamek/src/megamek/common/weapons/bombs/BombISPRL10.java @@ -0,0 +1,63 @@ +/* + * MegaMek - Copyright (C) 2005 Ben Mazur (bmazur@sev.org) + * + * This program 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 2 of the License, or (at your option) + * any later version. + * + * This program 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. + */ +package megamek.common.weapons.bombs; + +import megamek.common.AmmoType; +import megamek.common.BombType; +import megamek.common.SimpleTechLevel; +import megamek.common.TechAdvancement; +import megamek.common.weapons.missiles.MissileWeapon; + +/** + * @author Jay Lawson + */ +public class BombISPRL10 extends MissileWeapon { + private static final long serialVersionUID = 5763858241912399084L; + + public BombISPRL10() { + super(); + + this.name = "Prototype Rocket Launcher Pod"; + this.setInternalName(BombType.getBombWeaponName(BombType.B_PRL)); + addLookupName("PRL 10 (Bomb)"); + this.heat = 0; + this.rackSize = 10; + this.shortRange = 5; + this.mediumRange = 11; + this.longRange = 18; + this.extremeRange = 22; + this.tonnage = 1; + this.criticals = 0; + this.hittable = false; + this.bv = 0; + this.cost = 0; + this.flags = flags.or(F_MISSILE).or(F_BOMB_WEAPON).or(F_PROTOTYPE).andNot(F_MEK_WEAPON).andNot(F_TANK_WEAPON); + this.shortAV = 6; + this.medAV = 6; + this.maxRange = RANGE_MED; + this.toHitModifier = 1; + this.ammoType = AmmoType.T_RL_BOMB; + rulesRefs = "229, TM"; + techAdvancement.setTechBase(TECH_BASE_ALL) + .setIntroLevel(false) + .setUnofficial(false) + .setTechRating(RATING_B) + .setAvailability(RATING_D, RATING_F, RATING_X, RATING_X) + .setISAdvancement(DATE_ES, DATE_NONE, DATE_NONE, DATE_NONE, DATE_NONE) + .setISApproximate(true, false, false, false, false) + .setClanAdvancement(DATE_ES, DATE_NONE, DATE_NONE, 2823, DATE_NONE) + .setClanApproximate(true, false, false, true, false) + .setStaticTechLevel(SimpleTechLevel.EXPERIMENTAL); + } +} diff --git a/megamek/src/megamek/common/weapons/bombs/BombISRL10.java b/megamek/src/megamek/common/weapons/bombs/BombISRL10.java index 91835897d42..47f04627ba1 100644 --- a/megamek/src/megamek/common/weapons/bombs/BombISRL10.java +++ b/megamek/src/megamek/common/weapons/bombs/BombISRL10.java @@ -48,14 +48,14 @@ public BombISRL10() { this.toHitModifier = 1; this.ammoType = AmmoType.T_RL_BOMB; rulesRefs = "229, TM"; - new TechAdvancement(TECH_BASE_IS) - .setIntroLevel(false) - .setUnofficial(false) - .setTechRating(RATING_B ) - .setAvailability(RATING_X, RATING_X, RATING_B, RATING_B) - .setISAdvancement(3060, 3064, 3067, DATE_NONE, DATE_NONE) - .setISApproximate(true, false, false, false, false) - .setPrototypeFactions(F_MH) - .setProductionFactions(F_MH); + new TechAdvancement(TECH_BASE_IS) + .setIntroLevel(false) + .setUnofficial(false) + .setTechRating(RATING_B) + .setAvailability(RATING_X, RATING_X, RATING_B, RATING_B) + .setISAdvancement(3060, 3064, 3067, DATE_NONE, DATE_NONE) + .setISApproximate(true, false, false, false, false) + .setPrototypeFactions(F_MH) + .setProductionFactions(F_MH); } } From cebb547947c123780928cbb5837d9d84c20095e0 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 10 Dec 2024 13:54:03 -0800 Subject: [PATCH 5/9] Fix RL-P intro dates and bomb num; add more unittests --- .../generator/TeamLoadOutGenerator.java | 14 +++- megamek/src/megamek/common/BombType.java | 46 +++++++------ .../common/weapons/bombs/BombISPRL10.java | 9 ++- .../generator/TeamLoadOutGeneratorTest.java | 68 ++++++++++++++++++- 4 files changed, 110 insertions(+), 27 deletions(-) diff --git a/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java b/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java index f1ac7f31a9d..11ea9fc16d8 100644 --- a/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java +++ b/megamek/src/megamek/client/generator/TeamLoadOutGenerator.java @@ -1771,8 +1771,8 @@ public int[] generateExternalOrdnance( } // Oops, nothing left - rocket launchers are always popular - // XXX: Add prototype RLs as well. if (Arrays.stream(bombLoad).sum() == 0) { + // Rocket Launchers are a good option after CI era if ( checkLegality( BombType.createBombByType(BombType.B_RL), @@ -1784,6 +1784,18 @@ public int[] generateExternalOrdnance( bombLoad[BombType.B_RL] = bombUnits; return bombLoad; } + // Otherwise, Prototype Rocket Launchers are almost always in style. + if ( + checkLegality( + BombType.createBombByType(BombType.B_RLP), + faction, + techBase, + mixedTech + ) + ){ + bombLoad[BombType.B_RLP] = bombUnits; + return bombLoad; + } } // Randomly replace advanced ordnance with rockets or HE, depending on force diff --git a/megamek/src/megamek/common/BombType.java b/megamek/src/megamek/common/BombType.java index b860df51e86..76173c62f76 100644 --- a/megamek/src/megamek/common/BombType.java +++ b/megamek/src/megamek/common/BombType.java @@ -41,16 +41,17 @@ public class BombType extends AmmoType { public static final int B_ALAMO = 14; public static final int B_FAE_SMALL = 15; public static final int B_FAE_LARGE = 16; - public static final int B_NUM = 17; - // Prototype Rocket Launcher Pod - public static final int B_PRL = 18; + // Rocket Launcher (Prototype) Pod + public static final int B_RLP = 17; + public static final int B_NUM = 18; public static final String[] bombNames = { "HE Bomb","Cluster Bomb","Laser-guided Bomb", "Rocket", "TAG", "AAA Missile", "AS Missile", "ASEW Missile", "Arrow IV Missile", "Arrow IV Homing Missile", "Inferno Bomb", "LAA Missile", "Thunder Bomb", "Torpedo Bomb", - "Alamo Missile", "Fuel-Air Bomb (small)", "Fuel-Air Bomb (large)" }; + "Alamo Missile", "Fuel-Air Bomb (small)", "Fuel-Air Bomb (large)", + "Prototype Rocket"}; public static final String[] bombInternalNames = { "HEBomb","ClusterBomb","LGBomb", "RL 10 Ammo (Bomb)", "TAGBomb", "AAAMissile Ammo", @@ -58,13 +59,15 @@ public class BombType extends AmmoType { "ASEWMissile Ammo", "ArrowIVMissile Ammo", "ArrowIVHomingMissile Ammo", "InfernoBomb", "LAAMissile Ammo", "ThunderBomb", "TorpedoBomb", - "AlamoMissile Ammo", "FABombSmall Ammo", "FABombLarge Ammo" }; + "AlamoMissile Ammo", "FABombSmall Ammo", "FABombLarge Ammo", + "RL 10 Ammo (Bomb)" }; public static final String[] bombWeaponNames = { null, null, null, "BombRL", "BombTAG", "AAA Missile", "AS Missile", "ASEWMissile", "BombArrowIV", "BombArrowIV", - null, "LAAMissile", null, null, "AlamoMissile", null, null }; + null, "LAAMissile", null, null, "AlamoMissile", null, null, + "BombRLP"}; - public static final int[] bombCosts = { 1, 1, 1, 1, 1, 5, 6, 6, 5, 5, 1, 2, 1, 1, 10, 1, 2 }; + public static final int[] bombCosts = { 1, 1, 1, 1, 1, 5, 6, 6, 5, 5, 1, 2, 1, 1, 10, 1, 2, 1 }; public static final Map blastRadius; private int bombType; @@ -244,7 +247,7 @@ public static BombType createBombByType(int bType) { return createSmallFuelAirBomb(); case B_FAE_LARGE: return createLargeFuelAirBomb(); - case B_PRL: + case B_RLP: return createPrototypeRocketBomb(); default: return null; @@ -638,24 +641,29 @@ private static BombType createRocketBomb() { private static BombType createPrototypeRocketBomb() { BombType bomb = new BombType(); - bomb.name = "Prototype Rocket Launcher Pod"; - bomb.setInternalName("P" + BombType.getBombInternalName(BombType.B_RL)); - bomb.addLookupName("PRL 10 (Bomb)"); + bomb.name = "Rocket Launcher (Prototype) Pod"; + bomb.setInternalName(BombType.getBombInternalName(BombType.B_RLP)); + bomb.addLookupName("RL-P 10 (Bomb)"); bomb.damagePerShot = 1; // This works but is fragile bomb.flags = bomb.flags.or(AmmoType.F_OTHER_BOMB).or(WeaponType.F_PROTOTYPE); bomb.rackSize = 10; bomb.ammoType = AmmoType.T_RL_BOMB; - bomb.bombType = BombType.B_PRL; + bomb.bombType = BombType.B_RLP; bomb.shots = 1; - bomb.bv = 18; + bomb.bv = 15; bomb.cost = 15000; - bomb.rulesRefs = "229, TM"; - bomb.techAdvancement.setTechBase(TechAdvancement.TECH_BASE_IS); - bomb.techAdvancement.setISAdvancement(3055, 3064, 3067); - bomb.techAdvancement.setTechRating(RATING_B); - bomb.techAdvancement.setAvailability(RATING_X, RATING_X, RATING_B, RATING_B); - + bomb.rulesRefs = "73, 195, 217, IO"; + bomb.techAdvancement.setTechBase(TECH_BASE_ALL) + .setIntroLevel(false) + .setUnofficial(false) + .setTechRating(RATING_B) + .setAvailability(RATING_D, RATING_F, RATING_X, RATING_X) + .setISAdvancement(DATE_ES, DATE_NONE, DATE_NONE, DATE_NONE, DATE_NONE) + .setISApproximate(true, false, false, false, false) + .setClanAdvancement(DATE_ES, DATE_NONE, DATE_NONE, 2823, DATE_NONE) + .setClanApproximate(true, false, false, true, false) + .setStaticTechLevel(SimpleTechLevel.EXPERIMENTAL); return bomb; } diff --git a/megamek/src/megamek/common/weapons/bombs/BombISPRL10.java b/megamek/src/megamek/common/weapons/bombs/BombISPRL10.java index 275c1de978f..9588fe89271 100644 --- a/megamek/src/megamek/common/weapons/bombs/BombISPRL10.java +++ b/megamek/src/megamek/common/weapons/bombs/BombISPRL10.java @@ -16,7 +16,6 @@ import megamek.common.AmmoType; import megamek.common.BombType; import megamek.common.SimpleTechLevel; -import megamek.common.TechAdvancement; import megamek.common.weapons.missiles.MissileWeapon; /** @@ -28,9 +27,9 @@ public class BombISPRL10 extends MissileWeapon { public BombISPRL10() { super(); - this.name = "Prototype Rocket Launcher Pod"; - this.setInternalName(BombType.getBombWeaponName(BombType.B_PRL)); - addLookupName("PRL 10 (Bomb)"); + this.name = "Rocket Launcher (Prototype) Pod"; + this.setInternalName(BombType.getBombWeaponName(BombType.B_RLP)); + addLookupName("RL-P 10 (Bomb)"); this.heat = 0; this.rackSize = 10; this.shortRange = 5; @@ -48,7 +47,7 @@ public BombISPRL10() { this.maxRange = RANGE_MED; this.toHitModifier = 1; this.ammoType = AmmoType.T_RL_BOMB; - rulesRefs = "229, TM"; + rulesRefs = "73, 195, 217, IO"; techAdvancement.setTechBase(TECH_BASE_ALL) .setIntroLevel(false) .setUnofficial(false) diff --git a/megamek/unittests/megamek/client/generator/TeamLoadOutGeneratorTest.java b/megamek/unittests/megamek/client/generator/TeamLoadOutGeneratorTest.java index 377f0fa2593..7748d247431 100644 --- a/megamek/unittests/megamek/client/generator/TeamLoadOutGeneratorTest.java +++ b/megamek/unittests/megamek/client/generator/TeamLoadOutGeneratorTest.java @@ -598,7 +598,7 @@ void testClampAmmoShotsCannotExceedFull() throws LocationFullException { } /** - * We expect CAP Pirate flights in the 3SW era to mount no ordnance. + * We expect CAP Pirate flights in the 3SW era to mount ordnance only RL-P pods. */ @Test void testGenerateExternalOrdnanceCAP3SWEraPirates() { @@ -624,8 +624,72 @@ void testGenerateExternalOrdnanceCAP3SWEraPirates() { techBase, mixedTech ); - int[] expected = new int[17]; + int[] expected = new int[BombType.B_NUM]; + expected[BombType.B_RLP] = bombUnits; assertArrayEquals(expected, generatedBombs); + } + /** + * We expect CAP Pirate flights in the 3SW era to mount ordnance only RL-P pods. + */ + @Test + void testGenerateExternalOrdnanceCAPPostCIEraPirates() { + // Game setup + int year = 3075; + when(mockGameOptions.intOption(OptionsConstants.ALLOWED_YEAR)).thenReturn(year); + TeamLoadOutGenerator tlg = new TeamLoadOutGenerator(game); + // Bomber info + int bombUnits = 20; + boolean airOnly = true; + boolean isPirate = true; + int quality = ForceDescriptor.RATING_5; + String faction = "PIR"; + String techBase = "IS"; + boolean mixedTech = false; + int[] generatedBombs = tlg.generateExternalOrdnance( + bombUnits, + airOnly, + isPirate, + quality, + year, + faction, + techBase, + mixedTech + ); + // Should always get some regular rocket launchers + assertTrue(generatedBombs[BombType.B_RL] > 0); + // Should not use RL-Ps when RLs are available + assertEquals(0, generatedBombs[BombType.B_RLP]); + } + + /** + * We expect CAP Pirate flights in the 3SW era to mount ordnance only RL-P pods. + */ + @Test + void testGenerateExternalOrdnanceCAP2800Clan() { + // Game setup + int year = 2800; + when(mockGameOptions.intOption(OptionsConstants.ALLOWED_YEAR)).thenReturn(year); + TeamLoadOutGenerator tlg = new TeamLoadOutGenerator(game); + // Bomber info + int bombUnits = 20; + boolean airOnly = true; + boolean isPirate = false; + int quality = ForceDescriptor.RATING_1; + String faction = "CSJ"; + String techBase = "CL"; + boolean mixedTech = false; + int[] generatedBombs = tlg.generateExternalOrdnance( + bombUnits, + airOnly, + isPirate, + quality, + year, + faction, + techBase, + mixedTech + ); + // Pre-2823, Clan units can take RL-P bombs + assertTrue(generatedBombs[BombType.B_RLP] > 0); } } From 57d5fea43ce4c431966c56389d85de0027643644 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 10 Dec 2024 22:06:15 -0800 Subject: [PATCH 6/9] Clean up formatting and class name --- megamek/src/megamek/common/WeaponType.java | 2 +- .../src/megamek/common/weapons/RLHandler.java | 30 ++++++++----------- .../{BombISPRL10.java => BombISRLP10.java} | 4 +-- 3 files changed, 15 insertions(+), 21 deletions(-) rename megamek/src/megamek/common/weapons/bombs/{BombISPRL10.java => BombISRLP10.java} (96%) diff --git a/megamek/src/megamek/common/WeaponType.java b/megamek/src/megamek/common/WeaponType.java index a623eb9e3d7..ae5584a8059 100644 --- a/megamek/src/megamek/common/WeaponType.java +++ b/megamek/src/megamek/common/WeaponType.java @@ -2061,7 +2061,7 @@ public static void initializeTypes() { EquipmentType.addType(new ISBombTAG()); EquipmentType.addType(new CLBombTAG()); EquipmentType.addType(new BombISRL10()); - EquipmentType.addType(new BombISPRL10()); + EquipmentType.addType(new BombISRLP10()); EquipmentType.addType(new AlamoMissileWeapon()); EquipmentType.addType(new SpaceBombAttack()); EquipmentType.addType(new DiveBombAttack()); diff --git a/megamek/src/megamek/common/weapons/RLHandler.java b/megamek/src/megamek/common/weapons/RLHandler.java index 0a1baa2ed24..bd05e29a057 100644 --- a/megamek/src/megamek/common/weapons/RLHandler.java +++ b/megamek/src/megamek/common/weapons/RLHandler.java @@ -1,14 +1,14 @@ /** * MegaMek - Copyright (C) 2007 Ben Mazur (bmazur@sev.org) - * - * This program 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 2 of the License, or (at your option) + * + * This program 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 2 of the License, or (at your option) * any later version. - * - * This program 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 + * + * This program 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. */ package megamek.common.weapons; @@ -17,20 +17,14 @@ import java.util.Enumeration; import java.util.Vector; -import megamek.common.Coords; -import megamek.common.Entity; -import megamek.common.Game; -import megamek.common.Minefield; -import megamek.common.Report; -import megamek.common.Targetable; -import megamek.common.ToHitData; +import megamek.common.*; import megamek.common.actions.WeaponAttackAction; import megamek.server.totalwarfare.TWGameManager; public class RLHandler extends MissileWeaponHandler { /** - * + * */ private static final long serialVersionUID = -3848472655779311898L; @@ -46,7 +40,7 @@ public RLHandler(ToHitData t, WeaponAttackAction w, Game g, TWGameManager m) { /* * (non-Javadoc) - * + * * @see * megamek.common.weapons.WeaponHandler#specialResolution(java.util.Vector, * megamek.common.Entity, boolean) @@ -79,4 +73,4 @@ protected boolean specialResolution(Vector vPhaseReport, } return false; } -} \ No newline at end of file +} diff --git a/megamek/src/megamek/common/weapons/bombs/BombISPRL10.java b/megamek/src/megamek/common/weapons/bombs/BombISRLP10.java similarity index 96% rename from megamek/src/megamek/common/weapons/bombs/BombISPRL10.java rename to megamek/src/megamek/common/weapons/bombs/BombISRLP10.java index 9588fe89271..a942f72e41e 100644 --- a/megamek/src/megamek/common/weapons/bombs/BombISPRL10.java +++ b/megamek/src/megamek/common/weapons/bombs/BombISRLP10.java @@ -21,10 +21,10 @@ /** * @author Jay Lawson */ -public class BombISPRL10 extends MissileWeapon { +public class BombISRLP10 extends MissileWeapon { private static final long serialVersionUID = 5763858241912399084L; - public BombISPRL10() { + public BombISRLP10() { super(); this.name = "Rocket Launcher (Prototype) Pod"; From 7e6bf8afd7baa8546f38e1651cb152e03f8c99d9 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Tue, 10 Dec 2024 23:41:43 -0800 Subject: [PATCH 7/9] Explicitly set bomb RL handlers for correct behaviour --- .../megamek/common/weapons/bombs/BombISRL10.java | 15 ++++++++++++--- .../megamek/common/weapons/bombs/BombISRLP10.java | 14 +++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/megamek/src/megamek/common/weapons/bombs/BombISRL10.java b/megamek/src/megamek/common/weapons/bombs/BombISRL10.java index 47f04627ba1..6464169fe83 100644 --- a/megamek/src/megamek/common/weapons/bombs/BombISRL10.java +++ b/megamek/src/megamek/common/weapons/bombs/BombISRL10.java @@ -13,10 +13,13 @@ */ package megamek.common.weapons.bombs; -import megamek.common.AmmoType; -import megamek.common.BombType; -import megamek.common.TechAdvancement; +import megamek.common.*; +import megamek.common.actions.WeaponAttackAction; +import megamek.common.weapons.AttackHandler; +import megamek.common.weapons.PrototypeRLHandler; +import megamek.common.weapons.RLHandler; import megamek.common.weapons.missiles.MissileWeapon; +import megamek.server.totalwarfare.TWGameManager; /** * @author Jay Lawson @@ -58,4 +61,10 @@ public BombISRL10() { .setPrototypeFactions(F_MH) .setProductionFactions(F_MH); } + + @Override + protected AttackHandler getCorrectHandler(ToHitData toHit, + WeaponAttackAction waa, Game game, TWGameManager manager) { + return new RLHandler(toHit, waa, game, manager); + } } diff --git a/megamek/src/megamek/common/weapons/bombs/BombISRLP10.java b/megamek/src/megamek/common/weapons/bombs/BombISRLP10.java index a942f72e41e..b21f3e89d30 100644 --- a/megamek/src/megamek/common/weapons/bombs/BombISRLP10.java +++ b/megamek/src/megamek/common/weapons/bombs/BombISRLP10.java @@ -13,10 +13,12 @@ */ package megamek.common.weapons.bombs; -import megamek.common.AmmoType; -import megamek.common.BombType; -import megamek.common.SimpleTechLevel; +import megamek.common.*; +import megamek.common.actions.WeaponAttackAction; +import megamek.common.weapons.AttackHandler; +import megamek.common.weapons.PrototypeRLHandler; import megamek.common.weapons.missiles.MissileWeapon; +import megamek.server.totalwarfare.TWGameManager; /** * @author Jay Lawson @@ -59,4 +61,10 @@ public BombISRLP10() { .setClanApproximate(true, false, false, true, false) .setStaticTechLevel(SimpleTechLevel.EXPERIMENTAL); } + + @Override + protected AttackHandler getCorrectHandler(ToHitData toHit, + WeaponAttackAction waa, Game game, TWGameManager manager) { + return new PrototypeRLHandler(toHit, waa, game, manager); + } } From 10ec96d73d1ad3b0c276a97ef88ffc6d6c2b0a5b Mon Sep 17 00:00:00 2001 From: sleet01 Date: Wed, 11 Dec 2024 01:05:47 -0800 Subject: [PATCH 8/9] Add missing entry for equipment population --- megamek/src/megamek/common/BombType.java | 1 + 1 file changed, 1 insertion(+) diff --git a/megamek/src/megamek/common/BombType.java b/megamek/src/megamek/common/BombType.java index 76173c62f76..e55ef1d76d5 100644 --- a/megamek/src/megamek/common/BombType.java +++ b/megamek/src/megamek/common/BombType.java @@ -178,6 +178,7 @@ public static void initializeTypes() { EquipmentType.addType(BombType.createLaserGuidedBomb()); // EquipmentType.addType(BombType.createCLLaserGuidedBomb()); EquipmentType.addType(BombType.createRocketBomb()); + EquipmentType.addType(BombType.createPrototypeRocketBomb()); EquipmentType.addType(BombType.createTAGBomb()); // EquipmentType.addType(BombType.createCLTAGBomb()); EquipmentType.addType(BombType.createAAAMissileBomb()); From 5ad56d76f1301969e0b846801801924fe4e28951 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Wed, 11 Dec 2024 01:28:04 -0800 Subject: [PATCH 9/9] Update RL-P internal name --- megamek/src/megamek/common/BombType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/megamek/src/megamek/common/BombType.java b/megamek/src/megamek/common/BombType.java index e55ef1d76d5..7672bfecf33 100644 --- a/megamek/src/megamek/common/BombType.java +++ b/megamek/src/megamek/common/BombType.java @@ -60,7 +60,7 @@ public class BombType extends AmmoType { "ArrowIVHomingMissile Ammo", "InfernoBomb", "LAAMissile Ammo", "ThunderBomb", "TorpedoBomb", "AlamoMissile Ammo", "FABombSmall Ammo", "FABombLarge Ammo", - "RL 10 Ammo (Bomb)" }; + "RL-P 10 Ammo (Bomb)" }; public static final String[] bombWeaponNames = { null, null, null, "BombRL", "BombTAG", "AAA Missile", "AS Missile", "ASEWMissile", "BombArrowIV", "BombArrowIV",