From 25f7c50148eda5edc38c8a4a17c677ffda50b717 Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Fri, 26 Feb 2021 11:26:18 -0700 Subject: [PATCH 01/27] 2438: Adding more detail to fire damage logging --- .../megamek/common/report-messages.properties | 16 +++---- megamek/src/megamek/server/FireProcessor.java | 44 +++++++++---------- megamek/src/megamek/server/Server.java | 21 +++++---- 3 files changed, 39 insertions(+), 42 deletions(-) diff --git a/megamek/i18n/megamek/common/report-messages.properties b/megamek/i18n/megamek/common/report-messages.properties index 07c34f93999..a27e76ac982 100755 --- a/megamek/i18n/megamek/common/report-messages.properties +++ b/megamek/i18n/megamek/common/report-messages.properties @@ -623,7 +623,7 @@ 5087=fails and takes a critical hit. 5090= () is in extreme temperatures and dies. 5095= () is on fire, but is protected by its gear. -5100= () is on fire. Needs an 8+ to avoid damage, rolls : +5100= () is on fire in hex . Needs an 8+ to avoid damage, rolls : 5101=avoids successfully! 5102=fails to avoid damage. 5105=Luckily, there is no ammo to explode. @@ -635,13 +635,13 @@ 5130=Inferno fire at is burning brightly. 5135=Fire at was started this round. 5136=Fire at was started due to an engine explosion! -5140=Heavy woods at burns down to Light Woods! -5141=Ultra Heavy woods at burns down to Heavy Woods! -5142=Heavy jungle at burns down to Light jungle! -5143=Ultra heavy jungle at burns down to Heavy jungle! -5145=Light woods at burns down to Rough and goes out!! -5146=Light jungle at burns down to Rough and goes out!! -5150=Fire spreads to ! +5140=Heavy Woods at burns down to Light Woods! +5141=Ultra Heavy Woods at burns down to Heavy Woods! +5142=Heavy Jungle at burns down to Light Jungle! +5143=Ultra Heavy Jungle at burns down to Heavy Jungle! +5145=Light Woods at burns down to Rough and goes out!! +5146=Light Jungle at burns down to Rough and goes out!! +5150=Fire spreads to from ! 5155=Gains 30 heat and is now at heat. 5160=Luckily, there is no inferno ammo to explode. 5165= () is freed from its swarm attack. diff --git a/megamek/src/megamek/server/FireProcessor.java b/megamek/src/megamek/server/FireProcessor.java index b4755d1f41b..fa589888746 100644 --- a/megamek/src/megamek/server/FireProcessor.java +++ b/megamek/src/megamek/server/FireProcessor.java @@ -121,11 +121,8 @@ else if (!server.checkForCollapse(bldg, positionMap, coords, false, vPhaseReport bldg.setPhaseCF(cf, coords); } } - } - } - - debugTime("resolve fire 1", true); + } // Cycle through all hexes, checking for fire and the spread of fire for (int currentXCoord = 0; currentXCoord < width; currentXCoord++) { @@ -133,7 +130,7 @@ else if (!server.checkForCollapse(bldg, positionMap, coords, false, vPhaseReport Coords currentCoords = new Coords(currentXCoord, currentYCoord); IHex currentHex = board.getHex(currentXCoord, currentYCoord); - if(currentHex.containsTerrain(Terrains.FIRE)) { + if (currentHex.containsTerrain(Terrains.FIRE)) { //If the woods has been cleared, or the building // has collapsed put non-inferno fires out. if ((currentHex.terrainLevel(Terrains.FIRE) @@ -144,7 +141,7 @@ else if (!server.checkForCollapse(bldg, positionMap, coords, false, vPhaseReport } //only check spread for fires that didn't start this turn - if(currentHex.getFireTurn() > 0) { + if (currentHex.getFireTurn() > 0) { //optional rule, woods burn down Vector burnReports = null; if ((currentHex.containsTerrain(Terrains.WOODS) || currentHex @@ -168,8 +165,7 @@ else if (!server.checkForCollapse(bldg, positionMap, coords, false, vPhaseReport if (burnReports != null) { vPhaseReport.addAll(burnReports); } - spreadFire(currentXCoord, currentYCoord, windDirection, - windStrength); + spreadFire(currentXCoord, currentYCoord, windDirection, windStrength); } } } @@ -261,21 +257,20 @@ public void spreadFire(int x, int y, int windDir, int windStr) { //This means that a fire cannot spread from a level 6 building at base level 0 to //a level 1 building at base level 0, for example. - int curHeight = game.getBoard().getHex(src).ceiling(); + final int curHeight = game.getBoard().getHex(src).ceiling(); TargetRoll directroll = new TargetRoll(9, "spread downwind"); TargetRoll obliqueroll = new TargetRoll(11, "spread 60 degrees to downwind"); - if((windStr > PlanetaryConditions.WI_NONE) && (windStr < PlanetaryConditions.WI_STRONG_GALE)) { + if ((windStr > PlanetaryConditions.WI_NONE) && (windStr < PlanetaryConditions.WI_STRONG_GALE)) { directroll.addModifier(-2, "light/moderate gale"); obliqueroll.addModifier(-1, "light/moderate gale"); - } - else if(windStr > PlanetaryConditions.WI_MOD_GALE) { + } else if (windStr > PlanetaryConditions.WI_MOD_GALE) { directroll.addModifier(-3, "strong gale+"); directroll.addModifier(-2, "strong gale+"); } - spreadFire(nextCoords, directroll, curHeight); + spreadFire(src, nextCoords, directroll, curHeight); // Spread to the next hex downwind on a 12 if the first hex wasn't // burning... @@ -286,34 +281,37 @@ else if(windStr > PlanetaryConditions.WI_MOD_GALE) { && ((curHeight >= nextHex.ceiling()) || (jumpHex.ceiling() >= nextHex.ceiling()))) { // we've already gone one step in the wind direction, now go another directroll.addModifier(3, "crossing non-burning hex"); - spreadFire(nextCoords.translated(windDir), directroll, curHeight); + spreadFire(src, nextCoords.translated(windDir), directroll, curHeight); } // spread fire 60 degrees clockwise.... - spreadFire(src.translated((windDir + 1) % 6), obliqueroll, curHeight); + spreadFire(src, src.translated((windDir + 1) % 6), obliqueroll, curHeight); // spread fire 60 degrees counterclockwise - spreadFire(src.translated((windDir + 5) % 6), obliqueroll, curHeight); + spreadFire(src, src.translated((windDir + 5) % 6), obliqueroll, curHeight); } /** * Spreads the fire, and reports the spread, to the specified hex, if * possible, if the hex isn't already on fire, and the fire roll is made. + * + * @param origin the origin coordinates + * @param coords the coordinates to check to see if the fire spreads to them + * @param roll the target number for roll for fire to spread + * @param height the height of the origin hex */ - public void spreadFire(Coords coords, TargetRoll roll, int height) { + public void spreadFire(final Coords origin, final Coords coords, final TargetRoll roll, + final int height) { IHex hex = game.getBoard().getHex(coords); - if (hex == null) { - // Don't attempt to spread fire off the board. - return; - } - - if(Math.abs(hex.ceiling() - height) > 4) { + if ((hex == null) || (Math.abs(hex.ceiling() - height) > 4)) { + // Don't attempt to spread fire off the board or for large differences in height return; } if (!(hex.containsTerrain(Terrains.FIRE)) && server.checkIgnition(coords, roll)) { Report r = new Report(5150, Report.PUBLIC); r.add(coords.getBoardNum()); + r.add(origin.getBoardNum()); vPhaseReport.addElement(r); } } diff --git a/megamek/src/megamek/server/Server.java b/megamek/src/megamek/server/Server.java index df134d86a84..1fe251a0b31 100644 --- a/megamek/src/megamek/server/Server.java +++ b/megamek/src/megamek/server/Server.java @@ -98,6 +98,7 @@ import megamek.common.actions.UnjamTurretAction; import megamek.common.actions.UnloadStrandedAction; import megamek.common.actions.WeaponAttackAction; +import megamek.common.annotations.Nullable; import megamek.common.containers.PlayerIDandList; import megamek.common.event.GameListener; import megamek.common.event.GameVictoryEvent; @@ -8119,7 +8120,7 @@ private void processMovement(Entity entity, MovePath md, MapEntity that may experience flaming damage. + * @param coordinates the coordinate location of the fire */ - private void doFlamingDamage(Entity entity) { + private void doFlamingDamage(final Entity entity, final Coords coordinates) { Report r; int boomRoll = Compute.d6(2); - if ((entity.getMovementMode() == EntityMovementMode.VTOL) - && !entity.infernos.isStillBurning()) { + if ((entity.getMovementMode() == EntityMovementMode.VTOL) && !entity.infernos.isStillBurning()) { // VTOLs don't check as long as they are flying higher than // the burning terrain. TODO : Check for rules conformity (ATPM?) // according to maxtech, elevation 0 or 1 should be affected, // this makes sense for level 2 as well - if (entity.getElevation() > 1) { return; } @@ -20142,8 +20142,7 @@ private void doFlamingDamage(Entity entity) { // Battle Armor squads equipped with fire protection // gear automatically avoid flaming damage // TODO : can conventional infantry mount fire-resistant armor? - if ((entity instanceof BattleArmor) - && ((BattleArmor) entity).isFireResistant()) { + if ((entity instanceof BattleArmor) && ((BattleArmor) entity).isFireResistant()) { r = new Report(5095); r.subject = entity.getId(); r.indent(1); @@ -20167,6 +20166,7 @@ private void doFlamingDamage(Entity entity) { r.subject = entity.getId(); r.newlines = 0; r.addDesc(entity); + r.add(coordinates.getBoardNum()); r.add(boomRoll); if (boomRoll >= 8) { // phew! @@ -20191,8 +20191,7 @@ private void doFlamingDamage(Entity entity) { // (hurray!) } else if (entity instanceof Tank) { int bonus = -2; - if ((entity instanceof SupportTank) - || (entity instanceof SupportVTOL)) { + if ((entity instanceof SupportTank) || (entity instanceof SupportVTOL)) { bonus = 0; } // roll a critical hit @@ -20500,7 +20499,7 @@ public void checkForFlamingDamage() { if (curHex.containsTerrain(Terrains.FIRE) && !underwater && ((entity.getElevation() <= 1) || (entity.getElevation() <= numFloors))) { - doFlamingDamage(entity); + doFlamingDamage(entity, entity.getPosition()); } } } From 91598880db10028564a45070847279083343d91a Mon Sep 17 00:00:00 2001 From: NickAragua Date: Sat, 3 Apr 2021 22:48:44 -0400 Subject: [PATCH 02/27] enable air-launched Arrow IV --- megamek/src/megamek/common/IBomber.java | 4 +--- megamek/src/megamek/common/actions/WeaponAttackAction.java | 3 ++- megamek/src/megamek/common/weapons/bombs/BombArrowIV.java | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/megamek/src/megamek/common/IBomber.java b/megamek/src/megamek/common/IBomber.java index 268bed1e811..9ec7f8e197b 100644 --- a/megamek/src/megamek/common/IBomber.java +++ b/megamek/src/megamek/common/IBomber.java @@ -117,9 +117,7 @@ default void applyBombs() { // some bombs need an associated weapon and if so // they need a weapon for each bomb - if ((null != BombType.getBombWeaponName(type)) - && (type != BombType.B_ARROW) - && (type != BombType.B_HOMING)) { + if ((null != BombType.getBombWeaponName(type))) { Mounted m = null; try { m = ((Entity)this).addBomb(EquipmentType.get(BombType diff --git a/megamek/src/megamek/common/actions/WeaponAttackAction.java b/megamek/src/megamek/common/actions/WeaponAttackAction.java index c03bfccf544..b57e64f0e6c 100644 --- a/megamek/src/megamek/common/actions/WeaponAttackAction.java +++ b/megamek/src/megamek/common/actions/WeaponAttackAction.java @@ -1753,7 +1753,8 @@ private static String toHitIsImpossible(IGame game, Entity ae, int attackerId, T return Messages.getString("WeaponAttackAction.OnlyArrowArty"); } } - } else if (wtype.getAmmoType() != AmmoType.T_ARROW_IV) { + } else if (wtype.getAmmoType() != AmmoType.T_ARROW_IV && + wtype.getAmmoType() != AmmoType.T_ARROW_IV_BOMB) { //For Fighters, LAMs, Small Craft and VTOLs return Messages.getString("WeaponAttackAction.OnlyArrowArty"); } diff --git a/megamek/src/megamek/common/weapons/bombs/BombArrowIV.java b/megamek/src/megamek/common/weapons/bombs/BombArrowIV.java index 0326fc63180..abc534be9d3 100644 --- a/megamek/src/megamek/common/weapons/bombs/BombArrowIV.java +++ b/megamek/src/megamek/common/weapons/bombs/BombArrowIV.java @@ -20,11 +20,12 @@ import megamek.common.AmmoType; import megamek.common.BombType; import megamek.common.weapons.AmmoWeapon; +import megamek.common.weapons.artillery.ArtilleryWeapon; /** * @author Jay Lawson */ -public class BombArrowIV extends AmmoWeapon { +public class BombArrowIV extends ArtilleryWeapon { /** * From 51968e7853ceb8c21ff454837c29920810e7180a Mon Sep 17 00:00:00 2001 From: NickAragua Date: Sun, 4 Apr 2021 21:06:00 -0400 Subject: [PATCH 03/27] parens --- megamek/src/megamek/common/IBomber.java | 2 +- megamek/src/megamek/common/actions/WeaponAttackAction.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/megamek/src/megamek/common/IBomber.java b/megamek/src/megamek/common/IBomber.java index 9ec7f8e197b..5394c3caa46 100644 --- a/megamek/src/megamek/common/IBomber.java +++ b/megamek/src/megamek/common/IBomber.java @@ -117,7 +117,7 @@ default void applyBombs() { // some bombs need an associated weapon and if so // they need a weapon for each bomb - if ((null != BombType.getBombWeaponName(type))) { + if (null != BombType.getBombWeaponName(type)) { Mounted m = null; try { m = ((Entity)this).addBomb(EquipmentType.get(BombType diff --git a/megamek/src/megamek/common/actions/WeaponAttackAction.java b/megamek/src/megamek/common/actions/WeaponAttackAction.java index b57e64f0e6c..dbbbba388d8 100644 --- a/megamek/src/megamek/common/actions/WeaponAttackAction.java +++ b/megamek/src/megamek/common/actions/WeaponAttackAction.java @@ -54,7 +54,6 @@ import megamek.common.LosEffects; import megamek.common.Mech; import megamek.common.MechWarrior; -import megamek.common.MinefieldTarget; import megamek.common.MiscType; import megamek.common.Mounted; import megamek.common.PlanetaryConditions; @@ -1753,8 +1752,8 @@ private static String toHitIsImpossible(IGame game, Entity ae, int attackerId, T return Messages.getString("WeaponAttackAction.OnlyArrowArty"); } } - } else if (wtype.getAmmoType() != AmmoType.T_ARROW_IV && - wtype.getAmmoType() != AmmoType.T_ARROW_IV_BOMB) { + } else if ((wtype.getAmmoType() != AmmoType.T_ARROW_IV) && + (wtype.getAmmoType() != AmmoType.T_ARROW_IV_BOMB)) { //For Fighters, LAMs, Small Craft and VTOLs return Messages.getString("WeaponAttackAction.OnlyArrowArty"); } From 525c8c246fcc54818e3529c2cc86da57dcfbe8d5 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Tue, 6 Apr 2021 10:53:34 -0400 Subject: [PATCH 04/27] Update history.txt --- megamek/docs/history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/megamek/docs/history.txt b/megamek/docs/history.txt index 53abf8f3602..15e458adbd8 100644 --- a/megamek/docs/history.txt +++ b/megamek/docs/history.txt @@ -30,6 +30,7 @@ VERSION HISTORY: + Issue #2758, 2759: Infantry no longer able to leap up to flying VTOLs for swarm attacks; target movement modifier now applies to legal leg/swarm attacks. + Issue #2767: Incorrect Citation for "(Unofficial) Specify the number of pre-designated artillery autohit hexes per map area ++ Issue #547: Implement air-launched Arrow IV (homing and non-homing) 0.48.0 (Stable) - (2021-03-05 1530 UTC) + PR #2638, #2640: Turret data fixes From 3a27e4e092ae860470b01ab2c75112beae2ef905 Mon Sep 17 00:00:00 2001 From: Justin Bowen <39067288+Windchild292@users.noreply.github.com> Date: Wed, 7 Apr 2021 21:46:47 -0400 Subject: [PATCH 05/27] Update history.txt --- megamek/docs/history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/megamek/docs/history.txt b/megamek/docs/history.txt index 15e458adbd8..213ee45c371 100644 --- a/megamek/docs/history.txt +++ b/megamek/docs/history.txt @@ -11,6 +11,7 @@ VERSION HISTORY: + Data: New maps, unit fixes including fix for #2707 + Issue #2597: Add thread safety to duplicate name hash + Issue #2722: Do not send IP addresses in chat by default ++ PR #2691: Base Components: Creating Base Components based on the previous MekHQ Preference setup + PR #2724: "File>Unit List>Refresh Unit Cache" loads any changes in the unit files without having to restart + PR #2730: Tooltips for target number calculations on the round report + Issue #146: Disengage/Remove Unit Button for Offboard Artillery From 33a8cf08d4246fdfd474b895a7795b999eaf814b Mon Sep 17 00:00:00 2001 From: NickAragua Date: Wed, 7 Apr 2021 22:41:38 -0400 Subject: [PATCH 06/27] launch fighters --- .../megamek/client/bot/princess/Princess.java | 62 ++++++++++++++++++- .../client/ui/swing/MovementDisplay.java | 2 +- megamek/src/megamek/common/Bay.java | 7 +++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/megamek/src/megamek/client/bot/princess/Princess.java b/megamek/src/megamek/client/bot/princess/Princess.java index 739a188f294..c0047528812 100644 --- a/megamek/src/megamek/client/bot/princess/Princess.java +++ b/megamek/src/megamek/client/bot/princess/Princess.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; @@ -38,6 +39,7 @@ import megamek.client.ui.SharedUtility; import megamek.common.AmmoType; import megamek.common.BattleArmor; +import megamek.common.Bay; import megamek.common.Building; import megamek.common.BuildingTarget; import megamek.common.BulldozerMovePath; @@ -1027,8 +1029,7 @@ Entity getEntityToMove() { if ((entity.isOffBoard() || (null == entity.getPosition()) || entity.isUnloadedThisTurn() - || !getGame().getTurn().isValidEntity(entity, getGame())) - && !getGame().isPhaseSimultaneous()){ + || !getGame().getTurn().isValidEntity(entity, getGame(), true))) { msg.append("cannot be moved."); continue; } @@ -1976,6 +1977,7 @@ private MovePath performPathPostProcessing(MovePath path, double expectedDamage) evadeIfNotFiring(retval, expectedDamage >= 0); turnOnSearchLight(retval, expectedDamage >= 0); unloadTransportedInfantry(retval); + launchFighters(retval); unjamRAC(retval); // if we are using vector movement, there's a whole bunch of post-processing that happens to @@ -2057,7 +2059,7 @@ private void unloadTransportedInfantry(MovePath path) { Targetable closestEnemy = getPathRanker(movingEntity).findClosestEnemy(movingEntity, pathEndpoint, getGame(), false); // if there are no enemies on the board, then we're not unloading anything. - // infantry can't clear hexes, so let's not use th + // infantry can't clear hexes, so let's not unload them for that purpose if((null == closestEnemy) || (closestEnemy.getTargetType() == Targetable.TYPE_HEX_CLEAR)) { return; } @@ -2066,6 +2068,11 @@ private void unloadTransportedInfantry(MovePath path) { // loop through all entities carried by the current entity for(Transporter transport : movingEntity.getTransports()) { + // this operation is intended for entities on the ground + if (transport instanceof Bay) { + continue; + } + for(Entity loadedEntity : transport.getLoadedUnits()) { // there's really no good reason for Princess to disconnect trailers. // Let's skip those for now. We don't want to create a bogus 'unload' step for them anyhow. @@ -2098,6 +2105,55 @@ private void unloadTransportedInfantry(MovePath path) { } } + /** + * Helper function that adds an "launch" step for units that are transporting + * launchable units in some kind of bay. + */ + private void launchFighters(MovePath path) { + // if my objective is to cross the board, even though it's tempting, I won't be leaving the infantry + // behind. They're not that good at screening against high speed pursuit anyway. + if(getBehaviorSettings().shouldGoHome()) { + return; + } + + Entity movingEntity = path.getEntity(); + Coords pathEndpoint = path.getFinalCoords(); + Targetable closestEnemy = getPathRanker(movingEntity).findClosestEnemy(movingEntity, pathEndpoint, getGame(), false); + + // if there are no enemies on the board, then we're not launching anything. + if((null == closestEnemy) || (closestEnemy.getTargetType() != Targetable.TYPE_ENTITY)) { + return; + } + + TreeMap> unitsToLaunch = new TreeMap<>(); + boolean executeLaunch = false; + + // loop through all fighter (or smallcraft) bays in the current entity + // grouping launched craft by bay to limit launches to 'safe' rate. + Vector fighterBays = movingEntity.getFighterBays(); + + for(int bayIndex = 0; bayIndex < fighterBays.size(); bayIndex++) { + Bay bay = fighterBays.get(bayIndex); + + for(Entity loadedEntity : bay.getLaunchableUnits()) { + unitsToLaunch.putIfAbsent(bayIndex, new Vector()); + + // for now, just launch fighters at the 'safe' rate + if (unitsToLaunch.get(bayIndex).size() < bay.getSafeLaunchRate()) { + unitsToLaunch.get(bayIndex).add(loadedEntity.getId()); + executeLaunch = true; + } else { + break; + } + } + } + + // only add the step if we're actually launching something + if (executeLaunch) { + path.addStep(MoveStepType.LAUNCH, unitsToLaunch); + } + } + public void sendChat(final String message, final LogLevel logLevel) { if (getVerbosity().willLog(logLevel)) { diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index b28a073bfe7..afb7a1bb07f 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -3625,7 +3625,7 @@ private TreeMap> getLaunchedUnits() { continue; } int numChoices = choiceDialog.getChoices().length; - if ((numChoices > (doors * 2)) + if ((numChoices > currentBay.getSafeLaunchRate()) && GUIPreferences.getInstance().getNagForLaunchDoors()) { int aerosPerDoor = numChoices / doors; int remainder = numChoices % doors; diff --git a/megamek/src/megamek/common/Bay.java b/megamek/src/megamek/common/Bay.java index be423f84949..5887c6d337c 100644 --- a/megamek/src/megamek/common/Bay.java +++ b/megamek/src/megamek/common/Bay.java @@ -673,5 +673,12 @@ public boolean isCargo() { public long getCost() { return 0; } + + /** + * @return Safe launch rate for this particular bay: # of intact doors x 2 + */ + public int getSafeLaunchRate() { + return getCurrentDoors() * 2; + } } // End package class TroopSpace implements Transporter From 4b013c2d13be40afe20a768dfe3fa24c21ca3365 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Wed, 7 Apr 2021 23:01:19 -0400 Subject: [PATCH 07/27] undo showstopper --- megamek/src/megamek/client/bot/princess/Princess.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/megamek/src/megamek/client/bot/princess/Princess.java b/megamek/src/megamek/client/bot/princess/Princess.java index c0047528812..54219c32175 100644 --- a/megamek/src/megamek/client/bot/princess/Princess.java +++ b/megamek/src/megamek/client/bot/princess/Princess.java @@ -1029,7 +1029,8 @@ Entity getEntityToMove() { if ((entity.isOffBoard() || (null == entity.getPosition()) || entity.isUnloadedThisTurn() - || !getGame().getTurn().isValidEntity(entity, getGame(), true))) { + || !getGame().getTurn().isValidEntity(entity, getGame())) + && !getGame().isPhaseSimultaneous()){ msg.append("cannot be moved."); continue; } From 8c396ac7b8a1addcb7bc9640d9128ae222b8b140 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Wed, 7 Apr 2021 23:04:39 -0400 Subject: [PATCH 08/27] whitespace --- megamek/src/megamek/client/bot/princess/Princess.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/megamek/src/megamek/client/bot/princess/Princess.java b/megamek/src/megamek/client/bot/princess/Princess.java index 54219c32175..0df19f05426 100644 --- a/megamek/src/megamek/client/bot/princess/Princess.java +++ b/megamek/src/megamek/client/bot/princess/Princess.java @@ -2113,7 +2113,7 @@ private void unloadTransportedInfantry(MovePath path) { private void launchFighters(MovePath path) { // if my objective is to cross the board, even though it's tempting, I won't be leaving the infantry // behind. They're not that good at screening against high speed pursuit anyway. - if(getBehaviorSettings().shouldGoHome()) { + if (getBehaviorSettings().shouldGoHome()) { return; } @@ -2122,7 +2122,7 @@ private void launchFighters(MovePath path) { Targetable closestEnemy = getPathRanker(movingEntity).findClosestEnemy(movingEntity, pathEndpoint, getGame(), false); // if there are no enemies on the board, then we're not launching anything. - if((null == closestEnemy) || (closestEnemy.getTargetType() != Targetable.TYPE_ENTITY)) { + if ((null == closestEnemy) || (closestEnemy.getTargetType() != Targetable.TYPE_ENTITY)) { return; } @@ -2133,11 +2133,11 @@ private void launchFighters(MovePath path) { // grouping launched craft by bay to limit launches to 'safe' rate. Vector fighterBays = movingEntity.getFighterBays(); - for(int bayIndex = 0; bayIndex < fighterBays.size(); bayIndex++) { + for (int bayIndex = 0; bayIndex < fighterBays.size(); bayIndex++) { Bay bay = fighterBays.get(bayIndex); - for(Entity loadedEntity : bay.getLaunchableUnits()) { - unitsToLaunch.putIfAbsent(bayIndex, new Vector()); + for (Entity loadedEntity : bay.getLaunchableUnits()) { + unitsToLaunch.putIfAbsent(bayIndex, new Vector<>()); // for now, just launch fighters at the 'safe' rate if (unitsToLaunch.get(bayIndex).size() < bay.getSafeLaunchRate()) { From 6d9b82a24b5e1f7920d1aa17d438f14a0dc03d1e Mon Sep 17 00:00:00 2001 From: NickAragua Date: Wed, 7 Apr 2021 23:19:16 -0400 Subject: [PATCH 09/27] Update history.txt --- megamek/docs/history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/megamek/docs/history.txt b/megamek/docs/history.txt index 213ee45c371..e6172fb697c 100644 --- a/megamek/docs/history.txt +++ b/megamek/docs/history.txt @@ -32,6 +32,7 @@ VERSION HISTORY: target movement modifier now applies to legal leg/swarm attacks. + Issue #2767: Incorrect Citation for "(Unofficial) Specify the number of pre-designated artillery autohit hexes per map area + Issue #547: Implement air-launched Arrow IV (homing and non-homing) ++ PR #2773: Launch Fighters (bot version) 0.48.0 (Stable) - (2021-03-05 1530 UTC) + PR #2638, #2640: Turret data fixes From fd6a7cefb0f16b38aaedfe923ebeea819c94f79c Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Tue, 13 Apr 2021 10:06:02 -0600 Subject: [PATCH 10/27] 1219: Adding era defensive coding --- .../client/ratgenerator/RATGenerator.java | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/megamek/src/megamek/client/ratgenerator/RATGenerator.java b/megamek/src/megamek/client/ratgenerator/RATGenerator.java index d1b1b7c7196..172a76ee3eb 100644 --- a/megamek/src/megamek/client/ratgenerator/RATGenerator.java +++ b/megamek/src/megamek/client/ratgenerator/RATGenerator.java @@ -25,6 +25,7 @@ import javax.xml.parsers.DocumentBuilder; +import megamek.common.annotations.Nullable; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -339,11 +340,13 @@ public Collection getFactionKeySet() { return factions.keySet(); } - public int eraForYear(int year) { - if (year < eraSet.first()) { - return eraSet.first(); + public int eraForYear(final int year) { + if (year < getEraSet().first()) { + return getEraSet().first(); + } else { + final Integer floor = getEraSet().floor(year); + return (floor == null) ? year : floor; } - return eraSet.floor(year); } public boolean eraIsLoaded(int era) { @@ -841,18 +844,23 @@ private synchronized void initialize(File dir) { } /** - * If year is equal to one of the era marks, loads that era. If it is between, - * loads eras on both sides. + * If the year is equal to one of the era marks, it loads that era. If it is between two, it + * loads eras on both sides. Otherwise, just load the closest era. */ - public void loadYear(int year) { - if (eraSet.contains(year)) { + public void loadYear(final int year) { + if (getEraSet().isEmpty()) { + return; + } else if (getEraSet().contains(year)) { loadEra(year); + return; } - if (year > eraSet.first()) { - loadEra(eraSet.floor(year)); + + if (year > getEraSet().first()) { + loadEra(getEraSet().floor(year)); } - if (year < eraSet.last()) { - loadEra(eraSet.ceiling(year)); + + if (year < getEraSet().last()) { + loadEra(getEraSet().ceiling(year)); } } @@ -894,8 +902,13 @@ private void loadFactions(File dir) { } } - private void loadEra(int era) { - loadEra(era, Configuration.forceGeneratorDir()); + /** + * @param era the era to load, which may be null if there isn't anything found + */ + private void loadEra(final @Nullable Integer era) { + if (era != null) { + loadEra(era, Configuration.forceGeneratorDir()); + } } private synchronized void loadEra(int era, File dir) { From 2d09e70f4b8ecb22df202da6bf74cd1af098dff1 Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Tue, 13 Apr 2021 11:49:26 -0600 Subject: [PATCH 11/27] 2138/1785: Fixing and improving round report player stats --- .../megamek/common/report-messages.properties | 2 +- .../common/report-messages_de.properties | 1 - .../common/report-messages_ru.properties | 1 - .../megamek/client/ui/swing/MegaMekGUI.java | 2 +- megamek/src/megamek/common/IPlayer.java | 38 ++++++----- megamek/src/megamek/common/Player.java | 58 ++++++++++------ megamek/src/megamek/server/Server.java | 66 ++++++++++++------- 7 files changed, 105 insertions(+), 63 deletions(-) diff --git a/megamek/i18n/megamek/common/report-messages.properties b/megamek/i18n/megamek/common/report-messages.properties index 07c34f93999..3926262f1a2 100755 --- a/megamek/i18n/megamek/common/report-messages.properties +++ b/megamek/i18n/megamek/common/report-messages.properties @@ -940,7 +940,7 @@ 7005=A strange game. The only winning move is not to play. 7010=Winner is: 7015=Winner is: TEAM # -7016=: BV remaining (% of initially) BV fled +7016=: / BV remaining (%), BV fled. / units remaining (%). 7020=Survivors are: 7025= () 7030=Pilot : diff --git a/megamek/i18n/megamek/common/report-messages_de.properties b/megamek/i18n/megamek/common/report-messages_de.properties index 07a4d716570..d160e20551d 100644 --- a/megamek/i18n/megamek/common/report-messages_de.properties +++ b/megamek/i18n/megamek/common/report-messages_de.properties @@ -583,7 +583,6 @@ 7005=Der Gewinner ist: Die Chicago Cubs!!! 7010=Der Gewinner ist: 7015=Der Gewinner ist: TEAM # -7016=: BV \u00fcbrig (von urspr\u00fcnglich ) 7020=\u00dcberlebende sind: 7025= () 7030=Pilot: diff --git a/megamek/i18n/megamek/common/report-messages_ru.properties b/megamek/i18n/megamek/common/report-messages_ru.properties index fd86432cb2e..6e53e9aa239 100644 --- a/megamek/i18n/megamek/common/report-messages_ru.properties +++ b/megamek/i18n/megamek/common/report-messages_ru.properties @@ -815,7 +815,6 @@ 7005=Победитель: Чикаго Кабс!!! 7010=Победитель: 7015=Победитель: КОМАНДА # -7016=: BV осталось (из изначальных) BV убежало 7020=Выжившие: 7025= () 7030=Пилот : diff --git a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java index 423ca6c5c46..112caf87439 100644 --- a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java +++ b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java @@ -748,7 +748,7 @@ public String getDescription() { optionsDialog = null; // calculate initial BV - server.calculatePlayerBVs(); + server.calculatePlayerInitialCounts(); // setup any bots for (int x = 0; x < pa.length; x++) { diff --git a/megamek/src/megamek/common/IPlayer.java b/megamek/src/megamek/common/IPlayer.java index 801a261834b..a87fca4d426 100644 --- a/megamek/src/megamek/common/IPlayer.java +++ b/megamek/src/megamek/common/IPlayer.java @@ -1,15 +1,15 @@ /* * MegaMek - Copyright (C) 2003, 2004 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 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. + * 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; @@ -135,6 +135,14 @@ public interface IPlayer extends ITurnOrdered { boolean hasTAG(); + int getEntityCount(); + + int getInitialEntityCount(); + + void setInitialEntityCount(final int initialEntityCount); + + void changeInitialEntityCount(final int initialEntityCountChange); + /** * @return The combined Battle Value of all the player's current assets. */ @@ -148,18 +156,16 @@ public interface IPlayer extends ITurnOrdered { */ int getFledBV(); - void setInitialBV(); + int getInitialBV(); - /** - * Used to increase the initial BV by the specified value, which may be necessary if the player reinforces. - * @param bv - */ - void increaseInitialBV(int bv); + void setInitialBV(final int initialBV); - int getInitialBV(); + void changeInitialBV(final int initialBVChange); + @Override void setInitCompensationBonus(int newBonus); + @Override int getInitCompensationBonus(); void setConstantInitBonus(int b); @@ -185,7 +191,9 @@ public interface IPlayer extends ITurnOrdered { Vector getAirborneVTOL(); // Make sure IPlayer implements both + @Override boolean equals(Object obj); + @Override int hashCode(); } \ No newline at end of file diff --git a/megamek/src/megamek/common/Player.java b/megamek/src/megamek/common/Player.java index 63d90df2987..d38f6c28f49 100644 --- a/megamek/src/megamek/common/Player.java +++ b/megamek/src/megamek/common/Player.java @@ -13,16 +13,16 @@ */ package megamek.common; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Objects; -import java.util.Vector; - import megamek.client.ui.swing.util.PlayerColour; import megamek.common.event.GamePlayerChangeEvent; import megamek.common.icons.Camouflage; import megamek.common.options.OptionsConstants; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Objects; +import java.util.Vector; + /** * Represents a player in the game. */ @@ -55,6 +55,7 @@ public final class Player extends TurnOrdered implements IPlayer { // hexes that are automatically hit by artillery private Vector artyAutoHitHexes = new Vector<>(); + private int initialEntityCount; private int initialBV; // initiative bonuses go here because we don't know if teams are rolling @@ -72,7 +73,7 @@ public final class Player extends TurnOrdered implements IPlayer { /** * Boolean that keeps track of whether a player has accepted another - * player's request to chang teams. + * player's request to change teams. */ private boolean allowingTeamChange = false; @@ -393,20 +394,35 @@ public boolean accept(Entity entity) { return false; } + @Override + public int getEntityCount() { + return Math.toIntExact(game.getPlayerEntities(this, false).stream() + .filter(entity -> !entity.isDestroyed() && !entity.isTrapped()).count()); + } + + @Override + public int getInitialEntityCount() { + return initialEntityCount; + } + + @Override + public void setInitialEntityCount(final int initialEntityCount) { + this.initialEntityCount = initialEntityCount; + } + + @Override + public void changeInitialEntityCount(final int initialEntityCountChange) { + this.initialEntityCount += initialEntityCountChange; + } + /** * @return The combined Battle Value of all the player's current assets. */ @Override public int getBV() { - int bv = 0; - - for (Entity entity : game.getEntitiesVector()) { - if (equals(entity.getOwner()) && !entity.isDestroyed() - && !entity.isTrapped()) { - bv += entity.calculateBattleValue(); - } - } - return bv; + return game.getPlayerEntities(this, false).stream() + .filter(entity -> !entity.isDestroyed() && !entity.isTrapped()) + .mapToInt(Entity::calculateBattleValue).sum(); } /** @@ -429,18 +445,18 @@ public int getFledBV() { } @Override - public void setInitialBV() { - initialBV = getBV(); + public int getInitialBV() { + return initialBV; } @Override - public void increaseInitialBV(int bv) { - initialBV += bv; + public void setInitialBV(final int initialBV) { + this.initialBV = initialBV; } @Override - public int getInitialBV() { - return initialBV; + public void changeInitialBV(final int initialBVChange) { + this.initialBV += initialBVChange; } @Override diff --git a/megamek/src/megamek/server/Server.java b/megamek/src/megamek/server/Server.java index 3d36015c48f..830aaea24c2 100644 --- a/megamek/src/megamek/server/Server.java +++ b/megamek/src/megamek/server/Server.java @@ -1750,18 +1750,19 @@ private void prepareVictoryReport() { Enumeration players = game.getPlayers(); while (players.hasMoreElements()) { IPlayer player = players.nextElement(); - // Players who started the game as observers get ignored - if (player.getInitialBV() == 0) { + // Observers without initial entities get ignored + if (player.isObserver() && (player.getInitialEntityCount() == 0)) { continue; } - r = new Report(); - r.type = Report.PUBLIC; - r.messageId = 7016; + r = new Report(7016, Report.PUBLIC); r.add(Server.getColorForPlayer(player)); r.add(player.getBV()); - r.add(Double.toString(Math.round((double) player.getBV() / player.getInitialBV() * 10000.0) / 100.0)); r.add(player.getInitialBV()); + r.add(Double.toString(Math.round(((double) player.getBV() / player.getInitialBV()) * 10000.0) / 100.0)); r.add(player.getFledBV()); + r.add(player.getEntityCount()); + r.add(player.getInitialEntityCount()); + r.add(Double.toString(Math.round(((double) player.getEntityCount() / player.getInitialEntityCount()) * 10000.0) / 100.0)); addReport(r); } @@ -2404,30 +2405,32 @@ public boolean accept(Entity entity) { transmitAllPlayerUpdates(); entityAllUpdate(); break; - case PHASE_INITIATIVE_REPORT: + case PHASE_INITIATIVE_REPORT: { autoSave(); // Show player BVs Enumeration players2 = game.getPlayers(); while (players2.hasMoreElements()) { IPlayer player = players2.nextElement(); - // Players who started the game as observers get ignored - if (player.getInitialBV() == 0) { + // Observers without initial entities get ignored + if (player.isObserver() && (player.getInitialEntityCount() == 0)) { continue; } - Report r = new Report(); - r.type = Report.PUBLIC; + Report r = new Report(7016, Report.PUBLIC); if (doBlind() && suppressBlindBV()) { r.type = Report.PLAYER; r.player = player.getId(); } - r.messageId = 7016; r.add(Server.getColorForPlayer(player)); r.add(player.getBV()); - r.add(Double.toString(Math.round((double) player.getBV() / player.getInitialBV() * 10000.0) / 100.0)); r.add(player.getInitialBV()); + r.add(Double.toString(Math.round(((double) player.getBV() / player.getInitialBV()) * 10000.0) / 100.0)); r.add(player.getFledBV()); + r.add(player.getEntityCount()); + r.add(player.getInitialEntityCount()); + r.add(Double.toString(Math.round(((double) player.getEntityCount() / player.getInitialEntityCount()) * 10000.0) / 100.0)); addReport(r); } + } case PHASE_TARGETING_REPORT: case PHASE_MOVEMENT_REPORT: case PHASE_OFFBOARD_REPORT: @@ -2584,11 +2587,11 @@ private void executePhase(IGame.Phase phase) { switch (phase) { case PHASE_EXCHANGE: resetPlayersDone(); - calculatePlayerBVs(); // Update initial BVs, as things may have been modified in lounge for (Entity e : game.getEntitiesVector()) { e.setInitialBV(e.calculateBattleValue(false, false)); } + calculatePlayerInitialCounts(); // Build teams vector game.setupTeams(); applyBoardSettings(); @@ -2622,11 +2625,14 @@ private void executePhase(IGame.Phase phase) { } /** - * Calculates all players initial BV, should only be called at start of game + * Calculates the initial count and BV for all players, and thus should only be called at the + * start of a game */ - public void calculatePlayerBVs() { - for (Enumeration players = game.getPlayers(); players.hasMoreElements(); ) { - players.nextElement().setInitialBV(); + public void calculatePlayerInitialCounts() { + for (final Enumeration players = game.getPlayers(); players.hasMoreElements(); ) { + final IPlayer player = players.nextElement(); + player.setInitialEntityCount(player.getEntityCount()); + player.setInitialBV(player.getBV()); } } @@ -21029,15 +21035,28 @@ private Vector checkForTraitors() { continue; } if ((entity.getTraitorId() != -1) && (entity.getOwnerId() != entity.getTraitorId())) { - IPlayer p = game.getPlayer(entity.getTraitorId()); - if (null != p) { + final IPlayer oldPlayer = game.getPlayer(entity.getOwnerId()); + final IPlayer newPlayer = game.getPlayer(entity.getTraitorId()); + if (newPlayer != null) { Report r = new Report(7305); r.subject = entity.getId(); r.add(entity.getDisplayName()); - r.add(p.getName()); - entity.setOwner(p); + r.add(newPlayer.getName()); + entity.setOwner(newPlayer); entityUpdate(entity.getId()); vFullReport.add(r); + + // Move the initial count and BV to their new player + newPlayer.changeInitialEntityCount(1); + newPlayer.changeInitialBV(entity.calculateBattleValue()); + + // And remove it from their old player, if they exist + if (oldPlayer != null) { + oldPlayer.changeInitialEntityCount(-1); + // Note: I don't remove the full initial BV if I'm damaged, but that + // actually makes sense + oldPlayer.changeInitialBV(-1 * entity.calculateBattleValue()); + } } entity.setTraitorId(-1); } @@ -29969,7 +29988,8 @@ public boolean accept(Entity entity) { entityIds.add(entity.getId()); if (game.getPhase() != Phase.PHASE_LOUNGE) { - entity.getOwner().increaseInitialBV(entity.calculateBattleValue(false, false)); + entity.getOwner().changeInitialEntityCount(1); + entity.getOwner().changeInitialBV(entity.calculateBattleValue()); } } From 65545d3fb0dad51668806db940f4414259aec035 Mon Sep 17 00:00:00 2001 From: Justin Bowen <39067288+Windchild292@users.noreply.github.com> Date: Tue, 13 Apr 2021 20:38:57 -0400 Subject: [PATCH 12/27] Update history.txt --- megamek/docs/history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/megamek/docs/history.txt b/megamek/docs/history.txt index e6172fb697c..a10effde24a 100644 --- a/megamek/docs/history.txt +++ b/megamek/docs/history.txt @@ -33,6 +33,7 @@ VERSION HISTORY: + Issue #2767: Incorrect Citation for "(Unofficial) Specify the number of pre-designated artillery autohit hexes per map area + Issue #547: Implement air-launched Arrow IV (homing and non-homing) + PR #2773: Launch Fighters (bot version) ++ Issue #1219: Adding Defensive Code for RATGenerator Era 0.48.0 (Stable) - (2021-03-05 1530 UTC) + PR #2638, #2640: Turret data fixes From 114e951f5aa95afe140110e88f71929587fd8532 Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Tue, 13 Apr 2021 19:12:40 -0600 Subject: [PATCH 13/27] 2138: Adding supplied Russian translation --- megamek/i18n/megamek/common/report-messages_ru.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/megamek/i18n/megamek/common/report-messages_ru.properties b/megamek/i18n/megamek/common/report-messages_ru.properties index 6e53e9aa239..a4ddb3a21da 100644 --- a/megamek/i18n/megamek/common/report-messages_ru.properties +++ b/megamek/i18n/megamek/common/report-messages_ru.properties @@ -815,6 +815,7 @@ 7005=Победитель: Чикаго Кабс!!! 7010=Победитель: 7015=Победитель: КОМАНДА # +7016=: / BV осталось ( из изначальных), BV убежало. / бойцов осталось (%). 7020=Выжившие: 7025= () 7030=Пилот : From bb045aab0d8803de41502b35fb4c04e5b8be7cbb Mon Sep 17 00:00:00 2001 From: Justin Bowen <39067288+Windchild292@users.noreply.github.com> Date: Tue, 13 Apr 2021 21:17:49 -0400 Subject: [PATCH 14/27] Update history.txt --- megamek/docs/history.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/megamek/docs/history.txt b/megamek/docs/history.txt index a10effde24a..b83d11c538f 100644 --- a/megamek/docs/history.txt +++ b/megamek/docs/history.txt @@ -34,6 +34,8 @@ VERSION HISTORY: + Issue #547: Implement air-launched Arrow IV (homing and non-homing) + PR #2773: Launch Fighters (bot version) + Issue #1219: Adding Defensive Code for RATGenerator Era ++ Issues #1785/#2138: Fixing and Improving Round Report Player Stats + 0.48.0 (Stable) - (2021-03-05 1530 UTC) + PR #2638, #2640: Turret data fixes From f5436d3468e2f280610213a8eb156000da219cb6 Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Tue, 13 Apr 2021 19:25:58 -0600 Subject: [PATCH 15/27] Removing unusable old translations --- megamek/i18n/megamek/common/options/messages_en.properties | 1 - megamek/i18n/megamek/common/report-messages_de.properties | 2 -- megamek/i18n/megamek/common/report-messages_ru.properties | 2 -- 3 files changed, 5 deletions(-) delete mode 100644 megamek/i18n/megamek/common/options/messages_en.properties diff --git a/megamek/i18n/megamek/common/options/messages_en.properties b/megamek/i18n/megamek/common/options/messages_en.properties deleted file mode 100644 index 0519ecba6ea..00000000000 --- a/megamek/i18n/megamek/common/options/messages_en.properties +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/megamek/i18n/megamek/common/report-messages_de.properties b/megamek/i18n/megamek/common/report-messages_de.properties index 07a4d716570..0953ea71c06 100644 --- a/megamek/i18n/megamek/common/report-messages_de.properties +++ b/megamek/i18n/megamek/common/report-messages_de.properties @@ -396,7 +396,6 @@ 5087=versagt und erh\u00e4lt einen kritischen Treffer. 5090= () stirbt auf Grund extremer Temperatur. 5095= () brennt, wird aber durch Ausr\u00fcstung gesch\u00fctzt. -5100= () brennt. Ben\u00f6tigt 8+, um eine Zerst\u00f6rung zu vermeiden, w\u00fcrfelt : 5101=\u00fcberlebt! 5102=verbrennt. 5105=Gl\u00fccklicherweise war keine Munition da, die explodieren konnte. @@ -412,7 +411,6 @@ 5143=Ultra Dichter Jungle ist zu Dichtem Jungle heruntergebrannt! 5145=Lichter Wald in ist ausgebrannt!! 5146=Lichter Jungle in ist ausgebrannt!! -5150=Feuer breitet sich aus nach ! 5155=Erh\u00e4lt 30 zus\u00e4tzliche Hitze und ist jetzt bei . 5160=Gl\u00fccklicherweise war da keine Infernomunition, die explodieren konnte. 5165= () ist von eine Schwarmattacke befreit. diff --git a/megamek/i18n/megamek/common/report-messages_ru.properties b/megamek/i18n/megamek/common/report-messages_ru.properties index fd86432cb2e..a2df7e71068 100644 --- a/megamek/i18n/megamek/common/report-messages_ru.properties +++ b/megamek/i18n/megamek/common/report-messages_ru.properties @@ -527,7 +527,6 @@ 5087=неудачно и получает критический урон. 5090= () умирает при экстремальной температуре. 5095= () горит, но защищен своей броней. -5100= () горит. Нужно 8+ чтобы избежать урона, выбрасывает : 5101=избегает удачно! 5102=не удается избежать урона. 5105=К счастью, не осталось боеприпасов, которые могли бы взорваться. @@ -544,7 +543,6 @@ 5143=Ультра Густые Джунгли в выгорают до Густых Джунглей! 5145=Редкий Лес в выгорает до Пересеченной местности и пожар гаснет!! 5146=Редкие Джунгли в выгорают до Пересеченной местности и пожар гаснет!! -5150=Огонь переходит на ! 5155=Набирает 30 тепла и сейчас нагрет на тепла. 5160=К счастью, не осталось инферно боеприпасов, которые могли бы взорваться. 5165= () освобожден от штурмующей атаки. From c1eb1b579e1c4af2c1b39779997ac5c56e230576 Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Tue, 13 Apr 2021 22:06:03 -0600 Subject: [PATCH 16/27] Creating two Entity-based standard components --- .../client/ui/panels/EntityImagePanel.java | 99 +++++++++++++++++++ .../client/ui/panes/EntityViewPane.java | 96 ++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 megamek/src/megamek/client/ui/panels/EntityImagePanel.java create mode 100644 megamek/src/megamek/client/ui/panes/EntityViewPane.java diff --git a/megamek/src/megamek/client/ui/panels/EntityImagePanel.java b/megamek/src/megamek/client/ui/panels/EntityImagePanel.java new file mode 100644 index 00000000000..a72575a8817 --- /dev/null +++ b/megamek/src/megamek/client/ui/panels/EntityImagePanel.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 - 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.client.ui.panels; + +import megamek.client.ui.swing.tileset.EntityImage; +import megamek.client.ui.swing.tileset.MMStaticDirectoryManager; +import megamek.common.Entity; +import megamek.common.annotations.Nullable; +import megamek.common.icons.AbstractIcon; +import megamek.common.icons.Camouflage; + +import javax.swing.*; +import java.awt.*; + +/** + * The EntityImagePanel displays the Entity's Image using the provided camouflage. + */ +public class EntityImagePanel extends JPanel { + //region Variable Declarations + private JLabel imageLabel; + //endregion Variable Declarations + + //region Constructors + public EntityImagePanel(final @Nullable Entity entity, final AbstractIcon camouflage) { + super(); + initialize(); + updateDisplayedEntity(entity, camouflage); + } + //endregion Constructors + + //region Getters/Setters + public JLabel getImageLabel() { + return imageLabel; + } + + public void setImageLabel(final JLabel imageLabel) { + this.imageLabel = imageLabel; + } + //endregion Getters/Setters + + //region Initialization + private void initialize() { + // Create Panel Components + setImageLabel(new JLabel()); + + // Layout the UI + setName("entityImagePanel"); + GroupLayout layout = new GroupLayout(this); + setLayout(layout); + + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + layout.setVerticalGroup( + layout.createSequentialGroup() + .addComponent(getImageLabel()) + ); + + layout.setHorizontalGroup( + layout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(getImageLabel()) + ); + } + //endregion Initialization + + /** + * This updates the panel's currently displayed entity + * + * @param entity the entity to update to, or null if the label is to be reset. + * @param camouflage the camouflage to display + */ + public void updateDisplayedEntity(final @Nullable Entity entity, final AbstractIcon camouflage) { + if ((entity == null) || (MMStaticDirectoryManager.getMechTileset() == null) + || !(camouflage instanceof Camouflage)) { + getImageLabel().setIcon(null); + return; + } + + final Image base = MMStaticDirectoryManager.getMechTileset().imageFor(entity); + getImageLabel().setIcon(new ImageIcon(new EntityImage(base, (Camouflage) camouflage, this, entity) + .loadPreviewImage())); + } +} diff --git a/megamek/src/megamek/client/ui/panes/EntityViewPane.java b/megamek/src/megamek/client/ui/panes/EntityViewPane.java new file mode 100644 index 00000000000..b135baab847 --- /dev/null +++ b/megamek/src/megamek/client/ui/panes/EntityViewPane.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 - 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.client.ui.panes; + +import megamek.client.ui.baseComponents.AbstractTabbedPane; +import megamek.client.ui.swing.MechViewPanel; +import megamek.common.Entity; +import megamek.common.annotations.Nullable; +import megamek.common.templates.TROView; + +import javax.swing.*; + +/** + * The EntityViewPane displays the Entity Summary and the TRO panels within a Tabbed Pane. + */ +public class EntityViewPane extends AbstractTabbedPane { + //region Variable Declarations + private MechViewPanel entityPanel; + private MechViewPanel troPanel; + //endregion Variable Declarations + + //region Constructors + public EntityViewPane(final JFrame frame, final @Nullable Entity entity) { + super(frame, "EntityViewPane"); + initialize(); + updateDisplayedEntity(entity); + } + //endregion Constructors + + //region Getters/Setters + public MechViewPanel getEntityPanel() { + return entityPanel; + } + + public void setEntityPanel(final MechViewPanel entityPanel) { + this.entityPanel = entityPanel; + } + + public MechViewPanel getTROPanel() { + return troPanel; + } + + public void setTROPanel(final MechViewPanel troPanel) { + this.troPanel = troPanel; + } + //endregion Getters/Setters + + //region Initialization + /** + * This purposefully does not set preferences, as it may be used on differing panes for + * differing uses and thus you don't want to remember the selected tab between the different + * locations. + */ + @Override + protected void initialize() { + setEntityPanel(new MechViewPanel()); + getEntityPanel().setName("entityPanel"); + addTab(resources.getString("Summary.title"), getEntityPanel()); + + setTROPanel(new MechViewPanel()); + getTROPanel().setName("troPanel"); + addTab(resources.getString("TRO.title"), getTROPanel()); + } + //endregion Initialization + + /** + * This updates the pane's currently displayed entity + * @param entity the entity to update to, or null if the panels are to be reset. + */ + public void updateDisplayedEntity(final @Nullable Entity entity) { + // Null entity, which means to reset the panels + if (entity == null) { + getEntityPanel().reset(); + getTROPanel().reset(); + } else { + getEntityPanel().setMech(entity, true); + getTROPanel().setMech(entity, TROView.createView(entity, true)); + } + } +} From 9b038ab6c94d44769ba20ad3bc60a48615df82f2 Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Tue, 13 Apr 2021 22:16:11 -0600 Subject: [PATCH 17/27] Finishing backport with missing properties --- megamek/i18n/megamek/client/messages.properties | 15 +++++++++++++-- .../client/ui/panels/EntityImagePanel.java | 3 +-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties index 00e85b74484..192810edc39 100644 --- a/megamek/i18n/megamek/client/messages.properties +++ b/megamek/i18n/megamek/client/messages.properties @@ -7,13 +7,24 @@ Cancel.toolTipText=Cancel out of the dialog. No changes made will be saved. Ok.text=Ok Ok.toolTipText=Confirm the changes and close the dialog. + + ##### client/ui Resources -#### client/ui/dialogs Resources -# AbstractHelpDialog Class +#### Dialogs +### AbstractHelpDialog Class AbstractHelpDialog.errorReading=Error reading help file:\u0020 AbstractHelpDialog.helpFile=Help File:\u0020 AbstractHelpDialog.noHelp.title=No Help Available + + +#### Panes +### EntityViewPane Class +Summary.title=Summary +TRO.title=TRO + + + ##### Unsorted Resources # Generic Resources No=No diff --git a/megamek/src/megamek/client/ui/panels/EntityImagePanel.java b/megamek/src/megamek/client/ui/panels/EntityImagePanel.java index a72575a8817..bc01a2d288e 100644 --- a/megamek/src/megamek/client/ui/panels/EntityImagePanel.java +++ b/megamek/src/megamek/client/ui/panels/EntityImagePanel.java @@ -61,9 +61,8 @@ private void initialize() { // Layout the UI setName("entityImagePanel"); - GroupLayout layout = new GroupLayout(this); + final GroupLayout layout = new GroupLayout(this); setLayout(layout); - layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); From 53e8cec86c7031254364f8aa158f10cbb3935401 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Apr 2021 22:04:08 -0400 Subject: [PATCH 18/27] don't process assault drops if the unit isn't deployed --- megamek/src/megamek/server/Server.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/megamek/src/megamek/server/Server.java b/megamek/src/megamek/server/Server.java index 830aaea24c2..d712177d515 100644 --- a/megamek/src/megamek/server/Server.java +++ b/megamek/src/megamek/server/Server.java @@ -34842,7 +34842,7 @@ private void addNewLines() { * * @param entity the Entity for which to resolve it */ - public void doAssaultDrop(Entity entity) { + public void doAssaultDrop(Entity entity) { //resolve according to SO p.22 Report r = new Report(2380); @@ -34979,7 +34979,7 @@ public void doAssaultDrop(Entity entity) { */ void doAllAssaultDrops() { for (Entity e : game.getEntitiesVector()) { - if (e.isAssaultDropInProgress()) { + if (e.isAssaultDropInProgress() && e.isDeployed()) { doAssaultDrop(e); e.setLandedAssaultDrop(); } From 8f3bf095d21a8fd0faf5ba7922b068a3865d385d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Apr 2021 22:07:16 -0400 Subject: [PATCH 19/27] whitespace --- megamek/src/megamek/server/Server.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/megamek/src/megamek/server/Server.java b/megamek/src/megamek/server/Server.java index d712177d515..081717f61ac 100644 --- a/megamek/src/megamek/server/Server.java +++ b/megamek/src/megamek/server/Server.java @@ -34842,7 +34842,7 @@ private void addNewLines() { * * @param entity the Entity for which to resolve it */ - public void doAssaultDrop(Entity entity) { + public void doAssaultDrop(Entity entity) { //resolve according to SO p.22 Report r = new Report(2380); From 72ff8090a49ce4796ef4b294ce77f663f2df714b Mon Sep 17 00:00:00 2001 From: HammerGS Date: Fri, 16 Apr 2021 08:00:35 -0600 Subject: [PATCH 20/27] + Issues #2787: Protomech AC gun and ammo are different tech levels --- megamek/docs/history.txt | 2 +- megamek/src/megamek/common/AmmoType.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/megamek/docs/history.txt b/megamek/docs/history.txt index b83d11c538f..3a214124276 100644 --- a/megamek/docs/history.txt +++ b/megamek/docs/history.txt @@ -35,7 +35,7 @@ VERSION HISTORY: + PR #2773: Launch Fighters (bot version) + Issue #1219: Adding Defensive Code for RATGenerator Era + Issues #1785/#2138: Fixing and Improving Round Report Player Stats - ++ Issues #2787: Protomech AC gun and ammo are different tech levels 0.48.0 (Stable) - (2021-03-05 1530 UTC) + PR #2638, #2640: Turret data fixes diff --git a/megamek/src/megamek/common/AmmoType.java b/megamek/src/megamek/common/AmmoType.java index 900f6006aa8..9895efd7d43 100644 --- a/megamek/src/megamek/common/AmmoType.java +++ b/megamek/src/megamek/common/AmmoType.java @@ -4032,7 +4032,7 @@ private static AmmoType createCLPROAC2Ammo() { .setTechRating(RATING_B).setAvailability(RATING_X, RATING_X, RATING_D, RATING_D) .setClanAdvancement(3070, 3073, 3145).setClanApproximate(true, true, false) .setPrototypeFactions(F_CBS).setProductionFactions(F_CBS) - .setStaticTechLevel(SimpleTechLevel.EXPERIMENTAL); + .setStaticTechLevel(SimpleTechLevel.ADVANCED); return ammo; } @@ -4057,7 +4057,7 @@ private static AmmoType createCLPROAC4Ammo() { .setTechRating(RATING_B).setAvailability(RATING_X, RATING_X, RATING_D, RATING_D) .setClanAdvancement(3070, 3073, 3145).setClanApproximate(true, true, false) .setPrototypeFactions(F_CBS).setProductionFactions(F_CBS) - .setStaticTechLevel(SimpleTechLevel.EXPERIMENTAL); + .setStaticTechLevel(SimpleTechLevel.ADVANCED); return ammo; } @@ -4082,7 +4082,7 @@ private static AmmoType createCLPROAC8Ammo() { .setTechRating(RATING_B).setAvailability(RATING_X, RATING_X, RATING_D, RATING_D) .setClanAdvancement(3070, 3073, 3145).setClanApproximate(true, true, false) .setPrototypeFactions(F_CBS).setProductionFactions(F_CBS) - .setStaticTechLevel(SimpleTechLevel.EXPERIMENTAL); + .setStaticTechLevel(SimpleTechLevel.ADVANCED); return ammo; } From 833bef4182cac586c4210c715a48d28d141b1777 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Fri, 16 Apr 2021 23:52:43 -0400 Subject: [PATCH 21/27] Update history.txt --- megamek/docs/history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/megamek/docs/history.txt b/megamek/docs/history.txt index 3a214124276..c9352c9412d 100644 --- a/megamek/docs/history.txt +++ b/megamek/docs/history.txt @@ -36,6 +36,7 @@ VERSION HISTORY: + Issue #1219: Adding Defensive Code for RATGenerator Era + Issues #1785/#2138: Fixing and Improving Round Report Player Stats + Issues #2787: Protomech AC gun and ammo are different tech levels ++ PR #2786: Units set for a delayed assault drop no longer disappear on the first turn of the game. 0.48.0 (Stable) - (2021-03-05 1530 UTC) + PR #2638, #2640: Turret data fixes From ae9085ed8593022368c1c1abf8e30c89aec510c2 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 19 Apr 2021 23:02:53 -0400 Subject: [PATCH 22/27] second pass through mounted components --- megamek/src/megamek/common/Entity.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index 5c270ab781a..eb0d26052d3 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -11566,6 +11566,21 @@ public void destroyLocation(int loc, boolean blownOff) { } } + // some equipment is not present in critical slots + // but is present in the location, so we'll need to look at it as well + for (Mounted mounted : getEquipment()) { + if (((mounted.getLocation() == loc) && mounted.getType().isHittable()) + || (mounted.isSplit() && (mounted.getSecondLocation() == loc))) { + if (blownOff) { + mounted.setMissing(true); + // we don't want to hit something twice here to avoid triggering + // things that fire off when a mounted is hit + } else if (!mounted.isHit()) { + mounted.setHit(true); + } + } + } + // dependent locations destroyed, unless they are already destroyed if ((getDependentLocation(loc) != Entity.LOC_NONE) && !(getInternal(getDependentLocation(loc)) < 0)) { From 053f1a857df297f29a5d32ff4765392178231f8b Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Tue, 20 Apr 2021 09:22:17 -0400 Subject: [PATCH 23/27] Adding fixed Russian translation --- megamek/i18n/megamek/common/report-messages_ru.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/megamek/i18n/megamek/common/report-messages_ru.properties b/megamek/i18n/megamek/common/report-messages_ru.properties index a2df7e71068..89e0d21a8e9 100644 --- a/megamek/i18n/megamek/common/report-messages_ru.properties +++ b/megamek/i18n/megamek/common/report-messages_ru.properties @@ -543,6 +543,7 @@ 5143=Ультра Густые Джунгли в выгорают до Густых Джунглей! 5145=Редкий Лес в выгорает до Пересеченной местности и пожар гаснет!! 5146=Редкие Джунгли в выгорают до Пересеченной местности и пожар гаснет!! +5150=Огонь распространился с на ! 5155=Набирает 30 тепла и сейчас нагрет на тепла. 5160=К счастью, не осталось инферно боеприпасов, которые могли бы взорваться. 5165= () освобожден от штурмующей атаки. From 556c17edf41820635ea9bb405b6a7d8fa169a99e Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Tue, 20 Apr 2021 07:33:46 -0600 Subject: [PATCH 24/27] 2449: Adding base components required to handle ranks --- .../i18n/megamek/client/messages.properties | 2 + .../baseComponents/AbstractButtonDialog.java | 28 +-- .../AbstractValidationButtonDialog.java | 162 ++++++++++++++++++ .../client/ui/baseComponents/MMButton.java | 106 ++++++++++++ .../ui/baseComponents/SpinnerCellEditor.java | 60 +++++++ .../client/ui/enums/ValidationState.java | 46 +++++ 6 files changed, 382 insertions(+), 22 deletions(-) create mode 100644 megamek/src/megamek/client/ui/baseComponents/AbstractValidationButtonDialog.java create mode 100644 megamek/src/megamek/client/ui/baseComponents/MMButton.java create mode 100644 megamek/src/megamek/client/ui/baseComponents/SpinnerCellEditor.java create mode 100644 megamek/src/megamek/client/ui/enums/ValidationState.java diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties index 00e85b74484..5962f02426f 100644 --- a/megamek/i18n/megamek/client/messages.properties +++ b/megamek/i18n/megamek/client/messages.properties @@ -6,6 +6,8 @@ Cancel.text=Cancel Cancel.toolTipText=Cancel out of the dialog. No changes made will be saved. Ok.text=Ok Ok.toolTipText=Confirm the changes and close the dialog. +Validate.text=Validate +Validate.toolTipText=Validate the data contained within this dialog. ##### client/ui Resources #### client/ui/dialogs Resources diff --git a/megamek/src/megamek/client/ui/baseComponents/AbstractButtonDialog.java b/megamek/src/megamek/client/ui/baseComponents/AbstractButtonDialog.java index ae77810946b..91139baea7b 100644 --- a/megamek/src/megamek/client/ui/baseComponents/AbstractButtonDialog.java +++ b/megamek/src/megamek/client/ui/baseComponents/AbstractButtonDialog.java @@ -24,7 +24,6 @@ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.util.ResourceBundle; /** @@ -107,33 +106,18 @@ protected void initialize() { */ protected JPanel createButtonPanel() { JPanel panel = new JPanel(new GridLayout(1, 2)); - panel.add(createButton("Ok.text", "Ok.toolTipText", "okButton", this::okButtonActionPerformed)); - panel.add(createButton("Cancel.text", "Cancel.toolTipText", "cancelButton", this::cancelActionPerformed)); + panel.add(new MMButton("okButton", resources.getString("Ok.text"), + resources.getString("Ok.toolTipText"), this::okButtonActionPerformed)); + panel.add(new MMButton("cancelButton", resources.getString("Cancel.text"), + resources.getString("Cancel.toolTipText"), this::cancelActionPerformed)); return panel; } - - /** - * This creates a standard button for use in the dialog - * @param text the text resource string - * @param toolTipText the toolTipText resource string - * @param name the name of the button - * @param actionListener the {@link ActionListener} to assign to the button - * @return the created button - */ - protected JButton createButton(final String text, final String toolTipText, final String name, - final ActionListener actionListener) { - JButton button = new JButton(resources.getString(text)); - button.setToolTipText(resources.getString(toolTipText)); - button.setName(name); - button.addActionListener(actionListener); - return button; - } //endregion Initialization //region Button Actions /** - * This is the default Action Event Listener for the Ok Button's action. This triggers the Ok Action, - * sets the result to confirmed, and then sets the dialog so that it is no longer visible. + * This is the default Action Event Listener for the Ok Button's action. This triggers the Ok + * Action, sets the result to confirmed, and then sets the dialog so that it is no longer visible. * @param evt the event triggering this */ protected void okButtonActionPerformed(final ActionEvent evt) { diff --git a/megamek/src/megamek/client/ui/baseComponents/AbstractValidationButtonDialog.java b/megamek/src/megamek/client/ui/baseComponents/AbstractValidationButtonDialog.java new file mode 100644 index 00000000000..88f9e7090d2 --- /dev/null +++ b/megamek/src/megamek/client/ui/baseComponents/AbstractValidationButtonDialog.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021 - 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.client.ui.baseComponents; + +import megamek.MegaMek; +import megamek.client.ui.enums.ValidationState; +import megamek.common.annotations.Nullable; +import megamek.common.util.EncodeControl; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.ResourceBundle; + +/** + * This is the Base Dialog for a dialog with buttons where the Ok button requires data validation + * in MegaMek. It extends Base Button Dialog, and adds a Validate button to the button panel. It + * also includes an enum tracker for the result of the validation. + * + * Inheriting classes must call initialize() in their constructors and override createCenterPane() + * and validateAction(); + * + * The resources associated with this dialog need to contain at least the following keys: + * - "Ok.text" -> text for the Ok button + * - "Ok.toolTipText" -> toolTipText for the Ok button + * - "Validate.text" -> text for the Validate button + * - "Validate.toolTipText" -> toolTipText for the Validate button + * - "Cancel.text" -> text for the Cancel button + * - "Cancel.toolTipText" -> toolTipText for the Cancel button + * + * This is directly tied to MekHQ's AbstractMHQValidationButtonDialog, and any changes here MUST be + * verified there. + */ +public abstract class AbstractValidationButtonDialog extends AbstractButtonDialog { + //region Variable Declarations + private ValidationState state; + private JButton okButton; + //endregion Variable Declarations + + //region Constructors + /** + * This creates a modal AbstractValidationButtonDialog using the default resource bundle. This + * is the normal constructor to use for an AbstractValidationButtonDialog. + */ + protected AbstractValidationButtonDialog(final JFrame frame, final String name, final String title) { + this(frame, true, name, title); + } + + /** + * This creates an AbstractValidationButtonDialog using the default resource bundle. It allows + * one to create non-modal button dialogs. + */ + protected AbstractValidationButtonDialog(final JFrame frame, final boolean modal, + final String name, final String title) { + this(frame, modal, ResourceBundle.getBundle("megamek.client.messages", new EncodeControl()), name, title); + } + + /** + * This creates an AbstractValidationButtonDialog using the specified resource bundle. This is + * not recommended by default. + */ + protected AbstractValidationButtonDialog(final JFrame frame, final boolean modal, + final ResourceBundle resources, final String name, + final String title) { + super(frame, modal, resources, name, title); + setState(ValidationState.PENDING); + } + //endregion Constructors + + //region Getters/Setters + public ValidationState getState() { + return state; + } + + public void setState(final ValidationState state) { + this.state = state; + } + + public JButton getOkButton() { + return okButton; + } + + public void setOkButton(final JButton okButton) { + this.okButton = okButton; + } + //endregion Getters/Setters + + //region Initialization + /** + * @return the created Button Panel + */ + @Override + protected JPanel createButtonPanel() { + JPanel panel = new JPanel(new GridLayout(1, 2)); + setOkButton(new MMButton("okButton", resources.getString("Ok.text"), + resources.getString("Ok.toolTipText"), this::okButtonActionPerformed)); + panel.add(getOkButton()); + panel.add(new MMButton("validateButton", resources.getString("Validate.text"), + resources.getString("Validate.toolTipText"), this::validateButtonActionPerformed)); + panel.add(new MMButton("cancelButton", resources.getString("Cancel.text"), + resources.getString("Cancel.toolTipText"), this::cancelActionPerformed)); + return panel; + } + //endregion Initialization + + //region Button Actions + /** + * This is the default Action Event Listener for the Ok Button's action. It performs data + * validation, then only triggers the AbstractButtonDialog's Ok Button's action if the data + * was successfully validated + * @param evt the event triggering this + */ + @Override + protected void okButtonActionPerformed(final ActionEvent evt) { + validateButtonActionPerformed(evt); + + if (getState().isSuccess()) { + super.okButtonActionPerformed(evt); + } else if (getState().isPending()) { + MegaMek.getLogger().error("Received a Pending validation state after performing validation, returning without closing the dialog."); + } + } + + /** + * This performs data validation on the dialog if the current validation state is pending or + * warning. + * @param evt the event triggering this, or null if you want to put output into a popup, if + * applicable + */ + protected void validateButtonActionPerformed(final @Nullable ActionEvent evt) { + if (getState().isPending() || getState().isWarning()) { + setState(validateAction(evt != null)); + } + } + + /** + * This validates the data contained within this dialog, returning the determined state (with + * SUCCESS being returned if all warnings are skipped). The Ok Button can be accessed through + * a getter, so the tooltip can be changed and it can be enabled/disabled. + * + * @param display put any outputs into a popup, if applicable + * @return the determined validation state of the dialog. + */ + protected abstract ValidationState validateAction(final boolean display); + //endregion Button Actions +} diff --git a/megamek/src/megamek/client/ui/baseComponents/MMButton.java b/megamek/src/megamek/client/ui/baseComponents/MMButton.java new file mode 100644 index 00000000000..92bf463b158 --- /dev/null +++ b/megamek/src/megamek/client/ui/baseComponents/MMButton.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021 - 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.client.ui.baseComponents; + +import megamek.common.annotations.Nullable; + +import javax.swing.*; +import java.awt.event.ActionListener; + +/** + * MMButton is an extension of JButton that overrides the constructors for JButton, albeit with the + * addition of the name of the button as the first value for all constructors, and adds two new + * constructors to create standardized JButtons with all of the required information. + * + * Note: All constructors contain the name of the JButton in question. This MUST be provided and + * means that they don't directly line up to the traditional JButton constructors. + */ +public class MMButton extends JButton { + //region Constructors + /** + * Creates a JButton with the provided name + * @see JButton#JButton() + */ + public MMButton(final String name) { + super(); + setName(name); + } + + /** + * Creates a JButton with the provided name and icon + * @see JButton#JButton(Icon) + */ + public MMButton(final String name, final Icon icon) { + super(icon); + setName(name); + } + + /** + * Creates a JButton with the provided text and name + * @see JButton#JButton(String) + */ + public MMButton(final String name, final String text) { + super(text); + setName(name); + } + + /** + * Creates a JButton with the provided name and action + * @see JButton#JButton(Action) + */ + public MMButton(final String name, final Action action) { + super(action); + setName(name); + } + + /** + * creates a JButton with the provided text, name, and icon + * @see JButton#JButton(String, Icon) + */ + public MMButton(final String name, final String text, final Icon icon) { + super(text, icon); + setName(name); + } + + /** + * This creates a JButton without any toolTipText. + * @param name the name of the button + * @param text the localized text string + * @param actionListener the {@link ActionListener} to assign to the button + */ + public MMButton(final String name, final String text, final ActionListener actionListener) { + this(text, null, name, actionListener); + } + + /** + * This creates a standard JButton for use in MegaMek. + * @param name the name of the button + * @param text the localized text string + * @param toolTipText the localized toolTipText string, or null if there is no tool tip (not recommended) + * @param actionListener the {@link ActionListener} to assign to the button + */ + public MMButton(final String name, final String text, final @Nullable String toolTipText, + final ActionListener actionListener) { + super(text); + setToolTipText(toolTipText); + setName(name); + addActionListener(actionListener); + } + //endregion Constructors +} diff --git a/megamek/src/megamek/client/ui/baseComponents/SpinnerCellEditor.java b/megamek/src/megamek/client/ui/baseComponents/SpinnerCellEditor.java new file mode 100644 index 00000000000..6cc253f305c --- /dev/null +++ b/megamek/src/megamek/client/ui/baseComponents/SpinnerCellEditor.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 - 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.client.ui.baseComponents; + +import javax.swing.*; +import java.awt.*; + +/** + * The SpinnerCellEditor is a Cell Editor for a cell that is edited through the provided spinner + * model. + */ +public class SpinnerCellEditor extends DefaultCellEditor { + //region Variable Declarations + private static final long serialVersionUID = 7956499745127048276L; + private final JSpinner spinner; + //endregion Variable Declarations + + //region Constructors + public SpinnerCellEditor(final AbstractSpinnerModel spinnerModel, final boolean editable) { + super(new JTextField()); + this.spinner = new JSpinner(spinnerModel); + ((JSpinner.DefaultEditor) getSpinner().getEditor()).getTextField().setEditable(editable); + } + //endregion Constructors + + //region Getters + public JSpinner getSpinner() { + return spinner; + } + //endregion Getters + + @Override + public Object getCellEditorValue() { + return getSpinner().getValue(); + } + + @Override + public Component getTableCellEditorComponent(final JTable table, final Object value, + final boolean isSelected, final int row, + final int column) { + getSpinner().setValue(value); + return getSpinner(); + } +} diff --git a/megamek/src/megamek/client/ui/enums/ValidationState.java b/megamek/src/megamek/client/ui/enums/ValidationState.java new file mode 100644 index 00000000000..9a72a019979 --- /dev/null +++ b/megamek/src/megamek/client/ui/enums/ValidationState.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 - 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.client.ui.enums; + +public enum ValidationState { + //region Enum Declarations + SUCCESS, + PENDING, + WARNING, + FAILURE; + //endregion Enum Declarations + + //region Boolean Comparison Methods + public boolean isSuccess() { + return this == SUCCESS; + } + + public boolean isPending() { + return this == PENDING; + } + + public boolean isWarning() { + return this == WARNING; + } + + public boolean isFailure() { + return this == FAILURE; + } + //endregion Boolean Comparison Methods +} From deed4ff7eeac38fb82b9852f23e3033a72657fba Mon Sep 17 00:00:00 2001 From: Justin Bowen <39067288+Windchild292@users.noreply.github.com> Date: Tue, 20 Apr 2021 09:46:57 -0400 Subject: [PATCH 25/27] Update history.txt --- megamek/docs/history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/megamek/docs/history.txt b/megamek/docs/history.txt index c9352c9412d..1ba5f443428 100644 --- a/megamek/docs/history.txt +++ b/megamek/docs/history.txt @@ -37,6 +37,7 @@ VERSION HISTORY: + Issues #1785/#2138: Fixing and Improving Round Report Player Stats + Issues #2787: Protomech AC gun and ammo are different tech levels + PR #2786: Units set for a delayed assault drop no longer disappear on the first turn of the game. ++ Issue #2438: Adding More Detail to Fire Damage Reporting 0.48.0 (Stable) - (2021-03-05 1530 UTC) + PR #2638, #2640: Turret data fixes From 3872fb5e2747839737fb45279874e781227f5f85 Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Tue, 20 Apr 2021 09:47:51 -0400 Subject: [PATCH 26/27] Adding revalidate to AbstractValidationButtonDialog --- .../AbstractValidationButtonDialog.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/megamek/src/megamek/client/ui/baseComponents/AbstractValidationButtonDialog.java b/megamek/src/megamek/client/ui/baseComponents/AbstractValidationButtonDialog.java index 88f9e7090d2..518666d2fdc 100644 --- a/megamek/src/megamek/client/ui/baseComponents/AbstractValidationButtonDialog.java +++ b/megamek/src/megamek/client/ui/baseComponents/AbstractValidationButtonDialog.java @@ -137,10 +137,22 @@ protected void okButtonActionPerformed(final ActionEvent evt) { } } + /** + * This runs revalidation on the dialog, which sets the validation state to pending before calling + * validateButtonActionPerformed to perform the data validation. + * + * @param evt the event triggering this, or null if you want to put the output into a popup, if + * applicable + */ + protected void revalidateAction(final @Nullable ActionEvent evt) { + setState(ValidationState.PENDING); + validateButtonActionPerformed(evt); + } + /** * This performs data validation on the dialog if the current validation state is pending or * warning. - * @param evt the event triggering this, or null if you want to put output into a popup, if + * @param evt the event triggering this, or null if you want to put the output into a popup, if * applicable */ protected void validateButtonActionPerformed(final @Nullable ActionEvent evt) { From 3a9b101a3efecb6a28266534ea1aa4dacd4d14d6 Mon Sep 17 00:00:00 2001 From: Windchild292 Date: Tue, 20 Apr 2021 17:56:50 -0400 Subject: [PATCH 27/27] 2449: Adding MMComboBox to remove the need to cast after getting the selected item --- .../client/ui/baseComponents/MMComboBox.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 megamek/src/megamek/client/ui/baseComponents/MMComboBox.java diff --git a/megamek/src/megamek/client/ui/baseComponents/MMComboBox.java b/megamek/src/megamek/client/ui/baseComponents/MMComboBox.java new file mode 100644 index 00000000000..e94ab1d8d13 --- /dev/null +++ b/megamek/src/megamek/client/ui/baseComponents/MMComboBox.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 - 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.client.ui.baseComponents; + +import megamek.common.annotations.Nullable; + +import javax.swing.*; +import java.util.Vector; + +/** + * MMBuMMComboBox is an extension of JComboBox that overrides the constructors for JComboBox, + * albeit with the addition of the name of the button as the first value for all constructors and + * a nicer override of getSelectedItem so that it returns in the proper class. + * + * Note: All constructors contain the name of the JComboBox in question. This MUST be provided and + * means that they don't directly line up to the traditional JComboBox constructors. + */ +public class MMComboBox extends JComboBox { + //region Constructors + /** + * Creates a JComboBox with the provided name and values + * @see JComboBox#JComboBox(ComboBoxModel) + */ + public MMComboBox(final String name, final ComboBoxModel model) { + super(model); + setName(name); + } + + /** + * Creates a JComboBox with the provided name and values + * @see JComboBox#JComboBox(E[]) + */ + public MMComboBox(final String name, final E[] items) { + super(items); + setName(name); + } + + /** + * Creates a JComboBox with the provided name and values + * @see JComboBox#JComboBox(Vector) + */ + public MMComboBox(final String name, Vector items) { + super(items); + setName(name); + } + + /** + * Creates a JComboBox with the provided name and values + * @see JComboBox#JComboBox() + */ + public MMComboBox(final String name) { + super(); + setName(name); + } + //endregion Constructors + + /** + * @return the selected item, cast to the proper class stored by the combo box + */ + @Override + @SuppressWarnings(value = "unchecked") + public @Nullable E getSelectedItem() { + final Object item = super.getSelectedItem(); + return (item == null) ? null : (E) item; + } +}