From 0811d623dd10247c77348d232366748c61f54f20 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 | 89 +++++++++++-------- .../client/ui/swing/boardview/BoardView.java | 8 +- .../gmCommands/GamemasterCommandPanel.java | 42 ++++++++- megamek/src/megamek/common/Board.java | 20 ----- megamek/src/megamek/common/Entity.java | 3 + megamek/src/megamek/common/Game.java | 14 ++- .../common/weapons/AreaEffectHelper.java | 27 +++--- megamek/src/megamek/server/Server.java | 7 -- .../server/commands/FirefightCommand.java | 77 ++++++++++++++++ .../commands/GamemasterServerCommand.java | 10 ++- .../server/commands/NoFiresCommand.java | 87 ++++++++++++++++++ .../server/commands/arguments/Argument.java | 14 ++- .../commands/arguments/EnumArgument.java | 5 ++ .../commands/arguments/IntegerArgument.java | 6 +- .../arguments/OptionalEnumArgument.java | 5 ++ .../arguments/OptionalIntegerArgument.java | 1 + .../server/totalwarfare/TWGameManager.java | 6 +- 18 files changed, 343 insertions(+), 95 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 8fdaeab24d9..086fe7ae2b1 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 02f40d37980..462ab581a62 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,17 +449,19 @@ 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)); + item.addActionListener(evt -> new GamemasterCommandPanel(gui.getFrame(), gui, cmd, coords).setVisible(true)); menu.add(item); }); @@ -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 50c9e923c09..0fd359ebd52 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 bc6e7b9c6e5..25d7b94acc5 100644 --- a/megamek/src/megamek/client/ui/swing/gmCommands/GamemasterCommandPanel.java +++ b/megamek/src/megamek/client/ui/swing/gmCommands/GamemasterCommandPanel.java @@ -1,6 +1,8 @@ package megamek.client.ui.swing.gmCommands; import megamek.client.ui.swing.ClientGUI; +import megamek.common.Coords; +import megamek.common.annotations.Nullable; import megamek.server.commands.GamemasterServerCommand; import megamek.server.commands.arguments.Argument; import megamek.server.commands.arguments.EnumArgument; @@ -14,15 +16,26 @@ 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; - - public GamemasterCommandPanel(JFrame parent, ClientGUI client, GamemasterServerCommand command) { + private final Coords coords; + + /** + * 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, @Nullable Coords coords) { super(parent, command.getName(), true); this.command = command; this.client = client; + this.coords = coords; initializeUI(parent); } @@ -87,9 +100,22 @@ private JComponent getArgumentComponent(Argument argument, JPanel argumentPan return null; } + private boolean isArgumentX(Argument argument) { + return argument.getName().equals("x"); + } + + private boolean isArgumentY(Argument argument) { + return argument.getName().equals("y"); + } + + private int getIntArgumentDefaultValue(IntegerArgument intArg) { + return intArg.hasDefaultValue() ? intArg.getValue() : isArgumentX(intArg) ? coords.getX() : + isArgumentY(intArg) ? coords.getY() : 0; + } + private JSpinner createSpinner(IntegerArgument intArg) { return new JSpinner(new SpinnerNumberModel( - intArg.hasDefaultValue() ? intArg.getValue() : 0, + getIntArgumentDefaultValue(intArg), intArg.getMinValue(), intArg.getMaxValue(), 1)); @@ -141,6 +167,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 +199,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 f3bdc36db1f..427cce25fd8 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 d54020dc530..ec645b1c008 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 55f5ea13373..1d50e446998 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/common/weapons/AreaEffectHelper.java b/megamek/src/megamek/common/weapons/AreaEffectHelper.java index 32ea8900b05..8b035f8b6e9 100644 --- a/megamek/src/megamek/common/weapons/AreaEffectHelper.java +++ b/megamek/src/megamek/common/weapons/AreaEffectHelper.java @@ -14,15 +14,12 @@ package megamek.common.weapons; import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.IntStream; import megamek.common.*; import megamek.common.planetaryconditions.Atmosphere; import megamek.common.planetaryconditions.PlanetaryConditions; import megamek.logging.MMLogger; import megamek.server.totalwarfare.TWGameManager; -import org.apache.commons.lang3.IntegerRange; /** * Class containing functionality that helps out with area effect weapons. @@ -621,7 +618,7 @@ else if (ammo.getMunitionType().contains(AmmoType.Munitions.M_FLECHETTE)) { * arty * attack, -1 otherwise * @param mineClear Whether or not we're clearing a minefield - * @return + * @return A DamageFalloff object containing the damage and falloff values and if it is cluster or not */ public static DamageFalloff calculateDamageFallOff(AmmoType ammo, int attackingBA, boolean mineClear) { if (ammo == null) { @@ -694,21 +691,23 @@ public static DamageFalloff calculateDamageFallOff(AmmoType ammo, int attackingB clusterMunitionsFlag = true; } else if (ammo.getMunitionType().contains(AmmoType.Munitions.M_FLECHETTE)) { - switch (ammo.getAmmoType()) { + falloff = switch (ammo.getAmmoType()) { // for flechette, damage and falloff is number of d6, not absolute // damage - case AmmoType.T_LONG_TOM: + case AmmoType.T_LONG_TOM -> { damage = 4; - falloff = 2; - break; - case AmmoType.T_SNIPER: + yield 2; + } + case AmmoType.T_SNIPER -> { damage = 2; - falloff = 1; - break; - case AmmoType.T_THUMPER: + yield 1; + } + case AmmoType.T_THUMPER -> { damage = 1; - falloff = 1; - } + yield 1; + } + default -> falloff; + }; // if this was a mine clearance, then it only affects the hex hit } else if (mineClear) { falloff = damage; diff --git a/megamek/src/megamek/server/Server.java b/megamek/src/megamek/server/Server.java index ee832da035b..d62d8555e92 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 00000000000..956f3508d1b --- /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/GamemasterServerCommand.java b/megamek/src/megamek/server/commands/GamemasterServerCommand.java index ef584614c06..dc346a6a147 100644 --- a/megamek/src/megamek/server/commands/GamemasterServerCommand.java +++ b/megamek/src/megamek/server/commands/GamemasterServerCommand.java @@ -22,7 +22,15 @@ import java.util.*; /** - * A ServerCommand that can only be used by Game Masters + * A ServerCommand that can only be used by Game Masters, + * This abstract class implements many features that are common to all Game Master commands, + * like the isGM check for users, it also uses the Argument class for building the command arguments + * and to abstract the parsing of the arguments, limit assertion and error handling, and for building + * a more dynamic "help" feature. + * It also has a more advanced parser and argument handling than the ServerCommand class, which allows for + * named arguments, positional arguments, optional arguments and default values. + * named arguments can be passed in any order, and positional arguments are parsed in order and MUST appear before named + * arguments. * * @author Luana Coppio */ diff --git a/megamek/src/megamek/server/commands/NoFiresCommand.java b/megamek/src/megamek/server/commands/NoFiresCommand.java new file mode 100644 index 00000000000..f097ff1fbb2 --- /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/commands/arguments/Argument.java b/megamek/src/megamek/server/commands/arguments/Argument.java index 5bcd84ef251..3b77343129a 100644 --- a/megamek/src/megamek/server/commands/arguments/Argument.java +++ b/megamek/src/megamek/server/commands/arguments/Argument.java @@ -1,11 +1,20 @@ package megamek.server.commands.arguments; -// A generic Argument class that can be extended for different argument types +/** + * Generic Argument class, can be extended for different argument types for server commands + * @param + * @author Luana Coppio + */ public abstract class Argument { protected T value; private final String name; private final String description; + /** + * Constructor for Generic Argument + * @param name name of the argument + * @param description description of the argument + */ public Argument(String name, String description) { this.name = name; this.description = description; @@ -23,6 +32,9 @@ public String getDescription() { return description; } + /** + * @return the string representation of the argument + */ public String getRepr() { return "<" + getName() + "=#>"; } diff --git a/megamek/src/megamek/server/commands/arguments/EnumArgument.java b/megamek/src/megamek/server/commands/arguments/EnumArgument.java index 85747815c97..cdaae3e7b0d 100644 --- a/megamek/src/megamek/server/commands/arguments/EnumArgument.java +++ b/megamek/src/megamek/server/commands/arguments/EnumArgument.java @@ -4,6 +4,11 @@ import java.util.Arrays; +/** + * Argument for an Enum type. + * @param + * @author Luana Coppio + */ public class EnumArgument> extends Argument { protected final Class enumType; protected final E defaultValue; diff --git a/megamek/src/megamek/server/commands/arguments/IntegerArgument.java b/megamek/src/megamek/server/commands/arguments/IntegerArgument.java index e3d7b27d962..2a942bf9e86 100644 --- a/megamek/src/megamek/server/commands/arguments/IntegerArgument.java +++ b/megamek/src/megamek/server/commands/arguments/IntegerArgument.java @@ -2,8 +2,10 @@ import megamek.client.ui.Messages; -import java.util.Arrays; - +/** + * Argument for an Integer type. + * @author Luana Coppio + */ public class IntegerArgument extends Argument { private final int minValue; private final int maxValue; diff --git a/megamek/src/megamek/server/commands/arguments/OptionalEnumArgument.java b/megamek/src/megamek/server/commands/arguments/OptionalEnumArgument.java index 2c7ce40a1c4..5e7fcd78630 100644 --- a/megamek/src/megamek/server/commands/arguments/OptionalEnumArgument.java +++ b/megamek/src/megamek/server/commands/arguments/OptionalEnumArgument.java @@ -4,6 +4,11 @@ import java.util.Arrays; +/** + * Nullable Argument for an Enum type. + * @param + * @author Luana Coppio + */ public class OptionalEnumArgument> extends EnumArgument { public OptionalEnumArgument(String name, String description, Class enumType) { diff --git a/megamek/src/megamek/server/commands/arguments/OptionalIntegerArgument.java b/megamek/src/megamek/server/commands/arguments/OptionalIntegerArgument.java index fc857b1148e..4bc3cd17139 100644 --- a/megamek/src/megamek/server/commands/arguments/OptionalIntegerArgument.java +++ b/megamek/src/megamek/server/commands/arguments/OptionalIntegerArgument.java @@ -18,6 +18,7 @@ import java.util.Optional; /** + * Optional Argument for an Integer type. * @author Luana Coppio */ public class OptionalIntegerArgument extends Argument> { diff --git a/megamek/src/megamek/server/totalwarfare/TWGameManager.java b/megamek/src/megamek/server/totalwarfare/TWGameManager.java index 2ac90e804de..d1de6a365f8 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. */