diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/BattleCalculatorPanel.java b/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/BattleCalculatorPanel.java index c4c61f1a3ba..ff3c238602c 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/BattleCalculatorPanel.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/BattleCalculatorPanel.java @@ -417,17 +417,12 @@ class BattleCalculatorPanel extends JPanel { Matches.unitIsOwnedBy(getDefender()) .and( Matches.unitCanBeInBattle( - true, - isLandBattle(), - 1, - hasMaxRounds(isLandBattle(), data), - true, - List.of()))); + true, isLand(), 1, hasMaxRounds(isLand(), data), true, List.of()))); final GamePlayer newDefender = getAttacker(); final List newDefenders = CollectionUtils.getMatches( attackingUnitsPanel.getUnits(), - Matches.unitCanBeInBattle(true, isLandBattle(), 1, true)); + Matches.unitCanBeInBattle(true, isLand(), 1, true)); setAttacker(newAttacker); setDefender(newDefender); setAttackingUnits(newAttackers); @@ -440,8 +435,8 @@ class BattleCalculatorPanel extends JPanel { new OrderOfLossesInputPanel( attackerOrderOfLosses, defenderOrderOfLosses, - attackingUnitsPanel.getUnitCategories(), - defendingUnitsPanel.getUnitCategories(), + attackingUnitsPanel.getCategories(), + defendingUnitsPanel.getCategories(), landBattleCheckBox.isSelected(), uiContext, data); @@ -564,12 +559,12 @@ private void updateStats() { try { final Territory location = findPotentialBattleSite(); if (location == null) { - throw new IllegalStateException("No territory found that is land:" + isLandBattle()); + throw new IllegalStateException("No territory found that is land:" + isLand()); } final List defending = defendingUnitsPanel.getUnits(); final List attacking = attackingUnitsPanel.getUnits(); List bombarding = new ArrayList<>(); - if (isLandBattle()) { + if (isLand()) { bombarding = CollectionUtils.getMatches(attacking, Matches.unitCanBombard(getAttacker())); attacking.removeAll(bombarding); @@ -627,7 +622,7 @@ private void updateStats() { attackerWin.setText(formatPercentage(results.getAttackerWinPercent())); defenderWin.setText(formatPercentage(results.getDefenderWinPercent())); draw.setText(formatPercentage(results.getDrawPercent())); - final boolean isLand = isLandBattle(); + final boolean isLand = isLand(); final List mainCombatAttackers = CollectionUtils.getMatches( attackers.get(), Matches.unitCanBeInBattle(true, isLand, 1, true)); @@ -663,9 +658,9 @@ private void updateStats() { private Territory findPotentialBattleSite() { Territory location = null; - if (this.location == null || this.location.isWater() == isLandBattle()) { + if (this.location == null || this.location.isWater() == isLand()) { for (final Territory t : data.getMap()) { - if (t.isWater() == !isLandBattle()) { + if (t.isWater() == !isLand()) { location = t; break; } @@ -698,9 +693,8 @@ private void setAttackingUnits(final List initialUnits) { CollectionUtils.getMatches( units, Matches.unitCanBeInBattle( - true, isLandBattle(), 1, hasMaxRounds(isLandBattle(), data), false, List.of())), - isLandBattle(), - location); + true, isLand(), 1, hasMaxRounds(isLand(), data), false, List.of())), + isLand()); } void addDefendingUnits(final List unitsToAdd) { @@ -714,10 +708,8 @@ private void setDefendingUnits(final List initialUnits) { final List units = Optional.ofNullable(initialUnits).orElseGet(List::of); defendingUnitsPanel.init( getDefender(), - CollectionUtils.getMatches( - units, Matches.unitCanBeInBattle(false, isLandBattle(), 1, false)), - isLandBattle(), - location); + CollectionUtils.getMatches(units, Matches.unitCanBeInBattle(false, isLand(), 1, false)), + isLand()); } public boolean hasAttackingUnitsAdded() { @@ -728,7 +720,7 @@ public boolean hasDefendingUnitsAdded() { return !defendingUnitsPanel.isEmpty(); } - private boolean isLandBattle() { + private boolean isLand() { return landBattleCheckBox.isSelected(); } @@ -750,7 +742,7 @@ private void setResultsToBlank() { private void setWidgetActivation() { keepOneAttackingLandUnitCheckBox.setEnabled(landBattleCheckBox.isSelected()); amphibiousCheckBox.setEnabled(landBattleCheckBox.isSelected()); - final boolean isLand = isLandBattle(); + final boolean isLand = isLand(); try (GameData.Unlocker ignored = data.acquireReadLock()) { final List attackers = CollectionUtils.getMatches( diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/OrderOfLossesInputPanel.java b/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/OrderOfLossesInputPanel.java index d0e445ac786..b24c0e1c55a 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/OrderOfLossesInputPanel.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/OrderOfLossesInputPanel.java @@ -12,7 +12,6 @@ import games.strategy.triplea.image.UnitImageFactory.ImageKey; import games.strategy.triplea.ui.UiContext; import games.strategy.triplea.util.UnitCategory; -import games.strategy.triplea.util.UnitSeparator; import java.awt.Color; import java.awt.Component; import java.util.ArrayList; @@ -253,13 +252,12 @@ private void layoutComponents() { } private JPanel getUnitButtonPanel( - final List unitCategories, final JTextField textField) { + final List categories, final JTextField textField) { final JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); - if (unitCategories != null) { - UnitSeparator.sortUnitCategories(unitCategories, data); + if (categories != null) { final Set typesUsed = new HashSet<>(); - for (final UnitCategory category : unitCategories) { + for (final UnitCategory category : categories) { // no duplicates or infrastructure allowed. no sea if land, no land if sea. if (typesUsed.contains(category.getType()) || Matches.unitTypeIsInfrastructure().test(category.getType()) diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/PlayerUnitsPanel.java b/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/PlayerUnitsPanel.java index 775a2302e11..42822b1cede 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/PlayerUnitsPanel.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/odds/calculator/PlayerUnitsPanel.java @@ -8,6 +8,7 @@ import games.strategy.engine.data.Territory; import games.strategy.engine.data.Unit; import games.strategy.engine.data.UnitType; +import games.strategy.triplea.attachments.UnitAttachment; import games.strategy.triplea.delegate.Matches; import games.strategy.triplea.ui.UiContext; import games.strategy.triplea.util.TuvCostsCalculator; @@ -33,8 +34,8 @@ public class PlayerUnitsPanel extends JPanel { private final GameData data; private final UiContext uiContext; private final boolean defender; - private boolean isLandBattle = true; - @Getter private List unitCategories = null; + private boolean isLand = true; + @Getter private List categories = null; private final List listeners = new ArrayList<>(); private final List unitPanels = new ArrayList<>(); @@ -58,17 +59,54 @@ public List getUnits() { } /** Sets up components to an initial state. */ - public void init( - final GamePlayer gamePlayer, - final List units, - final boolean isLandBattle, - final Territory territory) { - this.isLandBattle = isLandBattle; + public void init(final GamePlayer gamePlayer, final List units, final boolean land) { + isLand = land; unitPanels.clear(); + categories = new ArrayList<>(categorize(gamePlayer, units)); + + categories.sort( + (c1, c2) -> { + if (!c1.isOwnedBy(c2.getOwner())) { + if (c1.isOwnedBy(gamePlayer)) { + return -1; + } else if (c2.isOwnedBy(gamePlayer)) { + return 1; + } else { + return c1.getOwner().getName().compareTo(c2.getOwner().getName()); + } + } + final UnitType ut1 = c1.getType(); + final UnitType ut2 = c2.getType(); + final UnitAttachment u1 = ut1.getUnitAttachment(); + final UnitAttachment u2 = ut2.getUnitAttachment(); + // For land battles, sort by land, air, can't combat move (AA), bombarding + if (land) { + if (u1.getIsSea() != u2.getIsSea()) { + return u1.getIsSea() ? 1 : -1; + } + final boolean u1CanNotCombatMove = + Matches.unitTypeCanNotMoveDuringCombatMove().test(ut1) + || !Matches.unitTypeCanMove(gamePlayer).test(ut1); + final boolean u2CanNotCombatMove = + Matches.unitTypeCanNotMoveDuringCombatMove().test(ut2) + || !Matches.unitTypeCanMove(gamePlayer).test(ut2); + if (u1CanNotCombatMove != u2CanNotCombatMove) { + return u1CanNotCombatMove ? 1 : -1; + } + if (u1.getIsAir() != u2.getIsAir()) { + return u1.getIsAir() ? 1 : -1; + } + } else { + if (u1.getIsSea() != u2.getIsSea()) { + return u1.getIsSea() ? -1 : 1; + } + } + return u1.getName().compareTo(u2.getName()); + }); removeAll(); final Predicate predicate; - if (isLandBattle) { + if (land) { if (defender) { predicate = Matches.unitTypeIsNotSea(); } else { @@ -82,11 +120,9 @@ public void init( costs = new TuvCostsCalculator().getCostsForTuv(gamePlayer); } - unitCategories = getAllUnitCategories(gamePlayer, units); - UnitSeparator.sortUnitCategories(unitCategories, territory, gamePlayer); GamePlayer previousPlayer = null; JPanel panel = null; - for (final UnitCategory category : unitCategories) { + for (final UnitCategory category : categories) { if (predicate.test(category.getType())) { if (!category.isOwnedBy(previousPlayer)) { panel = new JPanel(); @@ -95,12 +131,10 @@ public void init( add(panel); previousPlayer = category.getOwner(); } - if (panel != null) { - final var unitPanel = new UnitPanel(uiContext, category, costs); - unitPanel.addChangeListener(this::notifyListeners); - panel.add(unitPanel); - unitPanels.add(unitPanel); - } + final var unitPanel = new UnitPanel(uiContext, category, costs); + unitPanel.addChangeListener(this::notifyListeners); + panel.add(unitPanel); + unitPanels.add(unitPanel); } } @@ -116,11 +150,10 @@ public void init( * production frontier and then any unit types the player owns on the map. Then populate the list * of units into the categories. */ - private List getAllUnitCategories( - final GamePlayer gamePlayer, final List units) { + private Set categorize(final GamePlayer gamePlayer, final List units) { // Get player unit types from production frontier and unit types on the map - final List categories = new ArrayList<>(); + final Set categories = new LinkedHashSet<>(); for (final UnitType t : getUnitTypes(gamePlayer)) { final UnitCategory category = new UnitCategory(t, gamePlayer); categories.add(category); @@ -139,15 +172,15 @@ private List getAllUnitCategories( } // Populate units into each category then add any remaining categories (damaged units, etc) - final Set unitCategoriesWithUnits = UnitSeparator.categorize(units); + final Set unitCategories = UnitSeparator.categorize(units); for (final UnitCategory category : categories) { - for (final UnitCategory unitCategoryWithUnits : unitCategoriesWithUnits) { - if (category.equals(unitCategoryWithUnits)) { - category.getUnits().addAll(unitCategoryWithUnits.getUnits()); + for (final UnitCategory unitCategory : unitCategories) { + if (category.equals(unitCategory)) { + category.getUnits().addAll(unitCategory.getUnits()); } } } - categories.addAll(unitCategoriesWithUnits); + categories.addAll(unitCategories); return categories; } @@ -183,10 +216,10 @@ private Collection getUnitTypes(final GamePlayer player) { unitTypes, Matches.unitTypeCanBeInBattle( !defender, - isLandBattle, + isLand, player, 1, - BattleCalculatorPanel.hasMaxRounds(isLandBattle, data), + BattleCalculatorPanel.hasMaxRounds(isLand, data), false, List.of())); diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleDisplay.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleDisplay.java index d949f184509..c375993c770 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleDisplay.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleDisplay.java @@ -47,8 +47,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; import javax.annotation.Nullable; import javax.swing.AbstractAction; import javax.swing.Action; @@ -232,10 +230,10 @@ private Collection updateKilledUnits( for (final Collection dependentCollection : dependentsMap.values()) { dependentUnitsReturned.addAll(dependentCollection); } - - List unitCategories = - UnitSeparator.getSortedUnitCategories(killedUnits, gameData, uiContext.getMapData()); - for (final UnitCategory category : unitCategories) { + for (final UnitCategory category : + UnitSeparator.categorize( + killedUnits, + UnitSeparator.SeparatorCategories.builder().dependents(dependentsMap).build())) { final JPanel panel = new JPanel(); JLabel unit = uiContext.newUnitImageLabel(category.getType(), category.getOwner()); panel.add(unit); @@ -777,14 +775,7 @@ void setNotificationShort( private void categorizeUnits( final Iterable categoryIter, final boolean damaged, final boolean disabled) { - final List unitCategories = - StreamSupport.stream(categoryIter.spliterator(), false).collect(Collectors.toList()); - if (unitCategories.isEmpty()) { - return; - } - final GameData gameData = unitCategories.get(0).getUnitAttachment().getData(); - UnitSeparator.sortUnitCategories(unitCategories, gameData); - for (final UnitCategory category : unitCategories) { + for (final UnitCategory category : categoryIter) { final JPanel panel = new JPanel(); final ImageIcon unitImage = uiContext diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleModel.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleModel.java index a16a286d4f5..f76f8495aed 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleModel.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleModel.java @@ -128,8 +128,7 @@ void refresh() { } unitPowerAndRollsMap = PowerStrengthAndRolls.build(units, combatValue); } - final List unitCategories = - UnitSeparator.getSortedUnitCategories(units, gameData, uiContext.getMapData()); + final Collection unitCategories = UnitSeparator.categorize(units); for (final UnitCategory category : unitCategories) { final int[] shift = new int[gameData.getDiceSides() + 1]; for (final Unit current : category.getUnits()) { diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/SimpleUnitPanel.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/SimpleUnitPanel.java index 6b04cd1f847..7114deedace 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/SimpleUnitPanel.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/SimpleUnitPanel.java @@ -1,7 +1,5 @@ package games.strategy.triplea.ui; -import com.google.common.collect.Lists; -import games.strategy.engine.data.GameData; import games.strategy.engine.data.GamePlayer; import games.strategy.engine.data.GameState; import games.strategy.engine.data.NamedAttachable; @@ -15,9 +13,7 @@ import games.strategy.triplea.delegate.Matches; import games.strategy.triplea.image.UnitImageFactory; import games.strategy.triplea.util.UnitCategory; -import games.strategy.triplea.util.UnitSeparator; import java.awt.Image; -import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.Set; @@ -124,13 +120,7 @@ public void setUnitsFromRepairRuleMap( */ public void setUnitsFromCategories(final Collection categories) { removeAll(); - if (categories.isEmpty()) { - return; - } - final GameData gameData = categories.iterator().next().getUnitAttachment().getData(); - final ArrayList unitCategories = Lists.newArrayList(categories); - UnitSeparator.sortUnitCategories(unitCategories, gameData); - for (final UnitCategory category : unitCategories) { + for (final UnitCategory category : categories) { addUnits( category.getOwner(), category.getUnits().size(), diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/UnitChooser.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/UnitChooser.java index 67884404369..a9576e99010 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/UnitChooser.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/UnitChooser.java @@ -1,7 +1,6 @@ package games.strategy.triplea.ui; import com.google.common.annotations.VisibleForTesting; -import games.strategy.engine.data.GameData; import games.strategy.engine.data.Unit; import games.strategy.triplea.Properties; import games.strategy.triplea.ResourceLoader; @@ -24,8 +23,6 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -39,10 +36,8 @@ import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; -import lombok.Getter; import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; -import lombok.val; import org.triplea.java.Postconditions; import org.triplea.java.collections.IntegerMap; import org.triplea.swing.jpanel.GridBagConstraintsAnchor; @@ -302,14 +297,7 @@ private void layoutEntries() { 0)); autoSelectButton.addActionListener(e -> autoSelect()); int rowIndex = 1; - if (entries.isEmpty()) { - return; - } - final GameData gameData = entries.get(0).getCategory().getUnitAttachment().getData(); - entries.sort( - Comparator.comparing( - ChooserEntry::getCategory, UnitSeparator.getComparatorUnitCategories(gameData))); - for (val entry : Collections.unmodifiableList(entries)) { + for (final ChooserEntry entry : entries) { entry.createComponents(this, rowIndex); rowIndex++; } @@ -442,7 +430,7 @@ void disableMax() { */ @VisibleForTesting public final class ChooserEntry { - @Getter @VisibleForTesting public final UnitCategory category; + @VisibleForTesting public final UnitCategory category; private final List defaultHits; private final List hitTexts; private final List hitLabel = new ArrayList<>(); @@ -520,6 +508,10 @@ void set(final int value) { hitTexts.get(0).setValue(value); } + UnitCategory getCategory() { + return category; + } + void selectAll() { hitTexts.get(0).setValue(hitTexts.get(0).getMax()); } diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/panels/map/MapPanel.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/panels/map/MapPanel.java index 03d3be12f4d..f967155b7bf 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/panels/map/MapPanel.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/panels/map/MapPanel.java @@ -742,9 +742,7 @@ public void paint(final Graphics g) { } final var unitImageFactory = uiContext.getUnitImageFactory(); for (final Collection value : highlightedUnits) { - List unitCategories = - UnitSeparator.getSortedUnitCategories(value, gameData, mapData); - for (final UnitCategory category : unitCategories) { + for (final UnitCategory category : UnitSeparator.categorize(value)) { final @Nullable Rectangle r = tileManager.getUnitRect(category.getUnits(), gameData); if (r == null) { continue; @@ -898,8 +896,7 @@ public void setMouseShadowUnits(final @Nullable Collection units) { } } - final List categories = - UnitSeparator.getSortedUnitCategories(units, gameData, uiContext.getMapData()); + final Set categories = UnitSeparator.categorize(units); final int iconWidth = uiContext.getUnitImageFactory().getUnitImageWidth(); final int iconHeight = uiContext.getUnitImageFactory().getUnitImageHeight(); final int horizontalSpace = 5; diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/unit/scroller/AvatarPanelFactory.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/unit/scroller/AvatarPanelFactory.java index d7d3d124427..6448d0a52d4 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/unit/scroller/AvatarPanelFactory.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/unit/scroller/AvatarPanelFactory.java @@ -2,7 +2,6 @@ import com.google.common.base.Preconditions; import games.strategy.engine.data.GamePlayer; -import games.strategy.engine.data.Territory; import games.strategy.engine.data.Unit; import games.strategy.engine.data.UnitType; import games.strategy.triplea.image.MapImage; @@ -10,12 +9,13 @@ import games.strategy.triplea.image.UnitImageFactory.ImageKey; import games.strategy.triplea.ui.panels.map.MapPanel; import games.strategy.triplea.ui.screen.UnitsDrawer; -import games.strategy.triplea.util.UnitSeparator; +import games.strategy.triplea.util.UnitCategory; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.image.BufferedImage; import java.util.Collection; +import java.util.Comparator; import java.util.List; import javax.swing.Icon; import javax.swing.ImageIcon; @@ -74,19 +74,14 @@ class AvatarPanelFactory { * @param panelWidth How much horizontal space we have for drawing. * @return A panel containing a drawing of the unique images for each unit type. */ - JPanel buildPanel( - final List units, - final GamePlayer currentPlayer, - final Territory territory, - final int panelWidth) { + JPanel buildPanel(final List units, final GamePlayer currentPlayer, final int panelWidth) { final int renderingWidth = Math.min(panelWidth, MAX_RENDERING_WIDTH); final Icon unitIcon = units.isEmpty() ? new ImageIcon(createEmptyUnitStackImage(renderingWidth)) : new ImageIcon( - createUnitStackImage( - unitImageFactory, currentPlayer, territory, units, renderingWidth)); + createUnitStackImage(unitImageFactory, currentPlayer, units, renderingWidth)); return new JPanelBuilder() // .borderLayout() @@ -104,13 +99,13 @@ private Image createEmptyUnitStackImage(final int renderingWidth) { private static Image createUnitStackImage( final UnitImageFactory unitImageFactory, final GamePlayer player, - final Territory territory, final List units, final int renderingWidth) { Preconditions.checkArgument(!units.isEmpty()); - final var unitsToDraw = UnitSeparator.getSortedUnitCategories(units, territory, player); + final var unitsToDraw = UnitScrollerModel.getUniqueUnitCategories(player, units); + final var dimension = unitImageFactory.getImageDimensions( ImageKey.builder().type(unitsToDraw.get(0).getType()).player(player).build()); @@ -139,6 +134,7 @@ private static Image createUnitStackImage( "Draw location count (%s) should have matched units draw size (%s)", drawLocations.size(), unitsToDraw.size())); + unitsToDraw.sort(unitRenderingOrder(player)); for (int i = 0; i < drawLocations.size(); i++) { final var unitToDraw = unitsToDraw.get(i); final var imageToDraw = unitImageFactory.getImage(ImageKey.of(unitToDraw)); @@ -163,6 +159,24 @@ private static Image createUnitStackImage( return combinedImage; } + private static Comparator unitRenderingOrder(final GamePlayer currentPlayer) { + final Comparator isAir = + Comparator.comparing(unitCategory -> unitCategory.getUnitAttachment().getIsAir()); + final Comparator isSea = + Comparator.comparing(unitCategory -> unitCategory.getUnitAttachment().getIsSea()); + final Comparator unitAttackPower = + Comparator.comparingInt( + unitCategory -> unitCategory.getUnitAttachment().getAttack(currentPlayer)); + final Comparator unitName = + Comparator.comparing(unitCategory -> unitCategory.getType().getName()); + + return isAir // + .thenComparing(isSea) + .thenComparing(unitAttackPower) + .thenComparing(unitName) + .reversed(); + } + private static int countUnit(final UnitType unitType, final Collection units) { return units.stream() // .map(Unit::getType) diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/unit/scroller/UnitScroller.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/unit/scroller/UnitScroller.java index bed6caec4c3..b7818561bc2 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/unit/scroller/UnitScroller.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/unit/scroller/UnitScroller.java @@ -220,7 +220,7 @@ private void drawUnitAvatarPane(final Territory t) { selectUnitImagePanel.removeAll(); if (player != null) { selectUnitImagePanel.add( - avatarPanelFactory.buildPanel(moveableUnits, player, t, renderingWidth)); + avatarPanelFactory.buildPanel(moveableUnits, player, renderingWidth)); } SwingComponents.redraw(selectUnitImagePanel); }); diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/util/UnitSeparator.java b/game-app/game-core/src/main/java/games/strategy/triplea/util/UnitSeparator.java index 13f6ad371e3..08ec45696f8 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/util/UnitSeparator.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/util/UnitSeparator.java @@ -1,7 +1,7 @@ package games.strategy.triplea.util; -import games.strategy.engine.data.GameData; import games.strategy.engine.data.GamePlayer; +import games.strategy.engine.data.GameState; import games.strategy.engine.data.Territory; import games.strategy.engine.data.Unit; import games.strategy.engine.data.UnitType; @@ -14,7 +14,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.TreeSet; import javax.annotation.Nullable; @@ -49,152 +48,36 @@ public static class SeparatorCategories { } /** - * Finds unit categories from units of the Territory, removes not displayed ones - * according to MapData and then sorts them + * Finds unit categories, removes not displayed, and then sorts them into logical order to display + * based on: 1. Unit owner: territory owner, not at war with territory owner, player order in XML + * 2. Unit type: 0 movement, can't combat move, sea, air if sea territory, land, air if land + * territory 3. Within each of those groups sort the units by XML order in UnitList */ public static List getSortedUnitCategories( final Territory t, final MapData mapData) { + final GameState data = t.getData(); final List categories = new ArrayList<>(UnitSeparator.categorize(t.getUnits())); categories.removeIf(uc -> !mapData.shouldDrawUnit(uc.getType().getName())); - categories.sort(getComparatorUnitCategories(t)); + final List xmlUnitTypes = new ArrayList<>(data.getUnitTypeList().getAllUnitTypes()); + categories.sort( + Comparator.comparing( + UnitCategory::getOwner, + Comparator.comparing((final GamePlayer p) -> !p.equals(t.getOwner())) + .thenComparing(p -> Matches.isAtWar(p).test(t.getOwner())) + .thenComparing(data.getPlayerList().getPlayers()::indexOf)) + .thenComparing(uc -> Matches.unitTypeCanMove(uc.getOwner()).test(uc.getType())) + .thenComparing( + UnitCategory::getType, + Comparator.comparing( + (final UnitType ut) -> + !Matches.unitTypeCanNotMoveDuringCombatMove().test(ut)) + .thenComparing(ut -> !Matches.unitTypeIsSea().test(ut)) + .thenComparing(ut -> !(t.isWater() && Matches.unitTypeIsAir().test(ut))) + .thenComparing(ut -> !Matches.unitTypeIsLand().test(ut)) + .thenComparing(xmlUnitTypes::indexOf))); return categories; } - /** - * Finds unit categories from units of the Territory, removes not - * displayed ones according to MapData and then sorts them using also the - * GamePlayer - */ - public static List getSortedUnitCategories( - final Collection units, - final Territory t, - final MapData mapData, - final GamePlayer gamePlayer) { - final List categories = new ArrayList<>(UnitSeparator.categorize(units)); - categories.removeIf(uc -> !mapData.shouldDrawUnit(uc.getType().getName())); - categories.sort(getComparatorUnitCategories(t, gamePlayer)); - return categories; - } - - /** - * Finds unit categories from units of the Territory and then sorts them - * using also the GamePlayer - */ - public static List getSortedUnitCategories( - final Collection units, final Territory t, final GamePlayer gamePlayer) { - final List categories = new ArrayList<>(UnitSeparator.categorize(units)); - categories.sort(getComparatorUnitCategories(t, gamePlayer)); - return categories; - } - - /** - * Finds unit categories from units, removes not displayed ones according to - * MapData and then sorts them - */ - public static List getSortedUnitCategories( - final Collection units, final GameData gameData, final MapData mapData) { - final List categories = new ArrayList<>(UnitSeparator.categorize(units)); - categories.removeIf(uc -> !mapData.shouldDrawUnit(uc.getType().getName())); - categories.sort(getComparatorUnitCategories(gameData)); - return categories; - } - - /** Sorts a list of unit categories */ - public static void sortUnitCategories( - final List unitCategories, final GameData gameData) { - unitCategories.sort(getComparatorUnitCategories(gameData)); - } - - /** - * Sorts a list of UnitCategory with Territory and GamePlayer - * - */ - public static void sortUnitCategories( - final List unitCategories, final Territory t, final GamePlayer currentPlayer) { - unitCategories.sort(getComparatorUnitCategories(t, currentPlayer)); - } - - /** - * Returns Comparator for unit categories with current GameData Try to - * use a method returning List of UnitCategory> instead - */ - public static Comparator getComparatorUnitCategories(final GameData gameData) { - return getComparatorUnitCategories( - Optional.empty(), gameData, gameData.getHistory().getCurrentPlayer()); - } - - /** Returns Comparator for unit categories of a Territory */ - private static Comparator getComparatorUnitCategories( - final Territory t, final GamePlayer currentPlayer) { - final GameData gameData = t.getData(); - return getComparatorUnitCategories(Optional.of(t), gameData, currentPlayer); - } - - /** Returns Comparator for unit categories of a Territory */ - private static Comparator getComparatorUnitCategories(final Territory t) { - final GameData gameData = t.getData(); - return getComparatorUnitCategories( - Optional.of(t), gameData, gameData.getHistory().getCurrentPlayer()); - } - - /** Returns Comparator for unit categories of a Territory */ - private static Comparator getComparatorUnitCategories( - final Optional optionalTerritory, - final GameData gameData, - final GamePlayer currentPlayer) { - final List xmlUnitTypes = - new ArrayList<>(gameData.getUnitTypeList().getAllUnitTypes()); - final List players = gameData.getPlayerList().getPlayers(); - return getComparatorUnitCategories(optionalTerritory, currentPlayer, players, xmlUnitTypes); - } - - /** - * Returns Comparator for unit categories to allow sorting into logical order to - * display based on: 1. Unit owner: territory owner, not at war with territory owner, player order - * in XML 2. Unit type: 0 movement, can't combat move, sea, air if sea territory, land, air if - * land territory 3. Within each of those groups sort the units by XML order in UnitList - */ - private static Comparator getComparatorUnitCategories( - final Optional optionalTerritory, - final GamePlayer currentPlayer, - final List players, - final List xmlUnitTypes) { - return Comparator.comparing( - UnitCategory::getOwner, // 1. Unit owner - Comparator.comparing( - (final GamePlayer p) -> - !(optionalTerritory.isPresent() - && p.equals(optionalTerritory.get().getOwner()))) - .thenComparing( - p -> - (optionalTerritory.isPresent() - && Matches.isAtWar(p).test(optionalTerritory.get().getOwner()))) - .thenComparing(players::indexOf)) - .thenComparing( - uc -> Matches.unitTypeCanMove(uc.getOwner()).test(uc.getType())) // 2. Unit type - .thenComparing( - UnitCategory::getType, - Comparator.comparing( - (final UnitType ut) -> !Matches.unitTypeCanNotMoveDuringCombatMove().test(ut)) - .thenComparing(ut -> !Matches.unitTypeIsSea().test(ut)) - .thenComparing( - ut -> - !(optionalTerritory.isPresent() - && optionalTerritory.get().isWater() - && Matches.unitTypeIsAir().test(ut))) - .thenComparing(ut -> !Matches.unitTypeIsLand().test(ut))) - .thenComparingInt(ut -> ut.getUnitAttachment().getMaxBuiltPerPlayer()) - .thenComparing( - uc -> - uc.getUnitAttachment() - .getAttack( - (currentPlayer == null - ? uc.getOwner() - : currentPlayer))) // should be currentPlayer - .thenComparing( - UnitCategory::getType, Comparator.comparing(xmlUnitTypes::indexOf)); // 3. Final sorting - } - public static Set categorize(final Collection units) { return categorize(units, SeparatorCategories.builder().build()); } diff --git a/game-app/game-headed/src/main/java/games/strategy/triplea/ui/AbstractUndoableMovesPanel.java b/game-app/game-headed/src/main/java/games/strategy/triplea/ui/AbstractUndoableMovesPanel.java index d4683540af7..97005073419 100644 --- a/game-app/game-headed/src/main/java/games/strategy/triplea/ui/AbstractUndoableMovesPanel.java +++ b/game-app/game-headed/src/main/java/games/strategy/triplea/ui/AbstractUndoableMovesPanel.java @@ -118,10 +118,8 @@ public void paint(final Graphics g) { private JComponent newComponentForMove(final AbstractUndoableMove move) { final Box unitsBox = new Box(BoxLayout.X_AXIS); unitsBox.add(new JLabel((move.getIndex() + 1) + ") ")); + final Collection unitCategories = UnitSeparator.categorize(move.getUnits()); final Dimension buttonSize = new Dimension(80, 22); - final List unitCategories = - UnitSeparator.getSortedUnitCategories( - move.getUnits(), movePanel.getData(), movePanel.getMap().getUiContext().getMapData()); for (final UnitCategory category : unitCategories) { final ImageIcon icon = movePanel.getMap().getUiContext().getUnitImageFactory().getIcon(ImageKey.of(category));