Skip to content

Commit

Permalink
Merge branch 'master' into feat/refactor-flags
Browse files Browse the repository at this point in the history
  • Loading branch information
Scoppio authored Jan 31, 2025
2 parents 7a46414 + 10ac487 commit d90bee0
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 96 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,4 @@ megamek/userdata/**
/megamek/MegaMek.l4j.ini
OfficialUnitList.txt
equipment.txt
.github/copilot-instructions.md
5 changes: 5 additions & 0 deletions megamek/docs/history.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ MEGAMEK VERSION HISTORY:
+ Fix #6464: defaultminimap renamed to default, fixed error when persisting selected theme
+ PR #6462: Convert from StringBuilder to Logger
+ PR #6468: Fix NPE clicking on board with no unit selected
+ PR #6469: Fix: removes a double call for next-unit
+ PR #6470: Fix: minimap summary npe fix
+ PR #6471: Feat: Mini-Map Improvements (Hex Border options, facing arrows, sensor range)
+ PR #6474: makes Princess move way more than she otherwise would on no-contact


0.50.02 (2024-12-30 2130 UTC)
+ PR #6183: New GM Commands, princess commands on map menu, graphics for some explosions
Expand Down
20 changes: 9 additions & 11 deletions megamek/src/megamek/client/bot/princess/BasicPathRanker.java
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ protected RankedPath rankPath(MovePath path, Game game, int maxRange, double fal


// Movement is good, it gives defense and extends a player power in the game.
var movementMod = calculateMovementMod(pathCopy, game);
var movementMod = calculateMovementMod(pathCopy, game, enemies);

// Try to face the enemy.
double facingMod = calculateFacingMod(movingUnit, game, pathCopy);
Expand Down Expand Up @@ -654,19 +654,17 @@ private double getBraveryMod(double successProbability, FiringPhysicalDamage dam
return braveryMod;
}

private double calculateMovementMod(MovePath pathCopy, Game game) {
var hexMoved = (double) pathCopy.getHexesMoved();
// Only forces unit to move if there are no units around
private double calculateMovementMod(MovePath pathCopy, Game game, List<Entity> enemies) {
if (!enemies.isEmpty() || !getOwner().getEnemyHotSpots().isEmpty()) {
return 0.0;
}
var distanceMoved = pathCopy.getDistanceTravelled();
var tmm = Compute.getTargetMovementModifier(distanceMoved, pathCopy.isJumping(), pathCopy.isAirborne(), game);
double selfPreservation = getOwner().getBehaviorSettings().getSelfPreservationValue();
var tmmValue = tmm.getValue();
if (tmmValue == 0 || ((hexMoved + distanceMoved) == 0)) {
logger.trace("movement mod [0]");
return 0;
}
var movementFactor = tmmValue * (hexMoved * distanceMoved) / (hexMoved + distanceMoved);

logger.trace("movement mod [{} = {} * ({} * {}) / ({} + {})]", movementFactor, tmmValue, hexMoved, distanceMoved,
hexMoved, distanceMoved);
var movementFactor = tmmValue * selfPreservation;
logger.trace("movement mod [{} = {} * {})]", movementFactor, tmmValue, selfPreservation);
return movementFactor;
}

Expand Down
4 changes: 1 addition & 3 deletions megamek/src/megamek/client/ui/swing/MovementDisplay.java
Original file line number Diff line number Diff line change
Expand Up @@ -4686,9 +4686,7 @@ public synchronized void actionPerformed(ActionEvent ev) {
return;
}
final IGameOptions opts = clientgui.getClient().getGame().getOptions();
if (actionCmd.equals(MoveCommand.MOVE_NEXT.getCmd())) {
selectEntity(clientgui.getClient().getNextEntityNum(currentEntity));
} else if (actionCmd.equals(
if (actionCmd.equals(
MoveCommand.MOVE_FORWARD_INI.getCmd())) {
selectNextPlayer();
} else if (actionCmd.equals(MoveCommand.MOVE_CANCEL.getCmd())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1900,7 +1900,7 @@ private void drawHex(Coords c, Graphics boardGraph, boolean saveBoardImage) {
largestLevelDiff = levelDiff;
}
}
imgHeight += HEX_ELEV * scale * largestLevelDiff;
imgHeight += (int) (HEX_ELEV * scale * largestLevelDiff);
}
// If the base image isn't ready, we should signal a repaint and stop
if ((imgWidth < 0) || (imgHeight < 0)) {
Expand Down
155 changes: 88 additions & 67 deletions megamek/src/megamek/client/ui/swing/minimap/Minimap.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@

import megamek.MMConstants;
import megamek.client.Client;
import megamek.client.IClient;
import megamek.client.event.BoardViewEvent;
import megamek.client.event.BoardViewListener;
import megamek.client.event.BoardViewListenerAdapter;
Expand Down Expand Up @@ -237,12 +238,20 @@ public static BufferedImage getMinimapImage(Board board, int zoom, @Nullable Fil
* game and boardview object will be used to display additional information.
*/
public static BufferedImage getMinimapImage(Game game, BoardView bv, int zoom, @Nullable File minimapTheme) {
return getMinimapImage(game, bv, zoom, null, minimapTheme);
}

