Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #1031 - Show structure collapse warning when deploying and moving. #4984

Merged
merged 15 commits into from
Dec 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions megamek/i18n/megamek/client/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,8 @@ CommonMenuBar.viewToggleSensorRange=Visual & Sensor Ranges
CommonMenuBar.viewToggleSensorRangeToolTip=Outline Visual & Sensor Ranges
CommonMenuBar.viewToggleFiringSolutions=Firing Solutions
CommonMenuBar.viewToggleFiringSolutionsToolTip=During the firing phase, to-hit modifiers for each range bracket will be drawn on units
CommonMenuBar.viewToggleCFWarning=Structure Collapse Warning
CommonMenuBar.viewToggleCFWarningToolTip=Show buildings or bridges that could collapse if entered by selected unit
CommonMenuBar.viewChangeTheme=Change Tileset Theme
CommonMenuBar.viewResetWindowPos=Reset Window Positions
CommonMenuBar.moveLAMmechMode=Transform to mek mode
Expand Down
4 changes: 4 additions & 0 deletions megamek/src/megamek/client/ui/swing/ClientGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ public class ClientGUI extends JPanel implements BoardViewListener,
public static final String VIEW_TOGGLE_FOV_DARKEN = "viewToggleFovDarken";
public static final String VIEW_TOGGLE_FOV_HIGHLIGHT = "viewToggleFovHighlight";
public static final String VIEW_TOGGLE_FIRING_SOLUTIONS = "viewToggleFiringSolutions";
public static final String VIEW_TOGGLE_CF_WARNING = "viewToggleCFWarnings";
public static final String VIEW_MOVE_ENV = "viewMovementEnvelope";
public static final String VIEW_TURN_DETAILS_OVERLAY = "viewTurnDetailsOverlay";
public static final String VIEW_MOVE_MOD_ENV = "viewMovModEnvelope";
Expand Down Expand Up @@ -934,6 +935,9 @@ public void actionPerformed(ActionEvent event) {
}
bv.refreshDisplayables();
break;
case VIEW_TOGGLE_CF_WARNING:
ConstructionFactorWarning.handleActionPerformed();
break;
case VIEW_MOVE_ENV:
if (curPanel instanceof MovementDisplay) {
GUIP.setMoveEnvelope(!GUIP.getMoveEnvelope());
Expand Down
6 changes: 6 additions & 0 deletions megamek/src/megamek/client/ui/swing/CommonMenuBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public class CommonMenuBar extends JMenuBar implements ActionListener, IPreferen
private JCheckBoxMenuItem toggleFovHighlight = new JCheckBoxMenuItem(getString("CommonMenuBar.viewToggleFovHighlight"));
private JCheckBoxMenuItem toggleFovDarken = new JCheckBoxMenuItem(getString("CommonMenuBar.viewToggleFovDarken"));
private JCheckBoxMenuItem toggleFiringSolutions = new JCheckBoxMenuItem(getString("CommonMenuBar.viewToggleFiringSolutions"));
private JCheckBoxMenuItem toggleCFWarning = new JCheckBoxMenuItem(getString("CommonMenuBar.viewToggleCFWarning"));
private JCheckBoxMenuItem viewMovementEnvelope = new JCheckBoxMenuItem(getString("CommonMenuBar.movementEnvelope"));
private JCheckBoxMenuItem viewTurnDetailsOverlay = new JCheckBoxMenuItem(getString("CommonMenuBar.turnDetailsOverlay"));
private JMenuItem viewMovModEnvelope = new JMenuItem(getString("CommonMenuBar.movementModEnvelope"));
Expand Down Expand Up @@ -305,6 +306,10 @@ public CommonMenuBar() {
toggleFiringSolutions.setToolTipText(Messages.getString("CommonMenuBar.viewToggleFiringSolutionsToolTip"));
toggleFiringSolutions.setSelected(GUIP.getFiringSolutions());

initMenuItem(toggleCFWarning, menu, VIEW_TOGGLE_CF_WARNING);
toggleCFWarning.setToolTipText(Messages.getString("CommonMenuBar.viewToggleCFWarningToolTip"));
toggleCFWarning.setSelected(GUIP.getShowCFWarnings());

/* TODO: moveTraitor = createMenuItem(menu, getString("CommonMenuBar.moveTraitor"), MovementDisplay.MOVE_TRAITOR); */

// Create the Help menu
Expand Down Expand Up @@ -479,6 +484,7 @@ private synchronized void updateEnabledStates() {
toggleFovHighlight.setEnabled(isInGameBoardView);
toggleFovDarken.setEnabled(isInGameBoardView);
toggleFiringSolutions.setEnabled(isInGameBoardView);
toggleCFWarning.setEnabled(isInGameBoardView);
viewMovementEnvelope.setEnabled(isInGameBoardView);
viewTurnDetailsOverlay.setEnabled(isInGameBoardView);
viewMovModEnvelope.setEnabled(isInGameBoardView);
Expand Down
203 changes: 203 additions & 0 deletions megamek/src/megamek/client/ui/swing/ConstructionFactorWarning.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/*
* Copyright (c) 2023 - 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 <http://www.gnu.org/licenses/>.
*/
package megamek.client.ui.swing;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.apache.logging.log4j.LogManager;

import megamek.common.Board;
import megamek.common.Building;
import megamek.common.Coords;
import megamek.common.Entity;
import megamek.common.Game;
import megamek.common.enums.GamePhase;

/**
* Construction Factor Warning Logic. Handles events, help
* methods and logic related to CF Warning in a way that
* can be unit tested and encapsulated from BoardView and
* ClientGUI and other actors.
*/
public class ConstructionFactorWarning {
/*
* Handler for ClientGUI actionPerformed event. Encapsulates
* as much Construction Factory Warning logic possible.
*/
public static void handleActionPerformed() {
toggleCFWarning();
}

/*
* Return true if the passed in phase is a phase that should allow
* Construction Factor Warnings such as Deploy and Movement.
*/
public static boolean isCFWarningPhase(GamePhase gp) {
return (gp == GamePhase.DEPLOYMENT || gp == GamePhase.MOVEMENT);
}

public static boolean shouldShow(GamePhase gp) {
return shouldShow(gp, true);
}

/*
* Returns true if the show construction factor warning preference
* is enabled and in a phase that should show warnings.
*/
public static boolean shouldShow(GamePhase gp, boolean isEnabled) {
return (isEnabled && isCFWarningPhase(gp));
}

private static boolean toggleCFWarning() {
//Toggle the GUI Preference setting for CF Warning setting.
GUIPreferences GUIP = GUIPreferences.getInstance();
GUIP.setShowCFWarnings(!GUIP.getShowCFWarnings());
return (GUIP.getShowCFWarnings());
}

/**
* For the provided entity, find all hexes within movement range with
* buildings that would collapse if entered or landed upon. This is
* used by the {@link MovementDisplay} class.
*
* @param g {@link Game} provided by the phase display class
* @param e {@link Entity} currently selected in the movement phase.
* @param b {@link Board} board object with building data.
*
* @return returns a list of {@link Coords} that where warning flags
* should be placed.
*/
public static List<Coords> findCFWarningsMovement(Game g, Entity e, Board b) {
List<Coords> warnList = new ArrayList<Coords>();

try {
//Only calculate CF Warnings for entity types in the whitelist.
if (!entityTypeIsInWhiteList(e)) {
return warnList;
}

Coords pos = e.getPosition();
int range = Math.max(e.getJumpMP(), e.getRunMP());

List<Coords> hexesToCheck = new ArrayList<Coords>();
if (pos != null) {
hexesToCheck = pos.allAtDistanceOrLess(range + 1);
} else {
return hexesToCheck;
}

// For each hex in jumping range, look for buildings, if found check for collapse.
for (Coords c : hexesToCheck) {
//is there a building at this location? If so add it to hexes with buildings.
Building bld = b.getBuildingAt(c);

// If a building, compare total weight and add to warning list.
if (null != bld) {
if (calculateTotalTonnage(g, e, c) > bld.getCurrentCF(c)) {
warnList.add(c);
}
}
}
} catch (Exception exc) {
// Something bad is going to happen. This is a passive feature return an empty list.
LogManager.getLogger().error("Unable to calculate construction factor collapse candidates. (Movement)");
return new ArrayList<Coords>();
}

return warnList;
}

/*
* Returns true if the selected entity should have CF warnings calculated when selected.
*/
protected static boolean entityTypeIsInWhiteList(Entity e) {
// Include entities that are ground units and onboard only. Flying units need not apply.
return (e.isGround() && !e.isOffBoard());
}

/**
* Looks for all building locations in a legal deploy zone that would collapse
* if the currently selected entity would deploy there. This is used by
* {@link DeploymentDisplay} to render a warning sprite on danger hexes.
*
* @param g {@link Game} provided by the phase display class
* @param e {@link Entity} currently selected in the movement phase.
* @param b {@link Board} board object with building data.
*
* @return returns a list of {@link Coords} that where warning flags
* should be placed.
*/
public static List<Coords> findCFWarningsDeployment(Game g, Entity e, Board b) {
List<Coords> warnList = new ArrayList<Coords>();

try {
//Only calculate CF Warnings for entity types in the whitelist.
if (!entityTypeIsInWhiteList(e)) {
return warnList;
}

Enumeration<Building> buildings = b.getBuildings();

// Enumerate through all the buildings
while (buildings.hasMoreElements()) {
Building bld = buildings.nextElement();
List<Coords> buildingList = bld.getCoordsList();

// For each hex occupied by the building, check if it's a legal deploy hex.
for (Coords c : buildingList) {
if (b.isLegalDeployment(c, e)) {
// Check for weight limits for collapse and add a warning sprite.
if (calculateTotalTonnage(g, e, c) > bld.getCurrentCF(c)) {
warnList.add(c);
}
}
}
}
} catch (Exception exc) {
// Something bad is going to happen. This is a passive feature return an empty list.
LogManager.getLogger().error("Unable to calculate construction factor collapse candidates. (Deployment)");
return new ArrayList<Coords>();
}

return warnList;
}

/*
* Determine the total weight burden for a building hex at a location.
* This includes the entity current weight summed with any unit weights
* at the hex location that could cause a building to collapse.
*/
protected static double calculateTotalTonnage(Game g, Entity selected, Coords c) {
// Calculate total weight of entity and all entities at the location.
double totalWeight = selected.getWeight();
List<Entity> units = g.getEntitiesVector(c, true);
for (Entity ent : units) {
if (ConstructionFactorWarning.isEntityPartOfWeight(selected, ent)) {
totalWeight += ent.getWeight();
}
}
return totalWeight;
}

protected static boolean isEntityPartOfWeight(Entity selected, Entity inHex) {
return ((selected != inHex) && inHex.isGround() && !inHex.isAirborneVTOLorWIGE());
}
}
12 changes: 12 additions & 0 deletions megamek/src/megamek/client/ui/swing/DeploymentDisplay.java
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ public void selectEntity(int en) {
clientgui.getUnitDisplay().showPanel("movement");
clientgui.getBoardView().setWeaponFieldOfFire(ce().getFacing(), ce().getPosition());
clientgui.getBoardView().setSensorRange(ce(), ce().getPosition());
computeCFWarningHexes(ce());
} else {
disableButtons();
setNextEnabled(true);
Expand All @@ -239,6 +240,17 @@ public void selectEntity(int en) {
}
}

private void computeCFWarningHexes(Entity ce) {
List<Coords> warnList =
ConstructionFactorWarning.findCFWarningsDeployment(
clientgui.getBoardView().game,
ce,
clientgui.getBoardView().game.getBoard());

clientgui.getBoardView().setCFWarningSprites(warnList);

}

/** Enables relevant buttons and sets up for your turn. */
private void beginMyTurn() {
clientgui.maybeShowUnitDisplay();
Expand Down
12 changes: 11 additions & 1 deletion megamek/src/megamek/client/ui/swing/GUIPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ public class GUIPreferences extends PreferenceStoreProxy {
public static final String TEAM_COLORING = "EntityTeamLabelColor";
public static final String FOCUS = "Focus";
public static final String FIRING_SOLUTIONS = "FiringSolutions";
public static final String CONSTRUCTOR_FACTOR_WARNING = "ConstructionFactorWarning";
public static final String MOVE_ENVELOPE = "MoveEnvelope";
public static final String TURN_DETAILS_OVERLAY = "TurnDetailsOverlay";
public static final String FOV_HIGHLIGHT = "FovHighlight";
Expand Down Expand Up @@ -602,6 +603,7 @@ protected GUIPreferences() {
store.setDefault(UNIT_LABEL_BORDER, true);
store.setDefault(UNIT_LABEL_STYLE, LabelDisplayStyle.NICKNAME.name());
store.setDefault(FIRING_SOLUTIONS, true);
store.setDefault(CONSTRUCTOR_FACTOR_WARNING, true);
store.setDefault(GUI_SCALE, 1);
store.setDefault(LOBBY_MEKTABLE_UNIT_WIDTH, 170);
store.setDefault(LOBBY_MEKTABLE_PILOT_WIDTH, 80);
Expand Down Expand Up @@ -1003,6 +1005,10 @@ public boolean getFiringSolutions() {
return store.getBoolean(FIRING_SOLUTIONS);
}

public boolean getShowCFWarnings() {
return store.getBoolean(CONSTRUCTOR_FACTOR_WARNING);
}

public boolean getMoveEnvelope() {
return store.getBoolean(MOVE_ENVELOPE);
}
Expand Down Expand Up @@ -1783,6 +1789,10 @@ public void setFiringSolutions(boolean state) {
store.setValue(FIRING_SOLUTIONS, state);
}

public void setShowCFWarnings(boolean b) {
store.setValue(CONSTRUCTOR_FACTOR_WARNING, b);
}

public void setMoveEnvelope(boolean state) {
store.setValue(MOVE_ENVELOPE, state);
}
Expand Down Expand Up @@ -3141,4 +3151,4 @@ protected Color parseRGB(String text) {
return Color.BLUE;
}
//endregion Colours
}
}
11 changes: 11 additions & 0 deletions megamek/src/megamek/client/ui/swing/MovementDisplay.java
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,7 @@ public synchronized void selectEntity(int en) {
}
clientgui.getBoardView().clearFieldOfFire();
computeMovementEnvelope(ce);
computeCFWarningHexes(ce);
}

private MegamekButton getBtn(MoveCommand c) {
Expand Down Expand Up @@ -4544,6 +4545,16 @@ && ce().getMovementMode() == EntityMovementMode.TRACKED) {
clientgui.getBoardView().setMovementModifierEnvelope(lpf.getLongestComputedPaths());
}

private void computeCFWarningHexes(Entity ce) {
List<Coords> warnList =
ConstructionFactorWarning.findCFWarningsMovement(
clientgui.getBoardView().game,
ce,
clientgui.getBoardView().game.getBoard());

clientgui.getBoardView().setCFWarningSprites(warnList);
}

//
// ActionListener
//
Expand Down
Loading