From ad9a17a7b07eda6f1ad1804d08a224b4ef8433d1 Mon Sep 17 00:00:00 2001 From: Scoppio Date: Fri, 15 Nov 2024 22:29:45 -0300 Subject: [PATCH] feat: fire fight arguments added --- .../i18n/megamek/client/messages.properties | 17 +++- .../src/megamek/client/ui/swing/MapMenu.java | 87 +++++++++++-------- .../client/ui/swing/boardview/BoardView.java | 8 +- .../gmCommands/GamemasterCommandPanel.java | 19 +++- megamek/src/megamek/common/Board.java | 20 ----- megamek/src/megamek/common/Entity.java | 3 + megamek/src/megamek/common/Game.java | 14 ++- megamek/src/megamek/server/Server.java | 7 -- .../server/commands/FirefightCommand.java | 77 ++++++++++++++++ .../server/commands/NoFiresCommand.java | 87 +++++++++++++++++++ .../server/totalwarfare/TWGameManager.java | 6 +- 11 files changed, 272 insertions(+), 73 deletions(-) create mode 100644 megamek/src/megamek/server/commands/FirefightCommand.java create mode 100644 megamek/src/megamek/server/commands/NoFiresCommand.java diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties index 8fdaeab24d..086fe7ae2b 100644 --- a/megamek/i18n/megamek/client/messages.properties +++ b/megamek/i18n/megamek/client/messages.properties @@ -4636,7 +4636,14 @@ SBFTargetDialog.title=Targeting Gamemaster.Gamemaster=Gamemaster Gamemaster.EditDamage=Edit Damage (unstable) Gamemaster.Configure=Configure (unstable) +Gamemaster.Traitor.title=Traitor +Gamemaster.Traitor.confirm=Confirm Gamemaster.Traitor=Traitor Unit +Gamemaster.Traitor.text=Traitor Unit {0} +Gamemaster.Traitor.text.noplayers=No players available. Units cannot have their ownership passed to players that aren't assigned to a team. +Gamemaster.Traitor.text.selectplayer=Choose the player to gain ownership of this unit, {0}, when it turns traitor +Gamemaster.Traitor.confirmation={0} will switch to {1}'s side at the end of this turn. Are you sure? + Gamemaster.dialog.confirm=Confirm Gamemaster.KillUnit=Kill Unit Gamemaster.KillUnit.text=Kill Unit {0} @@ -4710,4 +4717,12 @@ Gamemaster.cmd.orbitalbombardment.help=Calls an orbital bombardment on the board Gamemaster.cmd.orbitalbombardment.dmg=Total damage at target hex. Gamemaster.cmd.orbitalbombardment.radius=Radius of the bombardment. Gamemaster.cmd.orbitalbombardment.error.outofbounds=Specified hex is not on the board. -Gamemaster.cmd.orbitalbombardment.success=Orbital bombardment incoming! \ No newline at end of file +Gamemaster.cmd.orbitalbombardment.success=Orbital bombardment incoming! +# Firefight +Gamemaster.cmd.firefight.longName=Firefight +Gamemaster.cmd.firefight.reason=Fire extinguished +Gamemaster.cmd.firefight.help=Extinguishes a fire on the board. +# No Fire +Gamemaster.cmd.nofire.longName=No Fires +Gamemaster.cmd.nofire.help=Extinguishes all fires on the board. + diff --git a/megamek/src/megamek/client/ui/swing/MapMenu.java b/megamek/src/megamek/client/ui/swing/MapMenu.java index 02f40d3798..e023d92fe6 100644 --- a/megamek/src/megamek/client/ui/swing/MapMenu.java +++ b/megamek/src/megamek/client/ui/swing/MapMenu.java @@ -229,17 +229,7 @@ private JMenuItem TargetMenuItem(Targetable t) { JMenuItem item = new JMenuItem(Messages.getString("ClientGUI.targetMenuItem") + t.getDisplayName()); - String targetCode; - - if (t instanceof Entity) { - targetCode = "E|" + ((Entity) t).getId(); - } else if (t instanceof BuildingTarget) { - targetCode = "B|" + t.getPosition().getX() + "|" + t.getPosition().getY() + "|" + t.getTargetType(); - } else if (t instanceof MinefieldTarget) { - targetCode = "M|" + t.getPosition().getX() + "|" + t.getPosition().getY(); - } else { - targetCode = "H|" + t.getPosition().getX() + "|" + t.getPosition().getY() + "|" + t.getTargetType(); - } + String targetCode = getTargetCode(t); item.setActionCommand(targetCode); item.addActionListener(evt -> { @@ -255,6 +245,21 @@ private JMenuItem TargetMenuItem(Targetable t) { return item; } + private static String getTargetCode(Targetable t) { + String targetCode; + + if (t instanceof Entity) { + targetCode = "E|" + ((Entity) t).getId(); + } else if (t instanceof BuildingTarget) { + targetCode = "B|" + t.getPosition().getX() + "|" + t.getPosition().getY() + "|" + t.getTargetType(); + } else if (t instanceof MinefieldTarget) { + targetCode = "M|" + t.getPosition().getX() + "|" + t.getPosition().getY(); + } else { + targetCode = "H|" + t.getPosition().getX() + "|" + t.getPosition().getY() + "|" + t.getTargetType(); + } + return targetCode; + } + private @Nullable JMenuItem createChargeMenuItem() { if (!client.getGame().getEntities(coords).hasNext()) { return null; @@ -403,9 +408,7 @@ private JMenu createSpecialHexDisplayMenu() { */ private JMenu createGamemasterMenu() { JMenu menu = new JMenu(Messages.getString("Gamemaster.Gamemaster")); - if (!client.getLocalPlayer().getGameMaster()) { - return menu; - } else { + if (client.getLocalPlayer().getGameMaster()) { JMenu dmgMenu = new JMenu(Messages.getString("Gamemaster.EditDamage")); JMenu cfgMenu = new JMenu(Messages.getString("Gamemaster.Configure")); JMenu traitorMenu = new JMenu(Messages.getString("Gamemaster.Traitor")); @@ -435,8 +438,8 @@ private JMenu createGamemasterMenu() { menu.addSeparator(); } menu.add(specialCommandsMenu); - return menu; } + return menu; } /** @@ -446,14 +449,16 @@ private JMenu createGamemasterMenu() { private JMenu createGMSpecialCommandsMenu() { JMenu menu = new JMenu(Messages.getString("Gamemaster.SpecialCommands")); List.of( - new KillCommand(null, null), - new OrbitalBombardmentCommand(null, null), new ChangeOwnershipCommand(null, null), + new ChangeWeatherCommand(null, null), new DisasterCommand(null, null), + new KillCommand(null, null), + new FirefightCommand(null, null), new FirestarterCommand(null, null), new FirestormCommand(null, null), - new RemoveSmokeCommand(null, null), - new ChangeWeatherCommand(null, null) + new NoFiresCommand(null, null), + new OrbitalBombardmentCommand(null, null), + new RemoveSmokeCommand(null, null) ).forEach(cmd -> { JMenuItem item = new JMenuItem(cmd.getLongName()); item.addActionListener(evt -> new GamemasterCommandPanel(gui.getFrame(), gui, cmd).setVisible(true)); @@ -488,9 +493,14 @@ JMenuItem createUnitEditorMenuItem(Entity entity) { return item; } - private JMenuItem createTraitorMenuItem(Entity en) { + /** + * Create traitor menu for game master options + * @param entity the entity to create the traitor menu for + * @return JMenu the traitor menu + */ + private JMenuItem createTraitorMenuItem(Entity entity) { // Traitor Command - JMenuItem item = new JMenuItem(Messages.getString("Gamemaster.Traitor") + " " + en.getDisplayName()); + JMenuItem item = new JMenuItem(Messages.getString("Gamemaster.Traitor.text", entity.getDisplayName())); item.addActionListener(evt -> { gui.getBoardView().setShouldIgnoreKeys(false); var players = client.getGame().getPlayersList(); @@ -498,7 +508,7 @@ private JMenuItem createTraitorMenuItem(Entity en) { String[] playerNames = new String[players.size() - 1]; String[] options = new String[players.size() - 1]; - Player currentOwner = en.getOwner(); + Player currentOwner = entity.getOwner(); // Loop through the players vector and fill in the arrays int idx = 0; for (var player : players) { @@ -515,15 +525,14 @@ private JMenuItem createTraitorMenuItem(Entity en) { // No players available? if (idx == 0) { JOptionPane.showMessageDialog(gui.getFrame(), - "No players available. Units cannot be traitored to players " - + "that aren't assigned to a team."); + Messages.getString("Gamemaster.Traitor.text.noplayers")); return; } // Dialog for choosing which player to transfer to String option = (String) JOptionPane.showInputDialog(gui.getFrame(), - "Choose the player to gain ownership of this unit (" + en.getDisplayName() + ") when it turns traitor", - "Traitor", JOptionPane.QUESTION_MESSAGE, null, + Messages.getString("Gamemaster.Traitor.text.selectplayer", entity.getDisplayName()), + Messages.getString("Gamemaster.Traitor.title"), JOptionPane.QUESTION_MESSAGE, null, options, options[0]); // Verify that we have a valid option... @@ -535,12 +544,12 @@ private JMenuItem createTraitorMenuItem(Entity en) { // And now we perform the actual transfer int confirm = JOptionPane.showConfirmDialog( gui.getFrame(), - en.getDisplayName() + " will switch to " + name - + "'s side at the end of this turn. Are you sure?", - "Confirm", + Messages.getString("Gamemaster.Traitor.confirmation", entity.getDisplayName(), name), + Messages.getString("Gamemaster.Traitor.confirm"), JOptionPane.YES_NO_OPTION); + if (confirm == JOptionPane.YES_OPTION) { - client.sendChat(String.format("/changeOwner %d %d", en.getId(), id)); + client.sendChat(String.format("/changeOwner %d %d", entity.getId(), id)); } } }); @@ -548,14 +557,20 @@ private JMenuItem createTraitorMenuItem(Entity en) { return item; } - private JMenuItem createKillMenuItem(Entity en) { - JMenuItem item = new JMenuItem(Messages.getString("Gamemaster.KillUnit.text", en.getDisplayName())); + /** + * Create a menu for killing a specific entity + * + * @param entity the entity to create the kill menu for + * @return JMenuItem the kill menu item + */ + private JMenuItem createKillMenuItem(Entity entity) { + JMenuItem item = new JMenuItem(Messages.getString("Gamemaster.KillUnit.text", entity.getDisplayName())); item.addActionListener(evt -> { int confirm = JOptionPane.showConfirmDialog( - gui.getFrame(), Messages.getString("Gamemaster.KillUnit.confirmation", en.getDisplayName()), + gui.getFrame(), Messages.getString("Gamemaster.KillUnit.confirmation", entity.getDisplayName()), Messages.getString("Gamemaster.dialog.confirm"), JOptionPane.YES_NO_OPTION); if (confirm == JOptionPane.YES_OPTION) { - client.sendChat(String.format("/kill %d", en.getId())); + client.sendChat(String.format("/kill %d", entity.getId())); } }); return item; @@ -1509,9 +1524,7 @@ private void selectTarget() { if (list.size() == 1) { myTarget = selectedEntity = list.firstElement(); - - if (currentPanel instanceof FiringDisplay) { - FiringDisplay panel = (FiringDisplay) currentPanel; + if (currentPanel instanceof FiringDisplay panel) { panel.target(myTarget); } else if (currentPanel instanceof PhysicalDisplay) { ((PhysicalDisplay) currentPanel).target(myTarget); diff --git a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java index 50c9e923c0..0fd359ebd5 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java +++ b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java @@ -1456,10 +1456,16 @@ private Mounted selectedWeapon() { return (clientgui != null) ? clientgui.getDisplayedWeapon().orElse(null) : null; } + /** + * Draw the orbital bombardment attacks on the board view + * + * @author Luana Coppio + * @param boardGraphics The graphics object to draw on + */ private void drawOrbitalBombardmentHexes(Graphics boardGraphics) { Image orbitalBombardmentImage = tileManager.getOrbitalBombardmentImage(); Rectangle view = boardGraphics.getClipBounds(); - boolean justDraw = false; + // Compute the origin of the viewing area int drawX = (view.x / (int) (HEX_WC * scale)) - 1; int drawY = (view.y / (int) (HEX_H * scale)) - 1; diff --git a/megamek/src/megamek/client/ui/swing/gmCommands/GamemasterCommandPanel.java b/megamek/src/megamek/client/ui/swing/gmCommands/GamemasterCommandPanel.java index bc6e7b9c6e..f4710ecf90 100644 --- a/megamek/src/megamek/client/ui/swing/gmCommands/GamemasterCommandPanel.java +++ b/megamek/src/megamek/client/ui/swing/gmCommands/GamemasterCommandPanel.java @@ -14,11 +14,20 @@ import java.util.Map; import java.util.Objects; -// JPanel wrapper for game master commands +/** + * Dialog for executing a gamemaster command. + */ public class GamemasterCommandPanel extends JDialog { private final GamemasterServerCommand command; private final ClientGUI client; + /** + * Constructor for the dialog for executing a gamemaster command. + * + * @param parent The parent frame. + * @param client The client GUI. + * @param command The command to render. + */ public GamemasterCommandPanel(JFrame parent, ClientGUI client, GamemasterServerCommand command) { super(parent, command.getName(), true); this.command = command; @@ -141,6 +150,13 @@ private JButton getExecuteButton(Map argumentComponents) { return executeButton; } + /** + * Execute the command with the given arguments. + * It runs the command using the client chat, this way the command is sent to the server. + * All arguments are loaded as named variables in the form of "argumentName=argumentValue". + * + * @param argumentComponents The components that hold the arguments selected. + */ private void executeCommand(Map argumentComponents) { List> arguments = command.defineArguments(); String[] args = new String[arguments.size()]; @@ -166,6 +182,7 @@ private void executeCommand(Map argumentComponents) { } } } + client.getClient().sendChat("/" + command.getName() + " " + String.join(" ", args)); } } diff --git a/megamek/src/megamek/common/Board.java b/megamek/src/megamek/common/Board.java index f3bdc36db1..427cce25fd 100644 --- a/megamek/src/megamek/common/Board.java +++ b/megamek/src/megamek/common/Board.java @@ -2084,24 +2084,4 @@ public static int encodeCustomDeploymentZoneID(int zoneID) { return zoneID + NUM_ZONES_X2; } - public void clearOrbitalBombardmentIcons() { - for (Coords coords : specialHexes.keySet()) { - removeOrbitalBombardmentIcons(coords); - } - } - - public void removeOrbitalBombardmentIcons(Coords coords) { - // Do nothing if the coords aren't on this board. - if (!this.contains(coords) || null == specialHexes.get(coords)) { - return; - } - - // Use iterator so we can remove while traversing - for (Iterator iterator = specialHexes.get(coords).iterator(); iterator.hasNext();) { - SpecialHexDisplay shd = iterator.next(); - if (ORBITAL_BOMBARDMENT.equals(shd.getType())) { - iterator.remove(); - } - } - } } diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index d54020dc53..ec645b1c00 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -4790,6 +4790,9 @@ public List getCriticalSlots(int location) { return result; } + /** + * @return true if the entity has any critical slot that isn't damaged yet + */ public boolean hasUndamagedCriticalSlots() { return IntStream.range(0, locations()) .mapToLong(i -> getCriticalSlots(i) diff --git a/megamek/src/megamek/common/Game.java b/megamek/src/megamek/common/Game.java index 55f5ea1337..1d50e44699 100644 --- a/megamek/src/megamek/common/Game.java +++ b/megamek/src/megamek/common/Game.java @@ -2203,15 +2203,25 @@ public int removeSpecificEntityTurnsFor(Entity entity) { return turnsToRemove.size(); } - public void setOrbitalBombardmentVector(Vector v) { - orbitalBombardmentAttacks = v; + /** + * Set the new vector of orbital bombardments for this round. + * @param orbitalBombardments + */ + public void setOrbitalBombardmentVector(Vector orbitalBombardments) { + orbitalBombardmentAttacks = orbitalBombardments; processGameEvent(new GameBoardChangeEvent(this)); } + /** + * Resets the orbital bombardment attacks list. + */ public void resetOrbitalBombardmentAttacks() { orbitalBombardmentAttacks.removeAllElements(); } + /** + * @return an Enumeration of orbital bombardment attacks. + */ public Enumeration getOrbitalBombardmentAttacks() { return orbitalBombardmentAttacks.elements(); } diff --git a/megamek/src/megamek/server/Server.java b/megamek/src/megamek/server/Server.java index ee832da035..d62d8555e9 100644 --- a/megamek/src/megamek/server/Server.java +++ b/megamek/src/megamek/server/Server.java @@ -538,13 +538,6 @@ public Collection getAllCommandNames() { return commandsHash.keySet(); } - /** - * Returns the list of all server commands - */ - public List getAllCommands() { - return new ArrayList<>(commandsHash.values()); - } - /** * Sent when a client attempts to connect. */ diff --git a/megamek/src/megamek/server/commands/FirefightCommand.java b/megamek/src/megamek/server/commands/FirefightCommand.java new file mode 100644 index 0000000000..956f3508d1 --- /dev/null +++ b/megamek/src/megamek/server/commands/FirefightCommand.java @@ -0,0 +1,77 @@ +/* + * MegaMek - Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +package megamek.server.commands; + +import megamek.client.ui.Messages; +import megamek.common.Coords; +import megamek.common.Hex; +import megamek.server.Server; +import megamek.server.commands.arguments.Argument; +import megamek.server.commands.arguments.IntegerArgument; +import megamek.server.totalwarfare.TWGameManager; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * The Server Command "/firefight" that will put one hex on fire. + * + * @author Luana Coppio + */ +public class FirefightCommand extends GamemasterServerCommand { + + private static final String FIRESTARTER = "firefight"; + private static final String X = "x"; + private static final String Y = "y"; + private static final String TYPE = "type"; + + public FirefightCommand(Server server, TWGameManager gameManager) { + super(server, + gameManager, + FIRESTARTER, + Messages.getString("Gamemaster.cmd.firefight.help"), + Messages.getString("Gamemaster.cmd.firefight.longName")); + } + + @Override + public List> defineArguments() { + return List.of( + new IntegerArgument(X, Messages.getString("Gamemaster.cmd.x")), + new IntegerArgument(Y, Messages.getString("Gamemaster.cmd.y")) + ); + } + + /** + * Run this command with the arguments supplied + * + * @see ServerCommand#run(int, String[]) + */ + @Override + protected void runAsGM(int connId, Map> args) { + int xArg = (int) args.get(X).getValue() - 1; + int yArg = (int) args.get(Y).getValue() - 1; + firefight(new Coords(xArg, yArg)); + } + + private void firefight(Coords coords) { + try { + Hex hex = gameManager.getGame().getBoard().getHex(coords); + Objects.requireNonNull(hex, "Hex not found."); + gameManager.removeFire(coords, Messages.getString("Gamemaster.cmd.firefight.reason")); + } catch (Exception e) { + throw new IllegalArgumentException("Failed to ignite hex: " + e.getMessage()); + } + } +} diff --git a/megamek/src/megamek/server/commands/NoFiresCommand.java b/megamek/src/megamek/server/commands/NoFiresCommand.java new file mode 100644 index 0000000000..f097ff1fbb --- /dev/null +++ b/megamek/src/megamek/server/commands/NoFiresCommand.java @@ -0,0 +1,87 @@ +/* + * MegaMek - Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +package megamek.server.commands; + +import megamek.client.ui.Messages; +import megamek.common.Coords; +import megamek.common.Hex; +import megamek.server.Server; +import megamek.server.commands.arguments.Argument; +import megamek.server.commands.arguments.IntegerArgument; +import megamek.server.totalwarfare.TWGameManager; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +/** + * The Server Command "/nofires" removes all fires on the board. + * + * @author Luana Coppio + */ +public class NoFiresCommand extends GamemasterServerCommand { + + private final String reason; + + public NoFiresCommand(Server server, TWGameManager gameManager) { + super(server, + gameManager, + "nofires", + Messages.getString("Gamemaster.cmd.nofire.help"), + Messages.getString("Gamemaster.cmd.nofire.longName")); + this.reason = Messages.getString("Gamemaster.cmd.firefight.reason"); + } + + @Override + public List> defineArguments() { + return List.of(); + } + + /** + * Run this command with the arguments supplied + * + * @see ServerCommand#run(int, String[]) + */ + @Override + protected void runAsGM(int connId, Map> args) { + try { + getAllCoords().forEach(this::firefight); + } catch (Exception e) { + logger.error(Messages.getString("Gamemaster.cmd.fire.failed"), e); + server.sendServerChat(connId, Messages.getString("Gamemaster.cmd.fire.failed")); + } + } + + private HashSet getAllCoords() { + var boardHeight = gameManager.getGame().getBoard().getHeight(); + var boardWidth = gameManager.getGame().getBoard().getWidth(); + var coordsSet = new HashSet(); + for (int x = 0; x < boardWidth; x++) { + for (int y = 0; y < boardHeight; y++) { + coordsSet.add(new Coords(x, y)); + } + } + return coordsSet; + } + + private void firefight(Coords coords) { + Hex hex = gameManager.getGame().getBoard().getHex(coords); + if (null == hex) { + // Just ignore null hexes... + // they should not happen, but I don't want to crash the command + return; + } + gameManager.removeFire(coords, reason); + } +} diff --git a/megamek/src/megamek/server/totalwarfare/TWGameManager.java b/megamek/src/megamek/server/totalwarfare/TWGameManager.java index 2ac90e804d..d1de6a365f 100644 --- a/megamek/src/megamek/server/totalwarfare/TWGameManager.java +++ b/megamek/src/megamek/server/totalwarfare/TWGameManager.java @@ -194,6 +194,8 @@ public List getCommandList(Server server) { commands.add(new ChangeOwnershipCommand(server, this)); commands.add(new DisasterCommand(server, this)); commands.add(new FirestarterCommand(server, this)); + commands.add(new NoFiresCommand(server, this)); + commands.add(new FirefightCommand(server, this)); commands.add(new FirestormCommand(server, this)); commands.add(new RemoveSmokeCommand(server, this)); commands.add(new ChangeWeatherCommand(server, this)); @@ -31696,10 +31698,6 @@ void clearBombIcons() { game.getBoard().clearBombIcons(); } - void clearOrbitalBombardmentIcons() { - game.getBoard().clearOrbitalBombardmentIcons(); - } - /** * Convenience function to send a ground object update. */