From d832beb31afa26030e92498832a7f7a78edf939a Mon Sep 17 00:00:00 2001 From: NickAragua Date: Mon, 8 Jul 2024 13:24:05 -0400 Subject: [PATCH 01/35] backing structures and player config --- .../ui/swing/lobby/PlayerSettingsDialog.java | 114 ++++++++++++++++++ megamek/src/megamek/common/Briefcase.java | 44 +++++++ megamek/src/megamek/common/Game.java | 47 ++++++++ megamek/src/megamek/common/ICarryable.java | 28 +++++ megamek/src/megamek/common/Player.java | 20 ++- 5 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 megamek/src/megamek/common/Briefcase.java create mode 100644 megamek/src/megamek/common/ICarryable.java diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java index 19373b5121e..fd9d2024de6 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java @@ -72,6 +72,10 @@ */ public class PlayerSettingsDialog extends AbstractButtonDialog { + private static final String CMD_ADD_GROUND_OBJECT = "CMD_ADD_GROUND_OBJECT"; + private static final String CMD_REMOVE_GROUND_OBJECT = "CMD_REMOVE_GROUND_OBJECT_%d"; + private static final String CMD_REMOVE_GROUND_OBJECT_PREFIX = "CMD_REMOVE_GROUND_OBJECT_"; + public PlayerSettingsDialog(ClientGUI cg, Client cl, BoardView bv) { super(cg.getFrame(), "PlayerSettingsDialog", "PlayerSettingsDialog.title"); client = cl; @@ -230,6 +234,12 @@ public String getEmail() { private JSpinner spinStartingAnyNWy; private JSpinner spinStartingAnySEx; private JSpinner spinStartingAnySEy; + + // ground object config section + private Content groundSectionContent = new Content(new GridLayout(2, 3)); + private final JTextField txtGroundObjectName = new JTextField(); + private final JFormattedTextField txtGroundObjectTonnage = new JFormattedTextField(formatterFactory, 0); + private List> groundSectionComponents = new ArrayList<>(); // Bot Settings Section private final JButton butBotSettings = new JButton(Messages.getString("PlayerSettingsDialog.botSettings")); @@ -269,6 +279,7 @@ protected Container createCenterPane() { if (client.getGame().getOptions().booleanOption(OptionsConstants.ADVANCED_MINEFIELDS)) { mainPanel.add(mineSection()); } + mainPanel.add(groundObjectConfigSection()); mainPanel.add(skillsSection()); if (!(client instanceof BotClient)) { mainPanel.add(emailSection()); @@ -330,6 +341,101 @@ private JPanel autoConfigSection() { butRestoreMT.setEnabled(false); return result; } + + private JPanel groundObjectConfigSection() { + JPanel result = new OptionPanel("Carryable Ground Objects"); + result.setToolTipText("Define carryable objects that can be placed prior to unit deployment"); + groundSectionContent = new Content(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 0; + gbc.gridy = 0; + JLabel lblName = new JLabel("Name"); + groundSectionContent.add(lblName, gbc); + + gbc.gridx = 1; + JLabel lblTonnage = new JLabel("Tonnage"); + groundSectionContent.add(lblTonnage, gbc); + + gbc.gridy = 1; + gbc.gridx = 0; + groundSectionContent.add(txtGroundObjectName, gbc); + + gbc.gridx = 1; + groundSectionContent.add(txtGroundObjectTonnage, gbc); + + gbc.gridx = 2; + JButton btnAdd = new JButton("Add"); + btnAdd.setActionCommand(CMD_ADD_GROUND_OBJECT); + btnAdd.addActionListener(listener); + groundSectionContent.add(btnAdd, gbc); + + for (ICarryable groundObject : player.getGroundObjectsToPlace()) { + + } + + result.add(groundSectionContent); + return result; + } + + private void addGroundObjectToUI(ICarryable groundObject) { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridy = groundSectionComponents.size() + 2; // there's always two extra rows - header + text fields + gbc.gridx = 0; + + JLabel nameLabel = new JLabel(groundObject.getName()); + groundSectionContent.add(nameLabel, gbc); + List row = new ArrayList<>(); + row.add(nameLabel); + + gbc.gridx = 1; + JLabel tonnageLabel = new JLabel(Double.toString(groundObject.getTonnage())); + groundSectionContent.add(tonnageLabel, gbc); + row.add(tonnageLabel); + + gbc.gridx = 2; + JButton btnRemove = new JButton("Remove"); + btnRemove.setActionCommand(String.format(CMD_REMOVE_GROUND_OBJECT, player.getGroundObjectsToPlace().size() - 1)); + btnRemove.addActionListener(listener); + groundSectionContent.add(btnRemove, gbc); + row.add(btnRemove); + groundSectionComponents.add(row); + validate(); + } + + /** + * Worker function that uses the current state of the ground object inputs to + * add a new ground object to the backing player and the UI + */ + private void addGroundObject() { + Briefcase briefcase = new Briefcase(); + briefcase.setName(txtGroundObjectName.getText()); + briefcase.setTonnage(Double.parseDouble(txtGroundObjectTonnage.getText())); + player.getGroundObjectsToPlace().add(briefcase); + + addGroundObjectToUI(briefcase); + } + + /** + * Worker function that removes the chosen ground object from the backing player and the UI + */ + private void removeGroundObject(String command) { + int index = Integer.parseInt(command.substring(CMD_REMOVE_GROUND_OBJECT_PREFIX.length())); + player.getGroundObjectsToPlace().remove(index); + for(Component component : groundSectionComponents.get(index)) { + groundSectionContent.remove(component); + } + groundSectionComponents.remove(index); + + // kind of a hack, but I'm being lazy - re-index all the CMD_REMOVE_GROUND_OBJECT commands beyond + // the one that just removed, so they're not pointing to higher indexes than they need to + for (int componentIndex = index; componentIndex < groundSectionComponents.size(); componentIndex++) { + ((JButton) groundSectionComponents.get(index).get(2)) + .setActionCommand(String.format(CMD_REMOVE_GROUND_OBJECT, componentIndex)); + } + + validate(); + } private JPanel botSection() { JPanel result = new OptionPanel("PlayerSettingsDialog.header.botPlayer"); @@ -681,6 +787,14 @@ public void actionPerformed(ActionEvent e) { ((Princess) client).setBehaviorSettings(bcd.getBehaviorSettings()); } } + + if (e.getActionCommand().equals(CMD_ADD_GROUND_OBJECT)) { + addGroundObject(); + } + + if (e.getActionCommand().contains(CMD_REMOVE_GROUND_OBJECT_PREFIX)) { + removeGroundObject(e.getActionCommand()); + } } }; diff --git a/megamek/src/megamek/common/Briefcase.java b/megamek/src/megamek/common/Briefcase.java new file mode 100644 index 00000000000..cdde880c3d2 --- /dev/null +++ b/megamek/src/megamek/common/Briefcase.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 - 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.common; + +/** + * Represents a basic carryable object with no additional other properties + */ +public class Briefcase implements ICarryable { + private double tonnage; + private String name; + + public void setTonnage(double value) { + tonnage = value; + } + + public double getTonnage() { + return tonnage; + } + + public void setName(String value) { + name = value; + } + + public String getName() { + return name; + } +} diff --git a/megamek/src/megamek/common/Game.java b/megamek/src/megamek/common/Game.java index 74c99bd1904..9a804847807 100644 --- a/megamek/src/megamek/common/Game.java +++ b/megamek/src/megamek/common/Game.java @@ -136,6 +136,11 @@ public final class Game extends AbstractGame implements Serializable, PlanetaryC */ private Map botSettings = new HashMap<>(); + /** + * Piles of carry-able objects, sorted by coordinates + */ + private HashMap> groundObjects = new HashMap<>(); + /** * Constructor */ @@ -3277,8 +3282,50 @@ public Map getBotSettings() { public void setBotSettings(Map botSettings) { this.botSettings = botSettings; } + + /** + * Place a carryable object on the ground at the given coordinates + */ + public void placeGroundObject(Coords coords, ICarryable carryable) { + if (!groundObjects.containsKey(coords)) { + groundObjects.put(coords, new ArrayList<>()); + } + + groundObjects.get(coords).add(carryable); + } + + /** + * Remove the given carryable object from the ground at the given coordinates + */ + public void removeGroundObject(Coords coords, ICarryable carryable) { + if (groundObjects.containsKey(coords)) { + groundObjects.get(coords).remove(carryable); + } + } /** + * Get a list of all the objects on the ground at the given coordinates + */ + public List getGroundObjects(Coords coords) { + return groundObjects.containsKey(coords) ? groundObjects.get(coords) : new ArrayList(); + } + + /** + * @return Collection of objects on the ground. Best to use getGroundObjects(Coords) + * if looking for objects in specific hex + */ + public HashMap> getGroundObjects() { + return groundObjects; + } + + /** + * @param groundObjects the groundObjects to set + */ + public void setGroundObjects(HashMap> groundObjects) { + this.groundObjects = groundObjects; + } + + /** * Cancels a victory */ public void cancelVictory() { diff --git a/megamek/src/megamek/common/ICarryable.java b/megamek/src/megamek/common/ICarryable.java new file mode 100644 index 00000000000..51502090b4e --- /dev/null +++ b/megamek/src/megamek/common/ICarryable.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 - 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.common; + +/** + * An interface defining all the required properties of a carryable object. + */ +public interface ICarryable { + double getTonnage(); + String getName(); +} \ No newline at end of file diff --git a/megamek/src/megamek/common/Player.java b/megamek/src/megamek/common/Player.java index d5745ee57f0..b6de354c105 100644 --- a/megamek/src/megamek/common/Player.java +++ b/megamek/src/megamek/common/Player.java @@ -23,6 +23,8 @@ import megamek.common.icons.Camouflage; import megamek.common.options.OptionsConstants; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.Vector; @@ -93,6 +95,8 @@ public final class Player extends TurnOrdered { private Vector visibleMinefields = new Vector<>(); private boolean admitsDefeat = false; + + private List groundObjectsToPlace = new ArrayList<>(); //Voting should not be stored in save game so marked transient private transient boolean votedToAllowTeamChange = false; @@ -455,7 +459,21 @@ public boolean admitsDefeat() { return admitsDefeat; } - public void setVotedToAllowTeamChange(boolean allowChange) { + /** + * Collection of carryable objects that this player will be placing during the game. + */ + public List getGroundObjectsToPlace() { + return groundObjectsToPlace; + } + + /** + * Present for serialization purposes only + */ + public void setGroundObjectsToPlace(List groundObjectsToPlace) { + this.groundObjectsToPlace = groundObjectsToPlace; + } + + public void setVotedToAllowTeamChange(boolean allowChange) { votedToAllowTeamChange = allowChange; } From ddd16b79383fcdf1003c0e118153b1941a0c0cf5 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Mon, 8 Jul 2024 13:26:30 -0400 Subject: [PATCH 02/35] wtf is this nonsense --- .../ui/swing/lobby/PlayerSettingsDialog.java | 109 +++++++----------- 1 file changed, 43 insertions(+), 66 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java index cb447f00a9e..b1239de099b 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java @@ -19,31 +19,6 @@ */ package megamek.client.ui.swing.lobby; -import static megamek.client.ui.Messages.getString; -import static megamek.client.ui.swing.lobby.LobbyUtility.isValidStartPos; -import static megamek.client.ui.swing.util.UIUtil.guiScaledFontHTML; -import static megamek.client.ui.swing.util.UIUtil.teamColor; -import static megamek.client.ui.swing.util.UIUtil.uiYellow; - -import java.awt.Component; -import java.awt.Container; -import java.awt.FlowLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.text.NumberFormat; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import javax.swing.filechooser.FileNameExtensionFilter; -import javax.swing.text.DefaultFormatterFactory; -import javax.swing.text.NumberFormatter; - import megamek.MMConstants; import megamek.client.Client; import megamek.client.bot.BotClient; @@ -63,21 +38,30 @@ import megamek.client.ui.swing.GUIPreferences; import megamek.client.ui.swing.boardview.BoardView; import megamek.client.ui.swing.util.UIUtil; -import megamek.client.ui.swing.util.UIUtil.Content; -import megamek.client.ui.swing.util.UIUtil.FixedYPanel; -import megamek.client.ui.swing.util.UIUtil.OptionPanel; -import megamek.client.ui.swing.util.UIUtil.TipButton; -import megamek.client.ui.swing.util.UIUtil.TipLabel; -import megamek.client.ui.swing.util.UIUtil.TipTextField; -import megamek.common.Entity; -import megamek.common.IStartingPositions; -import megamek.common.MapSettings; -import megamek.common.OffBoardDirection; -import megamek.common.Player; -import megamek.common.Team; +import megamek.common.*; +import megamek.common.annotations.Nullable; import megamek.common.containers.MunitionTree; import megamek.common.options.GameOptions; import megamek.common.options.OptionsConstants; +import org.apache.commons.collections.IteratorUtils; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.text.DefaultFormatterFactory; +import javax.swing.text.NumberFormatter; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import static megamek.client.ui.Messages.getString; +import static megamek.client.ui.swing.lobby.LobbyUtility.isValidStartPos; +import static megamek.client.ui.swing.util.UIUtil.*; /** * A dialog that can be used to adjust advanced player settings like initiative, @@ -113,7 +97,7 @@ public PlayerSettingsDialog(ClientGUI cg, Client cl, BoardView bv) { @Override public Component getListCellRendererComponent(JList list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { + boolean isSelected, boolean cellHasFocus) { if (value == null) { setText("General"); } else { @@ -217,15 +201,12 @@ public String getEmail() { private Player player; // Initiative Section - private final JLabel labInit = new TipLabel(Messages.getString("PlayerSettingsDialog.initMod"), - SwingConstants.RIGHT); + private final JLabel labInit = new TipLabel(Messages.getString("PlayerSettingsDialog.initMod"), SwingConstants.RIGHT); private final TipTextField fldInit = new TipTextField(3); // Mines Section - private final JLabel labConventional = new JLabel(getString("PlayerSettingsDialog.labConventional"), - SwingConstants.RIGHT); - private final JLabel labVibrabomb = new JLabel(getString("PlayerSettingsDialog.labVibrabomb"), - SwingConstants.RIGHT); + private final JLabel labConventional = new JLabel(getString("PlayerSettingsDialog.labConventional"), SwingConstants.RIGHT); + private final JLabel labVibrabomb = new JLabel(getString("PlayerSettingsDialog.labVibrabomb"), SwingConstants.RIGHT); private final JLabel labActive = new JLabel(getString("PlayerSettingsDialog.labActive"), SwingConstants.RIGHT); private final JLabel labInferno = new JLabel(getString("PlayerSettingsDialog.labInferno"), SwingConstants.RIGHT); private final JTextField fldConventional = new JTextField(3); @@ -243,8 +224,7 @@ public String getEmail() { // Deployment Section private final JPanel panStartButtons = new JPanel(); private final TipButton[] butStartPos = new TipButton[11]; - // this might seem like kind of a dumb way to declare it, but - // JFormattedTextField doesn't have an overload that + // this might seem like kind of a dumb way to declare it, but JFormattedTextField doesn't have an overload that // takes both a number formatter and a default value. private final NumberFormatter numFormatter = new NumberFormatter(NumberFormat.getIntegerInstance()); private final DefaultFormatterFactory formatterFactory = new DefaultFormatterFactory(numFormatter); @@ -391,13 +371,16 @@ private JPanel groundObjectConfigSection() { groundSectionContent.add(btnAdd, gbc); for (ICarryable groundObject : player.getGroundObjectsToPlace()) { - + addGroundObjectToUI(groundObject); } result.add(groundSectionContent); return result; } - + + /** + * Worker function that adds the given ground object to the UI + */ private void addGroundObjectToUI(ICarryable groundObject) { GridBagConstraints gbc = new GridBagConstraints(); gbc.gridy = groundSectionComponents.size() + 2; // there's always two extra rows - header + text fields @@ -512,6 +495,7 @@ private JPanel deploymentParametersPanel() { return result; } + private void useRuler() { if (bv.getRulerStart() != null && bv.getRulerEnd() != null) { int x = Math.min(bv.getRulerStart().getX(), bv.getRulerEnd().getX()); @@ -537,13 +521,12 @@ private void apply() { final GameOptions gOpts = clientgui.getClient().getGame().getOptions(); - // If the gameoption set_arty_player_homeedge is set, adjust the player's - // offboard + // If the gameoption set_arty_player_homeedge is set, adjust the player's offboard // arty units to be behind the newly selected home edge. OffBoardDirection direction = OffBoardDirection.translateStartPosition(getStartPos()); if (direction != OffBoardDirection.NONE && gOpts.booleanOption(OptionsConstants.BASE_SET_ARTY_PLAYER_HOMEEDGE)) { - for (Entity entity : client.getGame().getPlayerEntities(client.getLocalPlayer(), false)) { + for (Entity entity: client.getGame().getPlayerEntities(client.getLocalPlayer(), false)) { if (entity.getOffBoardDirection() != OffBoardDirection.NONE) { entity.setOffBoard(entity.getOffBoardDistance(), direction); } @@ -558,8 +541,7 @@ private void apply() { if (null != munitionTree) { // TODO: create and set up default adf file path for bots tlg.reconfigureEntities(updateEntities, faction, munitionTree); - // Use sendUpdate because we want the Game to allow us to change on Bot's - // behalf. + // Use sendUpdate because we want the Game to allow us to change on Bot's behalf. clientgui.chatlounge.sendProxyUpdates(updateEntities, client.getLocalPlayer()); // clientgui.chatlounge.sendUpdate(updateEntities); } @@ -646,7 +628,7 @@ private void setupValues() { int bh = ms.getBoardHeight() * ms.getMapHeight(); int bw = ms.getBoardWidth() * ms.getMapWidth(); - SpinnerNumberModel mStartingAnyNWx = new SpinnerNumberModel(0, 0, bw, 1); + SpinnerNumberModel mStartingAnyNWx = new SpinnerNumberModel(0, 0,bw, 1); spinStartingAnyNWx = new JSpinner(mStartingAnyNWx); SpinnerNumberModel mStartingAnyNWy = new SpinnerNumberModel(0, 0, bh, 1); spinStartingAnyNWy = new JSpinner(mStartingAnyNWy); @@ -802,8 +784,7 @@ public void actionPerformed(ActionEvent e) { // Bot settings button if (butBotSettings.equals(e.getSource()) && client instanceof Princess) { BehaviorSettings behavior = ((Princess) client).getBehaviorSettings(); - var bcd = new BotConfigDialog(clientgui.getFrame(), client.getLocalPlayer().getName(), behavior, - clientgui); + var bcd = new BotConfigDialog(clientgui.getFrame(), client.getLocalPlayer().getName(), behavior, clientgui); bcd.setVisible(true); if (bcd.getResult() == DialogResult.CONFIRMED) { ((Princess) client).setBehaviorSettings(bcd.getBehaviorSettings()); @@ -821,10 +802,8 @@ public void actionPerformed(ActionEvent e) { }; /** - * Let user select an ADF file (Autoconfiguration Definition File) from which to - * load munition loadout + * Let user select an ADF file (Autoconfiguration Definition File) from which to load munition loadout * imperatives, which can then be applied to selected units. - * * @return */ private MunitionTree loadLoadout() { @@ -839,7 +818,7 @@ private MunitionTree loadLoadout() { int returnVal = fc.showOpenDialog(this); if ((returnVal != JFileChooser.APPROVE_OPTION) || (fc.getSelectedFile() == null)) { - // No file selected? No loadout! + // No file selected? No loadout! return null; } @@ -851,7 +830,7 @@ private MunitionTree loadLoadout() { } private void saveLoadout(MunitionTree source) { - // ignoreHotKeys = true; + //ignoreHotKeys = true; JFileChooser fc = new JFileChooser(MMConstants.USER_LOADOUTS_DIR); FileNameExtensionFilter adfFilter = new FileNameExtensionFilter( "adf files (*.adf)", "adf"); @@ -862,7 +841,7 @@ private void saveLoadout(MunitionTree source) { int returnVal = fc.showSaveDialog(this); if ((returnVal != JFileChooser.APPROVE_OPTION) || (fc.getSelectedFile() == null)) { - // No file selected? No loadout! + // No file selected? No loadout! return; } if (fc.getSelectedFile() != null) { @@ -885,9 +864,8 @@ private int parseField(JTextField field) { return 0; } } - private void adaptToGUIScale() { - UIUtil.adjustDialog(this, UIUtil.FONT_SCALE1); + UIUtil.adjustDialog(this, UIUtil.FONT_SCALE1); } public FactionRecord getFaction() { @@ -900,8 +878,7 @@ public String getFactionCode() { public FactionRecord getFactionFromCode(String code, int year) { for (FactionRecord fRec : RATGenerator.getInstance().getFactionList()) { - if ((!fRec.isMinor()) && !fRec.getKey().contains(".") && fRec.isActiveInYear(year) - && fRec.getKey().equals(code)) { + if ((!fRec.isMinor()) && !fRec.getKey().contains(".") && fRec.isActiveInYear(year) && fRec.getKey().equals(code)) { return fRec; } } From a042635da68ba9708c47fab532b67b7da41ea658 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Mon, 8 Jul 2024 15:41:59 -0400 Subject: [PATCH 03/35] minefield display refactor --- .../ui/swing/DeployMinefieldDisplay.java | 156 ++++++++---------- 1 file changed, 66 insertions(+), 90 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java index 804099d1449..089ff31b04e 100644 --- a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java +++ b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java @@ -15,7 +15,6 @@ import megamek.client.event.BoardViewEvent; import megamek.client.ui.Messages; -import megamek.client.ui.swing.util.KeyCommandBind; import megamek.client.ui.swing.widget.MegamekButton; import megamek.common.*; import megamek.common.event.GamePhaseChangeEvent; @@ -25,17 +24,13 @@ import java.awt.event.MouseEvent; import java.util.*; -import static megamek.client.ui.swing.util.UIUtil.guiScaledFontHTML; -import static megamek.client.ui.swing.util.UIUtil.uiLightViolet; - public class DeployMinefieldDisplay extends StatusBarPhaseDisplay { private static final long serialVersionUID = -1243277953037374936L; /** * This enumeration lists all of the possible ActionCommands that can be * carried out during the deploy minefield phase. Each command has a string - * for the command plus a flag that determines what unit type it is - * appropriate for. + * for the command. * @author arlith */ public static enum DeployMinefieldCommand implements PhaseCommand { @@ -44,6 +39,7 @@ public static enum DeployMinefieldCommand implements PhaseCommand { DEPLOY_MINE_VIBRA("deployMineVibra"), DEPLOY_MINE_ACTIVE("deployMineActive"), DEPLOY_MINE_INFERNO("deployMineInferno"), + DEPLOY_CARRYABLE("deployCarriable"), REMOVE_MINES("removeMines"); /** @@ -82,13 +78,28 @@ public String getHotKeyDesc() { // buttons protected Map buttons; - private boolean deployM = false; - private boolean deployC = false; - private boolean deployV = false; - private boolean deployA = false; - private boolean deployI = false; - private boolean remove = false; + DeployMinefieldCommand currentCommand; + private boolean deployingConventionalMinefields() { + return currentCommand.equals(DeployMinefieldCommand.DEPLOY_MINE_CONV); + } + + private boolean deployingActiveMinefields() { + return currentCommand.equals(DeployMinefieldCommand.DEPLOY_MINE_ACTIVE); + } + + private boolean deployingInfernoMinefields() { + return currentCommand.equals(DeployMinefieldCommand.DEPLOY_MINE_INFERNO); + } + + private boolean deployingCommandMinefields() { + return currentCommand.equals(DeployMinefieldCommand.DEPLOY_MINE_COM); + } + + private boolean deployingVibrabombMinefields() { + return currentCommand.equals(DeployMinefieldCommand.DEPLOY_MINE_VIBRA); + } + private Player p; private Vector deployedMinefields = new Vector<>(); @@ -150,6 +161,7 @@ private void beginMyTurn() { setVibrabombEnabled(p.getNbrMFVibra()); setActiveEnabled(p.getNbrMFActive()); setInfernoEnabled(p.getNbrMFInferno()); + setCarryableEnabled(p.getGroundObjectsToPlace().size()); setRemoveMineEnabled(true); butDone.setEnabled(true); @@ -179,28 +191,32 @@ private void disableButtons() { setVibrabombEnabled(0); setActiveEnabled(0); setInfernoEnabled(0); + setCarryableEnabled(0); setRemoveMineEnabled(false); butDone.setEnabled(false); } private void deployMinefield(Coords coords) { - if (!clientgui.getClient().getGame().getBoard().contains(coords)) { + Game game = clientgui.getClient().getGame(); + + if (!game.getBoard().contains(coords)) { return; } // check if this is a water hex boolean sea = false; - Hex hex = clientgui.getClient().getGame().getBoard().getHex(coords); + Hex hex = game.getBoard().getHex(coords); if (hex.containsTerrain(Terrains.WATER)) { sea = true; } - if (remove) { - if (!clientgui.getClient().getGame().containsMinefield(coords)) { + if (currentCommand == DeployMinefieldCommand.REMOVE_MINES) { + if (!game.containsMinefield(coords) && + game.getGroundObjects(coords).size() == 0) { return; } - Enumeration mfs = clientgui.getClient().getGame().getMinefields(coords).elements(); + Enumeration mfs = game.getMinefields(coords).elements(); ArrayList mfRemoved = new ArrayList<>(); while (mfs.hasMoreElements()) { Minefield mf = (Minefield) mfs.nextElement(); @@ -222,19 +238,29 @@ private void deployMinefield(Coords coords) { } for (Minefield mf : mfRemoved) { - clientgui.getClient().getGame().removeMinefield(mf); + game.removeMinefield(mf); } + + // remove all carryables here as well and put them back to the player + for (ICarryable carryable : game.getGroundObjects(coords)) { + clientgui.getClient().getLocalPlayer().getGroundObjectsToPlace().add(carryable); + } + + game.getGroundObjects().remove(coords); + } else { + + // first check that there is not already a mine of this type // deployed - Enumeration mfs = clientgui.getClient().getGame().getMinefields(coords).elements(); + Enumeration mfs = game.getMinefields(coords).elements(); while (mfs.hasMoreElements()) { Minefield mf = (Minefield) mfs.nextElement(); - if ((deployM && (mf.getType() == Minefield.TYPE_CONVENTIONAL)) - || (deployC && (mf.getType() == Minefield.TYPE_COMMAND_DETONATED)) - || (deployV && (mf.getType() == Minefield.TYPE_VIBRABOMB)) - || (deployA && (mf.getType() == Minefield.TYPE_ACTIVE)) - || (deployI && (mf.getType() == Minefield.TYPE_INFERNO))) { + if ((deployingConventionalMinefields() && (mf.getType() == Minefield.TYPE_CONVENTIONAL)) + || (deployingCommandMinefields() && (mf.getType() == Minefield.TYPE_COMMAND_DETONATED)) + || (deployingVibrabombMinefields() && (mf.getType() == Minefield.TYPE_VIBRABOMB)) + || (deployingActiveMinefields() && (mf.getType() == Minefield.TYPE_ACTIVE)) + || (deployingInfernoMinefields() && (mf.getType() == Minefield.TYPE_INFERNO))) { clientgui.doAlertDialog(Messages.getString("DeployMinefieldDisplay.IllegalPlacement"), Messages.getString("DeployMinefieldDisplay.DuplicateMinefield")); return; @@ -242,13 +268,13 @@ private void deployMinefield(Coords coords) { } Minefield mf = null; - if (sea && !(deployM || deployI)) { + if (sea && !(deployingConventionalMinefields() || deployingInfernoMinefields())) { clientgui.doAlertDialog(Messages.getString("DeployMinefieldDisplay.IllegalPlacement"), Messages.getString("DeployMinefieldDisplay.WaterPlacement")); return; } int depth = 0; - if (deployM) { + if (deployingConventionalMinefields()) { if (sea) { SeaMineDepthDialog smd = new SeaMineDepthDialog( clientgui.frame, hex.depth()); @@ -265,7 +291,7 @@ private void deployMinefield(Coords coords) { depth); p.setNbrMFConventional(p.getNbrMFConventional() - 1); } - } else if (deployC) { + } else if (deployingCommandMinefields()) { MineDensityDialog mfd = new MineDensityDialog(clientgui.frame); mfd.setVisible(true); @@ -275,7 +301,7 @@ private void deployMinefield(Coords coords) { sea, depth); p.setNbrMFCommand(p.getNbrMFCommand() - 1); } - } else if (deployA) { + } else if (deployingActiveMinefields()) { MineDensityDialog mfd = new MineDensityDialog(clientgui.frame); mfd.setVisible(true); @@ -284,7 +310,7 @@ private void deployMinefield(Coords coords) { Minefield.TYPE_ACTIVE, mfd.getDensity()); p.setNbrMFActive(p.getNbrMFActive() - 1); } - } else if (deployI) { + } else if (deployingInfernoMinefields()) { MineDensityDialog mfd = new MineDensityDialog(clientgui.frame); mfd.setVisible(true); @@ -294,7 +320,7 @@ private void deployMinefield(Coords coords) { depth); p.setNbrMFInferno(p.getNbrMFInferno() - 1); } - } else if (deployV) { + } else if (deployingVibrabombMinefields()) { MineDensityDialog mfd = new MineDensityDialog(clientgui.frame); mfd.setVisible(true); @@ -313,7 +339,7 @@ private void deployMinefield(Coords coords) { } if (mf != null) { mf.setWeaponDelivered(false); - clientgui.getClient().getGame().addMinefield(mf); + game.addMinefield(mf); deployedMinefields.addElement(mf); } clientgui.getBoardView().refreshDisplayables(); @@ -324,23 +350,7 @@ private void deployMinefield(Coords coords) { setVibrabombEnabled(p.getNbrMFVibra()); setActiveEnabled(p.getNbrMFActive()); setInfernoEnabled(p.getNbrMFInferno()); - - if (p.getNbrMFConventional() == 0) { - deployM = false; - } - if (p.getNbrMFCommand() == 0) { - deployC = false; - } - if (p.getNbrMFVibra() == 0) { - deployV = false; - } - if (p.getNbrMFActive() == 0) { - deployA = false; - } - if (p.getNbrMFInferno() == 0) { - deployI = false; - } - + setCarryableEnabled(p.getGroundObjectsToPlace().size()); } @Override @@ -432,49 +442,9 @@ public void actionPerformed(ActionEvent ev) { if (!clientgui.getClient().isMyTurn()) { // odd... return; - } else if (ev.getActionCommand().equals(DeployMinefieldCommand.DEPLOY_MINE_CONV.getCmd())) { - deployM = true; - deployC = false; - deployV = false; - deployA = false; - deployI = false; - remove = false; - } else if (ev.getActionCommand().equals(DeployMinefieldCommand.DEPLOY_MINE_COM.getCmd())) { - deployM = false; - deployC = true; - deployV = false; - deployA = false; - deployI = false; - remove = false; - } else if (ev.getActionCommand().equals(DeployMinefieldCommand.DEPLOY_MINE_VIBRA.getCmd())) { - deployM = false; - deployC = false; - deployV = true; - deployA = false; - deployI = false; - remove = false; - } else if (ev.getActionCommand().equals(DeployMinefieldCommand.DEPLOY_MINE_ACTIVE.getCmd())) { - deployM = false; - deployC = false; - deployV = false; - deployA = true; - deployI = false; - remove = false; - } else if (ev.getActionCommand().equals(DeployMinefieldCommand.DEPLOY_MINE_INFERNO.getCmd())) { - deployM = false; - deployC = false; - deployV = false; - deployA = false; - deployI = true; - remove = false; - } else if (ev.getActionCommand().equals(DeployMinefieldCommand.REMOVE_MINES.getCmd())) { - deployM = false; - deployC = false; - deployV = false; - deployA = false; - deployI = false; - remove = true; } + + currentCommand = DeployMinefieldCommand.valueOf(ev.getActionCommand()); } // End public void actionPerformed(ActionEvent ev) @Override @@ -513,6 +483,12 @@ private void setInfernoEnabled(int nbr) { "DeployMinefieldDisplay." + DeployMinefieldCommand.DEPLOY_MINE_INFERNO.getCmd(), nbr)); buttons.get(DeployMinefieldCommand.DEPLOY_MINE_INFERNO).setEnabled(nbr > 0); } + + private void setCarryableEnabled(int nbr) { + buttons.get(DeployMinefieldCommand.DEPLOY_CARRYABLE).setText(Messages.getString( + "DeployMinefieldDisplay." + DeployMinefieldCommand.DEPLOY_CARRYABLE.getCmd(), nbr)); + buttons.get(DeployMinefieldCommand.DEPLOY_CARRYABLE).setEnabled(nbr > 0); + } private void setRemoveMineEnabled(boolean enable) { buttons.get(DeployMinefieldCommand.REMOVE_MINES).setEnabled(enable); From 0e26a517725e38ef7ccbad66456a26eee731cc89 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Mon, 8 Jul 2024 21:17:45 -0400 Subject: [PATCH 04/35] deploy minefield refactor --- .../client/ui/swing/DeployMinefieldDisplay.java | 12 +++++++++++- .../client/ui/swing/lobby/PlayerSettingsDialog.java | 2 +- megamek/src/megamek/common/Briefcase.java | 9 ++++++++- megamek/src/megamek/common/Game.java | 3 ++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java index 089ff31b04e..9ac1c60ee53 100644 --- a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java +++ b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java @@ -52,6 +52,16 @@ public static enum DeployMinefieldCommand implements PhaseCommand { cmd = c; } + public static DeployMinefieldCommand fromString(String command) { + for (DeployMinefieldCommand value : values()) { + if (value.getCmd().equals(command)) { + return value; + } + } + + return null; + } + @Override public String getCmd() { return cmd; @@ -444,7 +454,7 @@ public void actionPerformed(ActionEvent ev) { return; } - currentCommand = DeployMinefieldCommand.valueOf(ev.getActionCommand()); + currentCommand = DeployMinefieldCommand.fromString(ev.getActionCommand()); } // End public void actionPerformed(ActionEvent ev) @Override diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java index b1239de099b..81dab0980c5 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java @@ -43,7 +43,7 @@ import megamek.common.containers.MunitionTree; import megamek.common.options.GameOptions; import megamek.common.options.OptionsConstants; -import org.apache.commons.collections.IteratorUtils; +import org.apache.commons.collections4.IteratorUtils; import javax.swing.*; import javax.swing.border.EmptyBorder; diff --git a/megamek/src/megamek/common/Briefcase.java b/megamek/src/megamek/common/Briefcase.java index cdde880c3d2..d08570cb09d 100644 --- a/megamek/src/megamek/common/Briefcase.java +++ b/megamek/src/megamek/common/Briefcase.java @@ -19,10 +19,17 @@ package megamek.common; +import java.io.Serializable; + /** * Represents a basic carryable object with no additional other properties */ -public class Briefcase implements ICarryable { +public class Briefcase implements ICarryable, Serializable { + /** + * + */ + private static final long serialVersionUID = 8849879320465375457L; + private double tonnage; private String name; diff --git a/megamek/src/megamek/common/Game.java b/megamek/src/megamek/common/Game.java index 9a804847807..99a52952624 100644 --- a/megamek/src/megamek/common/Game.java +++ b/megamek/src/megamek/common/Game.java @@ -3305,9 +3305,10 @@ public void removeGroundObject(Coords coords, ICarryable carryable) { /** * Get a list of all the objects on the ground at the given coordinates + * guaranteed to return non-null, but may return empty list */ public List getGroundObjects(Coords coords) { - return groundObjects.containsKey(coords) ? groundObjects.get(coords) : new ArrayList(); + return groundObjects.containsKey(coords) ? groundObjects.get(coords) : new ArrayList<>(); } /** From 7640d7d12c5528d753e6edf9d04d44f84651bae7 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Tue, 9 Jul 2024 23:51:02 -0400 Subject: [PATCH 05/35] minefield deployment component; start of gui display --- .../i18n/megamek/client/messages.properties | 1 + .../megamek/client/ui/swing/ClientGUI.java | 16 +++- .../ui/swing/DeployMinefieldDisplay.java | 73 ++++++++++++++++--- .../client/ui/swing/DeploymentDisplay.java | 2 +- .../boardview/GroundObjectSpriteHandler.java | 59 +++++++++++++++ .../ui/swing/dialog/DropdownDialog.java | 43 +++++++++++ .../ui/swing/lobby/PlayerSettingsDialog.java | 2 - megamek/src/megamek/common/Briefcase.java | 4 + megamek/src/megamek/common/Player.java | 3 +- .../common/util/SerializationHelper.java | 3 +- megamek/src/megamek/server/Server.java | 1 + 11 files changed, 191 insertions(+), 16 deletions(-) create mode 100644 megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java create mode 100644 megamek/src/megamek/client/ui/swing/dialog/DropdownDialog.java diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties index d8691229ae4..b871768ce8c 100644 --- a/megamek/i18n/megamek/client/messages.properties +++ b/megamek/i18n/megamek/client/messages.properties @@ -1693,6 +1693,7 @@ DeployMinefieldDisplay.its_your_turn=It's your turn to deploy minefields. DeployMinefieldDisplay.removeMines=Remove DeployMinefieldDisplay.waitingForDeploymentPhase=Waiting to begin Deployment phase... DeployMinefieldDisplay.waitingForDeployMinefieldPhase=Waiting to begin Deploy minefield phase... +DeployMinefieldDisplay.deployCarriable=Deploy Ground Object #Expand Map Dialog ExpandMapDialog.title=Expand map settings diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index 1ef2563f43e..8412278de62 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -233,6 +233,7 @@ public class ClientGUI extends AbstractClientGUI implements BoardViewListener, private MovementModifierSpriteHandler movementModifierSpriteHandler; private SensorRangeSpriteHandler sensorRangeSpriteHandler; private CollapseWarningSpriteHandler collapseWarningSpriteHandler; + private GroundObjectSpriteHandler groundObjectSpriteHandler; private FiringSolutionSpriteHandler firingSolutionSpriteHandler; private FiringArcSpriteHandler firingArcSpriteHandler; private final List spriteHandlers = new ArrayList<>(); @@ -490,12 +491,13 @@ private void initializeSpriteHandlers() { FlareSpritesHandler flareSpritesHandler = new FlareSpritesHandler(bv, client.getGame()); sensorRangeSpriteHandler = new SensorRangeSpriteHandler(bv, client.getGame()); collapseWarningSpriteHandler = new CollapseWarningSpriteHandler(bv); + groundObjectSpriteHandler = new GroundObjectSpriteHandler(bv); firingSolutionSpriteHandler = new FiringSolutionSpriteHandler(bv, client); firingArcSpriteHandler = new FiringArcSpriteHandler(bv, this); spriteHandlers.addAll(List.of(movementEnvelopeHandler, movementModifierSpriteHandler, sensorRangeSpriteHandler, flareSpritesHandler, collapseWarningSpriteHandler, - firingSolutionSpriteHandler, firingArcSpriteHandler)); + groundObjectSpriteHandler, firingSolutionSpriteHandler, firingArcSpriteHandler)); spriteHandlers.forEach(BoardViewSpriteHandler::initialize); } @@ -2852,6 +2854,7 @@ public void clearTemporarySprites() { movementModifierSpriteHandler.clear(); sensorRangeSpriteHandler.clear(); collapseWarningSpriteHandler.clear(); + groundObjectSpriteHandler.clear(); firingSolutionSpriteHandler.clear(); firingArcSpriteHandler.clear(); } @@ -2888,11 +2891,20 @@ public void showSensorRanges(Entity entity, Coords assumedPosition) { /** * Shows collapse warnings in the given list of Coords in the BoardView * - * @param warnList The Coords to show the warning on + * @param warnList The list of coordinates to show the warning on */ public void showCollapseWarning(List warnList) { collapseWarningSpriteHandler.setCFWarningSprites(warnList); } + + /** + * Shows ground object icons in the given list of Coords in the BoardView + * + * @param groundObjectList The list of coordinates to show + */ + public void showGroundObjects(Iterable groundObjectList) { + groundObjectSpriteHandler.setGroundObjectSprites(groundObjectList); + } /** * Shows firing solutions from the viewpoint of the given entity on targets diff --git a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java index 9ac1c60ee53..651abd6a6da 100644 --- a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java +++ b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java @@ -15,6 +15,7 @@ import megamek.client.event.BoardViewEvent; import megamek.client.ui.Messages; +import megamek.client.ui.swing.dialog.DropdownDialog; import megamek.client.ui.swing.widget.MegamekButton; import megamek.common.*; import megamek.common.event.GamePhaseChangeEvent; @@ -34,6 +35,7 @@ public class DeployMinefieldDisplay extends StatusBarPhaseDisplay { * @author arlith */ public static enum DeployMinefieldCommand implements PhaseCommand { + COMMAND_NONE("noCommand"), DEPLOY_MINE_CONV("deployMineConv"), DEPLOY_MINE_COM("deployMineCom"), DEPLOY_MINE_VIBRA("deployMineVibra"), @@ -41,6 +43,10 @@ public static enum DeployMinefieldCommand implements PhaseCommand { DEPLOY_MINE_INFERNO("deployMineInferno"), DEPLOY_CARRYABLE("deployCarriable"), REMOVE_MINES("removeMines"); + + private static DeployMinefieldCommand[] actualCommands = + { DEPLOY_MINE_CONV, DEPLOY_MINE_COM, DEPLOY_MINE_VIBRA, DEPLOY_MINE_ACTIVE, + DEPLOY_MINE_INFERNO, DEPLOY_CARRYABLE, REMOVE_MINES }; /** * Priority that determines this buttons order @@ -52,6 +58,9 @@ public static enum DeployMinefieldCommand implements PhaseCommand { cmd = c; } + /** + * Given a string, figure out the command value + */ public static DeployMinefieldCommand fromString(String command) { for (DeployMinefieldCommand value : values()) { if (value.getCmd().equals(command)) { @@ -62,6 +71,13 @@ public static DeployMinefieldCommand fromString(String command) { return null; } + /** + * Get all the commands that aren't NO-OP + */ + public static DeployMinefieldCommand[] getActualCommands() { + return actualCommands; + } + @Override public String getCmd() { return cmd; @@ -137,16 +153,16 @@ public DeployMinefieldDisplay(ClientGUI clientgui) { @Override protected void setButtons() { - buttons = new HashMap<>((int) (DeployMinefieldCommand.values().length * 1.25 + 0.5)); - for (DeployMinefieldCommand cmd : DeployMinefieldCommand.values()) { - buttons.put(cmd, createButton(cmd.getCmd(), "DeployMinefieldDisplay.")); + buttons = new HashMap<>((int) (DeployMinefieldCommand.getActualCommands().length * 1.25 + 0.5)); + for (DeployMinefieldCommand cmd : DeployMinefieldCommand.getActualCommands()) { + buttons.put(cmd, createButton(cmd.getCmd(), "DeployMinefieldDisplay.")); } numButtonGroups = (int) Math.ceil((buttons.size()+0.0) / buttonsPerGroup); } @Override protected void setButtonsTooltips() { - for (DeployMinefieldCommand cmd : DeployMinefieldCommand.values()) { + for (DeployMinefieldCommand cmd : DeployMinefieldCommand.getActualCommands()) { String tt = createToolTip(cmd.getCmd(), "DeployMinefieldDisplay.", cmd.getHotKeyDesc()); buttons.get(cmd).setToolTipText(tt); } @@ -155,7 +171,7 @@ protected void setButtonsTooltips() { @Override protected ArrayList getButtonList() { ArrayList buttonList = new ArrayList<>(); - for (DeployMinefieldCommand cmd : DeployMinefieldCommand.values()) { + for (DeployMinefieldCommand cmd : DeployMinefieldCommand.getActualCommands()) { buttonList.add(buttons.get(cmd)); } return buttonList; @@ -253,15 +269,34 @@ private void deployMinefield(Coords coords) { // remove all carryables here as well and put them back to the player for (ICarryable carryable : game.getGroundObjects(coords)) { - clientgui.getClient().getLocalPlayer().getGroundObjectsToPlace().add(carryable); + p.getGroundObjectsToPlace().add(carryable); } game.getGroundObjects().remove(coords); - } else { + clientgui.showGroundObjects(game.getGroundObjects().keySet()); + + } else if (currentCommand == DeployMinefieldCommand.DEPLOY_CARRYABLE) { + List groundObjects = p.getGroundObjectsToPlace(); + ICarryable toDeploy = groundObjects.get(0); + + if (groundObjects.size() > 1) { + DropdownDialog ddl = new DropdownDialog<>(clientgui.frame, "DeployMinefieldDisplay.deployCarriable", "DeployMinefieldDisplay.deployCarriable", clientgui.getClient().getLocalPlayer().getGroundObjectsToPlace()); + ddl.setVisible(true); + toDeploy = ddl.getSelectedItem(); + } - // first check that there is not already a mine of this type + game.placeGroundObject(coords, toDeploy); + groundObjects.remove(toDeploy); + + if (groundObjects.size() <= 0) { + currentCommand = DeployMinefieldCommand.COMMAND_NONE; + } + + clientgui.showGroundObjects(game.getGroundObjects().keySet()); + } else { + // first check that there is not already a mine of this type // deployed Enumeration mfs = game.getMinefields(coords).elements(); while (mfs.hasMoreElements()) { @@ -300,6 +335,10 @@ private void deployMinefield(Coords coords) { Minefield.TYPE_CONVENTIONAL, mfd.getDensity(), sea, depth); p.setNbrMFConventional(p.getNbrMFConventional() - 1); + + if (p.getNbrMFConventional() <= 0) { + currentCommand = DeployMinefieldCommand.COMMAND_NONE; + } } } else if (deployingCommandMinefields()) { MineDensityDialog mfd = new MineDensityDialog(clientgui.frame); @@ -310,6 +349,10 @@ private void deployMinefield(Coords coords) { Minefield.TYPE_COMMAND_DETONATED, mfd.getDensity(), sea, depth); p.setNbrMFCommand(p.getNbrMFCommand() - 1); + + if (p.getNbrMFCommand() <= 0) { + currentCommand = DeployMinefieldCommand.COMMAND_NONE; + } } } else if (deployingActiveMinefields()) { MineDensityDialog mfd = new MineDensityDialog(clientgui.frame); @@ -319,6 +362,10 @@ private void deployMinefield(Coords coords) { mf = Minefield.createMinefield(coords, p.getId(), Minefield.TYPE_ACTIVE, mfd.getDensity()); p.setNbrMFActive(p.getNbrMFActive() - 1); + + if (p.getNbrMFActive() <= 0) { + currentCommand = DeployMinefieldCommand.COMMAND_NONE; + } } } else if (deployingInfernoMinefields()) { MineDensityDialog mfd = new MineDensityDialog(clientgui.frame); @@ -329,6 +376,10 @@ private void deployMinefield(Coords coords) { Minefield.TYPE_INFERNO, mfd.getDensity(), sea, depth); p.setNbrMFInferno(p.getNbrMFInferno() - 1); + + if (p.getNbrMFInferno() <= 0) { + currentCommand = DeployMinefieldCommand.COMMAND_NONE; + } } } else if (deployingVibrabombMinefields()) { MineDensityDialog mfd = new MineDensityDialog(clientgui.frame); @@ -343,6 +394,10 @@ private void deployMinefield(Coords coords) { Minefield.TYPE_VIBRABOMB, mfd.getDensity(), vsd.getSetting()); p.setNbrMFVibra(p.getNbrMFVibra() - 1); + + if (p.getNbrMFVibra() <= 0) { + currentCommand = DeployMinefieldCommand.COMMAND_NONE; + } } } else { return; @@ -365,7 +420,7 @@ private void deployMinefield(Coords coords) { @Override public void clear() { - //TODO: undefined for now + } // diff --git a/megamek/src/megamek/client/ui/swing/DeploymentDisplay.java b/megamek/src/megamek/client/ui/swing/DeploymentDisplay.java index 089952b1029..45af22c0da5 100644 --- a/megamek/src/megamek/client/ui/swing/DeploymentDisplay.java +++ b/megamek/src/megamek/client/ui/swing/DeploymentDisplay.java @@ -225,7 +225,7 @@ public void selectEntity(int en) { clientgui.getUnitDisplay().showPanel("movement"); clientgui.updateFiringArc(ce()); clientgui.showSensorRanges(ce()); - computeCFWarningHexes(ce()); + computeCFWarningHexes(ce()); } else { disableButtons(); setNextEnabled(true); diff --git a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java new file mode 100644 index 00000000000..11ae8512d40 --- /dev/null +++ b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 - 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.swing.boardview; + +import megamek.common.Coords; +import java.util.List; + +public class GroundObjectSpriteHandler extends BoardViewSpriteHandler { + + // Cache the warn list; thus, when CF warning is turned on the sprites can easily be created + private Iterable currentGroundObjectList; + + public GroundObjectSpriteHandler(BoardView boardView) { + super(boardView); + } + + public void setGroundObjectSprites(Iterable objectCoordList) { + clear(); + currentGroundObjectList = objectCoordList; + if (currentGroundObjectList != null) { + for (Coords coords : currentGroundObjectList) { + CollapseWarningSprite cws = new CollapseWarningSprite(boardView, coords); + currentSprites.add(cws); + } + } + boardView.addSprites(currentSprites); + } + + @Override + public void clear() { + super.clear(); + currentGroundObjectList = null; + } + + @Override + public void initialize() { + } + + @Override + public void dispose() { + clear(); + } +} diff --git a/megamek/src/megamek/client/ui/swing/dialog/DropdownDialog.java b/megamek/src/megamek/client/ui/swing/dialog/DropdownDialog.java new file mode 100644 index 00000000000..e5f09fa253a --- /dev/null +++ b/megamek/src/megamek/client/ui/swing/dialog/DropdownDialog.java @@ -0,0 +1,43 @@ +package megamek.client.ui.swing.dialog; + +import java.awt.Container; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import megamek.client.ui.baseComponents.AbstractDialog; +import megamek.client.ui.baseComponents.MMComboBox; + +/** + * This class displays a single dropdown from which we can make a single choice + * @param + */ +public class DropdownDialog extends AbstractDialog { + + List dataSource; + MMComboBox comboBox; + + public DropdownDialog(JFrame frame, String name, String title, List data) { + super(frame, name, title); + dataSource = data; + initialize(); + } + + @Override + protected Container createCenterPane() { + comboBox = new MMComboBox(getName(), dataSource); + comboBox.setSelectedIndex(0); + + JPanel result = new JPanel(); + result.add(comboBox); + return result; + } + + /** + * Returns the item that is currently selected in the combobox. + */ + public T getSelectedItem() { + return (T) comboBox.getSelectedItem(); + } +} diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java index 81dab0980c5..3f59a0d225e 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java @@ -39,11 +39,9 @@ import megamek.client.ui.swing.boardview.BoardView; import megamek.client.ui.swing.util.UIUtil; import megamek.common.*; -import megamek.common.annotations.Nullable; import megamek.common.containers.MunitionTree; import megamek.common.options.GameOptions; import megamek.common.options.OptionsConstants; -import org.apache.commons.collections4.IteratorUtils; import javax.swing.*; import javax.swing.border.EmptyBorder; diff --git a/megamek/src/megamek/common/Briefcase.java b/megamek/src/megamek/common/Briefcase.java index d08570cb09d..b25d7eee303 100644 --- a/megamek/src/megamek/common/Briefcase.java +++ b/megamek/src/megamek/common/Briefcase.java @@ -48,4 +48,8 @@ public void setName(String value) { public String getName() { return name; } + + public String toString() { + return name + " (" + tonnage + ")"; + } } diff --git a/megamek/src/megamek/common/Player.java b/megamek/src/megamek/common/Player.java index b6de354c105..607c62f57ba 100644 --- a/megamek/src/megamek/common/Player.java +++ b/megamek/src/megamek/common/Player.java @@ -141,7 +141,8 @@ public boolean containsMinefield(Minefield mf) { } public boolean hasMinefields() { - return (numMfCmd > 0) || (numMfConv > 0) || (numMfVibra > 0) || (numMfActive > 0) || (numMfInferno > 0); + return (numMfCmd > 0) || (numMfConv > 0) || (numMfVibra > 0) || (numMfActive > 0) || (numMfInferno > 0) + || getGroundObjectsToPlace().size() > 0; } public void setNbrMFConventional(int nbrMF) { diff --git a/megamek/src/megamek/common/util/SerializationHelper.java b/megamek/src/megamek/common/util/SerializationHelper.java index 2f92c580dd1..9a6f4e311ee 100644 --- a/megamek/src/megamek/common/util/SerializationHelper.java +++ b/megamek/src/megamek/common/util/SerializationHelper.java @@ -67,7 +67,8 @@ public static XStream getSaveGameXStream() { megamek.server.SmokeCloud.class, megamek.common.EntityFluff.class, megamek.common.NarcPod.class, - megamek.common.INarcPod.class + megamek.common.INarcPod.class, + megamek.common.Briefcase.class }); xStream.allowTypeHierarchy(megamek.common.BTObject.class); xStream.allowTypeHierarchy(megamek.common.Building.class); diff --git a/megamek/src/megamek/server/Server.java b/megamek/src/megamek/server/Server.java index b075094a2aa..870705b7c96 100644 --- a/megamek/src/megamek/server/Server.java +++ b/megamek/src/megamek/server/Server.java @@ -563,6 +563,7 @@ private void receivePlayerInfo(Packet packet, int connId) { } gamePlayer.setConstantInitBonus(player.getConstantInitBonus()); gamePlayer.setEmail(player.getEmail()); + gamePlayer.setGroundObjectsToPlace(new ArrayList<>(player.getGroundObjectsToPlace())); } } From c20172a60b8c6e528f04616beadd47b129b12039 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Wed, 10 Jul 2024 11:46:49 -0400 Subject: [PATCH 06/35] sprite display minus actual icon --- .../megamek/client/ui/swing/ClientGUI.java | 5 ++--- .../ui/swing/DeployMinefieldDisplay.java | 2 +- .../boardview/GroundObjectSpriteHandler.java | 22 ++++++++++++++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index 8412278de62..5bd7504adba 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -491,7 +491,7 @@ private void initializeSpriteHandlers() { FlareSpritesHandler flareSpritesHandler = new FlareSpritesHandler(bv, client.getGame()); sensorRangeSpriteHandler = new SensorRangeSpriteHandler(bv, client.getGame()); collapseWarningSpriteHandler = new CollapseWarningSpriteHandler(bv); - groundObjectSpriteHandler = new GroundObjectSpriteHandler(bv); + groundObjectSpriteHandler = new GroundObjectSpriteHandler(bv, client.getGame()); firingSolutionSpriteHandler = new FiringSolutionSpriteHandler(bv, client); firingArcSpriteHandler = new FiringArcSpriteHandler(bv, this); @@ -1180,7 +1180,7 @@ void switchPanel(GamePhase phase) { // otherwise, hide the panel panSecondary.setVisible(false); } - + // Set the new panel's listeners if (curPanel instanceof BoardViewListener) { bv.addBoardViewListener((BoardViewListener) curPanel); @@ -2854,7 +2854,6 @@ public void clearTemporarySprites() { movementModifierSpriteHandler.clear(); sensorRangeSpriteHandler.clear(); collapseWarningSpriteHandler.clear(); - groundObjectSpriteHandler.clear(); firingSolutionSpriteHandler.clear(); firingArcSpriteHandler.clear(); } diff --git a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java index 651abd6a6da..46c79664357 100644 --- a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java +++ b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java @@ -104,7 +104,7 @@ public String getHotKeyDesc() { // buttons protected Map buttons; - DeployMinefieldCommand currentCommand; + DeployMinefieldCommand currentCommand = DeployMinefieldCommand.COMMAND_NONE; private boolean deployingConventionalMinefields() { return currentCommand.equals(DeployMinefieldCommand.DEPLOY_MINE_CONV); diff --git a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java index 11ae8512d40..c813dcc9577 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java +++ b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java @@ -19,15 +19,19 @@ package megamek.client.ui.swing.boardview; import megamek.common.Coords; -import java.util.List; +import megamek.common.Game; +import megamek.common.event.GameBoardChangeEvent; +import megamek.common.event.GamePhaseChangeEvent; public class GroundObjectSpriteHandler extends BoardViewSpriteHandler { - // Cache the warn list; thus, when CF warning is turned on the sprites can easily be created + // Cache the ground object list as it does not change very often private Iterable currentGroundObjectList; + private final Game game; - public GroundObjectSpriteHandler(BoardView boardView) { + public GroundObjectSpriteHandler(BoardView boardView, Game game) { super(boardView); + this.game = game; } public void setGroundObjectSprites(Iterable objectCoordList) { @@ -50,10 +54,22 @@ public void clear() { @Override public void initialize() { + game.addGameListener(this); } @Override public void dispose() { clear(); + game.removeGameListener(this); + } + + @Override + public void gameBoardChanged(GameBoardChangeEvent e) { + setGroundObjectSprites(game.getGroundObjects().keySet()); + } + + @Override + public void gamePhaseChange(GamePhaseChangeEvent e) { + setGroundObjectSprites(game.getGroundObjects().keySet()); } } From 09c325e1e941228757e745b462e9fb09bc5430d3 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Wed, 10 Jul 2024 23:29:21 -0400 Subject: [PATCH 07/35] pick up command on client side --- .../i18n/megamek/client/messages.properties | 4 +- .../ui/swing/DeployMinefieldDisplay.java | 12 +++- .../client/ui/swing/MovementDisplay.java | 69 +++++++++++++++++-- .../client/ui/swing/tooltip/HexTooltip.java | 10 +++ megamek/src/megamek/common/BipedMech.java | 25 +++++++ megamek/src/megamek/common/Briefcase.java | 2 +- megamek/src/megamek/common/Entity.java | 14 ++++ megamek/src/megamek/common/Game.java | 21 ++++++ megamek/src/megamek/common/HexTarget.java | 11 --- megamek/src/megamek/common/MovePath.java | 2 +- megamek/src/megamek/common/Protomech.java | 26 ++++++- megamek/src/megamek/common/TripodMech.java | 25 +++++++ 12 files changed, 198 insertions(+), 23 deletions(-) diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties index b871768ce8c..0876f97c982 100644 --- a/megamek/i18n/megamek/client/messages.properties +++ b/megamek/i18n/megamek/client/messages.properties @@ -1693,7 +1693,8 @@ DeployMinefieldDisplay.its_your_turn=It's your turn to deploy minefields. DeployMinefieldDisplay.removeMines=Remove DeployMinefieldDisplay.waitingForDeploymentPhase=Waiting to begin Deployment phase... DeployMinefieldDisplay.waitingForDeployMinefieldPhase=Waiting to begin Deploy minefield phase... -DeployMinefieldDisplay.deployCarriable=Deploy Ground Object +DeployMinefieldDisplay.deployCarriable=Cargo({0}) +DeployMinefieldDisplay.deployCarriableDialogHeader=Deploy Ground Object #Expand Map Dialog ExpandMapDialog.title=Expand map settings @@ -2424,6 +2425,7 @@ MovementDisplay.butLoad=Load MovementDisplay.butLand=Land MovementDisplay.butJoin=Join MovementDisplay.butManeuver=Maneuver +MovementDisplay.movePickup=Pick up MovementDisplay.moveModeConvert=Convert Mode MovementDisplay.moveModeLeg=Walk MovementDisplay.moveModeTrack=Engage tracks diff --git a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java index 46c79664357..5886932c1c7 100644 --- a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java +++ b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java @@ -282,7 +282,17 @@ private void deployMinefield(Coords coords) { ICarryable toDeploy = groundObjects.get(0); if (groundObjects.size() > 1) { - DropdownDialog ddl = new DropdownDialog<>(clientgui.frame, "DeployMinefieldDisplay.deployCarriable", "DeployMinefieldDisplay.deployCarriable", clientgui.getClient().getLocalPlayer().getGroundObjectsToPlace()); + // lol use this instead + /*String input = (String) JOptionPane.showInputDialog( + clientgui.getFrame(), + Messages.getString("MovementDisplay.UnloadUnitDialog.message", ce.getShortName(), ce.getUnusedString()), + Messages.getString("MovementDisplay.UnloadUnitDialog.title"), + JOptionPane.QUESTION_MESSAGE, null, + SharedUtility.getDisplayArray(loadedUnits), null); + getTargetPicked() to get result*/ + DropdownDialog ddl = new DropdownDialog<>(clientgui.frame, + "DeployMinefieldDisplay.deployCarriableDialogHeader", + "DeployMinefieldDisplay.deployCarriableDialogHeader", clientgui.getClient().getLocalPlayer().getGroundObjectsToPlace()); ddl.setVisible(true); toDeploy = ddl.getSelectedItem(); } diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index 82deab7193b..66a796a73a9 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -77,12 +77,13 @@ public class MovementDisplay extends ActionPhaseDisplay { public static final int CMD_AIRMECH = 1 << 7; // Command used only in menus and has no associated button public static final int CMD_NO_BUTTON = 1 << 8; + public static final int CMD_PROTOMECH = 1 << 9; // Convenience defines for common combinations public static final int CMD_AERO_BOTH = CMD_AERO | CMD_AERO_VECTORED; - public static final int CMD_GROUND = CMD_MECH | CMD_TANK | CMD_VTOL | CMD_INF; - public static final int CMD_NON_VECTORED = CMD_MECH | CMD_TANK | CMD_VTOL | CMD_INF | CMD_AERO; - public static final int CMD_ALL = CMD_MECH | CMD_TANK | CMD_VTOL | CMD_INF | CMD_AERO | CMD_AERO_VECTORED; - public static final int CMD_NON_INF = CMD_MECH | CMD_TANK | CMD_VTOL | CMD_AERO | CMD_AERO_VECTORED; + public static final int CMD_GROUND = CMD_MECH | CMD_TANK | CMD_VTOL | CMD_INF | CMD_PROTOMECH; + public static final int CMD_NON_VECTORED = CMD_MECH | CMD_TANK | CMD_VTOL | CMD_INF | CMD_AERO | CMD_PROTOMECH; + public static final int CMD_ALL = CMD_MECH | CMD_TANK | CMD_VTOL | CMD_INF | CMD_AERO | CMD_AERO_VECTORED | CMD_PROTOMECH; + public static final int CMD_NON_INF = CMD_MECH | CMD_TANK | CMD_VTOL | CMD_AERO | CMD_AERO_VECTORED | CMD_PROTOMECH; private boolean isUnJammingRAC; private boolean isUsingChaff; @@ -179,6 +180,7 @@ public enum MoveCommand implements PhaseCommand { MOVE_LONGEST_WALK("MoveLongestWalk", CMD_NONE), // Traitor MOVE_TRAITOR("Traitor", CMD_NONE), + MOVE_PICKUP("movePickup", CMD_MECH | CMD_PROTOMECH), MOVE_MORE("MoveMore", CMD_NONE); /** @@ -566,7 +568,9 @@ protected ArrayList getButtonList() { } else if ((ce instanceof Mech) && ((Mech) ce).hasTracks()) { flag = CMD_MECH | CMD_CONVERTER; } else if ((ce instanceof Protomech) && ce.getMovementMode().isWiGE()) { - flag = CMD_MECH | CMD_AIRMECH; + flag = CMD_PROTOMECH | CMD_MECH | CMD_AIRMECH; + } else if (ce instanceof Protomech) { + flag = CMD_PROTOMECH; } } return getButtonList(flag); @@ -1188,6 +1192,7 @@ private void disableButtons() { setManeuverEnabled(false); setStrafeEnabled(false); setBombEnabled(false); + setPickupEnabled(false); getBtn(MoveCommand.MOVE_CLIMB_MODE).setEnabled(false); getBtn(MoveCommand.MOVE_DIG_IN).setEnabled(false); @@ -2797,8 +2802,24 @@ private synchronized void updateLoadButtons() { updateUnloadButton(); updateTowingButtons(); updateMountButton(); - } - + updatePickupButton(); + } + + /** Updates the status of the "pickup" button */ + private void updatePickupButton() { + final Entity ce = ce(); + // there has to be an entity, objects are on the ground, + // the entity can pick them up + if ((ce == null) || (game().getGroundObjects(finalPosition(), ce()).size() <= 0) || + ((cmd.getLastStep() != null) && + (cmd.getLastStep().getType() == MoveStepType.PICKUP))) { + setPickupEnabled(false); + return; + } + + setPickupEnabled(true); + } + /** Updates the status of the Load button. */ private void updateLoadButton() { final Entity ce = ce(); @@ -4870,6 +4891,35 @@ public synchronized void actionPerformed(ActionEvent ev) { addStepToMovePath(MoveStepType.UNLOAD, other); } } // else - Player canceled the unload. + } else if (actionCmd.equals(MoveCommand.MOVE_PICKUP.getCmd())) { + // this is kind of obnoxious: + // we have a list of all the possible ground objects + // we supply a list of eligible ground objects to the dropdown dialog + // we use the selected object to look up the ground object index in the hex + // and pass it to the generated move step + + var options = game().getGroundObjects(finalPosition()); + var displayedOptions = game().getGroundObjects(finalPosition(), ce()); + + if (displayedOptions.size() == 1) { + int index = options.indexOf(displayedOptions.get(0)); + addStepToMovePath(MoveStepType.PICKUP, index); + updateDonePanel(); + } else if (displayedOptions.size() > 1) { + // Dialog for choosing which object to pick up + String title = "Choose Cargo to Pick Up"; + String body = "Choose the cargo to pick up:"; + ICarryable option = (ICarryable) JOptionPane.showInputDialog(clientgui.getFrame(), + body, title, JOptionPane.QUESTION_MESSAGE, null, + displayedOptions.toArray(), displayedOptions.get(0)); + + // Verify that we have a valid option... + if (option != null) { + int index = options.indexOf(option); + addStepToMovePath(MoveStepType.PICKUP, index); + updateDonePanel(); + } + } } else if (actionCmd.equals(MoveCommand.MOVE_RAISE_ELEVATION.getCmd())) { addStepToMovePath(MoveStepType.UP); } else if (actionCmd.equals(MoveCommand.MOVE_LOWER_ELEVATION.getCmd())) { @@ -5675,6 +5725,11 @@ private void setBombEnabled(boolean enabled) { getBtn(MoveCommand.MOVE_BOMB).setEnabled(enabled); clientgui.getMenuBar().setEnabled(MoveCommand.MOVE_BOMB.getCmd(), enabled); } + + private void setPickupEnabled(boolean enabled) { + getBtn(MoveCommand.MOVE_PICKUP).setEnabled(enabled); + clientgui.getMenuBar().setEnabled(MoveCommand.MOVE_PICKUP.getCmd(), enabled); + } @Override public void removeAllListeners() { diff --git a/megamek/src/megamek/client/ui/swing/tooltip/HexTooltip.java b/megamek/src/megamek/client/ui/swing/tooltip/HexTooltip.java index 34a7d9bbd02..2e353b30bcd 100644 --- a/megamek/src/megamek/client/ui/swing/tooltip/HexTooltip.java +++ b/megamek/src/megamek/client/ui/swing/tooltip/HexTooltip.java @@ -159,6 +159,16 @@ public static String getHexTip(Hex mhex, @Nullable Client client, GUIPreferences result.append("
"); } } + + if ((game != null) && game.getGroundObjects(mcoords).size() > 0) { + for (ICarryable groundObject : game.getGroundObjects(mcoords)) { + result.append(" "); + result.append(guiScaledFontHTML(UIUtil.uiWhite())); + result.append(groundObject.toString()); + result.append(""); + result.append("
"); + } + } return result.toString(); } diff --git a/megamek/src/megamek/common/BipedMech.java b/megamek/src/megamek/common/BipedMech.java index 2203256a7c0..b377da24cc0 100644 --- a/megamek/src/megamek/common/BipedMech.java +++ b/megamek/src/megamek/common/BipedMech.java @@ -85,6 +85,31 @@ public boolean canFlipArms() { return canFlip; } + + /** + * Returns true if the entity can pick up ground objects + */ + public boolean canPickupGroundObject() { + return !isProne() && + hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) || + hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM); + } + + /** + * The maximum tonnage of ground objects that can be picked up by this unit + */ + public double maxGroundObjectTonnage() { + double percentage = 0.0; + + if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM)) { + percentage += 0.05; + } + if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM)) { + percentage += 0.05; + } + + return getWeight() * percentage; + } @Override public int getWalkMP(MPCalculationSetting mpCalculationSetting) { diff --git a/megamek/src/megamek/common/Briefcase.java b/megamek/src/megamek/common/Briefcase.java index b25d7eee303..d27ef7853ae 100644 --- a/megamek/src/megamek/common/Briefcase.java +++ b/megamek/src/megamek/common/Briefcase.java @@ -50,6 +50,6 @@ public String getName() { } public String toString() { - return name + " (" + tonnage + ")"; + return name + " (" + tonnage + " tons)"; } } diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index 45ebbe4d07f..e4faf6f1a94 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -2761,6 +2761,20 @@ public boolean canUnjamRAC() { public boolean canFlipArms() { return false; } + + /** + * Returns true if the entity can pick up ground objects + */ + public boolean canPickupGroundObject() { + return false; + } + + /** + * The maximum tonnage of ground objects that can be picked up by this unit + */ + public double maxGroundObjectTonnage() { + return 0.0; + } /** * Returns this entity's original walking movement points diff --git a/megamek/src/megamek/common/Game.java b/megamek/src/megamek/common/Game.java index 99a52952624..83a957c8ad5 100644 --- a/megamek/src/megamek/common/Game.java +++ b/megamek/src/megamek/common/Game.java @@ -3311,6 +3311,27 @@ public List getGroundObjects(Coords coords) { return groundObjects.containsKey(coords) ? groundObjects.get(coords) : new ArrayList<>(); } + /** + * Get a list of all objects on the ground at the given coordinates + * that can be picked up by the given entity + */ + public List getGroundObjects(Coords coords, Entity entity) { + if (!groundObjects.containsKey(coords)) { + return new ArrayList<>(); + } + + double maxTonnage = entity.maxGroundObjectTonnage(); + ArrayList result = new ArrayList<>(); + + for (ICarryable object : groundObjects.get(coords)) { + if (maxTonnage >= object.getTonnage()) { + result.add(object); + } + } + + return result; + } + /** * @return Collection of objects on the ground. Best to use getGroundObjects(Coords) * if looking for objects in specific hex diff --git a/megamek/src/megamek/common/HexTarget.java b/megamek/src/megamek/common/HexTarget.java index 1fa780b8e25..d963b8b2a53 100644 --- a/megamek/src/megamek/common/HexTarget.java +++ b/megamek/src/megamek/common/HexTarget.java @@ -29,17 +29,6 @@ public HexTarget(Coords c, int nType) { m_bIgnite = (nType == Targetable.TYPE_HEX_IGNITE); } - /** - * Creates a new HexTarget given a set of coordinates and a type defined in Targetable. - * the board parameter is ignored. - */ - @Deprecated - public HexTarget(Coords c, Board board, int nType) { - m_coords = c; - m_type = nType; - m_bIgnite = (nType == Targetable.TYPE_HEX_IGNITE); - } - @Override public int getTargetType() { return m_type; diff --git a/megamek/src/megamek/common/MovePath.java b/megamek/src/megamek/common/MovePath.java index 0dcc284d871..ca11c5bb999 100644 --- a/megamek/src/megamek/common/MovePath.java +++ b/megamek/src/megamek/common/MovePath.java @@ -58,7 +58,7 @@ public enum MoveStepType { CLIMB_MODE_OFF, SWIM, DIG_IN, FORTIFY, SHAKE_OFF_SWARMERS, TAKEOFF, VTAKEOFF, LAND, ACC, DEC, EVADE, SHUTDOWN, STARTUP, SELF_DESTRUCT, ACCN, DECN, ROLL, OFF, RETURN, LAUNCH, THRUST, YAW, CRASH, RECOVER, RAM, HOVER, MANEUVER, LOOP, CAREFUL_STAND, JOIN, DROP, VLAND, MOUNT, UNDOCK, TAKE_COVER, - CONVERT_MODE, BOOTLEGGER, TOW, DISCONNECT, BRACE, CHAFF; + CONVERT_MODE, BOOTLEGGER, TOW, DISCONNECT, BRACE, CHAFF, PICKUP; /** * Whether this move step type will result in the unit entering a new hex diff --git a/megamek/src/megamek/common/Protomech.java b/megamek/src/megamek/common/Protomech.java index 18d86cc347a..21029f78466 100644 --- a/megamek/src/megamek/common/Protomech.java +++ b/megamek/src/megamek/common/Protomech.java @@ -24,7 +24,6 @@ import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Enumeration; import java.util.List; import java.util.Vector; import java.util.stream.Collectors; @@ -557,6 +556,31 @@ public double getArmorWeight() { public boolean hasRearArmor(int loc) { return false; } + + /** + * Returns true if the entity can pick up ground objects + */ + public boolean canPickupGroundObject() { + return !isProne() && + !(isLocationBad(Mech.LOC_LARM) || + isLocationBad(Mech.LOC_RARM)); + } + + /** + * The maximum tonnage of ground objects that can be picked up by this unit + */ + public double maxGroundObjectTonnage() { + double percentage = 0.0; + + if (!isLocationBad(Mech.LOC_LARM)) { + percentage += 0.05; + } + if (!isLocationBad(Mech.LOC_RARM)) { + percentage += 0.05; + } + + return getWeight() * percentage; + } @Override public int getRunMP(MPCalculationSetting mpCalculationSetting) { diff --git a/megamek/src/megamek/common/TripodMech.java b/megamek/src/megamek/common/TripodMech.java index 92ee039874d..e1b1b9e2802 100644 --- a/megamek/src/megamek/common/TripodMech.java +++ b/megamek/src/megamek/common/TripodMech.java @@ -134,6 +134,31 @@ public boolean canFlipArms() { return canFlip; } + + /** + * Returns true if the entity can pick up ground objects + */ + public boolean canPickupGroundObject() { + return !isProne() && + hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) || + hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM); + } + + /** + * The maximum tonnage of ground objects that can be picked up by this unit + */ + public double maxGroundObjectTonnage() { + double percentage = 0.0; + + if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM)) { + percentage += 0.05; + } + if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM)) { + percentage += 0.05; + } + + return getWeight() * percentage; + } @Override public int getWalkMP(MPCalculationSetting mpCalculationSetting) { From 93e386f85fc70dca47c0a158f0be92b5ec6970d8 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Wed, 10 Jul 2024 23:35:23 -0400 Subject: [PATCH 08/35] refactor minefield display popup --- .../ui/swing/DeployMinefieldDisplay.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java index 5886932c1c7..b81083c472c 100644 --- a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java +++ b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java @@ -25,6 +25,8 @@ import java.awt.event.MouseEvent; import java.util.*; +import javax.swing.JOptionPane; + public class DeployMinefieldDisplay extends StatusBarPhaseDisplay { private static final long serialVersionUID = -1243277953037374936L; @@ -282,19 +284,11 @@ private void deployMinefield(Coords coords) { ICarryable toDeploy = groundObjects.get(0); if (groundObjects.size() > 1) { - // lol use this instead - /*String input = (String) JOptionPane.showInputDialog( - clientgui.getFrame(), - Messages.getString("MovementDisplay.UnloadUnitDialog.message", ce.getShortName(), ce.getUnusedString()), - Messages.getString("MovementDisplay.UnloadUnitDialog.title"), - JOptionPane.QUESTION_MESSAGE, null, - SharedUtility.getDisplayArray(loadedUnits), null); - getTargetPicked() to get result*/ - DropdownDialog ddl = new DropdownDialog<>(clientgui.frame, - "DeployMinefieldDisplay.deployCarriableDialogHeader", - "DeployMinefieldDisplay.deployCarriableDialogHeader", clientgui.getClient().getLocalPlayer().getGroundObjectsToPlace()); - ddl.setVisible(true); - toDeploy = ddl.getSelectedItem(); + String title = "Choose Cargo to Place"; + String body = "Choose the cargo to place:"; + toDeploy = (ICarryable) JOptionPane.showInputDialog(clientgui.getFrame(), + body, title, JOptionPane.QUESTION_MESSAGE, null, + groundObjects.toArray(), groundObjects.get(0)); } game.placeGroundObject(coords, toDeploy); From 5e8d0fffe9d3b60536d0fc3829f2a6c06d71163a Mon Sep 17 00:00:00 2001 From: NickAragua Date: Wed, 10 Jul 2024 23:38:08 -0400 Subject: [PATCH 09/35] remove unnecessary dialog class --- .../ui/swing/dialog/DropdownDialog.java | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 megamek/src/megamek/client/ui/swing/dialog/DropdownDialog.java diff --git a/megamek/src/megamek/client/ui/swing/dialog/DropdownDialog.java b/megamek/src/megamek/client/ui/swing/dialog/DropdownDialog.java deleted file mode 100644 index e5f09fa253a..00000000000 --- a/megamek/src/megamek/client/ui/swing/dialog/DropdownDialog.java +++ /dev/null @@ -1,43 +0,0 @@ -package megamek.client.ui.swing.dialog; - -import java.awt.Container; -import java.util.List; - -import javax.swing.JFrame; -import javax.swing.JPanel; - -import megamek.client.ui.baseComponents.AbstractDialog; -import megamek.client.ui.baseComponents.MMComboBox; - -/** - * This class displays a single dropdown from which we can make a single choice - * @param - */ -public class DropdownDialog extends AbstractDialog { - - List dataSource; - MMComboBox comboBox; - - public DropdownDialog(JFrame frame, String name, String title, List data) { - super(frame, name, title); - dataSource = data; - initialize(); - } - - @Override - protected Container createCenterPane() { - comboBox = new MMComboBox(getName(), dataSource); - comboBox.setSelectedIndex(0); - - JPanel result = new JPanel(); - result.add(comboBox); - return result; - } - - /** - * Returns the item that is currently selected in the combobox. - */ - public T getSelectedItem() { - return (T) comboBox.getSelectedItem(); - } -} From 812af5f48afe984722e71fb68e68232b685accdd Mon Sep 17 00:00:00 2001 From: NickAragua Date: Thu, 11 Jul 2024 16:50:13 -0400 Subject: [PATCH 10/35] server side processing; firing restrictions --- .../i18n/megamek/client/messages.properties | 1 + .../megamek/common/report-messages.properties | 1 + megamek/src/megamek/client/Client.java | 15 ++++ .../ui/swing/DeployMinefieldDisplay.java | 2 +- .../client/ui/swing/MovementDisplay.java | 8 +- .../swing/boardview/GroundObjectSprite.java | 83 +++++++++++++++++++ .../boardview/GroundObjectSpriteHandler.java | 4 +- .../client/ui/swing/boardview/StepSprite.java | 4 + megamek/src/megamek/common/BipedMech.java | 3 +- megamek/src/megamek/common/Entity.java | 52 ++++++++++++ megamek/src/megamek/common/Game.java | 8 +- megamek/src/megamek/common/Mech.java | 34 ++++++++ megamek/src/megamek/common/MovePath.java | 7 ++ megamek/src/megamek/common/MoveStep.java | 44 +++++++++- .../common/actions/ClubAttackAction.java | 7 ++ .../common/actions/PunchAttackAction.java | 5 ++ .../common/actions/PushAttackAction.java | 6 ++ .../common/actions/WeaponAttackAction.java | 9 ++ .../common/net/enums/PacketCommand.java | 1 + megamek/src/megamek/server/GameManager.java | 48 ++++++++++- 20 files changed, 330 insertions(+), 12 deletions(-) create mode 100644 megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties index 0876f97c982..d44e0cf5ceb 100644 --- a/megamek/i18n/megamek/client/messages.properties +++ b/megamek/i18n/megamek/client/messages.properties @@ -3929,6 +3929,7 @@ WeaponAttackAction.BPodAtInf=B-Pod firing at infantry WeaponAttackAction.BPodOnlyAtInf=B-Pods can't target non-infantry. WeaponAttackAction.CantAimAndCallShots=you can't combine aimed shots and called shots. WeaponAttackAction.CantClearMines=Weapon can't clear minefields. +WeaponAttackAction.CantFireWhileCarryingCargo=Carrying cargo prevents arm/torso weapon firing. WeaponAttackAction.CantFireWhileGrappled=Can only fire head and front torso weapons when grappled. WeaponAttackAction.CantFireWithOtherWeapons=Already firing a weapon that can only be fired by itself! (%s) WeaponAttackAction.CantFireArmsAndMainGun=Can't fire arm-mounted weapons and the main gun in the same turn. diff --git a/megamek/i18n/megamek/common/report-messages.properties b/megamek/i18n/megamek/common/report-messages.properties index 6a9c54306bc..26069825bc4 100755 --- a/megamek/i18n/megamek/common/report-messages.properties +++ b/megamek/i18n/megamek/common/report-messages.properties @@ -254,6 +254,7 @@ 2510=, Starts to flip. 2511=, Capsizes and sinks. 2512= deploys a chaff pod. +2513= picks up . 3000=Weapon Attack Phase------------------- 3003=Inferno fire (bombs) started in hex . diff --git a/megamek/src/megamek/client/Client.java b/megamek/src/megamek/client/Client.java index 09d7a99ed0f..108697a5c83 100644 --- a/megamek/src/megamek/client/Client.java +++ b/megamek/src/megamek/client/Client.java @@ -385,6 +385,13 @@ public void sendAddSquadron(FighterSquadron fs, Collection fighterIds) public void sendDeployMinefields(Vector minefields) { send(new Packet(PacketCommand.DEPLOY_MINEFIELDS, minefields)); } + + /** + * Sends an updated state of ground objects (i.e. cargo etc) + */ + public void sendDeployGroundObjects(Map> groundObjects) { + send(new Packet(PacketCommand.UPDATE_GROUND_OBJECTS, groundObjects)); + } /** * Sends a "set Artillery Autohit Hexes" packet @@ -576,6 +583,11 @@ protected void receiveEntityVisibilityIndicator(Packet packet) { game.processGameEvent(new GameEntityChangeEvent(this, e)); } } + + @SuppressWarnings("unchecked") + protected void receiveUpdateGroundObjects(Packet packet) { + game.setGroundObjects((Map>) packet.getObject(0)); + } @SuppressWarnings("unchecked") protected void receiveDeployMinefields(Packet packet) { @@ -890,6 +902,9 @@ protected boolean handleGameSpecificPacket(Packet packet) { case REMOVE_MINEFIELD: receiveRemoveMinefield(packet); break; + case UPDATE_GROUND_OBJECTS: + receiveUpdateGroundObjects(packet); + break; case ADD_SMOKE_CLOUD: SmokeCloud cloud = (SmokeCloud) packet.getObject(0); game.addSmokeCloud(cloud); diff --git a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java index b81083c472c..464f2a1cc93 100644 --- a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java +++ b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java @@ -15,7 +15,6 @@ import megamek.client.event.BoardViewEvent; import megamek.client.ui.Messages; -import megamek.client.ui.swing.dialog.DropdownDialog; import megamek.client.ui.swing.widget.MegamekButton; import megamek.common.*; import megamek.common.event.GamePhaseChangeEvent; @@ -519,6 +518,7 @@ public void actionPerformed(ActionEvent ev) { @Override public void ready() { endMyTurn(); + clientgui.getClient().sendDeployGroundObjects(clientgui.getClient().getGame().getGroundObjects()); clientgui.getClient().sendDeployMinefields(deployedMinefields); clientgui.getClient().sendPlayerInfo(); } diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index 66a796a73a9..a9d5fd62167 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -985,6 +985,11 @@ private void updateAeroButtons() { */ @Override protected void updateDonePanel() { + // we don't need to be doing all this stuff if we're not showing this + if (!getClientgui().getClient().getGame().getPhase().isMovement()) { + return; + } + if (cmd == null || cmd.length() == 0) { updateDonePanelButtons(Messages.getString("MovementDisplay.Move"), Messages.getString("MovementDisplay.Skip"), false, null); return; @@ -1122,6 +1127,7 @@ private synchronized void endMyTurn() { clientgui.getBoardView().clearMovementData(); clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); + cmd = null; } /** @@ -2810,7 +2816,7 @@ private void updatePickupButton() { final Entity ce = ce(); // there has to be an entity, objects are on the ground, // the entity can pick them up - if ((ce == null) || (game().getGroundObjects(finalPosition(), ce()).size() <= 0) || + if ((ce == null) || (game().getGroundObjects(finalPosition(), ce).size() <= 0) || ((cmd.getLastStep() != null) && (cmd.getLastStep().getType() == MoveStepType.PICKUP))) { setPickupEnabled(false); diff --git a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java new file mode 100644 index 00000000000..479d9596b86 --- /dev/null +++ b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java @@ -0,0 +1,83 @@ +/* + * 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 . + */ +package megamek.client.ui.swing.boardview; + +import java.awt.Color; +import java.awt.Graphics2D; + +import megamek.client.ui.swing.tileset.HexTileset; +import megamek.client.ui.swing.util.FontHandler; +import megamek.client.ui.swing.util.StringDrawer; +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.Coords; + +/** + * Represents structure CF warnings for entities during deployment + * and movement phase that will collapse if the entity lands-on + * or is deployed on that structure. + * + * From TW: If a units tonnage exceeds the CF of a building + * or bridge, it will collapse. (Or the sum of tonnage of stacked + * units if multiple units occupy the hex) + */ +public class GroundObjectSprite extends HexSprite { + + private static final int TEXT_SIZE = HexTileset.HEX_H / 2; + private static final Color TEXT_COLOR = new Color(255, 255, 255, 128); + private static final Color OUTLINE_COLOR = new Color(40, 40, 40,200); + + private static final int HEX_CENTER_X = HexTileset.HEX_W / 2; + private static final int HEX_CENTER_Y = HexTileset.HEX_H / 2; + + // Draw a special character 'warning sign'. + private final StringDrawer xWriter = new StringDrawer("\uF3C1") + .at(HEX_CENTER_X, HEX_CENTER_Y) + .color(TEXT_COLOR) + .fontSize(TEXT_SIZE) + .absoluteCenter().outline(OUTLINE_COLOR, 2.5f); + + /** + * @param boardView1 - parent BoardView object this sprite will be displayed on. + * @param loc - Hex location coordinates of building or bridge where warning will be visible. + */ + public GroundObjectSprite(BoardView boardView1, Coords loc) { + super(boardView1, loc); + } + + @Override + public void prepare() { + Graphics2D graph = spriteSetup(); + xWriter.draw(graph); + graph.dispose(); + } + + /* + * Standard Hex Sprite 2D Graphics setup. Creates the context, base hex image + * settings, scale, and fonts. + */ + private Graphics2D spriteSetup() { + updateBounds(); + image = createNewHexImage(); + Graphics2D graph = (Graphics2D) image.getGraphics(); + UIUtil.setHighQualityRendering(graph); + graph.scale(bv.scale, bv.scale); + graph.setFont(FontHandler.symbolFont()); + return graph; + } +} \ No newline at end of file diff --git a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java index c813dcc9577..19ade3a1190 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java +++ b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java @@ -39,8 +39,8 @@ public void setGroundObjectSprites(Iterable objectCoordList) { currentGroundObjectList = objectCoordList; if (currentGroundObjectList != null) { for (Coords coords : currentGroundObjectList) { - CollapseWarningSprite cws = new CollapseWarningSprite(boardView, coords); - currentSprites.add(cws); + GroundObjectSprite gos = new GroundObjectSprite(boardView, coords); + currentSprites.add(gos); } } boardView.addSprites(currentSprites); diff --git a/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java b/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java index f184fd530da..4c7cfb527f6 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java +++ b/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java @@ -205,6 +205,10 @@ public void prepare() { String load = Messages.getString("BoardView1.Load"); drawAnnouncement(g2D, load, step, col); break; + case PICKUP: + String pickup = Messages.getString("MovementDisplay.movePickup"); + drawAnnouncement(g2D, pickup, step, col); + break; case TOW: String tow = Messages.getString("BoardView1.Tow"); drawAnnouncement(g2D, tow, step, col); diff --git a/megamek/src/megamek/common/BipedMech.java b/megamek/src/megamek/common/BipedMech.java index b377da24cc0..b112f817ff7 100644 --- a/megamek/src/megamek/common/BipedMech.java +++ b/megamek/src/megamek/common/BipedMech.java @@ -90,8 +90,7 @@ public boolean canFlipArms() { * Returns true if the entity can pick up ground objects */ public boolean canPickupGroundObject() { - return !isProne() && - hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) || + return hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) || hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM); } diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index e4faf6f1a94..4ee8acd9a7e 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -836,7 +836,17 @@ public abstract class Entity extends TurnOrdered implements Transporter, Targeta * Primarily used by Princess to speed up TAG utility calculations. */ protected ArrayList incomingGuidedAttacks; + + /** + * Map containing all the objects this entity is carrying as cargo, indexed by location + */ + private Map carriedObjects = new HashMap<>(); + /** + * Round-long flag indicating that this entity has picked up an object this round. + */ + private boolean pickedUpObject; + /** The icon for this unit; This is empty unless the unit file has an embedded icon. */ protected Base64Image icon = new Base64Image(); @@ -878,6 +888,7 @@ public Entity() { initTechAdvancement(); offBoardShotObservers = new HashSet<>(); incomingGuidedAttacks = new ArrayList(); + carriedObjects = new HashMap<>(); } /** @@ -2775,6 +2786,41 @@ public boolean canPickupGroundObject() { public double maxGroundObjectTonnage() { return 0.0; } + + /** + * Put a ground object into the given location + */ + public void pickupGroundObject(ICarryable carryable, int location) { + if (carriedObjects == null) { + carriedObjects = new HashMap<>(); + } + + carriedObjects.put(location, carryable); + pickedUpObject = true; + } + + /** + * Remove a ground object from the given location + */ + public void dropGroundObject(int location) { + carriedObjects.remove(location); + pickedUpObject = true; + } + + /** + * Get the object carried in the given location. May return null. + */ + public ICarryable getCarriedObject(int location) { + return carriedObjects.get(location); + } + + public Map getCarriedObjects() { + return carriedObjects; + } + + public void setCarriedObjects(Map value) { + carriedObjects = value; + } /** * Returns this entity's original walking movement points @@ -6542,6 +6588,8 @@ public void newRound(int roundNumber) { setClimbMode(GUIP.getMoveDefaultClimbMode()); + pickedUpObject = false; + setTurnInterrupted(false); } @@ -12208,6 +12256,10 @@ public void setTurnInterrupted(boolean interrupted) { public boolean turnWasInterrupted() { return turnWasInterrupted; } + + public boolean pickedUpObjectThisTurn() { + return pickedUpObject; + } public Vector getSensors() { return sensors; diff --git a/megamek/src/megamek/common/Game.java b/megamek/src/megamek/common/Game.java index 83a957c8ad5..a64187ab837 100644 --- a/megamek/src/megamek/common/Game.java +++ b/megamek/src/megamek/common/Game.java @@ -139,7 +139,7 @@ public final class Game extends AbstractGame implements Serializable, PlanetaryC /** * Piles of carry-able objects, sorted by coordinates */ - private HashMap> groundObjects = new HashMap<>(); + private Map> groundObjects = new HashMap<>(); /** * Constructor @@ -3336,14 +3336,16 @@ public List getGroundObjects(Coords coords, Entity entity) { * @return Collection of objects on the ground. Best to use getGroundObjects(Coords) * if looking for objects in specific hex */ - public HashMap> getGroundObjects() { + public Map> getGroundObjects() { + // ground objects not going out to server for some stupid farking reason; + // check deployment display return groundObjects; } /** * @param groundObjects the groundObjects to set */ - public void setGroundObjects(HashMap> groundObjects) { + public void setGroundObjects(Map> groundObjects) { this.groundObjects = groundObjects; } diff --git a/megamek/src/megamek/common/Mech.java b/megamek/src/megamek/common/Mech.java index d9425f84968..b5bf5acae8d 100644 --- a/megamek/src/megamek/common/Mech.java +++ b/megamek/src/megamek/common/Mech.java @@ -37,6 +37,7 @@ import java.math.BigInteger; import java.time.LocalDate; import java.util.*; +import static java.util.Map.entry; import java.util.stream.Collectors; /** @@ -183,6 +184,26 @@ public abstract class Mech extends Entity { "Small Command", "Tripod Industrial", "Superheavy Tripod Industrial" }; public static final String FULL_HEAD_EJECT_STRING = "Full Head Ejection System"; + + /** + * Contains a mapping of locations which are blocked when carrying cargo in the "key" location + */ + public static final Map> BLOCKED_FIRING_LOCATIONS; + + static { + BLOCKED_FIRING_LOCATIONS = new HashMap<>(); + BLOCKED_FIRING_LOCATIONS.put(LOC_LARM, new ArrayList<>()); + BLOCKED_FIRING_LOCATIONS.get(LOC_LARM).add(LOC_LARM); + BLOCKED_FIRING_LOCATIONS.get(LOC_LARM).add(LOC_LT); + BLOCKED_FIRING_LOCATIONS.get(LOC_LARM).add(LOC_CT); + BLOCKED_FIRING_LOCATIONS.get(LOC_LARM).add(LOC_RT); + + BLOCKED_FIRING_LOCATIONS.put(LOC_RARM, new ArrayList<>()); + BLOCKED_FIRING_LOCATIONS.get(LOC_RARM).add(LOC_RARM); + BLOCKED_FIRING_LOCATIONS.get(LOC_RARM).add(LOC_LT); + BLOCKED_FIRING_LOCATIONS.get(LOC_RARM).add(LOC_CT); + BLOCKED_FIRING_LOCATIONS.get(LOC_RARM).add(LOC_RT); + } // jump types public static final int JUMP_UNKNOWN = -1; @@ -6454,6 +6475,19 @@ public int getGenericBattleValue() { public boolean getsAutoExternalSearchlight() { return true; } + + public boolean canFireWeapon(int location) { + // loop through everything we are carrying + // if the weapon location is blocked by the carried object, then we cannot fire the weapon + for (int carriedObjectLocation : getCarriedObjects().keySet()) { + if (BLOCKED_FIRING_LOCATIONS.containsKey(carriedObjectLocation) && + BLOCKED_FIRING_LOCATIONS.get(carriedObjectLocation).contains(location)) { + return false; + } + } + + return true; + } public static Map getAllCockpitCodeName() { Map result = new HashMap<>(); diff --git a/megamek/src/megamek/common/MovePath.java b/megamek/src/megamek/common/MovePath.java index ca11c5bb999..96bf94027f8 100644 --- a/megamek/src/megamek/common/MovePath.java +++ b/megamek/src/megamek/common/MovePath.java @@ -541,6 +541,11 @@ && getMpUsed() > getCachedEntityState().getWalkMP() step.setMovementType(EntityMovementType.MOVE_ILLEGAL); } } + + // if we have a PICKUP, then we can't do anything else after it + if (contains(MoveStepType.PICKUP)) { + step.setMovementType(EntityMovementType.MOVE_ILLEGAL); + } } public void compile(final Game g, final Entity en) { @@ -575,6 +580,8 @@ public void compile(final Game g, final Entity en, boolean clip) { step = new MoveStep(this, step.getType(), step.hasNoCost()); } else if (null != step.getMinefield()) { step = new MoveStep(this, step.getType(), step.getMinefield()); + } else if (null != step.getAdditionalData(MoveStep.CARGO_PICKUP_INDEX)) { + step = new MoveStep(this, step.getType(), step.getAdditionalData(MoveStep.CARGO_PICKUP_INDEX)); } else { step = new MoveStep(this, step.getType()); } diff --git a/megamek/src/megamek/common/MoveStep.java b/megamek/src/megamek/common/MoveStep.java index 71881773e38..c84fadbff31 100644 --- a/megamek/src/megamek/common/MoveStep.java +++ b/megamek/src/megamek/common/MoveStep.java @@ -28,7 +28,9 @@ import java.io.Serializable; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map; import java.util.TreeMap; import java.util.Vector; @@ -40,6 +42,9 @@ */ public class MoveStep implements Serializable { private static final long serialVersionUID = -6075640793056182285L; + public static final int CARGO_PICKUP_INDEX = 0; + public static final int CARGO_PICKUP_LOCATION = 1; + private MoveStepType type; private int targetId = Entity.NONE; private int targetType = Targetable.TYPE_ENTITY; @@ -156,6 +161,12 @@ public class MoveStep implements Serializable { private boolean maneuver = false; int braceLocation = Entity.LOC_NONE; + + /** + * A map used to hold any additional data that this move step requires. + * Preferable to constantly adding new fields for low-usage one-shot data + */ + Map additionalData = new HashMap<>(); private Minefield mf; @@ -255,8 +266,10 @@ public MoveStep(MovePath path, MoveStepType type, int additionalIntData) { if (type == MoveStepType.BRACE) { this.braceLocation = additionalIntData; - } else { + } else if (type == MoveStepType.LAY_MINE) { this.mineToLay = additionalIntData; + } else if (type == MoveStepType.PICKUP) { + this.additionalData.put(CARGO_PICKUP_INDEX, additionalIntData); } } @@ -402,6 +415,8 @@ public String toString() { return "Brace"; case CHAFF: return "Chaff"; + case PICKUP: + return "Pickup"; default: return "???"; } @@ -469,6 +484,10 @@ public Coords getTargetPosition() { return targetPos; } + public Integer getAdditionalData(int key) { + return additionalData.containsKey(key) ? additionalData.get(key) : null; + } + public TreeMap> getLaunched() { if (launched == null) { launched = new TreeMap<>(); @@ -1211,6 +1230,7 @@ public void copy(final Game game, MoveStep prev) { nStraight = prev.nStraight; nDown = prev.nDown; nMoved = prev.nMoved; + additionalData = new HashMap<>(additionalData); } /** @@ -2815,6 +2835,10 @@ && isHullDown()) { danger = true; } } + + if (stepType == MoveStepType.PICKUP) { + movementType = EntityMovementType.MOVE_NONE; + } // check if this movement is illegal for reasons other than points if (!isMovementPossible(game, lastPos, prev.getElevation(), cachedEntityState) @@ -3371,6 +3395,10 @@ && isThisStepBackwards() if (type == MoveStepType.MOUNT) { return true; } + + if (type == MoveStepType.PICKUP) { + return !isProne(); + } // The entity is trying to load. Check for a valid move. if (type == MoveStepType.LOAD) { @@ -4100,7 +4128,21 @@ public boolean isFacingChangeManeuver() { public Minefield getMinefield() { return mf; } + + /** + * For serialization purposes + */ + public Map getAdditionalData() { + return additionalData; + } + /** + * Setter for serialization purposes + */ + public void setAdditionalData(Map value) { + additionalData = value; + } + /** * Should we treat this movement as if it is occurring for an aerodyne unit * flying in atmosphere? diff --git a/megamek/src/megamek/common/actions/ClubAttackAction.java b/megamek/src/megamek/common/actions/ClubAttackAction.java index 23dae2bd785..76798bc263c 100644 --- a/megamek/src/megamek/common/actions/ClubAttackAction.java +++ b/megamek/src/megamek/common/actions/ClubAttackAction.java @@ -285,6 +285,13 @@ public static ToHitData toHit(Game game, int attackerId, if (!(ae instanceof Mech)) { return new ToHitData(TargetRoll.IMPOSSIBLE, "Non-mechs can't club"); } + + // if somehow carrying cargo while holding a club + if (!((Mech) ae).canFireWeapon(Mech.LOC_LARM) || + !((Mech) ae).canFireWeapon(Mech.LOC_LARM) ) { + return new ToHitData(TargetRoll.IMPOSSIBLE, + Messages.getString("WeaponAttackAction.CantFireWhileCarryingCargo")); + } // Quads can't club... // except for torso mounted industrial tools of course! diff --git a/megamek/src/megamek/common/actions/PunchAttackAction.java b/megamek/src/megamek/common/actions/PunchAttackAction.java index 90b2f0c31ff..ecc5f9ad18f 100644 --- a/megamek/src/megamek/common/actions/PunchAttackAction.java +++ b/megamek/src/megamek/common/actions/PunchAttackAction.java @@ -176,6 +176,11 @@ protected static String toHitIsImpossible(Game game, Entity ae, if (ae.hasActiveShield(armLoc)) { return "Cannot punch with shield in active mode"; } + + if (!((Mech) ae).canFireWeapon(armLoc)) { + return Messages.getString("WeaponAttackAction.CantFireWhileCarryingCargo"); + } + return null; } diff --git a/megamek/src/megamek/common/actions/PushAttackAction.java b/megamek/src/megamek/common/actions/PushAttackAction.java index 4aa85d7ff76..5857ac0224d 100644 --- a/megamek/src/megamek/common/actions/PushAttackAction.java +++ b/megamek/src/megamek/common/actions/PushAttackAction.java @@ -62,6 +62,12 @@ protected static String toHitIsImpossible(Game game, Entity ae, Targetable targe if (ae.isEvading()) { return "attacker is evading."; } + + if ((ae instanceof Mech) && + !((Mech) ae).canFireWeapon(Mech.LOC_LARM) || + !((Mech) ae).canFireWeapon(Mech.LOC_LARM) ) { + return Messages.getString("WeaponAttackAction.CantFireWhileCarryingCargo"); + } if (!game.getOptions().booleanOption(OptionsConstants.BASE_FRIENDLY_FIRE)) { // a friendly unit can never be the target of a direct attack. diff --git a/megamek/src/megamek/common/actions/WeaponAttackAction.java b/megamek/src/megamek/common/actions/WeaponAttackAction.java index dd9ebaef9c8..b801ba0dca0 100644 --- a/megamek/src/megamek/common/actions/WeaponAttackAction.java +++ b/megamek/src/megamek/common/actions/WeaponAttackAction.java @@ -1078,6 +1078,15 @@ private static String toHitIsImpossible(Game game, Entity ae, int attackerId, Ta } } } + + // can't fire arm/forward facing torso weapons if carrying cargo in hands + if ((weapon != null)) { + int loc = weapon.getLocation(); + + if ((ae instanceof Mech) && !weapon.isRearMounted() && !((Mech) ae).canFireWeapon(loc)) { + return Messages.getString("WeaponAttackAction.CantFireWhileCarryingCargo"); + } + } // Only large spacecraft can shoot while evading if (ae.isEvading() && !(ae instanceof Dropship) && !(ae instanceof Jumpship)) { diff --git a/megamek/src/megamek/common/net/enums/PacketCommand.java b/megamek/src/megamek/common/net/enums/PacketCommand.java index b9067d5001a..41faffa8218 100644 --- a/megamek/src/megamek/common/net/enums/PacketCommand.java +++ b/megamek/src/megamek/common/net/enums/PacketCommand.java @@ -112,6 +112,7 @@ public enum PacketCommand { REMOVE_MINEFIELD, SENDING_MINEFIELDS, UPDATE_MINEFIELDS, + UPDATE_GROUND_OBJECTS, REROLL_INITIATIVE, UNLOAD_STRANDED, SET_ARTILLERY_AUTOHIT_HEXES, diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 10b31a10af3..6ebae2fa2f5 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -36,8 +36,6 @@ import megamek.common.planetaryconditions.PlanetaryConditions; import megamek.common.planetaryconditions.Wind; import megamek.common.preference.PreferenceManager; -import megamek.common.Report; -import megamek.common.ReportMessages; import megamek.common.util.*; import megamek.common.util.fileUtils.MegaMekFile; import megamek.common.verifier.*; @@ -630,6 +628,9 @@ public void handlePacket(int connId, Packet packet) { case DEPLOY_MINEFIELDS: receiveDeployMinefields(packet, connId); break; + case UPDATE_GROUND_OBJECTS: + receiveGroundObjectUpdate(packet, connId); + break; case ENTITY_ATTACK: receiveAttack(packet, connId); break; @@ -7298,6 +7299,37 @@ else if ((step.getElevation() + entity.height()) == 0) { } } // End STEP_MOUNT + if (step.getType() == MovePath.MoveStepType.PICKUP) { + var groundObjects = game.getGroundObjects(step.getPosition()); + Integer cargoPickupIndex = step.getAdditionalData(MoveStep.CARGO_PICKUP_INDEX); + + // there have to be objects on the ground and we have to be trying to pick up one of them + if ((groundObjects.size() > 0) && + (cargoPickupIndex != null) && (cargoPickupIndex >= 0) && (cargoPickupIndex < groundObjects.size())) { + + ICarryable pickupTarget = groundObjects.get(cargoPickupIndex); + if (entity.maxGroundObjectTonnage() >= pickupTarget.getTonnage()) { + groundObjects.remove(cargoPickupIndex); + // hack for now: goes into both arms + entity.pickupGroundObject(pickupTarget, Mech.LOC_LARM); + entity.pickupGroundObject(pickupTarget, Mech.LOC_RARM); + + r = new Report(2513); + r.subject = entity.getId(); + r.add(entity.getDisplayName()); + r.add(pickupTarget.toString()); + addReport(r); + + entityUpdate(entity.getId()); + return; // a pickup should be the last step + } else { + // add report about entity not being able to pick it up or maybe log it or something + } + } else { + // add report about there not being a valid object to pick up + } + } + // handle fighter recovery, and also DropShip docking with another large craft if (step.getType() == MovePath.MoveStepType.RECOVER) { @@ -12326,6 +12358,18 @@ private void receiveArtyAutoHitHexes(Packet packet, int connId) { } endCurrentTurn(null); } + + /** + * Receives an updated data structure containing carryable objects on the ground + */ + private void receiveGroundObjectUpdate(Packet packet, int connId) { + Map> groundObjects = (Map>) packet.getObject(0); + + getGame().setGroundObjects(groundObjects); + + // make sure to update the other clients with the new ground objects data structure + send(packet); + } /** * receive a packet that contains minefields From fb6cb51a0746bbdf589106240f60c5c3c01819cf Mon Sep 17 00:00:00 2001 From: NickAragua Date: Thu, 11 Jul 2024 23:44:38 -0400 Subject: [PATCH 11/35] push tuning --- .../common/actions/PushAttackAction.java | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/megamek/src/megamek/common/actions/PushAttackAction.java b/megamek/src/megamek/common/actions/PushAttackAction.java index 5857ac0224d..10639495b44 100644 --- a/megamek/src/megamek/common/actions/PushAttackAction.java +++ b/megamek/src/megamek/common/actions/PushAttackAction.java @@ -46,11 +46,7 @@ public ToHitData toHit(Game game) { */ protected static String toHitIsImpossible(Game game, Entity ae, Targetable target) { String physicalImpossible = PhysicalAttackAction.toHitIsImpossible(game, ae, target); - String extendedBladeImpossible = null; - if ((ae instanceof Mech) && ((Mech) ae).hasExtendedRetractableBlade()) { - extendedBladeImpossible = "Extended retractable blade"; - } - + if (physicalImpossible != null) { return physicalImpossible; } @@ -58,29 +54,19 @@ protected static String toHitIsImpossible(Game game, Entity ae, Targetable targe if (ae.getGrappled() != Entity.NONE) { return "Unit Grappled"; } - - if (ae.isEvading()) { - return "attacker is evading."; - } + // can't push if carrying any cargo per TW if ((ae instanceof Mech) && !((Mech) ae).canFireWeapon(Mech.LOC_LARM) || !((Mech) ae).canFireWeapon(Mech.LOC_LARM) ) { return Messages.getString("WeaponAttackAction.CantFireWhileCarryingCargo"); - } - - if (!game.getOptions().booleanOption(OptionsConstants.BASE_FRIENDLY_FIRE)) { - // a friendly unit can never be the target of a direct attack. - if ((target.getTargetType() == Targetable.TYPE_ENTITY) - && ((((Entity) target).getOwnerId() == ae.getOwnerId()) - || ((((Entity) target).getOwner().getTeam() != Player.TEAM_NONE) - && (ae.getOwner().getTeam() != Player.TEAM_NONE) - && (ae.getOwner().getTeam() == ((Entity) target).getOwner().getTeam())))) { - return "A friendly unit can never be the target of a direct attack."; - } + } + + if ((ae instanceof Mech) && ((Mech) ae).hasExtendedRetractableBlade()) { + return "Extended retractable blade"; } - - return extendedBladeImpossible; + + return null; } /** @@ -254,6 +240,11 @@ public static ToHitData toHit(Game game, int attackerId, Targetable target) { return new ToHitData(TargetRoll.IMPOSSIBLE, "Invalid attack"); } + String otherImpossible = toHitIsImpossible(game, ae, target); + if (otherImpossible != null) { + return new ToHitData(TargetRoll.IMPOSSIBLE, otherImpossible); + } + // Set the base BTH int base = ae.getCrew().getPiloting() - 1; From 0d7a8ccbac4665769c9e63d70e8a74da24ee2259 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Fri, 12 Jul 2024 18:50:26 -0400 Subject: [PATCH 12/35] adjustments --- .../i18n/megamek/client/messages.properties | 3 +- .../client/ui/swing/MovementDisplay.java | 43 ++++++++++++++----- megamek/src/megamek/common/BipedMech.java | 8 ++-- megamek/src/megamek/common/Entity.java | 31 ++++++++++++- megamek/src/megamek/common/Game.java | 9 ++-- megamek/src/megamek/common/Mech.java | 22 ++++------ megamek/src/megamek/common/Protomech.java | 36 +++++++++++++--- megamek/src/megamek/common/TripodMech.java | 9 ++-- megamek/src/megamek/server/GameManager.java | 14 ++++-- 9 files changed, 127 insertions(+), 48 deletions(-) diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties index d44e0cf5ceb..cd9e9e8c05e 100644 --- a/megamek/i18n/megamek/client/messages.properties +++ b/megamek/i18n/megamek/client/messages.properties @@ -2425,7 +2425,8 @@ MovementDisplay.butLoad=Load MovementDisplay.butLand=Land MovementDisplay.butJoin=Join MovementDisplay.butManeuver=Maneuver -MovementDisplay.movePickup=Pick up +MovementDisplay.moveDropCargo=Drop Cargo +MovementDisplay.movePickupCargo=Pick Up Cargo MovementDisplay.moveModeConvert=Convert Mode MovementDisplay.moveModeLeg=Walk MovementDisplay.moveModeTrack=Engage tracks diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index a9d5fd62167..016e890635a 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -180,7 +180,8 @@ public enum MoveCommand implements PhaseCommand { MOVE_LONGEST_WALK("MoveLongestWalk", CMD_NONE), // Traitor MOVE_TRAITOR("Traitor", CMD_NONE), - MOVE_PICKUP("movePickup", CMD_MECH | CMD_PROTOMECH), + MOVE_PICKUP_CARGO("movePickupCargo", CMD_MECH | CMD_PROTOMECH), + MOVE_DROP_CARGO("moveDropCargo", CMD_MECH | CMD_PROTOMECH), MOVE_MORE("MoveMore", CMD_NONE); /** @@ -1198,7 +1199,8 @@ private void disableButtons() { setManeuverEnabled(false); setStrafeEnabled(false); setBombEnabled(false); - setPickupEnabled(false); + setPickupCargoEnabled(false); + setDropCargoEnabled(false); getBtn(MoveCommand.MOVE_CLIMB_MODE).setEnabled(false); getBtn(MoveCommand.MOVE_DIG_IN).setEnabled(false); @@ -2808,22 +2810,36 @@ private synchronized void updateLoadButtons() { updateUnloadButton(); updateTowingButtons(); updateMountButton(); - updatePickupButton(); + updatePickupCargoButton(); + updateDropCargoButton(); } - /** Updates the status of the "pickup" button */ - private void updatePickupButton() { + /** Updates the status of the "pickup cargo" button */ + private void updatePickupCargoButton() { final Entity ce = ce(); // there has to be an entity, objects are on the ground, // the entity can pick them up if ((ce == null) || (game().getGroundObjects(finalPosition(), ce).size() <= 0) || ((cmd.getLastStep() != null) && (cmd.getLastStep().getType() == MoveStepType.PICKUP))) { - setPickupEnabled(false); + setPickupCargoEnabled(false); return; } - setPickupEnabled(true); + setPickupCargoEnabled(true); + } + + /** Updates the status of the "drop cargo" button */ + private void updateDropCargoButton() { + final Entity ce = ce(); + // there has to be an entity, objects are on the ground, + // the entity can pick them up + if ((ce == null) || ce.getCarriedObjects().size() == 0) { + setDropCargoEnabled(false); + return; + } + + setDropCargoEnabled(true); } /** Updates the status of the Load button. */ @@ -4897,7 +4913,7 @@ public synchronized void actionPerformed(ActionEvent ev) { addStepToMovePath(MoveStepType.UNLOAD, other); } } // else - Player canceled the unload. - } else if (actionCmd.equals(MoveCommand.MOVE_PICKUP.getCmd())) { + } else if (actionCmd.equals(MoveCommand.MOVE_PICKUP_CARGO.getCmd())) { // this is kind of obnoxious: // we have a list of all the possible ground objects // we supply a list of eligible ground objects to the dropdown dialog @@ -5732,9 +5748,14 @@ private void setBombEnabled(boolean enabled) { clientgui.getMenuBar().setEnabled(MoveCommand.MOVE_BOMB.getCmd(), enabled); } - private void setPickupEnabled(boolean enabled) { - getBtn(MoveCommand.MOVE_PICKUP).setEnabled(enabled); - clientgui.getMenuBar().setEnabled(MoveCommand.MOVE_PICKUP.getCmd(), enabled); + private void setPickupCargoEnabled(boolean enabled) { + getBtn(MoveCommand.MOVE_PICKUP_CARGO).setEnabled(enabled); + clientgui.getMenuBar().setEnabled(MoveCommand.MOVE_PICKUP_CARGO.getCmd(), enabled); + } + + private void setDropCargoEnabled(boolean enabled) { + getBtn(MoveCommand.MOVE_DROP_CARGO).setEnabled(enabled); + clientgui.getMenuBar().setEnabled(MoveCommand.MOVE_DROP_CARGO.getCmd(), enabled); } @Override diff --git a/megamek/src/megamek/common/BipedMech.java b/megamek/src/megamek/common/BipedMech.java index b112f817ff7..022b8d3f938 100644 --- a/megamek/src/megamek/common/BipedMech.java +++ b/megamek/src/megamek/common/BipedMech.java @@ -90,8 +90,8 @@ public boolean canFlipArms() { * Returns true if the entity can pick up ground objects */ public boolean canPickupGroundObject() { - return hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) || - hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM); + return hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null) || + hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null); } /** @@ -100,10 +100,10 @@ public boolean canPickupGroundObject() { public double maxGroundObjectTonnage() { double percentage = 0.0; - if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM)) { + if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null)) { percentage += 0.05; } - if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM)) { + if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null)) { percentage += 0.05; } diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index 4ee8acd9a7e..4072aae5ddc 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -136,7 +136,7 @@ public abstract class Entity extends TurnOrdered implements Transporter, Targeta public static final int DMG_CRIPPLED = 4; public static final int USE_STRUCTURAL_RATING = -1; - + protected transient Game game; protected int id = Entity.NONE; @@ -2821,6 +2821,35 @@ public Map getCarriedObjects() { public void setCarriedObjects(Map value) { carriedObjects = value; } + + /** + * Whether a weapon in a given location can be fired, + * given the entity's currently carried cargo + */ + public boolean canFireWeapon(int location) { + if (getBlockedFiringLocations() == null) { + return true; + } + + // loop through everything we are carrying + // if the weapon location is blocked by the carried object, then we cannot fire the weapon + for (int carriedObjectLocation : getCarriedObjects().keySet()) { + if (getBlockedFiringLocations().containsKey(carriedObjectLocation) && + getBlockedFiringLocations().get(carriedObjectLocation).contains(location)) { + return false; + } + } + + return true; + } + + /** + * Method that returns the mapping between locations which, if cargo is carried, + * block other locations from firing. + */ + protected Map> getBlockedFiringLocations() { + return null; + } /** * Returns this entity's original walking movement points diff --git a/megamek/src/megamek/common/Game.java b/megamek/src/megamek/common/Game.java index a64187ab837..0037ed8f47b 100644 --- a/megamek/src/megamek/common/Game.java +++ b/megamek/src/megamek/common/Game.java @@ -1314,7 +1314,6 @@ public synchronized void reset() { resetPSRs(); resetArtilleryAttacks(); resetAttacks(); - // removeMinefields(); Broken and bad! clearMinefields(); removeArtyAutoHitHexes(); flares.removeAllElements(); @@ -1328,6 +1327,7 @@ public synchronized void reset() { lastEntityId = 0; planetaryConditions = new PlanetaryConditions(); forces = new Forces(this); + groundObjects = new HashMap<>(); } private void removeArtyAutoHitHexes() { @@ -3320,6 +3320,11 @@ public List getGroundObjects(Coords coords, Entity entity) { return new ArrayList<>(); } + // if the entity doesn't have working actuators etc + if (!entity.canPickupGroundObject()) { + return new ArrayList<>(); + } + double maxTonnage = entity.maxGroundObjectTonnage(); ArrayList result = new ArrayList<>(); @@ -3337,8 +3342,6 @@ public List getGroundObjects(Coords coords, Entity entity) { * if looking for objects in specific hex */ public Map> getGroundObjects() { - // ground objects not going out to server for some stupid farking reason; - // check deployment display return groundObjects; } diff --git a/megamek/src/megamek/common/Mech.java b/megamek/src/megamek/common/Mech.java index b5bf5acae8d..eb05b004782 100644 --- a/megamek/src/megamek/common/Mech.java +++ b/megamek/src/megamek/common/Mech.java @@ -6476,19 +6476,6 @@ public boolean getsAutoExternalSearchlight() { return true; } - public boolean canFireWeapon(int location) { - // loop through everything we are carrying - // if the weapon location is blocked by the carried object, then we cannot fire the weapon - for (int carriedObjectLocation : getCarriedObjects().keySet()) { - if (BLOCKED_FIRING_LOCATIONS.containsKey(carriedObjectLocation) && - BLOCKED_FIRING_LOCATIONS.get(carriedObjectLocation).contains(location)) { - return false; - } - } - - return true; - } - public static Map getAllCockpitCodeName() { Map result = new HashMap<>(); @@ -6515,4 +6502,13 @@ public static Map getAllCockpitCodeName() { return result; } + + /** + * Method that returns the mapping between locations which, if cargo is carried, + * block other locations from firing. + */ + @Override + protected Map> getBlockedFiringLocations() { + return BLOCKED_FIRING_LOCATIONS; + } } diff --git a/megamek/src/megamek/common/Protomech.java b/megamek/src/megamek/common/Protomech.java index 21029f78466..2645db23458 100644 --- a/megamek/src/megamek/common/Protomech.java +++ b/megamek/src/megamek/common/Protomech.java @@ -24,7 +24,9 @@ import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Vector; import java.util.stream.Collectors; @@ -88,6 +90,22 @@ public class Protomech extends Entity { public static final int[] POSSIBLE_PILOT_DAMAGE = { 0, 1, 3, 1, 1, 1, 0 }; public static final String[] systemNames = { "Arm", "Leg", "Head", "Torso" }; + + /** + * Contains a mapping of locations which are blocked when carrying cargo in the "key" location + */ + public static final Map> BLOCKED_FIRING_LOCATIONS; + + static { + BLOCKED_FIRING_LOCATIONS = new HashMap<>(); + BLOCKED_FIRING_LOCATIONS.put(LOC_LARM, new ArrayList<>()); + BLOCKED_FIRING_LOCATIONS.get(LOC_LARM).add(LOC_LARM); + BLOCKED_FIRING_LOCATIONS.get(LOC_LARM).add(LOC_TORSO); + + BLOCKED_FIRING_LOCATIONS.put(LOC_RARM, new ArrayList<>()); + BLOCKED_FIRING_LOCATIONS.get(LOC_RARM).add(LOC_RARM); + BLOCKED_FIRING_LOCATIONS.get(LOC_RARM).add(LOC_TORSO); + } // For grapple attacks private int grappled_id = Entity.NONE; @@ -561,9 +579,8 @@ public boolean hasRearArmor(int loc) { * Returns true if the entity can pick up ground objects */ public boolean canPickupGroundObject() { - return !isProne() && - !(isLocationBad(Mech.LOC_LARM) || - isLocationBad(Mech.LOC_RARM)); + return !isLocationBad(Protomech.LOC_LARM) && (getCarriedObject(Protomech.LOC_LARM) == null) || + !isLocationBad(Protomech.LOC_RARM) && (getCarriedObject(Protomech.LOC_RARM) == null); } /** @@ -572,10 +589,10 @@ public boolean canPickupGroundObject() { public double maxGroundObjectTonnage() { double percentage = 0.0; - if (!isLocationBad(Mech.LOC_LARM)) { + if (!isLocationBad(Protomech.LOC_LARM) && (getCarriedObject(Protomech.LOC_LARM) == null)) { percentage += 0.05; } - if (!isLocationBad(Mech.LOC_RARM)) { + if (!isLocationBad(Protomech.LOC_RARM) && (getCarriedObject(Protomech.LOC_RARM) == null)) { percentage += 0.05; } @@ -1550,6 +1567,13 @@ protected Mounted getEquipmentForWeaponQuirk(QuirkEntry quirkEntry) { } } return null; - + } + + /** + * Method that returns the mapping between locations which, if cargo is carried, + * block other locations from firing. + */ + protected Map> getBlockedFiringLocations() { + return BLOCKED_FIRING_LOCATIONS; } } diff --git a/megamek/src/megamek/common/TripodMech.java b/megamek/src/megamek/common/TripodMech.java index e1b1b9e2802..f741ae8eacb 100644 --- a/megamek/src/megamek/common/TripodMech.java +++ b/megamek/src/megamek/common/TripodMech.java @@ -139,9 +139,8 @@ public boolean canFlipArms() { * Returns true if the entity can pick up ground objects */ public boolean canPickupGroundObject() { - return !isProne() && - hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) || - hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM); + return hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null) || + hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null); } /** @@ -150,10 +149,10 @@ public boolean canPickupGroundObject() { public double maxGroundObjectTonnage() { double percentage = 0.0; - if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM)) { + if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null)) { percentage += 0.05; } - if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM)) { + if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null)) { percentage += 0.05; } diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 6ebae2fa2f5..7ecd182357d 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -563,6 +563,7 @@ public void sendCurrentInfo(int connId) { send(connId, createFlarePacket()); send(connId, createSpecialHexDisplayPacket(connId)); send(connId, new Packet(PacketCommand.PRINCESS_SETTINGS, getGame().getBotSettings())); + send(connId, new Packet(PacketCommand.UPDATE_GROUND_OBJECTS, getGame().getGroundObjects())); } } @@ -7309,10 +7310,15 @@ else if ((step.getElevation() + entity.height()) == 0) { ICarryable pickupTarget = groundObjects.get(cargoPickupIndex); if (entity.maxGroundObjectTonnage() >= pickupTarget.getTonnage()) { - groundObjects.remove(cargoPickupIndex); + game.removeGroundObject(step.getPosition(), pickupTarget); // hack for now: goes into both arms - entity.pickupGroundObject(pickupTarget, Mech.LOC_LARM); - entity.pickupGroundObject(pickupTarget, Mech.LOC_RARM); + if (entity instanceof Mech) { + entity.pickupGroundObject(pickupTarget, Mech.LOC_LARM); + entity.pickupGroundObject(pickupTarget, Mech.LOC_RARM); + } else if (entity instanceof Protomech) { + entity.pickupGroundObject(pickupTarget, Protomech.LOC_LARM); + entity.pickupGroundObject(pickupTarget, Protomech.LOC_RARM); + } r = new Report(2513); r.subject = entity.getId(); @@ -7321,7 +7327,7 @@ else if ((step.getElevation() + entity.height()) == 0) { addReport(r); entityUpdate(entity.getId()); - return; // a pickup should be the last step + //return; // a pickup should be the last step } else { // add report about entity not being able to pick it up or maybe log it or something } From ca543065eddf18006b28def1ef6ac872a1454095 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Fri, 12 Jul 2024 18:51:20 -0400 Subject: [PATCH 13/35] drop button work --- megamek/src/megamek/client/ui/swing/MovementDisplay.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index 016e890635a..cc94e409a9e 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -4942,7 +4942,11 @@ public synchronized void actionPerformed(ActionEvent ev) { updateDonePanel(); } } - } else if (actionCmd.equals(MoveCommand.MOVE_RAISE_ELEVATION.getCmd())) { + } else if (actionCmd.equals(MoveCommand.MOVE_DROP_CARGO.getCmd())) { + // need to figure out a way to state which hand + // to drop if necessary var options = ce().getCarriedObjects().values(). + } + if (actionCmd.equals(MoveCommand.MOVE_RAISE_ELEVATION.getCmd())) { addStepToMovePath(MoveStepType.UP); } else if (actionCmd.equals(MoveCommand.MOVE_LOWER_ELEVATION.getCmd())) { if (ce.isAero()) { From c261387cd6821d1955ca2409519cd86299289318 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 16 Jul 2024 22:07:23 -0400 Subject: [PATCH 14/35] drop object; display fix --- .../megamek/common/report-messages.properties | 3 +- megamek/src/megamek/client/Client.java | 1 + .../megamek/client/ui/swing/ClientGUI.java | 2 +- .../ui/swing/DeployMinefieldDisplay.java | 4 +- .../client/ui/swing/MovementDisplay.java | 42 ++++++++++-- .../swing/boardview/GroundObjectSprite.java | 67 +++++++------------ .../boardview/GroundObjectSpriteHandler.java | 23 ++++--- .../client/ui/swing/boardview/StepSprite.java | 2 +- megamek/src/megamek/common/Entity.java | 21 +++++- megamek/src/megamek/common/MovePath.java | 12 ++-- megamek/src/megamek/common/MoveStep.java | 42 +++++++++--- .../common/util/SerializationHelper.java | 2 +- megamek/src/megamek/server/GameManager.java | 50 ++++++++++++-- 13 files changed, 184 insertions(+), 87 deletions(-) diff --git a/megamek/i18n/megamek/common/report-messages.properties b/megamek/i18n/megamek/common/report-messages.properties index 26069825bc4..a23654b720e 100755 --- a/megamek/i18n/megamek/common/report-messages.properties +++ b/megamek/i18n/megamek/common/report-messages.properties @@ -254,7 +254,8 @@ 2510=, Starts to flip. 2511=, Capsizes and sinks. 2512= deploys a chaff pod. -2513= picks up . +2513= picks up from . +2514= drops in 3000=Weapon Attack Phase------------------- 3003=Inferno fire (bombs) started in hex . diff --git a/megamek/src/megamek/client/Client.java b/megamek/src/megamek/client/Client.java index 8abddf3736e..123b66abd58 100644 --- a/megamek/src/megamek/client/Client.java +++ b/megamek/src/megamek/client/Client.java @@ -585,6 +585,7 @@ protected void receiveEntityVisibilityIndicator(Packet packet) { @SuppressWarnings("unchecked") protected void receiveUpdateGroundObjects(Packet packet) { game.setGroundObjects((Map>) packet.getObject(0)); + game.processGameEvent(new GameBoardChangeEvent(this)); } @SuppressWarnings("unchecked") diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index 70d4ea175ef..2db706e1b7b 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -2925,7 +2925,7 @@ public void showCollapseWarning(List warnList) { * * @param groundObjectList The list of coordinates to show */ - public void showGroundObjects(Iterable groundObjectList) { + public void showGroundObjects(Map> groundObjectList) { groundObjectSpriteHandler.setGroundObjectSprites(groundObjectList); } diff --git a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java index d5f957732f1..232c4bd03bd 100644 --- a/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java +++ b/megamek/src/megamek/client/ui/swing/DeployMinefieldDisplay.java @@ -278,7 +278,7 @@ private void deployMinefield(Coords coords) { game.getGroundObjects().remove(coords); - clientgui.showGroundObjects(game.getGroundObjects().keySet()); + clientgui.showGroundObjects(game.getGroundObjects()); } else if (currentCommand == DeployMinefieldCommand.DEPLOY_CARRYABLE) { List groundObjects = p.getGroundObjectsToPlace(); @@ -300,7 +300,7 @@ private void deployMinefield(Coords coords) { currentCommand = DeployMinefieldCommand.COMMAND_NONE; } - clientgui.showGroundObjects(game.getGroundObjects().keySet()); + clientgui.showGroundObjects(game.getGroundObjects()); } else { // first check that there is not already a mine of this type // deployed diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index 0c6f48cc6e4..0c83ea72d52 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -949,6 +949,11 @@ private void addStepToMovePath(MoveStepType moveStep, int recover, int mineToLay cmd.addStep(moveStep, recover, mineToLay); updateMove(); } + + private void addStepToMovePath(MoveStepType moveStep, Map additionalIntData) { + cmd.addStep(moveStep, additionalIntData); + updateMove(); + } private void updateMove() { updateMove(true); @@ -2823,7 +2828,7 @@ private void updatePickupCargoButton() { // the entity can pick them up if ((ce == null) || (game().getGroundObjects(finalPosition(), ce).size() <= 0) || ((cmd.getLastStep() != null) && - (cmd.getLastStep().getType() == MoveStepType.PICKUP))) { + (cmd.getLastStep().getType() == MoveStepType.PICKUP_CARGO))) { setPickupCargoEnabled(false); return; } @@ -4928,8 +4933,7 @@ public synchronized void actionPerformed(ActionEvent ev) { var displayedOptions = game().getGroundObjects(finalPosition(), ce()); if (displayedOptions.size() == 1) { - int index = options.indexOf(displayedOptions.get(0)); - addStepToMovePath(MoveStepType.PICKUP, index); + addStepToMovePath(MoveStepType.PICKUP_CARGO); updateDonePanel(); } else if (displayedOptions.size() > 1) { // Dialog for choosing which object to pick up @@ -4942,13 +4946,39 @@ public synchronized void actionPerformed(ActionEvent ev) { // Verify that we have a valid option... if (option != null) { int index = options.indexOf(option); - addStepToMovePath(MoveStepType.PICKUP, index); + addStepToMovePath(MoveStepType.PICKUP_CARGO, index); updateDonePanel(); } } } else if (actionCmd.equals(MoveCommand.MOVE_DROP_CARGO.getCmd())) { - // need to figure out a way to state which hand - // to drop if necessary var options = ce().getCarriedObjects().values(). + var options = ce().getDistinctCarriedObjects(); + + if (options.size() == 1) { + addStepToMovePath(MoveStepType.DROP_CARGO); + updateDonePanel(); + } else if (options.size() > 1) { + // reverse lookup: location name to location ID - we're going to wind up with a name chosen + // but need to send the ID in the move path. + Map locationMap = new HashMap<>(); + + for (int location : ce().getCarriedObjects().keySet()) { + locationMap.put(ce().getLocationName(location), location); + } + + // Dialog for choosing which object to pick up + String title = "Choose Cargo to Drop"; + String body = "Choose the cargo to drop:"; + String option = (String) JOptionPane.showInputDialog(clientgui.getFrame(), + body, title, JOptionPane.QUESTION_MESSAGE, null, + locationMap.keySet().toArray(), locationMap.keySet().toArray()[0]); + + // Verify that we have a valid option... + if (option != null) { + int location = locationMap.get(option); + addStepToMovePath(MoveStepType.DROP_CARGO, location); + updateDonePanel(); + } + } } if (actionCmd.equals(MoveCommand.MOVE_RAISE_ELEVATION.getCmd())) { addStepToMovePath(MoveStepType.UP); diff --git a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java index 479d9596b86..5bce6f83c0e 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java +++ b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java @@ -18,39 +18,27 @@ */ package megamek.client.ui.swing.boardview; -import java.awt.Color; -import java.awt.Graphics2D; +import java.awt.Dimension; +import java.awt.Image; +import java.awt.Rectangle; -import megamek.client.ui.swing.tileset.HexTileset; -import megamek.client.ui.swing.util.FontHandler; -import megamek.client.ui.swing.util.StringDrawer; -import megamek.client.ui.swing.util.UIUtil; + +import megamek.common.Configuration; import megamek.common.Coords; +import megamek.common.util.ImageUtil; +import megamek.common.util.fileUtils.MegaMekFile; /** - * Represents structure CF warnings for entities during deployment - * and movement phase that will collapse if the entity lands-on - * or is deployed on that structure. - * - * From TW: If a units tonnage exceeds the CF of a building - * or bridge, it will collapse. (Or the sum of tonnage of stacked - * units if multiple units occupy the hex) + * Represents cargo that can be picked up from the ground. */ public class GroundObjectSprite extends HexSprite { + private static final String FILENAME_CARGO_IMAGE = "cargo.png"; + private static final Image CARGO_IMAGE; - private static final int TEXT_SIZE = HexTileset.HEX_H / 2; - private static final Color TEXT_COLOR = new Color(255, 255, 255, 128); - private static final Color OUTLINE_COLOR = new Color(40, 40, 40,200); - - private static final int HEX_CENTER_X = HexTileset.HEX_W / 2; - private static final int HEX_CENTER_Y = HexTileset.HEX_H / 2; - - // Draw a special character 'warning sign'. - private final StringDrawer xWriter = new StringDrawer("\uF3C1") - .at(HEX_CENTER_X, HEX_CENTER_Y) - .color(TEXT_COLOR) - .fontSize(TEXT_SIZE) - .absoluteCenter().outline(OUTLINE_COLOR, 2.5f); + static { + CARGO_IMAGE = ImageUtil.loadImageFromFile( + new MegaMekFile(Configuration.miscImagesDir(), FILENAME_CARGO_IMAGE).toString()); + } /** * @param boardView1 - parent BoardView object this sprite will be displayed on. @@ -58,26 +46,17 @@ public class GroundObjectSprite extends HexSprite { */ public GroundObjectSprite(BoardView boardView1, Coords loc) { super(boardView1, loc); + image = CARGO_IMAGE; } - + @Override - public void prepare() { - Graphics2D graph = spriteSetup(); - xWriter.draw(graph); - graph.dispose(); + public Rectangle getBounds() { + Dimension dim = new Dimension(bv.hex_size.width, bv.hex_size.height); + bounds = new Rectangle(dim); + bounds.setLocation(bv.getHexLocation(loc)); + return bounds; } - /* - * Standard Hex Sprite 2D Graphics setup. Creates the context, base hex image - * settings, scale, and fonts. - */ - private Graphics2D spriteSetup() { - updateBounds(); - image = createNewHexImage(); - Graphics2D graph = (Graphics2D) image.getGraphics(); - UIUtil.setHighQualityRendering(graph); - graph.scale(bv.scale, bv.scale); - graph.setFont(FontHandler.symbolFont()); - return graph; - } + @Override + public void prepare() { } } \ No newline at end of file diff --git a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java index 19ade3a1190..c1635db3d39 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java +++ b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java @@ -18,15 +18,19 @@ */ package megamek.client.ui.swing.boardview; +import java.util.List; +import java.util.Map; + import megamek.common.Coords; import megamek.common.Game; +import megamek.common.ICarryable; import megamek.common.event.GameBoardChangeEvent; import megamek.common.event.GamePhaseChangeEvent; public class GroundObjectSpriteHandler extends BoardViewSpriteHandler { // Cache the ground object list as it does not change very often - private Iterable currentGroundObjectList; + private Map> currentGroundObjectList; private final Game game; public GroundObjectSpriteHandler(BoardView boardView, Game game) { @@ -34,13 +38,15 @@ public GroundObjectSpriteHandler(BoardView boardView, Game game) { this.game = game; } - public void setGroundObjectSprites(Iterable objectCoordList) { + public void setGroundObjectSprites(Map> objectCoordList) { clear(); currentGroundObjectList = objectCoordList; if (currentGroundObjectList != null) { - for (Coords coords : currentGroundObjectList) { - GroundObjectSprite gos = new GroundObjectSprite(boardView, coords); - currentSprites.add(gos); + for (Coords coords : currentGroundObjectList.keySet()) { + for (ICarryable groundObject : currentGroundObjectList.get(coords)) { + GroundObjectSprite gos = new GroundObjectSprite(boardView, coords); + currentSprites.add(gos); + } } } boardView.addSprites(currentSprites); @@ -65,11 +71,6 @@ public void dispose() { @Override public void gameBoardChanged(GameBoardChangeEvent e) { - setGroundObjectSprites(game.getGroundObjects().keySet()); - } - - @Override - public void gamePhaseChange(GamePhaseChangeEvent e) { - setGroundObjectSprites(game.getGroundObjects().keySet()); + setGroundObjectSprites(game.getGroundObjects()); } } diff --git a/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java b/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java index 4c7cfb527f6..81eae9c73b4 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java +++ b/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java @@ -205,7 +205,7 @@ public void prepare() { String load = Messages.getString("BoardView1.Load"); drawAnnouncement(g2D, load, step, col); break; - case PICKUP: + case PICKUP_CARGO: String pickup = Messages.getString("MovementDisplay.movePickup"); drawAnnouncement(g2D, pickup, step, col); break; diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index a38200db21c..2b2d1c7fa22 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -2799,11 +2799,17 @@ public void pickupGroundObject(ICarryable carryable, int location) { } /** - * Remove a ground object from the given location + * Remove a ground object (cargo) from the given location */ public void dropGroundObject(int location) { carriedObjects.remove(location); - pickedUpObject = true; + } + + /** + * Convenience method to drop all cargo. + */ + public void dropGroundObjects() { + carriedObjects.clear(); } /** @@ -2821,6 +2827,17 @@ public void setCarriedObjects(Map value) { carriedObjects = value; } + public List getDistinctCarriedObjects() { + return carriedObjects.values().stream().distinct().toList(); + } + + /** + * A list of all the locations that the entity can use to pick up cargo. + */ + public List getValidPickupLocations() { + return null; + } + /** * Whether a weapon in a given location can be fired, * given the entity's currently carried cargo diff --git a/megamek/src/megamek/common/MovePath.java b/megamek/src/megamek/common/MovePath.java index 96bf94027f8..a3e77c727e4 100644 --- a/megamek/src/megamek/common/MovePath.java +++ b/megamek/src/megamek/common/MovePath.java @@ -58,7 +58,7 @@ public enum MoveStepType { CLIMB_MODE_OFF, SWIM, DIG_IN, FORTIFY, SHAKE_OFF_SWARMERS, TAKEOFF, VTAKEOFF, LAND, ACC, DEC, EVADE, SHUTDOWN, STARTUP, SELF_DESTRUCT, ACCN, DECN, ROLL, OFF, RETURN, LAUNCH, THRUST, YAW, CRASH, RECOVER, RAM, HOVER, MANEUVER, LOOP, CAREFUL_STAND, JOIN, DROP, VLAND, MOUNT, UNDOCK, TAKE_COVER, - CONVERT_MODE, BOOTLEGGER, TOW, DISCONNECT, BRACE, CHAFF, PICKUP; + CONVERT_MODE, BOOTLEGGER, TOW, DISCONNECT, BRACE, CHAFF, PICKUP_CARGO, DROP_CARGO; /** * Whether this move step type will result in the unit entering a new hex @@ -206,6 +206,10 @@ public MovePath addStep(final MoveStepType type, final boolean noCost) { return addStep(new MoveStep(this, type, noCost)); } + public MovePath addStep(final MoveStepType type, final Map additionalIntData) { + return addStep(new MoveStep(this, type, additionalIntData)); + } + public MovePath addStep(final MoveStepType type, final boolean noCost, final boolean isManeuver, final int maneuverType) { return addStep(new MoveStep(this, type, noCost, isManeuver, maneuverType)); } @@ -543,7 +547,7 @@ && getMpUsed() > getCachedEntityState().getWalkMP() } // if we have a PICKUP, then we can't do anything else after it - if (contains(MoveStepType.PICKUP)) { + if (contains(MoveStepType.PICKUP_CARGO)) { step.setMovementType(EntityMovementType.MOVE_ILLEGAL); } } @@ -580,8 +584,8 @@ public void compile(final Game g, final Entity en, boolean clip) { step = new MoveStep(this, step.getType(), step.hasNoCost()); } else if (null != step.getMinefield()) { step = new MoveStep(this, step.getType(), step.getMinefield()); - } else if (null != step.getAdditionalData(MoveStep.CARGO_PICKUP_INDEX)) { - step = new MoveStep(this, step.getType(), step.getAdditionalData(MoveStep.CARGO_PICKUP_INDEX)); + } else if (null != step.getAdditionalData(MoveStep.CARGO_PICKUP_KEY)) { + step = new MoveStep(this, step.getType(), step.getAdditionalData(MoveStep.CARGO_PICKUP_KEY)); } else { step = new MoveStep(this, step.getType()); } diff --git a/megamek/src/megamek/common/MoveStep.java b/megamek/src/megamek/common/MoveStep.java index c84fadbff31..f12d21b30b5 100644 --- a/megamek/src/megamek/common/MoveStep.java +++ b/megamek/src/megamek/common/MoveStep.java @@ -42,8 +42,16 @@ */ public class MoveStep implements Serializable { private static final long serialVersionUID = -6075640793056182285L; - public static final int CARGO_PICKUP_INDEX = 0; - public static final int CARGO_PICKUP_LOCATION = 1; + /** + * When supplying additional int data, use this to key the index of the cargo being picked up + */ + public static final int CARGO_PICKUP_KEY = 0; + + /** + * When supplying additional int data, use this to key the location of the cargo being picked up + * (i.e. mech left arm/right arm, vehicle body, etc) + */ + public static final int CARGO_LOCATION_KEY = 1; private MoveStepType type; private int targetId = Entity.NONE; @@ -268,10 +276,21 @@ public MoveStep(MovePath path, MoveStepType type, int additionalIntData) { this.braceLocation = additionalIntData; } else if (type == MoveStepType.LAY_MINE) { this.mineToLay = additionalIntData; - } else if (type == MoveStepType.PICKUP) { - this.additionalData.put(CARGO_PICKUP_INDEX, additionalIntData); + } else if (type == MoveStepType.PICKUP_CARGO) { + this.additionalData.put(CARGO_PICKUP_KEY, additionalIntData); + } else if (type == MoveStepType.DROP_CARGO) { + this.additionalData.put(CARGO_LOCATION_KEY, additionalIntData); } } + + /** + * Creates a step with an arbitrary int-to-int mapping of additional data. + */ + public MoveStep(MovePath path, MoveStepType type, Map additionalIntData) { + this(path, type); + + additionalData.putAll(additionalIntData); + } /** * Create a step with the units to launch or drop. @@ -415,8 +434,10 @@ public String toString() { return "Brace"; case CHAFF: return "Chaff"; - case PICKUP: - return "Pickup"; + case PICKUP_CARGO: + return "Pickup Cargo"; + case DROP_CARGO: + return "Drop Cargo"; default: return "???"; } @@ -1125,6 +1146,9 @@ protected void compile(final Game game, final Entity entity, MoveStep prev, Cach case BRACE: setMp(entity.getBraceMPCost()); break; + case DROP_CARGO: + setMp(1); + break; case CHAFF: default: setMp(0); @@ -2836,7 +2860,8 @@ && isHullDown()) { } } - if (stepType == MoveStepType.PICKUP) { + if (stepType == MoveStepType.PICKUP_CARGO || + stepType == MoveStepType.DROP_CARGO) { movementType = EntityMovementType.MOVE_NONE; } @@ -3396,7 +3421,8 @@ && isThisStepBackwards() return true; } - if (type == MoveStepType.PICKUP) { + if (type == MoveStepType.PICKUP_CARGO || + type == MoveStepType.DROP_CARGO) { return !isProne(); } diff --git a/megamek/src/megamek/common/util/SerializationHelper.java b/megamek/src/megamek/common/util/SerializationHelper.java index 4af8b847fc0..5fa2276f19c 100644 --- a/megamek/src/megamek/common/util/SerializationHelper.java +++ b/megamek/src/megamek/common/util/SerializationHelper.java @@ -68,7 +68,7 @@ public static XStream getSaveGameXStream() { megamek.common.EntityFluff.class, megamek.common.NarcPod.class, megamek.common.INarcPod.class, - megamek.common.Briefcase.class + megamek.common.Briefcase.class, megamek.common.net.packets.Packet.class, megamek.common.BoardLocation.class, megamek.common.strategicBattleSystems.SBFMovePath.class, diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 59fc0c998af..e2257873508 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -7257,9 +7257,16 @@ else if ((step.getElevation() + entity.height()) == 0) { } } // End STEP_MOUNT - if (step.getType() == MovePath.MoveStepType.PICKUP) { + if (step.getType() == MovePath.MoveStepType.PICKUP_CARGO) { var groundObjects = game.getGroundObjects(step.getPosition()); - Integer cargoPickupIndex = step.getAdditionalData(MoveStep.CARGO_PICKUP_INDEX); + Integer cargoPickupIndex; + + // if there's only one object on the ground, let's just get that one and ignore any parameters + if (groundObjects.size() == 1) { + cargoPickupIndex = 0; + } else { + cargoPickupIndex = step.getAdditionalData(MoveStep.CARGO_PICKUP_KEY); + } // there have to be objects on the ground and we have to be trying to pick up one of them if ((groundObjects.size() > 0) && @@ -7281,16 +7288,47 @@ else if ((step.getElevation() + entity.height()) == 0) { r.subject = entity.getId(); r.add(entity.getDisplayName()); r.add(pickupTarget.toString()); + r.add(step.getPosition().toFriendlyString()); addReport(r); - entityUpdate(entity.getId()); - //return; // a pickup should be the last step + // a pickup should be the last step. Send an update for the overall ground object list. + send(new Packet(PacketCommand.UPDATE_GROUND_OBJECTS, getGame().getGroundObjects())); + break; } else { - // add report about entity not being able to pick it up or maybe log it or something + LogManager.getLogger().warn(entity.getShortName() + " attempted to pick up object but it is too heavy. Carry capacity: " + + entity.maxGroundObjectTonnage() + ", object weight: " + pickupTarget.getTonnage()); } } else { - // add report about there not being a valid object to pick up + LogManager.getLogger().warn(entity.getShortName() + " attempted to pick up non existent object at coords " + + step.getPosition() + ", index " + cargoPickupIndex); + } + } + + if (step.getType() == MovePath.MoveStepType.DROP_CARGO) { + Integer cargoLocation = step.getAdditionalData(MoveStep.CARGO_LOCATION_KEY); + ICarryable cargo; + + // if we're not supplied a specific location, then the assumption is we only have one + // piece of cargo and we're going to just drop that one + if (cargoLocation == null) { + cargo = entity.getDistinctCarriedObjects().get(0); + entity.dropGroundObjects(); + } else { + cargo = entity.getCarriedObject(cargoLocation); + entity.dropGroundObject(cargoLocation); } + + game.placeGroundObject(step.getPosition(), cargo); + + r = new Report(2514); + r.subject = entity.getId(); + r.add(entity.getDisplayName()); + r.add(cargo.getName()); + r.add(step.getPosition().toFriendlyString()); + addReport(r); + + // a drop changes board state. Send an update for the overall ground object list. + send(new Packet(PacketCommand.UPDATE_GROUND_OBJECTS, getGame().getGroundObjects())); } // handle fighter recovery, and also DropShip docking with another large craft From 6527a4cafd541ba5b819cdead33f889f726b6ef6 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 Jul 2024 00:31:34 -0400 Subject: [PATCH 15/35] drop; status --- megamek/data/images/misc/cargo.png | Bin 0 -> 1078 bytes .../client/ui/swing/MovementDisplay.java | 109 +++++++++++++----- .../boardview/GroundObjectSpriteHandler.java | 2 +- .../client/ui/swing/boardview/StepSprite.java | 6 +- .../ui/swing/unitDisplay/ExtraPanel.java | 6 + megamek/src/megamek/common/BipedMech.java | 46 +++++++- megamek/src/megamek/common/Entity.java | 28 ++++- megamek/src/megamek/common/MovePath.java | 4 +- megamek/src/megamek/common/Protomech.java | 32 +++++ megamek/src/megamek/common/Tank.java | 2 + megamek/src/megamek/common/TripodMech.java | 36 ++++++ megamek/src/megamek/server/GameManager.java | 11 +- 12 files changed, 234 insertions(+), 48 deletions(-) create mode 100644 megamek/data/images/misc/cargo.png diff --git a/megamek/data/images/misc/cargo.png b/megamek/data/images/misc/cargo.png new file mode 100644 index 0000000000000000000000000000000000000000..1a7b17f827445394e47fbc9b89de3865f5461dad GIT binary patch literal 1078 zcmV-61j+k}P)EX>4Tx04R}tkv&MmKpe$iQ>8^)9PA+CkfA!+MT}?mh0_0Ya)R&;^Mfxh}i>#<}9Kz%wI8K08MoAr>n=EcY<08Y*#|II5@`ER!B{26k|eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00Hz#L_t(|+U=WN(t;oqhL7L07nhOOEhN_jyXdBCTL!D4 zn`p|pC&UNL|;XgIs^p zG{G93O0%aiMnpu;=kw$kH!hUF(gdAy=8|#63+n4U9EwA6C=Nx?V>;Aka3phR>^yWd zkxtpxG>vr$dTfWDQia&i1h;6b>)K1Kr4C&huHqA3FNa#ClM{j+PRRZ5A7$mjWWZ9lF>DJ7T7<=t2EEnIG*yWv4Zd9+kChzeG8bKu1&<#OQ1kIad)h4a=zrGZ0OVgih z1$Al4mi5ph2^yNF#KdPFRrU^*^soE8%Z{P6ZEM%gcT!NdCTXqZe!qXMQSREc_xp3e zl^EwpIq$U<`;hMpde}DO(9tUXSfzhyg+4Uos~?B5*V}JHJ9YnIo3BH))}v0H>dScz z9c1e5w_m0{W1_|ovzWx*i5gqXqKYG*qQ(-FTTcXCjtldC_#YBEnFf1000hUSV?A0O#mtY000O800000007cclK=n!07*qoM6N<$f>SEs 1) { - // Dialog for choosing which object to pick up - String title = "Choose Cargo to Pick Up"; - String body = "Choose the cargo to pick up:"; - ICarryable option = (ICarryable) JOptionPane.showInputDialog(clientgui.getFrame(), - body, title, JOptionPane.QUESTION_MESSAGE, null, - displayedOptions.toArray(), displayedOptions.get(0)); - - // Verify that we have a valid option... - if (option != null) { - int index = options.indexOf(option); - addStepToMovePath(MoveStepType.PICKUP_CARGO, index); - updateDonePanel(); - } - } + processPickupCargoCommand(); } else if (actionCmd.equals(MoveCommand.MOVE_DROP_CARGO.getCmd())) { var options = ce().getDistinctCarriedObjects(); @@ -5340,6 +5314,87 @@ public synchronized void actionPerformed(ActionEvent ev) { } } + /** + * Worker function containing the "pickup cargo" command. + */ + private void processPickupCargoCommand() { + var options = game().getGroundObjects(finalPosition()); + var displayedOptions = game().getGroundObjects(finalPosition(), ce()); + + // if there's only one thing to pick up, just pick it up. + // regardless of how many objects we are picking up, + // we may have to choose the location with which to pick it up + if (displayedOptions.size() == 1) { + Integer pickupLocation = getPickupLocation(options.get(0)); + + if (pickupLocation != null) { + Map data = new HashMap<>(); + data.put(MoveStep.CARGO_PICKUP_KEY, 0); + data.put(MoveStep.CARGO_LOCATION_KEY, pickupLocation); + + addStepToMovePath(MoveStepType.PICKUP_CARGO, data); + updateDonePanel(); + } + } else if (displayedOptions.size() > 1) { + // Dialog for choosing which object to pick up + String title = "Choose Cargo to Pick Up"; + String body = "Choose the cargo to pick up:"; + ICarryable option = (ICarryable) JOptionPane.showInputDialog(clientgui.getFrame(), + body, title, JOptionPane.QUESTION_MESSAGE, null, + displayedOptions.toArray(), displayedOptions.get(0)); + + if (option != null) { + Integer pickupLocation = getPickupLocation(option); + + if (pickupLocation != null) { + int cargoIndex = options.indexOf(option); + Map data = new HashMap<>(); + data.put(MoveStep.CARGO_PICKUP_KEY, cargoIndex); + data.put(MoveStep.CARGO_LOCATION_KEY, pickupLocation); + + addStepToMovePath(MoveStepType.PICKUP_CARGO, data); + updateDonePanel(); + } + } + } + } + + /** + * Worker function to chose a limb (or whatever) with which to pick up cargo + */ + private Integer getPickupLocation(ICarryable cargo) { + var validPickupLocations = ce().getValidHalfWeightPickupLocations(cargo); + int pickupLocation = Entity.LOC_NONE; + + // if we need to choose a pickup location, then do so + if (validPickupLocations.size() > 1) { + // reverse lookup: location name to location ID - we're going to wind up with a name chosen + // but need to send the ID in the move path. + Map locationMap = new HashMap<>(); + + for (int location : ce().getValidHalfWeightPickupLocations(cargo)) { + locationMap.put(ce().getLocationName(location), location); + } + + // Dialog for choosing which object to pick up + String title = "Choose Pickup Location"; + String body = "Choose the location with which to pick up cargo:"; + String locationChoice = (String) JOptionPane.showInputDialog(clientgui.getFrame(), + body, title, JOptionPane.QUESTION_MESSAGE, null, + locationMap.keySet().toArray(), locationMap.keySet().toArray()[0]); + + if (locationChoice != null) { + pickupLocation = locationMap.get(locationChoice); + } else { + return null; + } + } else if (validPickupLocations.size() == 1) { + pickupLocation = validPickupLocations.get(0); + } + + return pickupLocation; + } + /** * Add enough MoveStepType.CONVERT_MODE steps to get to the requested mode, or * clear the path if the unit is in the requested mode at the beginning of the turn. diff --git a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java index c1635db3d39..1babead780d 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java +++ b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSpriteHandler.java @@ -25,7 +25,6 @@ import megamek.common.Game; import megamek.common.ICarryable; import megamek.common.event.GameBoardChangeEvent; -import megamek.common.event.GamePhaseChangeEvent; public class GroundObjectSpriteHandler extends BoardViewSpriteHandler { @@ -49,6 +48,7 @@ public void setGroundObjectSprites(Map> objectCoordList } } } + boardView.addSprites(currentSprites); } diff --git a/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java b/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java index 81eae9c73b4..360185d1ee0 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java +++ b/megamek/src/megamek/client/ui/swing/boardview/StepSprite.java @@ -206,9 +206,13 @@ public void prepare() { drawAnnouncement(g2D, load, step, col); break; case PICKUP_CARGO: - String pickup = Messages.getString("MovementDisplay.movePickup"); + String pickup = Messages.getString("MovementDisplay.movePickupCargo"); drawAnnouncement(g2D, pickup, step, col); break; + case DROP_CARGO: + String dropCargo = Messages.getString("MovementDisplay.moveDropCargo"); + drawAnnouncement(g2D, dropCargo, step, col); + break; case TOW: String tow = Messages.getString("BoardView1.Tow"); drawAnnouncement(g2D, tow, step, col); diff --git a/megamek/src/megamek/client/ui/swing/unitDisplay/ExtraPanel.java b/megamek/src/megamek/client/ui/swing/unitDisplay/ExtraPanel.java index 1c41b0d1b3a..c934e798676 100644 --- a/megamek/src/megamek/client/ui/swing/unitDisplay/ExtraPanel.java +++ b/megamek/src/megamek/client/ui/swing/unitDisplay/ExtraPanel.java @@ -490,6 +490,12 @@ public void displayMech(Entity en) { carrysR.append(club.getName()); carrysR.append("\n"); } + + // show cargo. + for (ICarryable cargo : en.getDistinctCarriedObjects()) { + carrysR.append(cargo.toString()); + carrysR.append("\n"); + } // Show searchlight if (en.hasSearchlight()) { diff --git a/megamek/src/megamek/common/BipedMech.java b/megamek/src/megamek/common/BipedMech.java index 022b8d3f938..d80461cb1a8 100644 --- a/megamek/src/megamek/common/BipedMech.java +++ b/megamek/src/megamek/common/BipedMech.java @@ -90,8 +90,8 @@ public boolean canFlipArms() { * Returns true if the entity can pick up ground objects */ public boolean canPickupGroundObject() { - return hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null) || - hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null); + return hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null) || + hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null); } /** @@ -100,15 +100,53 @@ public boolean canPickupGroundObject() { public double maxGroundObjectTonnage() { double percentage = 0.0; - if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null)) { + if (hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null) && + !isLocationBad(Mech.LOC_LARM)) { percentage += 0.05; } - if (hasSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null)) { + if (hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null) && + !isLocationBad(Mech.LOC_RARM)) { percentage += 0.05; } return getWeight() * percentage; } + + @Override + public List getDefaultPickupLocations() { + List result = new ArrayList<>(); + + if (hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null) && + !isLocationBad(Mech.LOC_LARM)) { + result.add(Mech.LOC_LARM); + } + if (hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null) && + !isLocationBad(Mech.LOC_RARM)) { + result.add(Mech.LOC_RARM); + } + + return result; + } + + @Override + public List getValidHalfWeightPickupLocations(ICarryable cargo) { + List result = new ArrayList<>(); + + // if we can pick the object up according to "one handed pick up rules" in TacOps + if (cargo.getTonnage() <= (getWeight() / 20)) { + if (hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null) && + !isLocationBad(Mech.LOC_LARM)) { + result.add(Mech.LOC_LARM); + } + + if (hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null) && + !isLocationBad(Mech.LOC_RARM)) { + result.add(Mech.LOC_RARM); + } + } + + return result; + } @Override public int getWalkMP(MPCalculationSetting mpCalculationSetting) { diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index 2b2d1c7fa22..1c6c78cbd80 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -2789,15 +2789,25 @@ public double maxGroundObjectTonnage() { /** * Put a ground object into the given location */ - public void pickupGroundObject(ICarryable carryable, int location) { + public void pickupGroundObject(ICarryable carryable, Integer location) { if (carriedObjects == null) { carriedObjects = new HashMap<>(); } - carriedObjects.put(location, carryable); + // "none" means we should just put it wherever it goes by default. + // rules checks are done prior to this, so we just set the data + if (location == null || location == LOC_NONE) { + for (Integer defaultLocation : getDefaultPickupLocations()) { + carriedObjects.put(defaultLocation, carryable); + } + } else { + carriedObjects.put(location, carryable); + } pickedUpObject = true; } + + /** * Remove a ground object (cargo) from the given location */ @@ -2832,10 +2842,18 @@ public List getDistinctCarriedObjects() { } /** - * A list of all the locations that the entity can use to pick up cargo. + * A list of the "default" cargo pick up locations for when none is specified */ - public List getValidPickupLocations() { - return null; + protected List getDefaultPickupLocations() { + return Arrays.asList(LOC_NONE); + } + + /** + * A list of all the locations that the entity can use to pick up cargo following the TacOps + * "one handed" pickup rules + */ + public List getValidHalfWeightPickupLocations(ICarryable cargo) { + return Arrays.asList(LOC_NONE); } /** diff --git a/megamek/src/megamek/common/MovePath.java b/megamek/src/megamek/common/MovePath.java index a3e77c727e4..3ba6f4e6d46 100644 --- a/megamek/src/megamek/common/MovePath.java +++ b/megamek/src/megamek/common/MovePath.java @@ -584,8 +584,8 @@ public void compile(final Game g, final Entity en, boolean clip) { step = new MoveStep(this, step.getType(), step.hasNoCost()); } else if (null != step.getMinefield()) { step = new MoveStep(this, step.getType(), step.getMinefield()); - } else if (null != step.getAdditionalData(MoveStep.CARGO_PICKUP_KEY)) { - step = new MoveStep(this, step.getType(), step.getAdditionalData(MoveStep.CARGO_PICKUP_KEY)); + } else if (null != step.getAdditionalData() && step.getAdditionalData().size() > 0) { + step = new MoveStep(this, step.getType(), step.getAdditionalData()); } else { step = new MoveStep(this, step.getType()); } diff --git a/megamek/src/megamek/common/Protomech.java b/megamek/src/megamek/common/Protomech.java index 2645db23458..11eebb940ab 100644 --- a/megamek/src/megamek/common/Protomech.java +++ b/megamek/src/megamek/common/Protomech.java @@ -598,6 +598,38 @@ public double maxGroundObjectTonnage() { return getWeight() * percentage; } + + @Override + public List getDefaultPickupLocations() { + List result = new ArrayList<>(); + + if ((getCarriedObject(Protomech.LOC_LARM) == null) && !isLocationBad(Protomech.LOC_LARM)) { + result.add(Protomech.LOC_LARM); + } + if ((getCarriedObject(Protomech.LOC_RARM) == null) && !isLocationBad(Protomech.LOC_RARM)) { + result.add(Protomech.LOC_RARM); + } + + return result; + } + + @Override + public List getValidHalfWeightPickupLocations(ICarryable cargo) { + List result = new ArrayList<>(); + + // if we can pick the object up according to "one handed pick up rules" in TacOps + if (cargo.getTonnage() <= (getWeight() / 20)) { + if ((getCarriedObject(Protomech.LOC_LARM) == null) && !isLocationBad(Protomech.LOC_LARM)) { + result.add(Protomech.LOC_LARM); + } + + if ((getCarriedObject(Protomech.LOC_RARM) == null) && !isLocationBad(Protomech.LOC_RARM)) { + result.add(Protomech.LOC_RARM); + } + } + + return result; + } @Override public int getRunMP(MPCalculationSetting mpCalculationSetting) { diff --git a/megamek/src/megamek/common/Tank.java b/megamek/src/megamek/common/Tank.java index 9fab2b3d9d6..4f8c15ab434 100644 --- a/megamek/src/megamek/common/Tank.java +++ b/megamek/src/megamek/common/Tank.java @@ -285,9 +285,11 @@ protected void addSystemTechAdvancement(CompositeTechLevel ctl) { @Override public int getWalkMP(MPCalculationSetting mpCalculationSetting) { int mp = getOriginalWalkMP(); + if (engineHit || isImmobile()) { return 0; } + if (hasWorkingMisc(MiscType.F_HYDROFOIL)) { mp = (int) Math.round(mp * 1.25); } diff --git a/megamek/src/megamek/common/TripodMech.java b/megamek/src/megamek/common/TripodMech.java index f741ae8eacb..809400c0ad3 100644 --- a/megamek/src/megamek/common/TripodMech.java +++ b/megamek/src/megamek/common/TripodMech.java @@ -158,6 +158,42 @@ public double maxGroundObjectTonnage() { return getWeight() * percentage; } + + @Override + public List getDefaultPickupLocations() { + List result = new ArrayList<>(); + + if (hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null) && + !isLocationBad(Mech.LOC_LARM)) { + result.add(Mech.LOC_LARM); + } + if (hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null) && + !isLocationBad(Mech.LOC_RARM)) { + result.add(Mech.LOC_RARM); + } + + return result; + } + + @Override + public List getValidHalfWeightPickupLocations(ICarryable cargo) { + List result = new ArrayList<>(); + + // if we can pick the object up according to "one handed pick up rules" in TacOps + if (cargo.getTonnage() <= (getWeight() / 20)) { + if (hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_LARM) && (getCarriedObject(Mech.LOC_LARM) == null) && + !isLocationBad(Mech.LOC_LARM)) { + result.add(Mech.LOC_LARM); + } + + if (hasWorkingSystem(Mech.ACTUATOR_HAND, Mech.LOC_RARM) && (getCarriedObject(Mech.LOC_RARM) == null) && + !isLocationBad(Mech.LOC_RARM)) { + result.add(Mech.LOC_RARM); + } + } + + return result; + } @Override public int getWalkMP(MPCalculationSetting mpCalculationSetting) { diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index e2257873508..6aa722c2fb8 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -7268,6 +7268,8 @@ else if ((step.getElevation() + entity.height()) == 0) { cargoPickupIndex = step.getAdditionalData(MoveStep.CARGO_PICKUP_KEY); } + Integer cargoPickupLocation = step.getAdditionalData(MoveStep.CARGO_LOCATION_KEY); + // there have to be objects on the ground and we have to be trying to pick up one of them if ((groundObjects.size() > 0) && (cargoPickupIndex != null) && (cargoPickupIndex >= 0) && (cargoPickupIndex < groundObjects.size())) { @@ -7275,14 +7277,7 @@ else if ((step.getElevation() + entity.height()) == 0) { ICarryable pickupTarget = groundObjects.get(cargoPickupIndex); if (entity.maxGroundObjectTonnage() >= pickupTarget.getTonnage()) { game.removeGroundObject(step.getPosition(), pickupTarget); - // hack for now: goes into both arms - if (entity instanceof Mech) { - entity.pickupGroundObject(pickupTarget, Mech.LOC_LARM); - entity.pickupGroundObject(pickupTarget, Mech.LOC_RARM); - } else if (entity instanceof Protomech) { - entity.pickupGroundObject(pickupTarget, Protomech.LOC_LARM); - entity.pickupGroundObject(pickupTarget, Protomech.LOC_RARM); - } + entity.pickupGroundObject(pickupTarget, cargoPickupLocation); r = new Report(2513); r.subject = entity.getId(); From b38812df3103b72900c48363d08137e31f173eac Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 18 Jul 2024 16:07:07 -0400 Subject: [PATCH 16/35] cargo damage from incoming attacks --- .../megamek/common/report-messages.properties | 4 +++ megamek/src/megamek/common/Briefcase.java | 4 +++ megamek/src/megamek/common/Entity.java | 19 ++++++++++- megamek/src/megamek/common/ICarryable.java | 1 + megamek/src/megamek/server/GameManager.java | 33 +++++++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/megamek/i18n/megamek/common/report-messages.properties b/megamek/i18n/megamek/common/report-messages.properties index a23654b720e..02541edc6bd 100755 --- a/megamek/i18n/megamek/common/report-messages.properties +++ b/megamek/i18n/megamek/common/report-messages.properties @@ -982,6 +982,10 @@ 6700=Limb Blow off avoided due to armored actuator. 6710=Critical hit to avoided due to armored component. +#cargo reports. +6720= damaged by incoming attack. tons remaining. +6721= damaged by incoming attack and is completely destroyed. + 6800= () was not properly secured prior to takeoff. 7000=Victory!------------------- diff --git a/megamek/src/megamek/common/Briefcase.java b/megamek/src/megamek/common/Briefcase.java index d27ef7853ae..6fe42c1c3f6 100644 --- a/megamek/src/megamek/common/Briefcase.java +++ b/megamek/src/megamek/common/Briefcase.java @@ -33,6 +33,10 @@ public class Briefcase implements ICarryable, Serializable { private double tonnage; private String name; + public void damage(double amount) { + tonnage -= amount; + } + public void setTonnage(double value) { tonnage = value; } diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index 1c6c78cbd80..fd835467609 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -2806,7 +2806,24 @@ public void pickupGroundObject(ICarryable carryable, Integer location) { pickedUpObject = true; } - + /** + * Remove a specific carried object - useful for when you have the object + * but not its location, or when an object is being carried in multiple locations. + */ + public void dropGroundObject(ICarryable carryable) { + // build list of locations to clear out + List locationsToClear = new ArrayList<>(); + + for (Integer location : carriedObjects.keySet()) { + if (carriedObjects.get(location).equals(carryable)) { + locationsToClear.add(location); + } + } + + for (Integer location : locationsToClear) { + carriedObjects.remove(location); + } + } /** * Remove a ground object (cargo) from the given location diff --git a/megamek/src/megamek/common/ICarryable.java b/megamek/src/megamek/common/ICarryable.java index 51502090b4e..86f89aec58d 100644 --- a/megamek/src/megamek/common/ICarryable.java +++ b/megamek/src/megamek/common/ICarryable.java @@ -24,5 +24,6 @@ */ public interface ICarryable { double getTonnage(); + void damage(double amount); String getName(); } \ No newline at end of file diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 6aa722c2fb8..0ed8c8130b2 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -21251,6 +21251,39 @@ public Vector damageEntity(Entity te, HitData hit, int damage, // Allocate the damage while (damage > 0) { + + // damage some cargo if we're taking damage + // maybe move past "exterior passenger" check + if (!ammoExplosion) { + int damageLeftToCargo = damage; + + for (ICarryable cargo : te.getDistinctCarriedObjects()) { + double tonnage = cargo.getTonnage(); + cargo.damage(damageLeftToCargo); + damageLeftToCargo -= Math.ceil(tonnage); + + // if we have destroyed the cargo, remove it, add a report + // and move on to the next piece of cargo + if (cargo.getTonnage() <= 0) { + te.dropGroundObject(cargo); + + r = new Report(6721); + r.subject = te_n; + r.indent(2); + r.add(cargo.getName()); + vDesc.addElement(r); + // we have not destroyed the cargo means there is no damage left + // report and stop destroying cargo + } else { + r = new Report(6720); + r.subject = te_n; + r.indent(2); + r.add(cargo.getName()); + r.add(Double.toString(cargo.getTonnage())); + break; + } + } + } // first check for ammo explosions on aeros separately, because it // must be done before From 100b74c19169fb9f1cb33cbda4aa76f156bbe82a Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 18 Jul 2024 17:38:04 -0400 Subject: [PATCH 17/35] further firing restrictions, dialog fix --- .../i18n/megamek/client/messages.properties | 2 + .../megamek/common/report-messages.properties | 7 ++- .../ui/swing/lobby/PlayerSettingsDialog.java | 2 +- megamek/src/megamek/common/Entity.java | 14 ++--- .../common/actions/PhysicalAttackAction.java | 5 ++ .../common/actions/WeaponAttackAction.java | 5 ++ megamek/src/megamek/server/GameManager.java | 54 ++++++++++++++----- 7 files changed, 67 insertions(+), 22 deletions(-) diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties index 7e08aecbfba..b04da7efda0 100644 --- a/megamek/i18n/megamek/client/messages.properties +++ b/megamek/i18n/megamek/client/messages.properties @@ -2870,6 +2870,7 @@ PlayerSettingsDialog.header.initMod=Initiative Modifier PlayerSettingsDialog.header.minefields=Minefields PlayerSettingsDialog.header.skills=Method for Rolling Pilot Skills PlayerSettingsDialog.header.email=Round Report Email +PlayerSettingsDialog.header.GroundObjects=Carryable Ground Objects PlayerSettingsDialog.botSettings=Bot Settings... PlayerSettingsDialog.autoConfigFaction=Faction: PlayerSettingsDialog.autoConfig=Autoconfig @@ -3930,6 +3931,7 @@ WeaponAttackAction.BPodOnlyAtInf=B-Pods can't target non-infantry. WeaponAttackAction.CantAimAndCallShots=you can't combine aimed shots and called shots. WeaponAttackAction.CantClearMines=Weapon can't clear minefields. WeaponAttackAction.CantFireWhileCarryingCargo=Carrying cargo prevents arm/torso weapon firing. +WeaponAttackAction.CantFireWhileLoadingUnloadingCargo=Cannot fire weapons while loading/unloading cargo. WeaponAttackAction.CantFireWhileGrappled=Can only fire head and front torso weapons when grappled. WeaponAttackAction.CantFireWithOtherWeapons=Already firing a weapon that can only be fired by itself! (%s) WeaponAttackAction.CantFireArmsAndMainGun=Can't fire arm-mounted weapons and the main gun in the same turn. diff --git a/megamek/i18n/megamek/common/report-messages.properties b/megamek/i18n/megamek/common/report-messages.properties index 02541edc6bd..9bb15915f8e 100755 --- a/megamek/i18n/megamek/common/report-messages.properties +++ b/megamek/i18n/megamek/common/report-messages.properties @@ -254,8 +254,11 @@ 2510=, Starts to flip. 2511=, Capsizes and sinks. 2512= deploys a chaff pod. -2513= picks up from . -2514= drops in +2513= loads from . +2514= unloads in +2515= attempts to drop . Needs , rolls : +2516=success - cargo placed on the ground. +2517=failure - cargo destroyed! 3000=Weapon Attack Phase------------------- 3003=Inferno fire (bombs) started in hex . diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java index 23b47940704..1c303ede09f 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java @@ -345,7 +345,7 @@ private JPanel autoConfigSection() { } private JPanel groundObjectConfigSection() { - JPanel result = new OptionPanel("Carryable Ground Objects"); + JPanel result = new OptionPanel("PlayerSettingsDialog.header.GroundObjects"); result.setToolTipText("Define carryable objects that can be placed prior to unit deployment"); groundSectionContent = new Content(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index fd835467609..7793afb5714 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -844,7 +844,7 @@ public abstract class Entity extends TurnOrdered implements Transporter, Targeta /** * Round-long flag indicating that this entity has picked up an object this round. */ - private boolean pickedUpObject; + private boolean endOfTurnCargoInteraction; /** The icon for this unit; This is empty unless the unit file has an embedded icon. */ protected Base64Image icon = new Base64Image(); @@ -2803,14 +2803,14 @@ public void pickupGroundObject(ICarryable carryable, Integer location) { } else { carriedObjects.put(location, carryable); } - pickedUpObject = true; + endOfTurnCargoInteraction = true; } /** * Remove a specific carried object - useful for when you have the object * but not its location, or when an object is being carried in multiple locations. */ - public void dropGroundObject(ICarryable carryable) { + public void dropGroundObject(ICarryable carryable, boolean isUnload) { // build list of locations to clear out List locationsToClear = new ArrayList<>(); @@ -2823,6 +2823,8 @@ public void dropGroundObject(ICarryable carryable) { for (Integer location : locationsToClear) { carriedObjects.remove(location); } + + endOfTurnCargoInteraction = isUnload; } /** @@ -6668,7 +6670,7 @@ public void newRound(int roundNumber) { setClimbMode(GUIP.getMoveDefaultClimbMode()); - pickedUpObject = false; + endOfTurnCargoInteraction = false; setTurnInterrupted(false); } @@ -12337,8 +12339,8 @@ public boolean turnWasInterrupted() { return turnWasInterrupted; } - public boolean pickedUpObjectThisTurn() { - return pickedUpObject; + public boolean endOfTurnCargoInteraction() { + return endOfTurnCargoInteraction; } public Vector getSensors() { diff --git a/megamek/src/megamek/common/actions/PhysicalAttackAction.java b/megamek/src/megamek/common/actions/PhysicalAttackAction.java index 65d12deedd6..7b42096c56e 100644 --- a/megamek/src/megamek/common/actions/PhysicalAttackAction.java +++ b/megamek/src/megamek/common/actions/PhysicalAttackAction.java @@ -65,6 +65,11 @@ public PhysicalAttackAction(int entityId, int targetType, int targetId) { if (ae.isEvading()) { return "Attacker is evading."; } + + // can't make physical attacks if loading/unloading cargo + if (ae.endOfTurnCargoInteraction()) { + return Messages.getString("WeaponAttackAction.CantFireWhileLoadingUnloadingCargo"); + } if (target.getTargetType() == Targetable.TYPE_ENTITY) { // Checks specific to entity targets diff --git a/megamek/src/megamek/common/actions/WeaponAttackAction.java b/megamek/src/megamek/common/actions/WeaponAttackAction.java index b801ba0dca0..19a137259b1 100644 --- a/megamek/src/megamek/common/actions/WeaponAttackAction.java +++ b/megamek/src/megamek/common/actions/WeaponAttackAction.java @@ -1079,6 +1079,11 @@ private static String toHitIsImpossible(Game game, Entity ae, int attackerId, Ta } } + // can't fire weapons if loading/unloading cargo + if (ae.endOfTurnCargoInteraction()) { + return Messages.getString("WeaponAttackAction.CantFireWhileLoadingUnloadingCargo"); + } + // can't fire arm/forward facing torso weapons if carrying cargo in hands if ((weapon != null)) { int loc = weapon.getLocation(); diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 0ed8c8130b2..73d22744395 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -7307,23 +7307,51 @@ else if ((step.getElevation() + entity.height()) == 0) { // piece of cargo and we're going to just drop that one if (cargoLocation == null) { cargo = entity.getDistinctCarriedObjects().get(0); - entity.dropGroundObjects(); } else { cargo = entity.getCarriedObject(cargoLocation); - entity.dropGroundObject(cargoLocation); } - game.placeGroundObject(step.getPosition(), cargo); + entity.dropGroundObject(cargo, isLastStep); + boolean cargoDestroyed = false; - r = new Report(2514); - r.subject = entity.getId(); - r.add(entity.getDisplayName()); - r.add(cargo.getName()); - r.add(step.getPosition().toFriendlyString()); - addReport(r); - - // a drop changes board state. Send an update for the overall ground object list. - send(new Packet(PacketCommand.UPDATE_GROUND_OBJECTS, getGame().getGroundObjects())); + if (!isLastStep) { + // cargo may be destroyed if we're not carefully unloading it + // very likely to be destroyed if we're airborne for some reason + int destructionThreshold = (step.isFlying() || step.isJumping()) ? 6 : 4; + int destructionRoll = Compute.d6(); + + r = new Report(2515); + r.subject = entity.getId(); + r.add(entity.getDisplayName()); + r.add(cargo.getName()); + r.add(destructionThreshold); + r.add(destructionRoll); + + if (destructionRoll < destructionThreshold) { + cargoDestroyed = true; + r.choose(false); + } else { + r.choose(true); + } + + addReport(r); + } + + // note that this should not be moved into the "!isLastStep" block above + // as cargo may be either unloaded peacefully or dumped on the move + if (!cargoDestroyed) { + game.placeGroundObject(step.getPosition(), cargo); + + r = new Report(2514); + r.subject = entity.getId(); + r.add(entity.getDisplayName()); + r.add(cargo.getName()); + r.add(step.getPosition().toFriendlyString()); + addReport(r); + + // a drop changes board state. Send an update for the overall ground object list. + send(new Packet(PacketCommand.UPDATE_GROUND_OBJECTS, getGame().getGroundObjects())); + } } // handle fighter recovery, and also DropShip docking with another large craft @@ -21265,7 +21293,7 @@ public Vector damageEntity(Entity te, HitData hit, int damage, // if we have destroyed the cargo, remove it, add a report // and move on to the next piece of cargo if (cargo.getTonnage() <= 0) { - te.dropGroundObject(cargo); + te.dropGroundObject(cargo, false); r = new Report(6721); r.subject = te_n; From 6e3454c49bb976543d09b3b20d0f477c4534dedb Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 Jul 2024 09:11:27 -0400 Subject: [PATCH 18/35] some changes I don't really remember --- .../megamek/common/report-messages.properties | 4 +- .../ui/swing/lobby/PlayerSettingsDialog.java | 14 ++++ megamek/src/megamek/common/Briefcase.java | 9 +++ megamek/src/megamek/common/ICarryable.java | 1 + megamek/src/megamek/server/GameManager.java | 65 +++++++++++++------ 5 files changed, 70 insertions(+), 23 deletions(-) diff --git a/megamek/i18n/megamek/common/report-messages.properties b/megamek/i18n/megamek/common/report-messages.properties index 9bb15915f8e..5a66f00876a 100755 --- a/megamek/i18n/megamek/common/report-messages.properties +++ b/megamek/i18n/megamek/common/report-messages.properties @@ -256,8 +256,8 @@ 2512= deploys a chaff pod. 2513= loads from . 2514= unloads in -2515= attempts to drop . Needs , rolls : -2516=success - cargo placed on the ground. +2515= is dropped to the ground. Needs , rolls : +2516=success - cargo remains intact. 2517=failure - cargo destroyed! 3000=Weapon Attack Phase------------------- diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java index 1c303ede09f..6ded42f80c1 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java @@ -240,6 +240,7 @@ public String getEmail() { private Content groundSectionContent = new Content(new GridLayout(2, 3)); private final JTextField txtGroundObjectName = new JTextField(); private final JFormattedTextField txtGroundObjectTonnage = new JFormattedTextField(formatterFactory, 0); + private final JCheckBox chkGroundObjectInvulnerable = new JCheckBox(); private List> groundSectionComponents = new ArrayList<>(); // Bot Settings Section @@ -359,6 +360,10 @@ private JPanel groundObjectConfigSection() { JLabel lblTonnage = new JLabel("Tonnage"); groundSectionContent.add(lblTonnage, gbc); + gbc.gridx = 2; + JLabel lblInvulnerable = new JLabel("Invulnerable"); + groundSectionContent.add(lblInvulnerable); + gbc.gridy = 1; gbc.gridx = 0; groundSectionContent.add(txtGroundObjectName, gbc); @@ -367,6 +372,9 @@ private JPanel groundObjectConfigSection() { groundSectionContent.add(txtGroundObjectTonnage, gbc); gbc.gridx = 2; + groundSectionContent.add(chkGroundObjectInvulnerable, gbc); + + gbc.gridx = 3; JButton btnAdd = new JButton("Add"); btnAdd.setActionCommand(CMD_ADD_GROUND_OBJECT); btnAdd.addActionListener(listener); @@ -399,6 +407,11 @@ private void addGroundObjectToUI(ICarryable groundObject) { row.add(tonnageLabel); gbc.gridx = 2; + JLabel flagLabel = new JLabel(groundObject.isInvulnerable() ? "Yes" : "No"); + groundSectionContent.add(flagLabel, gbc); + row.add(flagLabel); + + gbc.gridx = 3; JButton btnRemove = new JButton("Remove"); btnRemove.setActionCommand(String.format(CMD_REMOVE_GROUND_OBJECT, player.getGroundObjectsToPlace().size() - 1)); btnRemove.addActionListener(listener); @@ -416,6 +429,7 @@ private void addGroundObject() { Briefcase briefcase = new Briefcase(); briefcase.setName(txtGroundObjectName.getText()); briefcase.setTonnage(Double.parseDouble(txtGroundObjectTonnage.getText())); + briefcase.setInvulnerable(chkGroundObjectInvulnerable.isSelected()); player.getGroundObjectsToPlace().add(briefcase); addGroundObjectToUI(briefcase); diff --git a/megamek/src/megamek/common/Briefcase.java b/megamek/src/megamek/common/Briefcase.java index 6fe42c1c3f6..2a9d0326cec 100644 --- a/megamek/src/megamek/common/Briefcase.java +++ b/megamek/src/megamek/common/Briefcase.java @@ -32,6 +32,7 @@ public class Briefcase implements ICarryable, Serializable { private double tonnage; private String name; + private boolean invulnerable; public void damage(double amount) { tonnage -= amount; @@ -45,6 +46,14 @@ public double getTonnage() { return tonnage; } + public boolean isInvulnerable() { + return invulnerable; + } + + public void setInvulnerable(boolean value) { + invulnerable = value; + } + public void setName(String value) { name = value; } diff --git a/megamek/src/megamek/common/ICarryable.java b/megamek/src/megamek/common/ICarryable.java index 86f89aec58d..679406eb84b 100644 --- a/megamek/src/megamek/common/ICarryable.java +++ b/megamek/src/megamek/common/ICarryable.java @@ -26,4 +26,5 @@ public interface ICarryable { double getTonnage(); void damage(double amount); String getName(); + boolean isInvulnerable(); } \ No newline at end of file diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 73d22744395..84a5978387a 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -7314,27 +7314,8 @@ else if ((step.getElevation() + entity.height()) == 0) { entity.dropGroundObject(cargo, isLastStep); boolean cargoDestroyed = false; - if (!isLastStep) { - // cargo may be destroyed if we're not carefully unloading it - // very likely to be destroyed if we're airborne for some reason - int destructionThreshold = (step.isFlying() || step.isJumping()) ? 6 : 4; - int destructionRoll = Compute.d6(); - - r = new Report(2515); - r.subject = entity.getId(); - r.add(entity.getDisplayName()); - r.add(cargo.getName()); - r.add(destructionThreshold); - r.add(destructionRoll); - - if (destructionRoll < destructionThreshold) { - cargoDestroyed = true; - r.choose(false); - } else { - r.choose(true); - } - - addReport(r); + if (!isLastStep && !cargo.isInvulnerable()) { + cargoDestroyed = damageCargo(step.isFlying() || step.isJumping(), entity, cargo); } // note that this should not be moved into the "!isLastStep" block above @@ -27525,6 +27506,12 @@ else if (waterDepth > 0) { vPhaseReport.addAll(destroyEntity(entity, "a watery grave", false)); return vPhaseReport; } + + // drop cargo + for (ICarryable cargo : entity.getDistinctCarriedObjects()) { + entity.dropGroundObject(cargo, false); + damageCargo(false, entity, cargo); + } // set how deep the mech has fallen if (entity instanceof Mech) { @@ -34145,6 +34132,42 @@ private void layMine(Entity entity, int mineId, Coords coords) { entity.setLayingMines(true); } } + + /** + * Worker function that potentially damages a piece of cargo being carried + * by the given entity during the given move step. + * + * @param isFlying whether the entity's movement involved being in the air in any way + */ + private boolean damageCargo(boolean isFlying, Entity entity, ICarryable cargo) { + if (cargo.isInvulnerable()) { + return false; + } + + boolean cargoDestroyed = false; + + // cargo may be destroyed if we're not carefully unloading it + // very likely to be destroyed if we're airborne for some reason + int destructionThreshold = isFlying ? 6 : 4; + int destructionRoll = Compute.d6(); + + Report r = new Report(2515); + r.subject = entity.getId(); + r.add(cargo.getName()); + r.add(destructionThreshold); + r.add(destructionRoll); + + if (destructionRoll < destructionThreshold) { + cargoDestroyed = true; + r.choose(false); + } else { + r.choose(true); + } + + addReport(r); + + return cargoDestroyed; + } public Set getHexUpdateSet() { return hexUpdateSet; From e1fa6379b39a9e00f792a3aac0b1090d2c905412 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 Jul 2024 19:17:07 -0400 Subject: [PATCH 19/35] fix NPE; add flee message --- .../megamek/common/report-messages.properties | 1 + .../ui/swing/lobby/PlayerSettingsDialog.java | 33 +++++++++++++++---- megamek/src/megamek/server/GameManager.java | 8 +++++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/megamek/i18n/megamek/common/report-messages.properties b/megamek/i18n/megamek/common/report-messages.properties index 5a66f00876a..e4842ae25e5 100755 --- a/megamek/i18n/megamek/common/report-messages.properties +++ b/megamek/i18n/megamek/common/report-messages.properties @@ -73,6 +73,7 @@ 2005= () flees the battlefield. 2010=It carries () with it. 2015=It takes () with it. +2016=It carries with it. 2020= ejects from a (). 2025= () is abandoned by its crew. 2026= () is given the order to abandon ship. diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java index 6ded42f80c1..36fdcda39fd 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java @@ -52,7 +52,9 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.math.RoundingMode; import java.nio.file.Paths; +import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Comparator; @@ -87,8 +89,28 @@ public PlayerSettingsDialog(ClientGUI cg, Client cl, BoardView bv) { currentPlayerStartPos -= 10; } + NumberFormat numFormat = NumberFormat.getIntegerInstance(); + numFormat.setGroupingUsed(false); + + NumberFormatter numFormatter = new NumberFormatter(numFormat); numFormatter.setMinimum(0); numFormatter.setCommitsOnValidEdit(true); + + DefaultFormatterFactory formatterFactory = new DefaultFormatterFactory(numFormatter); + + txtOffset = new JFormattedTextField(formatterFactory, 0); + txtWidth = new JFormattedTextField(formatterFactory, 3); + + DecimalFormat tonnageFormat = new DecimalFormat(); + tonnageFormat.setGroupingUsed(false); + tonnageFormat.setRoundingMode(RoundingMode.UNNECESSARY); + /*NumberFormatter tonnageFormatter = new NumberFormatter(tonnageFormat); + tonnageFormatter.setMinimum(0); + tonnageFormatter.setCommitsOnValidEdit(true); + DefaultFormatterFactory tonnageFormatterFactory = new DefaultFormatterFactory(tonnageFormatter);*/ + + txtGroundObjectTonnage = new JFormattedTextField(tonnageFormat); + txtGroundObjectTonnage.setText("0"); initialize(); } @@ -225,12 +247,9 @@ public String getEmail() { // Deployment Section private final JPanel panStartButtons = new JPanel(); private final TipButton[] butStartPos = new TipButton[11]; - // this might seem like kind of a dumb way to declare it, but JFormattedTextField doesn't have an overload that - // takes both a number formatter and a default value. - private final NumberFormatter numFormatter = new NumberFormatter(NumberFormat.getIntegerInstance()); - private final DefaultFormatterFactory formatterFactory = new DefaultFormatterFactory(numFormatter); - private final JFormattedTextField txtOffset = new JFormattedTextField(formatterFactory, 0); - private final JFormattedTextField txtWidth = new JFormattedTextField(formatterFactory, 3); + + private final JFormattedTextField txtOffset; + private final JFormattedTextField txtWidth; private JSpinner spinStartingAnyNWx; private JSpinner spinStartingAnyNWy; private JSpinner spinStartingAnySEx; @@ -239,7 +258,7 @@ public String getEmail() { // ground object config section private Content groundSectionContent = new Content(new GridLayout(2, 3)); private final JTextField txtGroundObjectName = new JTextField(); - private final JFormattedTextField txtGroundObjectTonnage = new JFormattedTextField(formatterFactory, 0); + private final JFormattedTextField txtGroundObjectTonnage; private final JCheckBox chkGroundObjectInvulnerable = new JCheckBox(); private List> groundSectionComponents = new ArrayList<>(); diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 84a5978387a..7cb314d4523 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -5299,6 +5299,14 @@ private Vector processLeaveMap(MovePath movePath, boolean flewOff, int r game.removeEntity(swarmerId, IEntityRemovalConditions.REMOVE_CAPTURED); send(createRemoveEntityPacket(swarmerId, IEntityRemovalConditions.REMOVE_CAPTURED)); } + + for (ICarryable cargo : entity.getDistinctCarriedObjects()) { + r = new Report(2016, Report.PUBLIC); + r.indent(); + r.add(cargo.getName()); + addReport(r); + } + entity.setRetreatedDirection(fleeDirection); game.removeEntity(entity.getId(), IEntityRemovalConditions.REMOVE_IN_RETREAT); send(createRemoveEntityPacket(entity.getId(), IEntityRemovalConditions.REMOVE_IN_RETREAT)); From f01c9e5ab7800ec3e16b54ac104102edae9d813f Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 Jul 2024 19:22:18 -0400 Subject: [PATCH 20/35] weapons fire cannot damage invulnerable cargo --- .../megamek/client/ui/swing/lobby/PlayerSettingsDialog.java | 4 ---- megamek/src/megamek/server/GameManager.java | 6 +++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java index 36fdcda39fd..fbc2f6e4c71 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java @@ -104,10 +104,6 @@ public PlayerSettingsDialog(ClientGUI cg, Client cl, BoardView bv) { DecimalFormat tonnageFormat = new DecimalFormat(); tonnageFormat.setGroupingUsed(false); tonnageFormat.setRoundingMode(RoundingMode.UNNECESSARY); - /*NumberFormatter tonnageFormatter = new NumberFormatter(tonnageFormat); - tonnageFormatter.setMinimum(0); - tonnageFormatter.setCommitsOnValidEdit(true); - DefaultFormatterFactory tonnageFormatterFactory = new DefaultFormatterFactory(tonnageFormatter);*/ txtGroundObjectTonnage = new JFormattedTextField(tonnageFormat); txtGroundObjectTonnage.setText("0"); diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 7cb314d4523..c5436850a03 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -7322,7 +7322,7 @@ else if ((step.getElevation() + entity.height()) == 0) { entity.dropGroundObject(cargo, isLastStep); boolean cargoDestroyed = false; - if (!isLastStep && !cargo.isInvulnerable()) { + if (!isLastStep) { cargoDestroyed = damageCargo(step.isFlying() || step.isJumping(), entity, cargo); } @@ -21275,6 +21275,10 @@ public Vector damageEntity(Entity te, HitData hit, int damage, int damageLeftToCargo = damage; for (ICarryable cargo : te.getDistinctCarriedObjects()) { + if (cargo.isInvulnerable()) { + continue; + } + double tonnage = cargo.getTonnage(); cargo.damage(damageLeftToCargo); damageLeftToCargo -= Math.ceil(tonnage); From 1e096eec710f987af5e6559d3aa5d25907effcac Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 22 Jul 2024 10:11:49 -0400 Subject: [PATCH 21/35] no negative numbers; 20 char limit for object name --- .../ui/swing/lobby/PlayerSettingsDialog.java | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java index fbc2f6e4c71..caf982da266 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java @@ -45,6 +45,10 @@ import javax.swing.*; import javax.swing.border.EmptyBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.text.DefaultFormatterFactory; import javax.swing.text.NumberFormatter; @@ -52,6 +56,8 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; import java.math.RoundingMode; import java.nio.file.Paths; import java.text.DecimalFormat; @@ -104,10 +110,22 @@ public PlayerSettingsDialog(ClientGUI cg, Client cl, BoardView bv) { DecimalFormat tonnageFormat = new DecimalFormat(); tonnageFormat.setGroupingUsed(false); tonnageFormat.setRoundingMode(RoundingMode.UNNECESSARY); - + txtGroundObjectTonnage = new JFormattedTextField(tonnageFormat); txtGroundObjectTonnage.setText("0"); + txtGroundObjectName = new JTextField(); + txtGroundObjectName.setColumns(20); + // if it's longer than 20 characters, undo the edit + txtGroundObjectName.getDocument().addUndoableEditListener(new UndoableEditListener( ) { + @Override + public void undoableEditHappened(UndoableEditEvent e) { + if (txtGroundObjectName.getText().length() > 20 && e.getEdit().canUndo()) { + e.getEdit().undo(); + } + } + }); + initialize(); } @@ -253,7 +271,7 @@ public String getEmail() { // ground object config section private Content groundSectionContent = new Content(new GridLayout(2, 3)); - private final JTextField txtGroundObjectName = new JTextField(); + private final JTextField txtGroundObjectName; private final JFormattedTextField txtGroundObjectTonnage; private final JCheckBox chkGroundObjectInvulnerable = new JCheckBox(); private List> groundSectionComponents = new ArrayList<>(); @@ -443,7 +461,21 @@ private void addGroundObjectToUI(ICarryable groundObject) { private void addGroundObject() { Briefcase briefcase = new Briefcase(); briefcase.setName(txtGroundObjectName.getText()); - briefcase.setTonnage(Double.parseDouble(txtGroundObjectTonnage.getText())); + + Double tonnage = 0.0; + + try { + tonnage = Double.parseDouble(txtGroundObjectTonnage.getText()); + + // don't allow negative tonnage as we do not have anti-gravity technology + if (tonnage < 0) { + tonnage = 0.0; + } + } catch (Exception ignored) { + + } + + briefcase.setTonnage(tonnage); briefcase.setInvulnerable(chkGroundObjectInvulnerable.isSelected()); player.getGroundObjectsToPlace().add(briefcase); From 69cb27d21116fa712d40410b6b35dd0f1ba4d943 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 Jul 2024 09:32:56 -0400 Subject: [PATCH 22/35] add report for falling --- megamek/i18n/megamek/common/report-messages.properties | 1 + megamek/src/megamek/common/Entity.java | 5 ++++- megamek/src/megamek/server/GameManager.java | 9 ++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/megamek/i18n/megamek/common/report-messages.properties b/megamek/i18n/megamek/common/report-messages.properties index e4842ae25e5..a724b92ed6a 100755 --- a/megamek/i18n/megamek/common/report-messages.properties +++ b/megamek/i18n/megamek/common/report-messages.properties @@ -989,6 +989,7 @@ #cargo reports. 6720= damaged by incoming attack. tons remaining. 6721= damaged by incoming attack and is completely destroyed. +6722= is dropped to the ground due to the carrying unit falling. 6800= () was not properly secured prior to takeoff. diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index 7793afb5714..cbe0a5a5067 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -2824,7 +2824,10 @@ public void dropGroundObject(ICarryable carryable, boolean isUnload) { carriedObjects.remove(location); } - endOfTurnCargoInteraction = isUnload; + // if it's not an "unload", we're going to leave the "end of turn cargo interaction" flag alone + if (isUnload) { + endOfTurnCargoInteraction = true; + } } /** diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 455396cef37..6cd0f97d009 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -27527,7 +27527,14 @@ else if (waterDepth > 0) { // drop cargo for (ICarryable cargo : entity.getDistinctCarriedObjects()) { entity.dropGroundObject(cargo, false); - damageCargo(false, entity, cargo); + // if the cargo was dropped but not destroyed. + if (!damageCargo(false, entity, cargo)) { + r = new Report(6722); + r.indent(); + r.subject = entity.getId(); + r.add(cargo.getName()); + vPhaseReport.add(r); + } } // set how deep the mech has fallen From 85a6709e37b6356403937ff0439c811db04e3625 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 Jul 2024 09:46:27 -0400 Subject: [PATCH 23/35] actually drop cargo when knocked over --- megamek/src/megamek/server/GameManager.java | 13 +++++++++++-- megamek/src/megamek/server/TWPhaseEndManager.java | 4 ++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 6cd0f97d009..ad6794df4fe 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -7295,7 +7295,7 @@ else if ((step.getElevation() + entity.height()) == 0) { addReport(r); // a pickup should be the last step. Send an update for the overall ground object list. - send(new Packet(PacketCommand.UPDATE_GROUND_OBJECTS, getGame().getGroundObjects())); + sendGroundObjectUpdate(); break; } else { LogManager.getLogger().warn(entity.getShortName() + " attempted to pick up object but it is too heavy. Carry capacity: " @@ -7339,7 +7339,7 @@ else if ((step.getElevation() + entity.height()) == 0) { addReport(r); // a drop changes board state. Send an update for the overall ground object list. - send(new Packet(PacketCommand.UPDATE_GROUND_OBJECTS, getGame().getGroundObjects())); + sendGroundObjectUpdate(); } } @@ -27534,6 +27534,8 @@ else if (waterDepth > 0) { r.subject = entity.getId(); r.add(cargo.getName()); vPhaseReport.add(r); + + game.placeGroundObject(fallPos, cargo); } } @@ -34221,4 +34223,11 @@ List getTerrainProcessors() { void clearBombIcons() { game.getBoard().clearBombIcons(); } + + /** + * Convenience function to send a ground object update. + */ + public void sendGroundObjectUpdate() { + send(new Packet(PacketCommand.UPDATE_GROUND_OBJECTS, getGame().getGroundObjects())); + } } diff --git a/megamek/src/megamek/server/TWPhaseEndManager.java b/megamek/src/megamek/server/TWPhaseEndManager.java index 2cf18983850..021625f6993 100644 --- a/megamek/src/megamek/server/TWPhaseEndManager.java +++ b/megamek/src/megamek/server/TWPhaseEndManager.java @@ -23,6 +23,8 @@ import megamek.common.Report; import megamek.common.enums.GamePhase; import megamek.common.event.GameVictoryEvent; +import megamek.common.net.enums.PacketCommand; +import megamek.common.net.packets.Packet; class TWPhaseEndManager { @@ -150,6 +152,7 @@ void managePhase() { gameManager.getGame().addReports(gameManager.getvPhaseReport()); gameManager.changePhase(GamePhase.PHYSICAL); } + gameManager.sendGroundObjectUpdate(); // For bomb markers gameManager.sendSpecialHexDisplayPackets(); break; @@ -177,6 +180,7 @@ void managePhase() { gameManager.sendReport(); gameManager.changePhase(GamePhase.END); } + gameManager.sendGroundObjectUpdate(); break; case PHYSICAL_REPORT: gameManager.changePhase(GamePhase.END); From 81e1ba8dc1dc74c5c045465e320c8da3668a62e9 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 Jul 2024 09:47:50 -0400 Subject: [PATCH 24/35] cleanup --- megamek/src/megamek/server/TWPhaseEndManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/megamek/src/megamek/server/TWPhaseEndManager.java b/megamek/src/megamek/server/TWPhaseEndManager.java index 021625f6993..9a3bec51437 100644 --- a/megamek/src/megamek/server/TWPhaseEndManager.java +++ b/megamek/src/megamek/server/TWPhaseEndManager.java @@ -23,8 +23,6 @@ import megamek.common.Report; import megamek.common.enums.GamePhase; import megamek.common.event.GameVictoryEvent; -import megamek.common.net.enums.PacketCommand; -import megamek.common.net.packets.Packet; class TWPhaseEndManager { From 20be2aaf47c12e20a9182253a81201b3ac2f2240 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 Jul 2024 15:29:33 -0400 Subject: [PATCH 25/35] drop cargo on entity destruction (for any reason) --- megamek/src/megamek/server/GameManager.java | 47 +++++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index ad6794df4fe..6177906320f 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -27048,12 +27048,43 @@ else if ((null != Compute.stackingViolation(game, other.getId(), curPos, entity. sendChangedHex(curPos); } } + + // drop cargo + dropCargo(entity, curPos); // update our entity, so clients have correct data needed for MekWars stuff entityUpdate(entity.getId()); return vDesc; } + + /** + * Worker function that drops cargo from an entity at the given coordinates. + */ + private void dropCargo(Entity entity, Coords coords) { + boolean cargoDropped = false; + + for (ICarryable cargo : entity.getDistinctCarriedObjects()) { + entity.dropGroundObject(cargo, false); + // if the cargo was dropped but not destroyed. + if (!damageCargo(false, entity, cargo)) { + Report r = new Report(6722); + r.indent(); + r.subject = entity.getId(); + r.add(cargo.getName()); + vPhaseReport.add(r); + + if (game.getBoard().contains(coords)) { + game.placeGroundObject(coords, cargo); + cargoDropped = true; + } + } + } + + if (cargoDropped) { + sendGroundObjectUpdate(); + } + } /** * Makes a piece of equipment on a mech explode! POW! This expects either @@ -27524,20 +27555,8 @@ else if (waterDepth > 0) { return vPhaseReport; } - // drop cargo - for (ICarryable cargo : entity.getDistinctCarriedObjects()) { - entity.dropGroundObject(cargo, false); - // if the cargo was dropped but not destroyed. - if (!damageCargo(false, entity, cargo)) { - r = new Report(6722); - r.indent(); - r.subject = entity.getId(); - r.add(cargo.getName()); - vPhaseReport.add(r); - - game.placeGroundObject(fallPos, cargo); - } - } + // drop cargo if necessary + dropCargo(entity, fallPos); // set how deep the mech has fallen if (entity instanceof Mech) { From 552ff62a21d39311490f7094ff6eb4a22fe10732 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 25 Jul 2024 23:57:08 -0400 Subject: [PATCH 26/35] refactor into btobject hierarchy --- .../ui/swing/lobby/PlayerSettingsDialog.java | 2 +- .../client/ui/swing/tooltip/HexTooltip.java | 2 +- .../client/ui/swing/unitDisplay/ExtraPanel.java | 2 +- megamek/src/megamek/common/Briefcase.java | 4 ++-- megamek/src/megamek/common/ICarryable.java | 7 +++++-- megamek/src/megamek/server/GameManager.java | 14 +++++++------- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java index caf982da266..d324482f2d1 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/lobby/PlayerSettingsDialog.java @@ -429,7 +429,7 @@ private void addGroundObjectToUI(ICarryable groundObject) { gbc.gridy = groundSectionComponents.size() + 2; // there's always two extra rows - header + text fields gbc.gridx = 0; - JLabel nameLabel = new JLabel(groundObject.getName()); + JLabel nameLabel = new JLabel(groundObject.generalName()); groundSectionContent.add(nameLabel, gbc); List row = new ArrayList<>(); row.add(nameLabel); diff --git a/megamek/src/megamek/client/ui/swing/tooltip/HexTooltip.java b/megamek/src/megamek/client/ui/swing/tooltip/HexTooltip.java index 2e353b30bcd..b1ea159ebc1 100644 --- a/megamek/src/megamek/client/ui/swing/tooltip/HexTooltip.java +++ b/megamek/src/megamek/client/ui/swing/tooltip/HexTooltip.java @@ -164,7 +164,7 @@ public static String getHexTip(Hex mhex, @Nullable Client client, GUIPreferences for (ICarryable groundObject : game.getGroundObjects(mcoords)) { result.append(" "); result.append(guiScaledFontHTML(UIUtil.uiWhite())); - result.append(groundObject.toString()); + result.append(groundObject.specificName()); result.append(""); result.append("
"); } diff --git a/megamek/src/megamek/client/ui/swing/unitDisplay/ExtraPanel.java b/megamek/src/megamek/client/ui/swing/unitDisplay/ExtraPanel.java index c934e798676..5cc7320c226 100644 --- a/megamek/src/megamek/client/ui/swing/unitDisplay/ExtraPanel.java +++ b/megamek/src/megamek/client/ui/swing/unitDisplay/ExtraPanel.java @@ -493,7 +493,7 @@ public void displayMech(Entity en) { // show cargo. for (ICarryable cargo : en.getDistinctCarriedObjects()) { - carrysR.append(cargo.toString()); + carrysR.append(cargo.specificName()); carrysR.append("\n"); } diff --git a/megamek/src/megamek/common/Briefcase.java b/megamek/src/megamek/common/Briefcase.java index 2a9d0326cec..58095efbe62 100644 --- a/megamek/src/megamek/common/Briefcase.java +++ b/megamek/src/megamek/common/Briefcase.java @@ -58,11 +58,11 @@ public void setName(String value) { name = value; } - public String getName() { + public String generalName() { return name; } - public String toString() { + public String specificName() { return name + " (" + tonnage + " tons)"; } } diff --git a/megamek/src/megamek/common/ICarryable.java b/megamek/src/megamek/common/ICarryable.java index 679406eb84b..b0160cee950 100644 --- a/megamek/src/megamek/common/ICarryable.java +++ b/megamek/src/megamek/common/ICarryable.java @@ -22,9 +22,12 @@ /** * An interface defining all the required properties of a carryable object. */ -public interface ICarryable { +public interface ICarryable extends BTObject { double getTonnage(); void damage(double amount); - String getName(); boolean isInvulnerable(); + + default boolean isCarryableObject() { + return true; + } } \ No newline at end of file diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 6177906320f..11e70149e63 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -5303,7 +5303,7 @@ private Vector processLeaveMap(MovePath movePath, boolean flewOff, int r for (ICarryable cargo : entity.getDistinctCarriedObjects()) { r = new Report(2016, Report.PUBLIC); r.indent(); - r.add(cargo.getName()); + r.add(cargo.generalName()); addReport(r); } @@ -7290,7 +7290,7 @@ else if ((step.getElevation() + entity.height()) == 0) { r = new Report(2513); r.subject = entity.getId(); r.add(entity.getDisplayName()); - r.add(pickupTarget.toString()); + r.add(pickupTarget.specificName()); r.add(step.getPosition().toFriendlyString()); addReport(r); @@ -7334,7 +7334,7 @@ else if ((step.getElevation() + entity.height()) == 0) { r = new Report(2514); r.subject = entity.getId(); r.add(entity.getDisplayName()); - r.add(cargo.getName()); + r.add(cargo.generalName()); r.add(step.getPosition().toFriendlyString()); addReport(r); @@ -21296,7 +21296,7 @@ public Vector damageEntity(Entity te, HitData hit, int damage, r = new Report(6721); r.subject = te_n; r.indent(2); - r.add(cargo.getName()); + r.add(cargo.generalName()); vDesc.addElement(r); // we have not destroyed the cargo means there is no damage left // report and stop destroying cargo @@ -21304,7 +21304,7 @@ public Vector damageEntity(Entity te, HitData hit, int damage, r = new Report(6720); r.subject = te_n; r.indent(2); - r.add(cargo.getName()); + r.add(cargo.generalName()); r.add(Double.toString(cargo.getTonnage())); break; } @@ -27071,7 +27071,7 @@ private void dropCargo(Entity entity, Coords coords) { Report r = new Report(6722); r.indent(); r.subject = entity.getId(); - r.add(cargo.getName()); + r.add(cargo.generalName()); vPhaseReport.add(r); if (game.getBoard().contains(coords)) { @@ -34207,7 +34207,7 @@ private boolean damageCargo(boolean isFlying, Entity entity, ICarryable cargo) { Report r = new Report(2515); r.subject = entity.getId(); - r.add(cargo.getName()); + r.add(cargo.generalName()); r.add(destructionThreshold); r.add(destructionRoll); From 9060f4617f4d26f5c9324d26ee49de7e86c1bb9c Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 26 Jul 2024 16:33:31 +0200 Subject: [PATCH 27/35] work entity sprites in BoardView into allSprites processing --- .../client/ui/swing/boardview/BoardView.java | 31 ++++++++++++++----- .../ui/swing/boardview/IsometricSprite.java | 2 +- .../client/ui/swing/boardview/Sprite.java | 5 +++ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java index 5b35b6797fd..9fcfe228bf2 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java +++ b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java @@ -906,9 +906,6 @@ public void draw(Graphics g) { drawSprites(g, flyOverSprites); } - // draw onscreen entities - drawSprites(g, entitySprites); - // draw moving onscreen entities drawSprites(g, movingEntitySprites); @@ -1626,9 +1623,6 @@ public BufferedImage getEntireBoardImage(boolean ignoreUnits, boolean useBaseZoo drawSprites(boardGraph, flyOverSprites); } - // draw onscreen entities - drawSprites(boardGraph, entitySprites); - // draw moving onscreen entities drawSprites(boardGraph, movingEntitySprites); @@ -1723,7 +1717,7 @@ private void drawHexes(Graphics g, Rectangle view, boolean saveBoardImage) { if (GUIP.getShowWrecks()) { drawIsometricWreckSpritesForHex(c, g, isometricWreckSprites); } - drawIsometricSpritesForHex(c, g, isometricSprites); +// drawIsometricSpritesForHex(c, g, isometricSprites); } } } @@ -1731,7 +1725,7 @@ private void drawHexes(Graphics g, Rectangle view, boolean saveBoardImage) { if (!saveBoardImage) { // If we are using Isometric rendering, redraw the entity sprites at 50% transparent // so sprites hidden behind hills can still be seen by the user. - drawIsometricSprites(g, isometricSprites); +// drawIsometricSprites(g, isometricSprites); } } else { // Draw hexes without regard to elevation when not using Isometric, since it does not @@ -2622,6 +2616,7 @@ public void redrawMovingEntity(Entity entity, Coords position, int facing, int e // Remove sprite for Entity, so it's not displayed while moving if (sprite != null) { + removeSprite(sprite); newSprites = new PriorityQueue<>(entitySprites); newSpriteIds = new HashMap<>(entitySpriteIds); @@ -2633,6 +2628,7 @@ public void redrawMovingEntity(Entity entity, Coords position, int facing, int e } // Remove iso sprite for Entity, so it's not displayed while moving if (isoSprite != null) { + removeSprite(isoSprite); isoSprites = new PriorityQueue<>(isometricSprites); newIsoSpriteIds = new HashMap<>(isometricSpriteIds); @@ -2814,10 +2810,16 @@ public void redrawEntity(Entity entity, Entity oldEntity) { } // Update Sprite state with new collections + removeSprites(entitySprites); + removeSprites(isometricSprites); entitySprites = newSprites; entitySpriteIds = newSpriteIds; isometricSprites = isoSprites; isometricSpriteIds = newIsoSpriteIds; + addSprites(entitySprites); + if (drawIsometric) { + addSprites(isometricSprites); + } // Remove C3 sprites for (Iterator i = c3Sprites.iterator(); i.hasNext(); ) { @@ -2944,12 +2946,21 @@ void redrawAllEntities() { } } + removeSprites(entitySprites); + removeSprites(isometricSprites); + entitySprites = newSprites; entitySpriteIds = newSpriteIds; isometricSprites = newIsometricSprites; isometricSpriteIds = newIsoSpriteIds; + addSprites(entitySprites); + if (drawIsometric) { + addSprites(isometricSprites); + } + + wreckSprites = newWrecks; isometricWreckSprites = newIsometricWrecks; @@ -4885,6 +4896,10 @@ public void updateEntityLabels() { for (EntitySprite eS : entitySprites) { eS.prepare(); } + + for (IsometricSprite eS : isometricSprites) { + eS.prepare(); + } boardPanel.repaint(); } diff --git a/megamek/src/megamek/client/ui/swing/boardview/IsometricSprite.java b/megamek/src/megamek/client/ui/swing/boardview/IsometricSprite.java index 510f7e27828..db952264c1b 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/IsometricSprite.java +++ b/megamek/src/megamek/client/ui/swing/boardview/IsometricSprite.java @@ -237,6 +237,6 @@ private boolean onlyDetectedBySensors() { @Override protected int getSpritePriority() { - return entity.getSpriteDrawPriority(); + return entity.getSpriteDrawPriority() + 10; } } diff --git a/megamek/src/megamek/client/ui/swing/boardview/Sprite.java b/megamek/src/megamek/client/ui/swing/boardview/Sprite.java index b7a5558f9b6..375a2fb316c 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/Sprite.java +++ b/megamek/src/megamek/client/ui/swing/boardview/Sprite.java @@ -162,4 +162,9 @@ public int compareTo(Sprite o) { public boolean equals(Object obj) { return super.equals(obj); } + + @Override + public String toString() { + return "[" + getClass().getSimpleName() + "] Prio: " + getSpritePriority() + ((image == null) ? "; no image" : ""); + } } \ No newline at end of file From bd2c3d3d15bee7b7e475fe7d8018f145cea52324 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 26 Jul 2024 17:04:40 +0200 Subject: [PATCH 28/35] work entity sprites in BoardView into allSprites processing --- .../src/megamek/client/ui/swing/boardview/BoardView.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java index 9fcfe228bf2..73111b95863 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java +++ b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java @@ -378,7 +378,6 @@ public final class BoardView extends AbstractBoardView implements BoardListener, // to specialized lists when created private final TreeSet overTerrainSprites = new TreeSet<>(); private final TreeSet behindTerrainHexSprites = new TreeSet<>(); - private final TreeSet hexSprites = new TreeSet<>(); /** * Construct a new board view for the specified game @@ -4308,7 +4307,7 @@ public void clearSprites() { overTerrainSprites.clear(); behindTerrainHexSprites.clear(); - hexSprites.clear(); +// hexSprites.clear(); super.clearSprites(); } @@ -4880,7 +4879,6 @@ private Image scale(Image img, int width, int height) { public boolean toggleIsometric() { drawIsometric = !drawIsometric; allSprites.forEach(Sprite::prepare); - hexSprites.forEach(HexSprite::updateBounds); clearHexImageCache(); updateBoard(); @@ -5112,10 +5110,6 @@ public void addSprites(Collection sprites) { .map(s -> (HexSprite) s) .filter(HexSprite::isBehindTerrain) .forEach(behindTerrainHexSprites::add); - sprites.stream() - .filter(s -> s instanceof HexSprite) - .map(s -> (HexSprite) s) - .forEach(hexSprites::add); } @Override @@ -5123,6 +5117,5 @@ public void removeSprites(Collection sprites) { super.removeSprites(sprites); overTerrainSprites.removeAll(sprites); behindTerrainHexSprites.removeAll(sprites); - hexSprites.removeAll(sprites); } } \ No newline at end of file From d1e3b830acb72b38fd29b03540afb44f5cfde656 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 26 Jul 2024 17:12:24 +0200 Subject: [PATCH 29/35] make IsometricSprites HexSprites --- .../megamek/client/ui/swing/boardview/IsometricSprite.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/boardview/IsometricSprite.java b/megamek/src/megamek/client/ui/swing/boardview/IsometricSprite.java index db952264c1b..6f066478acb 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/IsometricSprite.java +++ b/megamek/src/megamek/client/ui/swing/boardview/IsometricSprite.java @@ -16,7 +16,7 @@ /** * Sprite used for isometric rendering to render an entity partially hidden behind a hill. */ -class IsometricSprite extends Sprite { +class IsometricSprite extends HexSprite { Entity entity; private Image radarBlipImage; @@ -26,7 +26,7 @@ class IsometricSprite extends Sprite { private static final GUIPreferences GUIP = GUIPreferences.getInstance(); public IsometricSprite(BoardView boardView1, Entity entity, int secondaryPos, Image radarBlipImage) { - super(boardView1); + super(boardView1, secondaryPos == -1 ? entity.getPosition() : entity.getSecondaryPositions().get(secondaryPos)); this.entity = entity; this.radarBlipImage = radarBlipImage; this.secondaryPos = secondaryPos; @@ -176,6 +176,7 @@ public void drawImmobileElements(Graphics graph, int x, int y, ImageObserver obs @Override public void prepare() { + updateBounds(); // create image for buffer GraphicsConfiguration config = GraphicsEnvironment .getLocalGraphicsEnvironment().getDefaultScreenDevice() From bdb51528df99f06aff060bb8f45a5f380ee4cc72 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 26 Jul 2024 15:42:21 -0400 Subject: [PATCH 30/35] restore toString method --- megamek/src/megamek/common/Briefcase.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/megamek/src/megamek/common/Briefcase.java b/megamek/src/megamek/common/Briefcase.java index 58095efbe62..d166f021142 100644 --- a/megamek/src/megamek/common/Briefcase.java +++ b/megamek/src/megamek/common/Briefcase.java @@ -65,4 +65,9 @@ public String generalName() { public String specificName() { return name + " (" + tonnage + " tons)"; } + + @Override + public String toString() { + return specificName(); + } } From da2c0cc5474d074f6968701b9c4b0011102a0bb7 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 26 Jul 2024 22:13:05 +0200 Subject: [PATCH 31/35] clean up, draw transparent iso sprite again --- megamek/src/megamek/client/ui/swing/boardview/BoardView.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java index 73111b95863..178e9faa6c0 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java +++ b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java @@ -1716,7 +1716,6 @@ private void drawHexes(Graphics g, Rectangle view, boolean saveBoardImage) { if (GUIP.getShowWrecks()) { drawIsometricWreckSpritesForHex(c, g, isometricWreckSprites); } -// drawIsometricSpritesForHex(c, g, isometricSprites); } } } @@ -1724,7 +1723,7 @@ private void drawHexes(Graphics g, Rectangle view, boolean saveBoardImage) { if (!saveBoardImage) { // If we are using Isometric rendering, redraw the entity sprites at 50% transparent // so sprites hidden behind hills can still be seen by the user. -// drawIsometricSprites(g, isometricSprites); + drawIsometricSprites(g, isometricSprites); } } else { // Draw hexes without regard to elevation when not using Isometric, since it does not From 1cc47e52d350e5fd2f8869158ae90b1aa97f40d7 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 26 Jul 2024 22:29:09 +0200 Subject: [PATCH 32/35] clean up --- megamek/src/megamek/client/ui/swing/boardview/BoardView.java | 1 - 1 file changed, 1 deletion(-) diff --git a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java index 178e9faa6c0..bc595b8ae83 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java +++ b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java @@ -4306,7 +4306,6 @@ public void clearSprites() { overTerrainSprites.clear(); behindTerrainHexSprites.clear(); -// hexSprites.clear(); super.clearSprites(); } From e6a1a2a2b77fe655217c5bafca7682dec2a2eb18 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 28 Jul 2024 11:09:31 -0400 Subject: [PATCH 33/35] sprite scaling; unit tooltip --- .../swing/boardview/GroundObjectSprite.java | 12 ++++++++-- .../client/ui/swing/tooltip/UnitToolTip.java | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java index 5bce6f83c0e..a5e1947558b 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java +++ b/megamek/src/megamek/client/ui/swing/boardview/GroundObjectSprite.java @@ -19,10 +19,11 @@ package megamek.client.ui.swing.boardview; import java.awt.Dimension; +import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; - +import megamek.client.ui.swing.util.UIUtil; import megamek.common.Configuration; import megamek.common.Coords; import megamek.common.util.ImageUtil; @@ -58,5 +59,12 @@ public Rectangle getBounds() { } @Override - public void prepare() { } + public void prepare() { + updateBounds(); + image = createNewHexImage(); + Graphics2D graph = (Graphics2D) image.getGraphics(); + UIUtil.setHighQualityRendering(graph); + graph.scale(bv.scale, bv.scale); + graph.drawImage(CARGO_IMAGE, 0, 0, null); + } } \ No newline at end of file diff --git a/megamek/src/megamek/client/ui/swing/tooltip/UnitToolTip.java b/megamek/src/megamek/client/ui/swing/tooltip/UnitToolTip.java index 55d9383ed02..71dea7b204b 100644 --- a/megamek/src/megamek/client/ui/swing/tooltip/UnitToolTip.java +++ b/megamek/src/megamek/client/ui/swing/tooltip/UnitToolTip.java @@ -164,6 +164,9 @@ private static StringBuilder getEntityTipTable(Entity entity, Player localPlayer // Carried Units result += carriedUnits(entity); + + // carried cargo + result += carriedCargo(entity); // C3 Info result += c3Info(entity, details); @@ -1843,6 +1846,25 @@ private static StringBuilder carriedUnits(Entity entity) { return new StringBuilder().append(result); } + + private static StringBuilder carriedCargo(Entity entity) { + StringBuilder sb = new StringBuilder(); + List cargoList = entity.getDistinctCarriedObjects(); + + if (!cargoList.isEmpty()) { + sb.append(guiScaledFontHTML()); + sb.append(Messages.getString("MissionRole.cargo")); + sb.append(":
  "); + + for (ICarryable cargo : entity.getDistinctCarriedObjects()) { + sb.append(cargo.toString()); + sb.append("
  "); + } + sb.append(""); + } + + return sb; + } /** Returns the full force chain the entity is in as one text line. */ private static StringBuilder forceEntry(Entity entity, Player localPlayer) { From 4c60b4654d27b8712afc2693fddcc211330d248f Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 28 Jul 2024 11:15:12 -0400 Subject: [PATCH 34/35] adjust report ordering --- megamek/src/megamek/server/GameManager.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index 2abf1efd6c1..b0bb74a4424 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -27050,7 +27050,7 @@ else if ((null != Compute.stackingViolation(game, other.getId(), curPos, entity. } // drop cargo - dropCargo(entity, curPos); + dropCargo(entity, curPos, vDesc); // update our entity, so clients have correct data needed for MekWars stuff entityUpdate(entity.getId()); @@ -27061,7 +27061,7 @@ else if ((null != Compute.stackingViolation(game, other.getId(), curPos, entity. /** * Worker function that drops cargo from an entity at the given coordinates. */ - private void dropCargo(Entity entity, Coords coords) { + private void dropCargo(Entity entity, Coords coords, Vector vPhaseReport) { boolean cargoDropped = false; for (ICarryable cargo : entity.getDistinctCarriedObjects()) { @@ -27555,9 +27555,6 @@ else if (waterDepth > 0) { return vPhaseReport; } - // drop cargo if necessary - dropCargo(entity, fallPos); - // set how deep the mech has fallen if (entity instanceof Mech) { Mech mech = (Mech) entity; @@ -27765,6 +27762,9 @@ else if (waterDepth > 0) { } } // End dislodge-infantry + // drop cargo if necessary + dropCargo(entity, fallPos, vPhaseReport); + // clear all PSRs after a fall -- the Mek has already failed ONE and // fallen, it'd be cruel to make it fail some more! game.resetPSRs(entity); From 6b48dd43694877a1541f30031958a6d6f55a7bd9 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 28 Jul 2024 11:27:35 -0400 Subject: [PATCH 35/35] InGameobject; pick up correct object when it's not first in list --- .../client/ui/swing/MovementDisplay.java | 5 ++-- megamek/src/megamek/common/Briefcase.java | 27 +++++++++++++++++++ megamek/src/megamek/common/ICarryable.java | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index 685190bf5ff..cd356bb1e26 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -5325,11 +5325,12 @@ private void processPickupCargoCommand() { // regardless of how many objects we are picking up, // we may have to choose the location with which to pick it up if (displayedOptions.size() == 1) { - Integer pickupLocation = getPickupLocation(options.get(0)); + Integer pickupLocation = getPickupLocation(displayedOptions.get(0)); if (pickupLocation != null) { Map data = new HashMap<>(); - data.put(MoveStep.CARGO_PICKUP_KEY, 0); + // we pick the only eligible object out of all the objects on the ground + data.put(MoveStep.CARGO_PICKUP_KEY, options.indexOf(displayedOptions.get(0))); data.put(MoveStep.CARGO_LOCATION_KEY, pickupLocation); addStepToMovePath(MoveStepType.PICKUP_CARGO, data); diff --git a/megamek/src/megamek/common/Briefcase.java b/megamek/src/megamek/common/Briefcase.java index d166f021142..4a5c33e0c0c 100644 --- a/megamek/src/megamek/common/Briefcase.java +++ b/megamek/src/megamek/common/Briefcase.java @@ -33,6 +33,8 @@ public class Briefcase implements ICarryable, Serializable { private double tonnage; private String name; private boolean invulnerable; + private int id; + private int ownerId; public void damage(double amount) { tonnage -= amount; @@ -70,4 +72,29 @@ public String specificName() { public String toString() { return specificName(); } + + @Override + public int getId() { + return id; + } + + @Override + public void setId(int newId) { + this.id = newId; + } + + @Override + public int getOwnerId() { + return ownerId; + } + + @Override + public void setOwnerId(int newOwnerId) { + this.ownerId = newOwnerId; + } + + @Override + public int getStrength() { + return 0; + } } diff --git a/megamek/src/megamek/common/ICarryable.java b/megamek/src/megamek/common/ICarryable.java index b0160cee950..6492c84d110 100644 --- a/megamek/src/megamek/common/ICarryable.java +++ b/megamek/src/megamek/common/ICarryable.java @@ -22,7 +22,7 @@ /** * An interface defining all the required properties of a carryable object. */ -public interface ICarryable extends BTObject { +public interface ICarryable extends InGameObject { double getTonnage(); void damage(double amount); boolean isInvulnerable();