/**
* Returns a minimap image of the given board at the given zoom index. The
* game and boardview object will be used to display additional information.
*/
public static BufferedImage getMinimapImage(Game game, BoardView bv, int zoom, IClientGUI clientGui, @Nullable File minimapTheme) {
try {
// Send the fail image when the zoom index is wrong to make this noticeable
if ((zoom < MIM_ZOOM) || (zoom > MAX_ZOOM)) {
throw new Exception("The given zoom index is out of bounds.");
}
Minimap tempMM = new Minimap(null, game, bv, null, minimapTheme);
Minimap tempMM = new Minimap(null, game, bv, clientGui, minimapTheme);
tempMM.zoom = zoom;
tempMM.initializeMap();
tempMM.drawMap(true);
Expand All @@ -269,6 +278,7 @@ private Minimap(@Nullable JDialog dlg, Game g, @Nullable BoardView bview, @Nulla
bv = bview;
dialog = dlg;
clientGui = cg;

if (clientGui != null && clientGui.getClient() instanceof Client castClient) {
client = castClient;
}
Expand All @@ -286,7 +296,59 @@ private Minimap(@Nullable JDialog dlg, Game g, @Nullable BoardView bview, @Nulla
* are not null).
*/
private void initializeListeners() {
game.addGameListener(gameListener);
game.addGameListener(new GameListenerAdapter() {
@Override
public void gamePhaseChange(GamePhaseChangeEvent e) {
if (GUIP.getGameSummaryMinimap()
&& (e.getOldPhase().isDeployment() || e.getOldPhase().isMovement()
|| e.getOldPhase().isTargeting() || e.getOldPhase().isPremovement()
|| e.getOldPhase().isPrefiring() || e.getOldPhase().isFiring()
|| e.getOldPhase().isPhysical())) {

File dir = new File(Configuration.gameSummaryImagesMMDir(), game.getUUIDString());
if (!dir.exists()) {
dir.mkdirs();
}
File imgFile = new File(dir, "round_" + game.getRoundCount() + "_" + e.getOldPhase().ordinal() + "_"
+ e.getOldPhase() + ".png");
try {
ImageIO.write(getMinimapImage(game, bv, GAME_SUMMARY_ZOOM, clientGui, null), "png", imgFile);
} catch (Exception ex) {
logger.error(ex, "");
}
}
refreshMap();
}

@Override
public void gameTurnChange(GameTurnChangeEvent e) {
refreshMap();
}

@Override
public void gameBoardNew(GameBoardNewEvent e) {
Board b = e.getOldBoard();
if (b != null) {
b.removeBoardListener(boardListener);
}
b = e.getNewBoard();
if (b != null) {
b.addBoardListener(boardListener);
}
board = b;
initializeMap();
}

@Override
public void gameBoardChanged(GameBoardChangeEvent e) {
refreshMap();
}

@Override
public void gameNewAction(GameNewActionEvent e) {
refreshMap();
}
});
board.addBoardListener(boardListener);
if (bv != null) {
bv.addBoardViewListener(boardViewListener);
Expand All @@ -308,6 +370,16 @@ private void initializeDialog() {
}
}

private @Nullable Player getLocalPlayer() {
if (client != null && client.getLocalPlayer() != null) {
return client.getLocalPlayer();
}
if (clientGui != null && clientGui.getClient() != null && clientGui.getClient().getLocalPlayer() != null) {
return clientGui.getClient().getLocalPlayer();
}
return null;
}

@Override
protected void paintComponent(Graphics g) {
if (mapImage != null) {
Expand Down Expand Up @@ -640,7 +712,10 @@ private void drawDeploymentZone(Graphics g) {
if ((null != client) && (null != game) && game.getPhase().isDeployment() && (bv != null)
&& (bv.getDeployingEntity() != null) && (dialog != null)) {
GameTurn turn = game.getTurn();
if ((turn != null) && (turn.playerId() == client.getLocalPlayer().getId())) {
if (getLocalPlayer() == null) {
return;
}
if ((turn != null) && (turn.playerId() == getLocalPlayer().getId())) {
Entity deployingUnit = bv.getDeployingEntity();

for (int j = 0; j < board.getWidth(); j++) {
Expand Down Expand Up @@ -935,8 +1010,8 @@ private void paintAttack(Graphics g, AttackAction attack) {
}
if (attack instanceof WeaponAttackAction) {
WeaponAttackAction waa = (WeaponAttackAction) attack;
if ((attack.getTargetType() == Targetable.TYPE_HEX_ARTILLERY)
&& (waa.getEntity(game).getOwner().getId() != client.getLocalPlayer().getId())) {
if ((getLocalPlayer() == null) ||( (attack.getTargetType() == Targetable.TYPE_HEX_ARTILLERY)
&& (waa.getEntity(game).getOwner().getId() != getLocalPlayer().getId()))) {
return;
}
}
Expand Down Expand Up @@ -1012,7 +1087,7 @@ private void paintUnit(Graphics g, Entity entity) {
int baseX = coordsXToPixel(x);
int baseY = coordsYtoPixel(y, x);

if (EntityVisibilityUtils.onlyDetectedBySensors(client.getLocalPlayer(), entity)) {
if (EntityVisibilityUtils.onlyDetectedBySensors(getLocalPlayer(), entity)) {
// This unit is visible only as a sensor Return
String sensorReturn = "?";
Font font = new Font(MMConstants.FONT_SANS_SERIF, Font.BOLD, FONT_SIZE[zoom]);
Expand All @@ -1022,7 +1097,7 @@ private void paintUnit(Graphics g, Entity entity) {
g.setColor(Color.RED);
g.drawString(sensorReturn, baseX - width, baseY + height);
return;
} else if (!EntityVisibilityUtils.detectedOrHasVisual(client.getLocalPlayer(), game, entity)) {
} else if (!EntityVisibilityUtils.detectedOrHasVisual(getLocalPlayer(), game, entity)) {
// This unit is not visible, don't draw it
return;
}
Expand All @@ -1035,8 +1110,8 @@ private void paintUnit(Graphics g, Entity entity) {
// Choose player or team color depending on preferences
Color iconColor = entity.getOwner().getColour().getColour(false);
if (GUIP.getTeamColoring() && (client != null)) {
boolean isLocalTeam = entity.getOwner().getTeam() == client.getLocalPlayer().getTeam();
boolean isLocalPlayer = entity.getOwner().equals(client.getLocalPlayer());
boolean isLocalTeam = (getLocalPlayer() != null) && (entity.getOwner().getTeam() == getLocalPlayer().getTeam());
boolean isLocalPlayer = entity.getOwner().equals(getLocalPlayer());
if (isLocalPlayer) {
iconColor = GUIP.getMyUnitColor();
} else if (isLocalTeam) {
Expand Down Expand Up @@ -1169,10 +1244,10 @@ private void paintUnit(Graphics g, Entity entity) {

/** Draws the symbol for a single entity. Checks visibility in double blind. */
private void paintSensor(Graphics g, Entity entity) {
if (EntityVisibilityUtils.onlyDetectedBySensors(client.getLocalPlayer(), entity)) {
if (EntityVisibilityUtils.onlyDetectedBySensors(getLocalPlayer(), entity)) {
// This unit is visible only as a sensor Return, we dont know the range of its sensor yet
return;
} else if (!EntityVisibilityUtils.detectedOrHasVisual(client.getLocalPlayer(), game, entity)) {
} else if (!EntityVisibilityUtils.detectedOrHasVisual(getLocalPlayer(), game, entity)) {
// This unit is not visible, don't draw it
return;
}
Expand All @@ -1195,8 +1270,8 @@ private void paintSensor(Graphics g, Entity entity) {
// Choose player or team color depending on preferences
Color iconColor = entity.getOwner().getColour().getColour(false);
if (GUIP.getTeamColoring() && (client != null)) {
boolean isLocalTeam = entity.getOwner().getTeam() == client.getLocalPlayer().getTeam();
boolean isLocalPlayer = entity.getOwner().equals(client.getLocalPlayer());
boolean isLocalTeam = (getLocalPlayer() != null) && (entity.getOwner().getTeam() == getLocalPlayer().getTeam());
boolean isLocalPlayer = entity.getOwner().equals(getLocalPlayer());
if (isLocalPlayer) {
iconColor = GUIP.getMyUnitColor();
} else if (isLocalTeam) {
Expand Down Expand Up @@ -1595,60 +1670,6 @@ public void boardChangedHex(BoardEvent b) {
}
};

private final GameListener gameListener = new GameListenerAdapter() {
@Override
public void gamePhaseChange(GamePhaseChangeEvent e) {
if (GUIP.getGameSummaryMinimap()
&& (e.getOldPhase().isDeployment() || e.getOldPhase().isMovement()
|| e.getOldPhase().isTargeting() || e.getOldPhase().isPremovement()
|| e.getOldPhase().isPrefiring() || e.getOldPhase().isFiring()
|| e.getOldPhase().isPhysical())) {

File dir = new File(Configuration.gameSummaryImagesMMDir(), game.getUUIDString());
if (!dir.exists()) {
dir.mkdirs();
}
File imgFile = new File(dir, "round_" + game.getRoundCount() + "_" + e.getOldPhase().ordinal() + "_"
+ e.getOldPhase() + ".png");
try {
ImageIO.write(getMinimapImage(game, bv, GAME_SUMMARY_ZOOM, null), "png", imgFile);
} catch (Exception ex) {
logger.error(ex, "");
}
}
refreshMap();
}

@Override
public void gameTurnChange(GameTurnChangeEvent e) {
refreshMap();
}

@Override
public void gameBoardNew(GameBoardNewEvent e) {
Board b = e.getOldBoard();
if (b != null) {
b.removeBoardListener(boardListener);
}
b = e.getNewBoard();
if (b != null) {
b.addBoardListener(boardListener);
}
board = b;
initializeMap();
}

@Override
public void gameBoardChanged(GameBoardChangeEvent e) {
refreshMap();
}

@Override
public void gameNewAction(GameNewActionEvent e) {
refreshMap();
}
};

BoardViewListener boardViewListener = new BoardViewListenerAdapter() {
@Override
public void hexCursor(BoardViewEvent b) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ public void displayMek(Entity en) {
}

if (ams == null) {
logger.error("The armor panel is null");
logger.info("The armor panel is null");
return;
}
ams.setEntity(en);
Expand Down
38 changes: 25 additions & 13 deletions megamek/src/megamek/common/EntityVisibilityUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package megamek.common;

import megamek.common.annotations.Nullable;
import megamek.common.options.OptionsConstants;

/**
Expand Down Expand Up @@ -62,21 +63,32 @@ public static boolean detectedOrHasVisual(Player localPlayer, Game game, Entity
*
* @return
*/
public static boolean onlyDetectedBySensors(Player localPlayer, Entity entity) {
boolean sensors = (entity.getGame().getOptions().booleanOption(
OptionsConstants.ADVANCED_TACOPS_SENSORS)
|| entity.getGame().getOptions()
.booleanOption(OptionsConstants.ADVAERORULES_STRATOPS_ADVANCED_SENSORS));
boolean sensorsDetectAll = entity.getGame().getOptions().booleanOption(
OptionsConstants.ADVANCED_SENSORS_DETECT_ALL);
boolean doubleBlind = entity.getGame().getOptions().booleanOption(
OptionsConstants.ADVANCED_DOUBLE_BLIND);
public static boolean onlyDetectedBySensors(@Nullable Player localPlayer, Entity entity) {
boolean usesAdvancedTacOpsSensors = entity.getGame().getOptions().booleanOption(OptionsConstants.ADVANCED_TACOPS_SENSORS);
boolean usesAdvancedStratOpsSensors = entity.getGame().getOptions().booleanOption(OptionsConstants.ADVAERORULES_STRATOPS_ADVANCED_SENSORS);

boolean usesSensors = usesAdvancedTacOpsSensors || usesAdvancedStratOpsSensors;

boolean sensorsDetectAll = entity.getGame().getOptions().booleanOption(OptionsConstants.ADVANCED_SENSORS_DETECT_ALL);
boolean doubleBlind = entity.getGame().getOptions().booleanOption(OptionsConstants.ADVANCED_DOUBLE_BLIND);

if (!doubleBlind) {
return false;
}

if (localPlayer == null) {
return true;
}

boolean hasVisual = entity.hasSeenEntity(localPlayer);
boolean hasDetected = entity.hasDetectedEntity(localPlayer);

if (sensors && doubleBlind && !sensorsDetectAll
&& !EntityVisibilityUtils.trackThisEntitiesVisibilityInfo(localPlayer, entity)
&& hasDetected && !hasVisual) {
boolean doesNotTrackThisEntitiesVisibilityInfo = !EntityVisibilityUtils.trackThisEntitiesVisibilityInfo(localPlayer, entity);
if (usesSensors
&& !sensorsDetectAll
&& doesNotTrackThisEntitiesVisibilityInfo
&& hasDetected
&& !hasVisual)
{
return true;
} else {
return false;
Expand Down

0 comments on commit d90bee0

Please sign in to comment.