From 584d61402701fd58f83d554b5027e2610b664540 Mon Sep 17 00:00:00 2001 From: Coppio Date: Wed, 30 Oct 2024 21:50:14 -0300 Subject: [PATCH 1/8] feat: added Auto-Resolve as a feature for resolving combats using Princess --- .../mekhq/resources/CampaignGUI.properties | 3 + .../resources/mekhq/resources/GUI.properties | 4 + MekHQ/src/mekhq/AtBGameThread.java | 151 +++-- MekHQ/src/mekhq/MekHQ.java | 8 +- MekHQ/src/mekhq/campaign/Campaign.java | 14 + .../mekhq/campaign/io/CampaignXmlParser.java | 8 + MekHQ/src/mekhq/gui/BriefingTab.java | 22 +- MekHQ/src/mekhq/gui/CampaignGUI.java | 15 +- .../AutoResolveBehaviorSettingsDialog.java | 560 ++++++++++++++++++ MekHQ/src/mekhq/utilities/MoreObjects.java | 12 + 10 files changed, 746 insertions(+), 51 deletions(-) create mode 100644 MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java create mode 100644 MekHQ/src/mekhq/utilities/MoreObjects.java diff --git a/MekHQ/resources/mekhq/resources/CampaignGUI.properties b/MekHQ/resources/mekhq/resources/CampaignGUI.properties index 7264303d81..8775ff3fcd 100644 --- a/MekHQ/resources/mekhq/resources/CampaignGUI.properties +++ b/MekHQ/resources/mekhq/resources/CampaignGUI.properties @@ -100,6 +100,7 @@ miMassPersonnelTraining.text=Mass Personnel Training... miMassPersonnelTraining.toolTipText=This launches the Mass Personnel Training Dialog, which allows you to train large numbers of personnel at the same time. miScenarioEditor.text=Scenario Template Editor... miCompanyGenerator.text=Company Generator... +miAutoResolveBehaviorSettings.text=Auto Resolve Behavior Settings # Help Menu menuHelp.text=Help @@ -177,6 +178,8 @@ btnClearAssignedUnits.toolTipText=Clear all assigned units for this scenario btnClearAssignedUnits.text=Clear Units btnResolveScenario.toolTipText=Bring up a wizard that will guide you through the process of resolving this scenario either by MUL files from a MegaMek game or by manually editing for tabletop games. btnResolveScenario.text=Resolve Manually +btnAutoResolveScenario.toolTipText=Start a game of MegaMek with all the assigned units played by bots.
At the game's conclusion, you will be presented with a series of dialogs for resolving the scenario. +btnAutoResolveScenario.text=Auto Resolve lblMission.text=Current Mission lblPartsChoice.text=Part Type: lblPartsChoiceView.text=Part Status: diff --git a/MekHQ/resources/mekhq/resources/GUI.properties b/MekHQ/resources/mekhq/resources/GUI.properties index 42f9f15646..5c0960aa76 100644 --- a/MekHQ/resources/mekhq/resources/GUI.properties +++ b/MekHQ/resources/mekhq/resources/GUI.properties @@ -452,6 +452,10 @@ CompleteMissionDialog.title=Complete Mission lblOutcomeStatus.text=Outcome lblOutcomeStatus.toolTipText=This is the mission's outcome, with Active meaning the mission has not been completed. +### AutoResolveBehaviorSettingsDialog Class +AutoResolveBehaviorSettingsDialog.title=Auto Resolve Behavior Settings + + ### ContractMarketDialog Class ContractMarketDialog.title=Contract Market diff --git a/MekHQ/src/mekhq/AtBGameThread.java b/MekHQ/src/mekhq/AtBGameThread.java index c677ff356d..8ac4d68dec 100644 --- a/MekHQ/src/mekhq/AtBGameThread.java +++ b/MekHQ/src/mekhq/AtBGameThread.java @@ -18,47 +18,32 @@ */ package mekhq; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; - -import javax.swing.JOptionPane; - import io.sentry.Sentry; import megamek.client.AbstractClient; import megamek.client.Client; import megamek.client.bot.BotClient; +import megamek.client.bot.princess.BehaviorSettings; import megamek.client.bot.princess.Princess; +import megamek.client.bot.princess.PrincessException; import megamek.client.generator.RandomCallsignGenerator; import megamek.client.ui.swing.ClientGUI; -import megamek.common.Entity; -import megamek.common.IAero; -import megamek.common.Infantry; -import megamek.common.MapSettings; -import megamek.common.Minefield; -import megamek.common.UnitType; +import megamek.common.*; import megamek.common.planetaryconditions.PlanetaryConditions; import megamek.logging.MMLogger; import mekhq.campaign.force.Force; import mekhq.campaign.force.Lance; -import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.mission.AtBDynamicScenario; -import mekhq.campaign.mission.AtBScenario; -import mekhq.campaign.mission.BotForce; -import mekhq.campaign.mission.Scenario; +import mekhq.campaign.mission.*; import mekhq.campaign.personnel.Person; import mekhq.campaign.unit.Unit; +import javax.swing.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.*; +import java.util.stream.Collectors; + /** * Enhanced version of GameThread which imports settings and non-player units * into the MM game @@ -69,16 +54,23 @@ public class AtBGameThread extends GameThread { private static final MMLogger logger = MMLogger.create(AtBGameThread.class); private final AtBScenario scenario; + private final BehaviorSettings autoResolveBehaviorSettings; public AtBGameThread(String name, String password, Client c, MekHQ app, List units, AtBScenario scenario) { - this(name, password, c, app, units, scenario, true); + this(name, password, c, app, units, scenario, null, true); + } + + public AtBGameThread(String name, String password, Client c, MekHQ app, List units, + AtBScenario scenario, BehaviorSettings autoResolveBehaviorSettings) { + this(name, password, c, app, units, scenario, autoResolveBehaviorSettings, true); } public AtBGameThread(String name, String password, Client c, MekHQ app, List units, - AtBScenario scenario, boolean started) { + AtBScenario scenario, BehaviorSettings autoResolveBehaviorSettings, boolean started) { super(name, password, c, app, units, scenario, started); this.scenario = Objects.requireNonNull(scenario); + this.autoResolveBehaviorSettings = autoResolveBehaviorSettings; } // String tokens for dialog boxes used for transport loading @@ -118,7 +110,7 @@ public void run() { while (client.getLocalPlayer() == null) { Thread.sleep(MekHQ.getMHQOptions().getStartGameClientDelay()); } - + var player = client.getLocalPlayer(); // if game is running, shouldn't do the following, so detect the phase for (int i = 0; (i < MekHQ.getMHQOptions().getStartGameClientRetryCount()) && client.getGame().getPhase().isUnknown(); i++) { @@ -185,23 +177,7 @@ public void run() { client.sendMapSettings(mapSettings); Thread.sleep(MekHQ.getMHQOptions().getStartGameDelay()); - PlanetaryConditions planetaryConditions = new PlanetaryConditions(); - if (campaign.getCampaignOptions().isUseLightConditions()) { - planetaryConditions.setLight(scenario.getLight()); - } - if (campaign.getCampaignOptions().isUseWeatherConditions()) { - planetaryConditions.setWeather(scenario.getWeather()); - planetaryConditions.setWind(scenario.getWind()); - planetaryConditions.setFog(scenario.getFog()); - planetaryConditions.setEMI(scenario.getEMI()); - planetaryConditions.setBlowingSand(scenario.getBlowingSand()); - planetaryConditions.setTemperature(scenario.getModifiedTemperature()); - } - if (campaign.getCampaignOptions().isUsePlanetaryConditions()) { - planetaryConditions.setAtmosphere(scenario.getAtmosphere()); - planetaryConditions.setGravity(scenario.getGravity()); - } - client.sendPlanetaryConditions(planetaryConditions); + client.sendPlanetaryConditions(getPlanetaryConditions()); Thread.sleep(MekHQ.getMHQOptions().getStartGameDelay()); // set player deployment @@ -392,7 +368,7 @@ public void run() { } // All player and bot units have been added to the lobby - // Prompt the player to auto load units into transports + // Prompt the player to autoload units into transports if (!scenario.getPlayerTransportLinkages().isEmpty()) { for (UUID id : scenario.getPlayerTransportLinkages().keySet()) { boolean loadDropShips = false; @@ -454,6 +430,14 @@ public void run() { } } } + + + // if AtB was loaded with the auto resolve bot behavior settings then it loads a new bot, + // set to the players team + // and then moves all the player forces under this new bot + if (Objects.nonNull(autoResolveBehaviorSettings)) { + setupPlayerBotForAutoResolve(player); + } } while (!stop) { @@ -471,6 +455,77 @@ public void run() { } } + private void setupPlayerBotForAutoResolve(Player player) throws InterruptedException, PrincessException { + var botName = player.getName() + ":AI"; + var autoResolveBot = new BotForce(); + autoResolveBot.setName(botName); + + Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); + var botClient = new Princess(botName, client.getHost(), client.getPort()); + botClient.setBehaviorSettings(autoResolveBehaviorSettings.getCopy()); + try { + botClient.connect(); + Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); + } catch (Exception e) { + Sentry.captureException(e); + logger.error(String.format("Could not connect with Bot name %s", botName), + e); + } + swingGui.getLocalBots().put(botName, botClient); + + var retryCount = MekHQ.getMHQOptions().getStartGameBotClientRetryCount(); + while (botClient.getLocalPlayer() == null) { + Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); + retryCount--; + if (retryCount <= 0) { + break; + } + } + if (retryCount <= 0) { + logger.error(String.format("Could not connect with Bot name %s", botName)); + } + botClient.getLocalPlayer().setName(botName); + botClient.getLocalPlayer().setStartingPos(player.getStartingPos()); + botClient.getLocalPlayer().setStartOffset(player.getStartOffset()); + botClient.getLocalPlayer().setStartWidth(player.getStartWidth()); + botClient.getLocalPlayer().setStartingAnyNWx(player.getStartingAnyNWx()); + botClient.getLocalPlayer().setStartingAnyNWy(player.getStartingAnyNWy()); + botClient.getLocalPlayer().setStartingAnySEx(player.getStartingAnySEx()); + botClient.getLocalPlayer().setStartingAnySEy(player.getStartingAnySEy()); + botClient.getLocalPlayer().setCamouflage(player.getCamouflage().clone()); + botClient.getLocalPlayer().setColour(player.getColour()); + botClient.getLocalPlayer().setTeam(player.getTeam()); + botClient.sendPlayerInfo(); + Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); + + var ent = client.getEntitiesVector().stream() + .filter(entity -> entity.getOwnerId() == player.getId()) + .collect(Collectors.toList()); + botClient.sendChangeOwner(ent, botClient.getLocalPlayer().getId()); + Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); + + } + + private PlanetaryConditions getPlanetaryConditions() { + PlanetaryConditions planetaryConditions = new PlanetaryConditions(); + if (campaign.getCampaignOptions().isUseLightConditions()) { + planetaryConditions.setLight(scenario.getLight()); + } + if (campaign.getCampaignOptions().isUseWeatherConditions()) { + planetaryConditions.setWeather(scenario.getWeather()); + planetaryConditions.setWind(scenario.getWind()); + planetaryConditions.setFog(scenario.getFog()); + planetaryConditions.setEMI(scenario.getEMI()); + planetaryConditions.setBlowingSand(scenario.getBlowingSand()); + planetaryConditions.setTemperature(scenario.getModifiedTemperature()); + } + if (campaign.getCampaignOptions().isUsePlanetaryConditions()) { + planetaryConditions.setAtmosphere(scenario.getAtmosphere()); + planetaryConditions.setGravity(scenario.getGravity()); + } + return planetaryConditions; + } + /** * wait for the server to add the bot client, then send starting position, * camo, and entities diff --git a/MekHQ/src/mekhq/MekHQ.java b/MekHQ/src/mekhq/MekHQ.java index 45c0ca79c7..1c82c20309 100644 --- a/MekHQ/src/mekhq/MekHQ.java +++ b/MekHQ/src/mekhq/MekHQ.java @@ -26,6 +26,7 @@ import megamek.MegaMek; import megamek.SuiteConstants; import megamek.client.Client; +import megamek.client.bot.princess.BehaviorSettings; import megamek.client.generator.RandomNameGenerator; import megamek.client.generator.RandomUnitGenerator; import megamek.client.ui.preferences.PreferencesNode; @@ -374,6 +375,11 @@ public void joinGame(Scenario scenario, List meks) { } public void startHost(Scenario scenario, boolean loadSavegame, List meks) { + startHost(scenario, loadSavegame, meks, null); + } + + public void startHost(Scenario scenario, boolean loadSavegame, List meks, BehaviorSettings autoResolveBehaviorSettings) + { HostDialog hostDialog = new HostDialog(campaignGUI.getFrame(), getCampaign().getName()); hostDialog.setVisible(true); @@ -426,7 +432,7 @@ public void startHost(Scenario scenario, boolean loadSavegame, List meks) // Start the game thread if (getCampaign().getCampaignOptions().isUseAtB() && (scenario instanceof AtBScenario)) { - gameThread = new AtBGameThread(playerName, password, client, this, meks, (AtBScenario) scenario); + gameThread = new AtBGameThread(playerName, password, client, this, meks, (AtBScenario) scenario, autoResolveBehaviorSettings); } else { gameThread = new GameThread(playerName, password, client, this, meks, scenario); } diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 605de15616..6a6c8c2144 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -21,6 +21,8 @@ */ package mekhq.campaign; +import megamek.client.bot.princess.BehaviorSettings; +import megamek.client.bot.princess.BehaviorSettingsFactory; import megamek.client.generator.RandomGenderGenerator; import megamek.client.generator.RandomNameGenerator; import megamek.client.generator.RandomUnitGenerator; @@ -268,6 +270,7 @@ public class Campaign implements ITechManager { private final Quartermaster quartermaster; private StoryArc storyArc; private FameAndInfamyController fameAndInfamy; + private BehaviorSettings autoResolveBehaviorSettings; private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Campaign", MekHQ.getMHQOptions().getLocale()); @@ -335,6 +338,7 @@ public Campaign() { quartermaster = new Quartermaster(this); fieldKitchenWithinCapacity = false; fameAndInfamy = new FameAndInfamyController(); + autoResolveBehaviorSettings = BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR; } /** @@ -5488,6 +5492,7 @@ public void writeToXML(final PrintWriter pw) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchType", shipSearchType); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchResult", shipSearchResult); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "shipSearchExpiration", getShipSearchExpiration()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoResolveBehaviorSettings", autoResolveBehaviorSettings.getDescription()); } retirementDefectionTracker.writeToXML(pw, indent); @@ -8374,4 +8379,13 @@ public boolean useVariableTechLevel() { public boolean showExtinct() { return !campaignOptions.isDisallowExtinctStuff(); } + + public BehaviorSettings getAutoResolveBehaviorSettings() { + return autoResolveBehaviorSettings; + } + + public void setAutoResolveBehaviorSettings(BehaviorSettings settings) { + autoResolveBehaviorSettings = settings; + } + } diff --git a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java index e9ddb64378..1291e5d5f7 100644 --- a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java +++ b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java @@ -19,6 +19,7 @@ package mekhq.campaign.io; import megamek.Version; +import megamek.client.bot.princess.BehaviorSettingsFactory; import megamek.client.generator.RandomGenderGenerator; import megamek.client.generator.RandomNameGenerator; import megamek.client.ui.swing.util.PlayerColour; @@ -77,6 +78,8 @@ import java.util.*; import java.util.Map.Entry; +import static mekhq.utilities.MoreObjects.firstNonNull; + public class CampaignXmlParser { private final InputStream is; private final MekHQ app; @@ -292,6 +295,11 @@ public Campaign parse() throws CampaignXmlParseException, NullEntityException { retVal.setShipSearchResult(wn.getTextContent()); } else if (xn.equalsIgnoreCase("shipSearchExpiration")) { retVal.setShipSearchExpiration(MHQXMLUtility.parseDate(wn.getTextContent().trim())); + } else if (xn.equalsIgnoreCase("autoResolveBehaviorSettings")) { + retVal.setAutoResolveBehaviorSettings( + firstNonNull(BehaviorSettingsFactory.getInstance().getBehavior(wn.getTextContent()), + BehaviorSettingsFactory.getInstance().DEFAULT_BEHAVIOR) + ); } else if (xn.equalsIgnoreCase("customPlanetaryEvents")) { updatePlanetaryEventsFromXML(wn); } diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java index 741bb29fc2..ca96aadcc8 100644 --- a/MekHQ/src/mekhq/gui/BriefingTab.java +++ b/MekHQ/src/mekhq/gui/BriefingTab.java @@ -18,6 +18,7 @@ */ package mekhq.gui; +import megamek.client.bot.princess.BehaviorSettings; import megamek.client.generator.ReconfigurationParameters; import megamek.client.generator.TeamLoadOutGenerator; import megamek.client.ui.baseComponents.MMComboBox; @@ -94,6 +95,7 @@ public final class BriefingTab extends CampaignGuiTab { private JButton btnGetMul; private JButton btnClearAssignedUnits; private JButton btnResolveScenario; + private JButton btnAutoResolveScenario; private ScenarioTableModel scenarioModel; @@ -267,6 +269,12 @@ public void initTab() { btnResolveScenario.setEnabled(false); panScenarioButtons.add(btnResolveScenario); + btnAutoResolveScenario = new JButton(resourceMap.getString("btnAutoResolveScenario.text")); + btnAutoResolveScenario.setToolTipText(resourceMap.getString("btnAutoResolveScenario.toolTipText")); + btnAutoResolveScenario.addActionListener(ev -> autoResolveScenario()); + btnAutoResolveScenario.setEnabled(false); + panScenarioButtons.add(btnAutoResolveScenario); + btnClearAssignedUnits = new JButton(resourceMap.getString("btnClearAssignedUnits.text")); btnClearAssignedUnits.setToolTipText(resourceMap.getString("btnClearAssignedUnits.toolTipText")); btnClearAssignedUnits.addActionListener(ev -> clearAssignedUnits()); @@ -842,6 +850,14 @@ private void loadScenario() { } private void startScenario() { + startScenario(null); + } + + private void autoResolveScenario() { + startScenario(getCampaign().getAutoResolveBehaviorSettings()); + } + + private void startScenario(BehaviorSettings autoResolveBehaviorSettings) { int row = scenarioTable.getSelectedRow(); if (row < 0) { return; @@ -927,7 +943,9 @@ private void startScenario() { // Ensure that the MegaMek year GameOption matches the campaign year getCampaign().getGameOptions().getOption(OptionsConstants.ALLOWED_YEAR) .setValue(getCampaign().getGameYear()); - getCampaignGui().getApplication().startHost(scenario, false, chosen); + getCampaignGui().getApplication() + .startHost(scenario, false, chosen, autoResolveBehaviorSettings); + } } @@ -1242,6 +1260,7 @@ public void refreshScenarioView() { btnGetMul.setEnabled(false); btnClearAssignedUnits.setEnabled(false); btnResolveScenario.setEnabled(false); + btnAutoResolveScenario.setEnabled(false); btnPrintRS.setEnabled(false); selectedScenario = -1; return; @@ -1269,6 +1288,7 @@ public void refreshScenarioView() { btnGetMul.setEnabled(canStartGame); btnClearAssignedUnits.setEnabled(canStartGame); btnResolveScenario.setEnabled(canStartGame); + btnAutoResolveScenario.setEnabled(canStartGame); btnPrintRS.setEnabled(canStartGame); } diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index bd8c2a55bd..147fd8b65a 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -1012,6 +1012,19 @@ private void initMenu() { .addActionListener(evt -> new CompanyGenerationDialog(getFrame(), getCampaign()).setVisible(true)); menuManage.add(miCompanyGenerator); + JMenuItem miAutoResolveBehaviorEditor = new JMenuItem(resourceMap.getString("miAutoResolveBehaviorSettings.text")); + miAutoResolveBehaviorEditor.setMnemonic(KeyEvent.VK_T); + miAutoResolveBehaviorEditor.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.ALT_DOWN_MASK)); + miAutoResolveBehaviorEditor + .addActionListener(evt -> { + var autoResolveBehaviorSettingsDialog = new AutoResolveBehaviorSettingsDialog(getFrame(), getCampaign()); + autoResolveBehaviorSettingsDialog.setVisible(true); + autoResolveBehaviorSettingsDialog.setModal(true); + autoResolveBehaviorSettingsDialog.pack(); + }); + + menuManage.add(miAutoResolveBehaviorEditor); + menuBar.add(menuManage); // endregion Manage Campaign Menu @@ -1614,7 +1627,7 @@ public void refitUnit(Refit r, boolean selectModelName) { StringBuilder nameBuilder = new StringBuilder(128); nameBuilder.append("") .append(tech.getFullName()) - .append(", ") + .append(", ") .append(SkillType.getColoredExperienceLevelName(tech.getSkillLevel(getCampaign(), false))) .append(" ") .append(tech.getPrimaryRoleDesc()) diff --git a/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java b/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java new file mode 100644 index 0000000000..724b04b351 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java @@ -0,0 +1,560 @@ +package mekhq.gui.dialog; + +import megamek.client.bot.princess.BehaviorSettings; +import megamek.client.bot.princess.BehaviorSettingsFactory; +import megamek.client.bot.princess.CardinalEdge; +import megamek.client.bot.princess.PrincessException; +import megamek.client.ui.Messages; +import megamek.client.ui.baseComponents.MMComboBox; +import megamek.client.ui.dialogs.helpDialogs.PrincessHelpDialog; +import megamek.client.ui.swing.MMToggleButton; +import megamek.client.ui.swing.util.ScalingPopup; +import megamek.client.ui.swing.util.UIUtil; +import megamek.logging.MMLogger; +import mekhq.campaign.Campaign; +import mekhq.gui.baseComponents.AbstractMHQDialog; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.awt.event.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AutoResolveBehaviorSettingsDialog + extends AbstractMHQDialog + implements ActionListener, ListSelectionListener, ChangeListener +{ + private final static MMLogger logger = MMLogger.create(AutoResolveBehaviorSettingsDialog.class); + + private static final String OK_ACTION = "Ok_Action"; + + private final transient BehaviorSettingsFactory behaviorSettingsFactory = BehaviorSettingsFactory.getInstance(); + private BehaviorSettings autoResolveBehavior; + + private final JLabel nameLabel = new JLabel(Messages.getString("BotConfigDialog.nameLabel")); + private final UIUtil.TipTextField nameField = new UIUtil.TipTextField("", 16); + + private final MMToggleButton forcedWithdrawalCheck = new UIUtil.TipMMToggleButton( + Messages.getString("BotConfigDialog.forcedWithdrawalCheck")); + private final JLabel withdrawEdgeLabel = new JLabel(Messages.getString("BotConfigDialog.retreatEdgeLabel")); + private final MMComboBox withdrawEdgeCombo = new UIUtil.TipCombo<>("EdgeToWithdraw", CardinalEdge.values()); + private final MMToggleButton autoFleeCheck = new UIUtil.TipMMToggleButton(Messages.getString("BotConfigDialog.autoFleeCheck")); + private final JLabel fleeEdgeLabel = new JLabel(Messages.getString("BotConfigDialog.homeEdgeLabel")); + private final MMComboBox fleeEdgeCombo = new UIUtil.TipCombo<>("EdgeToFlee", CardinalEdge.values()); + + private final UIUtil.TipSlider aggressionSlidebar = new UIUtil.TipSlider(SwingConstants.HORIZONTAL, 0, 10, 5); + private final UIUtil.TipSlider fallShameSlidebar = new UIUtil.TipSlider(SwingConstants.HORIZONTAL, 0, 10, 5); + private final UIUtil.TipSlider herdingSlidebar = new UIUtil.TipSlider(SwingConstants.HORIZONTAL, 0, 10, 5); + private final UIUtil.TipSlider selfPreservationSlidebar = new UIUtil.TipSlider(SwingConstants.HORIZONTAL, 0, 10, 5); + private final UIUtil.TipSlider braverySlidebar = new UIUtil.TipSlider(SwingConstants.HORIZONTAL, 0, 10, 5); +// private final UIUtil.TipButton savePreset = new UIUtil.TipButton(Messages.getString("BotConfigDialog.save")); + private final UIUtil.TipButton saveNewPreset = new UIUtil.TipButton(Messages.getString("BotConfigDialog.saveNew")); + + private final JButton princessHelpButton = new JButton(Messages.getString("BotConfigDialog.help")); + + private JPanel presetsPanel; + private final JLabel chooseLabel = new JLabel(Messages.getString("BotConfigDialog.behaviorNameLabel")); + /** + * A copy of the current presets. Modifications will only be saved when + * accepted. + */ + private List presets; + private final AutoResolveBehaviorSettingsDialog.PresetsModel presetsModel = new AutoResolveBehaviorSettingsDialog.PresetsModel(); + private final JList presetsList = new JList<>(presetsModel); + + private final JButton butOK = new JButton(Messages.getString("Okay")); + private final JButton butCancel = new JButton(Messages.getString("Cancel")); + + /** + * Stores the currently chosen preset. Used to detect if the player has changed + * the sliders. + */ + private BehaviorSettings chosenPreset; + private Campaign campaign; + + //region Constructors + public AutoResolveBehaviorSettingsDialog(final JFrame frame, final Campaign campaign) { + super(frame, "AutoResolveBehaviorSettingsDialog", "AutoResolveBehaviorSettingsDialog.title"); + setAlwaysOnTop(true); + setCampaign(campaign); + autoResolveBehavior = ( + campaign.getAutoResolveBehaviorSettings() != null ? + campaign.getAutoResolveBehaviorSettings() : new BehaviorSettings()); + updatePresets(); + initialize(); + updateDialogFields(); + } + + private String getAutoResolveBehaviorSettingName() { + return campaign.getName() + ":AI"; + } + + public void setCampaign(final Campaign campaign) { + this.campaign = campaign; + } + + @Override + protected void initialize() { + // Make Enter confirm and close the dialog + final KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); + getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(enter, OK_ACTION); + getRootPane().getInputMap(JComponent.WHEN_FOCUSED).put(enter, OK_ACTION); + getRootPane().getActionMap().put(OK_ACTION, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent evt) { + okAction(); + } + }); + super.initialize(); + } + + @Override + protected Container createCenterPane() { + JPanel result = new JPanel(); + result.setLayout(new BoxLayout(result, BoxLayout.PAGE_AXIS)); + result.add(nameSection()); + result.add(settingSection()); + return result; + } + + /** + * The setting section contains the presets list on the left side and the + * princess settings on the right. + */ + private JPanel settingSection() { +// var princessScroll = new JScrollPane(princessPanel()); +// princessScroll.getVerticalScrollBar().setUnitIncrement(16); +// princessScroll.setBorder(null); +// presetsPanel = presetsPanel(); + + var result = new JPanel(new BorderLayout(0, 0)); + result.setAlignmentX(LEFT_ALIGNMENT); + result.add(princessPanel(), BorderLayout.CENTER); +// result.add(presetsPanel, BorderLayout.LINE_START); + return result; + } + + /** The princess panel contains the individual princess settings. */ + private JPanel princessPanel() { + JPanel result = new JPanel(); + result.setLayout(new BoxLayout(result, BoxLayout.PAGE_AXIS)); + result.add(behaviorSection()); +// result.add(retreatSection()); + result.add(createButtonPanel()); + return result; + } + + private JPanel nameSection() { + JPanel result = new JPanel(); + result.setLayout(new BoxLayout(result, BoxLayout.PAGE_AXIS)); + result.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0)); + UIUtil.Content panContent = new UIUtil.Content(); + panContent.setLayout(new BoxLayout(panContent, BoxLayout.PAGE_AXIS)); + result.add(panContent); + + var namePanel = new JPanel(); + nameField.setToolTipText(Messages.getString("BotConfigDialog.namefield.tooltip")); + // When the dialog configures an existing player, the name must not be changed + nameField.setText(getAutoResolveBehaviorSettingName()); + nameField.setEnabled(false); + nameLabel.setLabelFor(nameField); + nameLabel.setDisplayedMnemonic(KeyEvent.VK_N); + namePanel.add(nameLabel); + namePanel.add(nameField); + + panContent.add(namePanel); + return result; + } + + /** The presets panel has a list of behavior presets for Princess. */ + private JPanel presetsPanel() { + var result = new JPanel(); + result.setLayout(new BoxLayout(result, BoxLayout.PAGE_AXIS)); + result.setBorder(new EmptyBorder(0, 10, 0, 20)); + + chooseLabel.setAlignmentX(CENTER_ALIGNMENT); + chooseLabel.setDisplayedMnemonic(KeyEvent.VK_P); + chooseLabel.setLabelFor(presetsList); + var headerPanel = new UIUtil.FixedYPanel(); + headerPanel.add(chooseLabel); + + presetsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + presetsList.addListSelectionListener(this); + presetsList.setCellRenderer(new PresetsRenderer()); + presetsList.addMouseListener(presetsMouseListener); + + result.add(headerPanel); + result.add(Box.createVerticalStrut(10)); + result.add(presetsList); + + return result; + } + + private JPanel behaviorSection() { + JPanel result = new UIUtil.OptionPanel("BotConfigDialog.behaviorSection"); + UIUtil.Content panContent = new UIUtil.Content(); + panContent.setLayout(new BoxLayout(panContent, BoxLayout.PAGE_AXIS)); + result.add(panContent); + + panContent.add(buildSlider(braverySlidebar, Messages.getString("BotConfigDialog.braverySliderMin"), + Messages.getString("BotConfigDialog.braverySliderMax"), + Messages.getString("BotConfigDialog.braveryTooltip"), + Messages.getString("BotConfigDialog.braverySliderTitle"))); + panContent.add(Box.createVerticalStrut(7)); + + panContent.add( + buildSlider(selfPreservationSlidebar, Messages.getString("BotConfigDialog.selfPreservationSliderMin"), + Messages.getString("BotConfigDialog.selfPreservationSliderMax"), + Messages.getString("BotConfigDialog.selfPreservationTooltip"), + Messages.getString("BotConfigDialog.selfPreservationSliderTitle"))); + panContent.add(Box.createVerticalStrut(7)); + + panContent.add(buildSlider(aggressionSlidebar, Messages.getString("BotConfigDialog.aggressionSliderMin"), + Messages.getString("BotConfigDialog.aggressionSliderMax"), + Messages.getString("BotConfigDialog.aggressionTooltip"), + Messages.getString("BotConfigDialog.aggressionSliderTitle"))); + panContent.add(Box.createVerticalStrut(7)); + + panContent.add(buildSlider(herdingSlidebar, Messages.getString("BotConfigDialog.herdingSliderMin"), + Messages.getString("BotConfigDialog.herdingSliderMax"), + Messages.getString("BotConfigDialog.herdingToolTip"), + Messages.getString("BotConfigDialog.herdingSliderTitle"))); + panContent.add(Box.createVerticalStrut(7)); + + panContent.add(buildSlider(fallShameSlidebar, Messages.getString("BotConfigDialog.fallShameSliderMin"), + Messages.getString("BotConfigDialog.fallShameSliderMax"), + Messages.getString("BotConfigDialog.fallShameToolTip"), + Messages.getString("BotConfigDialog.fallShameSliderTitle"))); + + var buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10)); + buttonPanel.setAlignmentX(SwingConstants.CENTER); + result.add(buttonPanel); + +// savePreset.addActionListener(this); +// savePreset.setMnemonic(KeyEvent.VK_S); +// savePreset.setToolTipText(Messages.getString("BotConfigDialog.saveTip")); +// buttonPanel.add(savePreset); +// saveNewPreset.addActionListener(this); +// saveNewPreset.setMnemonic(KeyEvent.VK_A); +// saveNewPreset.setToolTipText(Messages.getString("BotConfigDialog.saveNewTip")); +// buttonPanel.add(saveNewPreset); + + return result; + } + + + private JPanel retreatSection() { + JPanel result = new UIUtil.OptionPanel("BotConfigDialog.retreatSection"); + UIUtil.Content panContent = new UIUtil.Content(); + panContent.setLayout(new BoxLayout(panContent, BoxLayout.PAGE_AXIS)); + result.add(panContent); + + autoFleeCheck.setToolTipText(Messages.getString("BotConfigDialog.autoFleeTooltip")); + autoFleeCheck.addActionListener(this); + autoFleeCheck.setMnemonic(KeyEvent.VK_F); + + fleeEdgeCombo.removeItem(CardinalEdge.NONE); + fleeEdgeCombo.setToolTipText(Messages.getString("BotConfigDialog.homeEdgeTooltip")); + fleeEdgeCombo.setSelectedIndex(0); + fleeEdgeCombo.addActionListener(this); + + forcedWithdrawalCheck.setToolTipText(Messages.getString("BotConfigDialog.forcedWithdrawalTooltip")); + forcedWithdrawalCheck.addActionListener(this); + forcedWithdrawalCheck.setMnemonic(KeyEvent.VK_W); + + withdrawEdgeCombo.removeItem(CardinalEdge.NONE); + withdrawEdgeCombo.setToolTipText(Messages.getString("BotConfigDialog.retreatEdgeTooltip")); + withdrawEdgeCombo.setSelectedIndex(0); + + var firstLine = new JPanel(new FlowLayout(FlowLayout.LEFT)); + var secondLine = new JPanel(new FlowLayout(FlowLayout.LEFT)); + firstLine.add(forcedWithdrawalCheck); + firstLine.add(Box.createHorizontalStrut(20)); + firstLine.add(withdrawEdgeLabel); + firstLine.add(withdrawEdgeCombo); + secondLine.add(autoFleeCheck); + secondLine.add(Box.createHorizontalStrut(20)); + secondLine.add(fleeEdgeLabel); + secondLine.add(fleeEdgeCombo); + panContent.add(firstLine); + panContent.add(Box.createVerticalStrut(5)); + panContent.add(secondLine); + + return result; + } + + protected void updatePresetFields() { + selfPreservationSlidebar.setValue(autoResolveBehavior.getSelfPreservationIndex()); + aggressionSlidebar.setValue(autoResolveBehavior.getHyperAggressionIndex()); + fallShameSlidebar.setValue(autoResolveBehavior.getFallShameIndex()); + herdingSlidebar.setValue(autoResolveBehavior.getHerdMentalityIndex()); + braverySlidebar.setValue(autoResolveBehavior.getBraveryIndex()); + } + + private void updateDialogFields() { + updatePresetFields(); + + forcedWithdrawalCheck.setSelected(autoResolveBehavior.isForcedWithdrawal()); + withdrawEdgeCombo.setSelectedItem(autoResolveBehavior.getRetreatEdge()); + + autoFleeCheck.setSelected(autoResolveBehavior.shouldAutoFlee()); + fleeEdgeCombo.setSelectedItem(autoResolveBehavior.getDestinationEdge()); + + updateEnabledStates(); + } + + /** Updates all necessary enabled states of buttons/dropdowns. */ + private void updateEnabledStates() { + fleeEdgeLabel.setEnabled(autoFleeCheck.isSelected()); + fleeEdgeCombo.setEnabled(autoFleeCheck.isSelected()); + withdrawEdgeLabel.setEnabled(forcedWithdrawalCheck.isSelected()); + withdrawEdgeCombo.setEnabled(forcedWithdrawalCheck.isSelected()); +// savePreset.setEnabled(isChangedPreset()); + } + + /** + * Returns true if a preset is selected and is different from the current slider + * settings. + */ + private boolean isChangedPreset() { + return (chosenPreset != null) + && (chosenPreset.getSelfPreservationIndex() != selfPreservationSlidebar.getValue() + || chosenPreset.getHyperAggressionIndex() != aggressionSlidebar.getValue() + || chosenPreset.getFallShameIndex() != fallShameSlidebar.getValue() + || chosenPreset.getHerdMentalityIndex() != herdingSlidebar.getValue() + || chosenPreset.getBraveryIndex() != braverySlidebar.getValue()); + } + + private JPanel buildSlider(JSlider thisSlider, String minMsgProperty, + String maxMsgProperty, String toolTip, String title) { + TitledBorder border = BorderFactory.createTitledBorder(title); + border.setTitlePosition(TitledBorder.TOP); + border.setTitleJustification(TitledBorder.CENTER); + var result = new UIUtil.TipPanel(); + result.setBorder(border); + result.setLayout(new BoxLayout(result, BoxLayout.PAGE_AXIS)); + result.setToolTipText(toolTip); + thisSlider.setToolTipText(toolTip); + thisSlider.setPaintLabels(false); + thisSlider.setSnapToTicks(true); + thisSlider.addChangeListener(this); + + var panLabels = new JPanel(); + panLabels.setLayout(new BoxLayout(panLabels, BoxLayout.LINE_AXIS)); + panLabels.add(new JLabel(minMsgProperty, SwingConstants.LEFT)); + panLabels.add(Box.createHorizontalGlue()); + panLabels.add(new JLabel(maxMsgProperty, SwingConstants.RIGHT)); + + result.add(panLabels); + result.add(thisSlider); + result.revalidate(); + return result; + } + + protected JPanel createButtonPanel() { + JPanel result = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); + + butOK.addActionListener((l) -> { + okAction(); + setVisible(false); + }); + butOK.setMnemonic(KeyEvent.VK_K); + result.add(butOK); + + butCancel.addActionListener(this::cancelActionPerformed); + butCancel.setMnemonic(KeyEvent.VK_C); + result.add(butCancel); + + princessHelpButton.addActionListener(this); + princessHelpButton.setMnemonic(KeyEvent.VK_H); + result.add(princessHelpButton); + + return result; + } + + private void showPrincessHelp() { + new PrincessHelpDialog(getFrame()).setVisible(true); + } + + private void okAction() { + try { + savePrincessProperties(); + } catch (PrincessException e) { + logger.error("Error saving AutoResolveBehaviorSettings properties", e); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == princessHelpButton) { + showPrincessHelp(); + } + } + + /** Saves the current Behavior to the currently selected Behavior Preset. */ + private void savePreset() { + writePreset(getAutoResolveBehaviorSettingName()); + } + + /** Removes the given Behavior Preset. */ + private void removePreset(String name) { + behaviorSettingsFactory.removeBehavior(name); + behaviorSettingsFactory.saveBehaviorSettings(false); + updatePresets(); + } + + private void savePrincessProperties() throws PrincessException { + BehaviorSettings tempBehavior = new BehaviorSettings(); + tempBehavior.setFallShameIndex(fallShameSlidebar.getValue()); + tempBehavior.setForcedWithdrawal(forcedWithdrawalCheck.isSelected()); + tempBehavior.setAutoFlee(autoFleeCheck.isSelected()); + tempBehavior.setDestinationEdge(fleeEdgeCombo.getSelectedItem()); + tempBehavior.setRetreatEdge(withdrawEdgeCombo.getSelectedItem()); + tempBehavior.setHyperAggressionIndex(aggressionSlidebar.getValue()); + tempBehavior.setSelfPreservationIndex(selfPreservationSlidebar.getValue()); + tempBehavior.setHerdMentalityIndex(herdingSlidebar.getValue()); + tempBehavior.setBraveryIndex(braverySlidebar.getValue()); + tempBehavior.setDescription(getAutoResolveBehaviorSettingName()); + autoResolveBehavior = tempBehavior; + campaign.setAutoResolveBehaviorSettings(tempBehavior); + savePreset(); + } + + private void writePreset(String name) { + BehaviorSettings newBehavior = new BehaviorSettings(); + try { + newBehavior.setDescription(name); + } catch (PrincessException e1) { + return; + } + newBehavior.setFallShameIndex(fallShameSlidebar.getValue()); + newBehavior.setHyperAggressionIndex(aggressionSlidebar.getValue()); + newBehavior.setSelfPreservationIndex(selfPreservationSlidebar.getValue()); + newBehavior.setHerdMentalityIndex(herdingSlidebar.getValue()); + newBehavior.setBraveryIndex(braverySlidebar.getValue()); + behaviorSettingsFactory.addBehavior(newBehavior); + behaviorSettingsFactory.saveBehaviorSettings(false); + } + + @Override + public void valueChanged(ListSelectionEvent event) { + if (event.getValueIsAdjusting()) { + return; + } + + if (event.getSource() == presetsList) { + presetSelected(); + } + } + + /** Shows a popup menu for a behavior preset, allowing to delete it. */ + private transient MouseListener presetsMouseListener = new MouseAdapter() { + + @Override + public void mouseReleased(MouseEvent e) { + int row = presetsList.locationToIndex(e.getPoint()); + if (e.isPopupTrigger() && (row != -1)) { + ScalingPopup popup = new ScalingPopup(); + String behavior = presetsList.getModel().getElementAt(row); + var deleteItem = new JMenuItem("Delete " + behavior); + deleteItem.addActionListener(event -> removePreset(behavior)); + popup.add(deleteItem); + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + + @Override + public void mouseClicked(MouseEvent evt) { + if (SwingUtilities.isLeftMouseButton(evt) && evt.getClickCount() == 1) { + presetSelected(); + } + } + }; + + /** + * Called when a Preset is selected. This will often be called twice when + * clicking with the mouse (by the listselectionlistener and the mouselistener). + * In this way the list will react when copying a Preset from another bot and + * then clicking the already selected Preset again. And it will also react to + * keyboard navigation. + */ + private void presetSelected() { + if (presetsList.isSelectionEmpty()) { + chosenPreset = null; + } else { + autoResolveBehavior = behaviorSettingsFactory.getBehavior(presetsList.getSelectedValue()); + chosenPreset = behaviorSettingsFactory.getBehavior(presetsList.getSelectedValue()); + + if (autoResolveBehavior == null) { + autoResolveBehavior = new BehaviorSettings(); + } + updatePresetFields(); + } + updateEnabledStates(); + } + + /** + * Sets up/Updates the displayed preset list (e.g. after adding or deleting a + * preset) + */ + private void updatePresets() { + presets = new ArrayList<>(Arrays.asList(behaviorSettingsFactory.getBehaviorNames())); + ((AutoResolveBehaviorSettingsDialog.PresetsModel) presetsList.getModel()).fireUpdate(); + } + + @Override + public void stateChanged(ChangeEvent e) { + updateEnabledStates(); + } + + private class PresetsModel extends DefaultListModel { + + @Override + public int getSize() { + return presets.size(); + } + + @Override + public String getElementAt(int index) { + return presets.get(index); + } + + /** Call when elements of the list change. */ + private void fireUpdate() { + fireContentsChanged(this, 0, getSize() - 1); + } + } + + /** + * A renderer for the Behavior Presets list. Adapts the font size to the gui + * scaling and + * colors the special list elements (other bot Configurations and original + * Config). + */ + private class PresetsRenderer extends DefaultListCellRenderer { + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, + boolean cellHasFocus) { + Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + comp.setFont(UIUtil.getScaledFont()); + String preset = (String) value; + if (preset.startsWith(UIUtil.BOT_MARKER)) { + comp.setForeground(UIUtil.uiLightBlue()); + } + + if (preset.equals(Messages.getString("BotConfigDialog.previousConfig"))) { + comp.setForeground(UIUtil.uiGreen()); + } + + return comp; + } + } +} diff --git a/MekHQ/src/mekhq/utilities/MoreObjects.java b/MekHQ/src/mekhq/utilities/MoreObjects.java new file mode 100644 index 0000000000..088b361cad --- /dev/null +++ b/MekHQ/src/mekhq/utilities/MoreObjects.java @@ -0,0 +1,12 @@ +package mekhq.utilities; + +public class MoreObjects { + + private MoreObjects() { + } + + public static O firstNonNull(O first, O second) { + return first != null ? first : second; + } +} + From bc0163c6127349fa45f2a41713fbc57fb060a83c Mon Sep 17 00:00:00 2001 From: Coppio Date: Thu, 31 Oct 2024 01:21:50 -0300 Subject: [PATCH 2/8] fix: improved the documentation, reduced duplicated code, added better preset for auto resolve --- MekHQ/docs/help/en/AutoResolve.html | 54 ++ MekHQ/src/mekhq/AtBGameThread.java | 31 +- MekHQ/src/mekhq/MekHQ.java | 20 +- .../mekhq/campaign/io/CampaignXmlParser.java | 3 +- .../AutoResolveBehaviorSettingsDialog.java | 550 +----------------- MekHQ/src/mekhq/utilities/MoreObjects.java | 12 - 6 files changed, 123 insertions(+), 547 deletions(-) create mode 100644 MekHQ/docs/help/en/AutoResolve.html delete mode 100644 MekHQ/src/mekhq/utilities/MoreObjects.java diff --git a/MekHQ/docs/help/en/AutoResolve.html b/MekHQ/docs/help/en/AutoResolve.html new file mode 100644 index 0000000000..6334aecc52 --- /dev/null +++ b/MekHQ/docs/help/en/AutoResolve.html @@ -0,0 +1,54 @@ + + + + + Auto Resolve + + +
+

Auto Resolve Behavior Settings

+

Introduction

+

The Auto Resolve feature is a way to let the PrincessBot control your units so she can quickly play out the battles + for you, its pretty straight forward to setup and use if you want to focus on your company management or just dont + want to play every single lengthy battle.

+

How to Use

+

Auto Resolve is available as a new option in the Scenario panel, the button "Auto Resolve" will setup the game scenario + on MegaMek as normal, and then it adds a Princess bot with the name following the format :AI, it is setup + as the player is setup, same starting position, color, camouflage and team, and then all the players units change owner + to it.

+

To make the game faster, you can change configuration to not show the reports between phases, and select the skip + action if no available unit, with those options selected the game will not request you to press Done between those + phases.

+

Configuring the Auto Resolve Behavior

+

The configuration is accessible in the Manage Campaign menu, in the MekHQ toolbar, there is the entry + Auto Resolve Behavior Settings, it will open the Configure Princess Bot window, where you can define the + behavior for the auto resolve preset.

+

By default, every campaign now have an auto-resolve default preset, that is named :AI, you can change the + configuration as you like and select whatever preset you want.

+

Whenever you hit the "OK" button the current configuration being shown overwrites the auto resolve preset + for your company, so if you want to experiment with different behaviors, I suggest that you create new presets and then + just select the one you want before hitting "OK" to close the configuration.

+

Q&A

+

Q: Can I change the configuration during the game?

+

Sure, its a Princess Bot, so all chat commands to change behavior apply

+

Q: Can I change the configuration for a specific scenario?

+

Also yes, you can create many presets, like for example one for a scape scenario, another for a defend scenario, etc. + Just remember to change the selected preset behavior before entering the game with auto resolve. And remember to change + it back later to your "default" preset.

+

Q: Can I change the configuration for a specific unit?

+

No, you can't, you can emulate that by manually creating many bots with different configurations and then assigning + individual units or lances to them, but that is outside the current scope of the Auto Resolve.

+

Q: I deleted my company preset! Is everything lost?

+

You lost the configuration, sure, but MekHQ won't make any fuss about it, if you delete it by mistake inside MegaMek, + once you open the Auto Resolve Behavior Settings again, the default preset will be recreated for you. And if it is + missing before you enter an auto resolve game, it will use the default behavior for the Princess Bot instead.

+

Q: I want to share my preset with my friends, how can I do that?

+

It's just a preset, so you can use the same way you used to share it with your friends before.

+

Q: The preset is written to the campaign save?

+

No, the preset is saved in the MekHQ configuration folder, so it's available for all your campaigns, not just the one + that created it. So... if you create two different campaigns with the same name they would use the same preset. + Also means that if you change the preset in one save file, the preset is the same in your other campaign. Remember, it + is a preset that you are telling MekHQ to use, not a campaign specific configuration.

+
+ + \ No newline at end of file diff --git a/MekHQ/src/mekhq/AtBGameThread.java b/MekHQ/src/mekhq/AtBGameThread.java index 8ac4d68dec..3f4f3ae32d 100644 --- a/MekHQ/src/mekhq/AtBGameThread.java +++ b/MekHQ/src/mekhq/AtBGameThread.java @@ -28,6 +28,7 @@ import megamek.client.generator.RandomCallsignGenerator; import megamek.client.ui.swing.ClientGUI; import megamek.common.*; +import megamek.common.annotations.Nullable; import megamek.common.planetaryconditions.PlanetaryConditions; import megamek.logging.MMLogger; import mekhq.campaign.force.Force; @@ -56,18 +57,29 @@ public class AtBGameThread extends GameThread { private final AtBScenario scenario; private final BehaviorSettings autoResolveBehaviorSettings; + /** + * Constructor for AtBGameThread + * + *

+ * This constructor creates a new AtBGameThread with the given name, password, client, MekHQ application, list of + * units, scenario, and auto resolve behavior settings. The game thread is started by default. + *

+ * + * @param name The name of the player + * @param password The password for the game + * @param c The client + * @param app The MekHQ application + * @param units The list of units to import into the game + * @param scenario The scenario to use for this game + * @param autoResolveBehaviorSettings The behavior settings for the auto resolve bot + */ public AtBGameThread(String name, String password, Client c, MekHQ app, List units, - AtBScenario scenario) { - this(name, password, c, app, units, scenario, null, true); - } - - public AtBGameThread(String name, String password, Client c, MekHQ app, List units, - AtBScenario scenario, BehaviorSettings autoResolveBehaviorSettings) { + AtBScenario scenario, @Nullable BehaviorSettings autoResolveBehaviorSettings) { this(name, password, c, app, units, scenario, autoResolveBehaviorSettings, true); } public AtBGameThread(String name, String password, Client c, MekHQ app, List units, - AtBScenario scenario, BehaviorSettings autoResolveBehaviorSettings, boolean started) { + AtBScenario scenario, @Nullable BehaviorSettings autoResolveBehaviorSettings, boolean started) { super(name, password, c, app, units, scenario, started); this.scenario = Objects.requireNonNull(scenario); this.autoResolveBehaviorSettings = autoResolveBehaviorSettings; @@ -498,12 +510,11 @@ private void setupPlayerBotForAutoResolve(Player player) throws InterruptedExcep botClient.sendPlayerInfo(); Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); - var ent = client.getEntitiesVector().stream() + var playerEntities = client.getEntitiesVector().stream() .filter(entity -> entity.getOwnerId() == player.getId()) .collect(Collectors.toList()); - botClient.sendChangeOwner(ent, botClient.getLocalPlayer().getId()); + botClient.sendChangeOwner(playerEntities, botClient.getLocalPlayer().getId()); Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); - } private PlanetaryConditions getPlanetaryConditions() { diff --git a/MekHQ/src/mekhq/MekHQ.java b/MekHQ/src/mekhq/MekHQ.java index 1c82c20309..ed49457617 100644 --- a/MekHQ/src/mekhq/MekHQ.java +++ b/MekHQ/src/mekhq/MekHQ.java @@ -35,6 +35,7 @@ import megamek.client.ui.swing.gameConnectionDialogs.ConnectDialog; import megamek.client.ui.swing.gameConnectionDialogs.HostDialog; import megamek.client.ui.swing.util.UIUtil; +import megamek.common.annotations.Nullable; import megamek.common.event.*; import megamek.common.net.marshalling.SanityInputFilter; import megamek.logging.MMLogger; @@ -374,11 +375,28 @@ public void joinGame(Scenario scenario, List meks) { gameThread.start(); } + /** + * Start hosting a game. + * This method is used to start hosting a game. It will create a new server and a client and connect to it. + * + * @param scenario The scenario to host + * @param loadSavegame Whether to load a savegame + * @param meks The units you want to use in the scenario + */ public void startHost(Scenario scenario, boolean loadSavegame, List meks) { startHost(scenario, loadSavegame, meks, null); } - public void startHost(Scenario scenario, boolean loadSavegame, List meks, BehaviorSettings autoResolveBehaviorSettings) + /** + * Start hosting a game. + * This method is used to start hosting a game. It will create a new server and a client and connect to it. + * + * @param scenario The scenario to host + * @param loadSavegame Whether to load a savegame + * @param meks The units you want to use in the scenario + * @param autoResolveBehaviorSettings The auto resolve behavior settings to use if running an AtB scenario and auto resolve is wanted + */ + public void startHost(Scenario scenario, boolean loadSavegame, List meks, @Nullable BehaviorSettings autoResolveBehaviorSettings) { HostDialog hostDialog = new HostDialog(campaignGUI.getFrame(), getCampaign().getName()); hostDialog.setVisible(true); diff --git a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java index 1291e5d5f7..9d98cf8d81 100644 --- a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java +++ b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java @@ -78,7 +78,8 @@ import java.util.*; import java.util.Map.Entry; -import static mekhq.utilities.MoreObjects.firstNonNull; +import static org.apache.commons.lang3.ObjectUtils.firstNonNull; + public class CampaignXmlParser { private final InputStream is; diff --git a/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java b/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java index 724b04b351..38cfee7842 100644 --- a/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java @@ -1,560 +1,64 @@ package mekhq.gui.dialog; -import megamek.client.bot.princess.BehaviorSettings; import megamek.client.bot.princess.BehaviorSettingsFactory; -import megamek.client.bot.princess.CardinalEdge; import megamek.client.bot.princess.PrincessException; -import megamek.client.ui.Messages; -import megamek.client.ui.baseComponents.MMComboBox; -import megamek.client.ui.dialogs.helpDialogs.PrincessHelpDialog; -import megamek.client.ui.swing.MMToggleButton; -import megamek.client.ui.swing.util.ScalingPopup; -import megamek.client.ui.swing.util.UIUtil; +import megamek.client.ui.dialogs.BotConfigDialog; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; -import mekhq.gui.baseComponents.AbstractMHQDialog; import javax.swing.*; -import javax.swing.border.EmptyBorder; -import javax.swing.border.TitledBorder; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import java.awt.*; -import java.awt.event.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; public class AutoResolveBehaviorSettingsDialog - extends AbstractMHQDialog - implements ActionListener, ListSelectionListener, ChangeListener + extends BotConfigDialog { private final static MMLogger logger = MMLogger.create(AutoResolveBehaviorSettingsDialog.class); - private static final String OK_ACTION = "Ok_Action"; - - private final transient BehaviorSettingsFactory behaviorSettingsFactory = BehaviorSettingsFactory.getInstance(); - private BehaviorSettings autoResolveBehavior; - - private final JLabel nameLabel = new JLabel(Messages.getString("BotConfigDialog.nameLabel")); - private final UIUtil.TipTextField nameField = new UIUtil.TipTextField("", 16); - - private final MMToggleButton forcedWithdrawalCheck = new UIUtil.TipMMToggleButton( - Messages.getString("BotConfigDialog.forcedWithdrawalCheck")); - private final JLabel withdrawEdgeLabel = new JLabel(Messages.getString("BotConfigDialog.retreatEdgeLabel")); - private final MMComboBox withdrawEdgeCombo = new UIUtil.TipCombo<>("EdgeToWithdraw", CardinalEdge.values()); - private final MMToggleButton autoFleeCheck = new UIUtil.TipMMToggleButton(Messages.getString("BotConfigDialog.autoFleeCheck")); - private final JLabel fleeEdgeLabel = new JLabel(Messages.getString("BotConfigDialog.homeEdgeLabel")); - private final MMComboBox fleeEdgeCombo = new UIUtil.TipCombo<>("EdgeToFlee", CardinalEdge.values()); - - private final UIUtil.TipSlider aggressionSlidebar = new UIUtil.TipSlider(SwingConstants.HORIZONTAL, 0, 10, 5); - private final UIUtil.TipSlider fallShameSlidebar = new UIUtil.TipSlider(SwingConstants.HORIZONTAL, 0, 10, 5); - private final UIUtil.TipSlider herdingSlidebar = new UIUtil.TipSlider(SwingConstants.HORIZONTAL, 0, 10, 5); - private final UIUtil.TipSlider selfPreservationSlidebar = new UIUtil.TipSlider(SwingConstants.HORIZONTAL, 0, 10, 5); - private final UIUtil.TipSlider braverySlidebar = new UIUtil.TipSlider(SwingConstants.HORIZONTAL, 0, 10, 5); -// private final UIUtil.TipButton savePreset = new UIUtil.TipButton(Messages.getString("BotConfigDialog.save")); - private final UIUtil.TipButton saveNewPreset = new UIUtil.TipButton(Messages.getString("BotConfigDialog.saveNew")); - - private final JButton princessHelpButton = new JButton(Messages.getString("BotConfigDialog.help")); - - private JPanel presetsPanel; - private final JLabel chooseLabel = new JLabel(Messages.getString("BotConfigDialog.behaviorNameLabel")); - /** - * A copy of the current presets. Modifications will only be saved when - * accepted. - */ - private List presets; - private final AutoResolveBehaviorSettingsDialog.PresetsModel presetsModel = new AutoResolveBehaviorSettingsDialog.PresetsModel(); - private final JList presetsList = new JList<>(presetsModel); - - private final JButton butOK = new JButton(Messages.getString("Okay")); - private final JButton butCancel = new JButton(Messages.getString("Cancel")); + private Campaign campaign; + private final BehaviorSettingsFactory behaviorSettingsFactory = BehaviorSettingsFactory.getInstance(); /** - * Stores the currently chosen preset. Used to detect if the player has changed - * the sliders. + * Creates a new instance of AutoResolveBehaviorSettingsDialog. + *

+ * This dialog is used to configure the auto resolve behavior settings for a campaign. + * It creates a default preset with a predetermined name and sets the behavior settings + * to the campaign's auto resolve behavior settings. + *

+ * @param frame The parent frame. + * @param campaign The campaign to get the auto resolve behavior settings from. */ - private BehaviorSettings chosenPreset; - private Campaign campaign; - - //region Constructors public AutoResolveBehaviorSettingsDialog(final JFrame frame, final Campaign campaign) { - super(frame, "AutoResolveBehaviorSettingsDialog", "AutoResolveBehaviorSettingsDialog.title"); + super(frame, campaign.getName() + ":AI", campaign.getAutoResolveBehaviorSettings(), null); setAlwaysOnTop(true); setCampaign(campaign); - autoResolveBehavior = ( - campaign.getAutoResolveBehaviorSettings() != null ? - campaign.getAutoResolveBehaviorSettings() : new BehaviorSettings()); - updatePresets(); - initialize(); - updateDialogFields(); - } - - private String getAutoResolveBehaviorSettingName() { - return campaign.getName() + ":AI"; } public void setCampaign(final Campaign campaign) { this.campaign = campaign; } - @Override - protected void initialize() { - // Make Enter confirm and close the dialog - final KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); - getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(enter, OK_ACTION); - getRootPane().getInputMap(JComponent.WHEN_FOCUSED).put(enter, OK_ACTION); - getRootPane().getActionMap().put(OK_ACTION, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent evt) { - okAction(); - } - }); - super.initialize(); - } - - @Override - protected Container createCenterPane() { - JPanel result = new JPanel(); - result.setLayout(new BoxLayout(result, BoxLayout.PAGE_AXIS)); - result.add(nameSection()); - result.add(settingSection()); - return result; - } - - /** - * The setting section contains the presets list on the left side and the - * princess settings on the right. - */ - private JPanel settingSection() { -// var princessScroll = new JScrollPane(princessPanel()); -// princessScroll.getVerticalScrollBar().setUnitIncrement(16); -// princessScroll.setBorder(null); -// presetsPanel = presetsPanel(); - - var result = new JPanel(new BorderLayout(0, 0)); - result.setAlignmentX(LEFT_ALIGNMENT); - result.add(princessPanel(), BorderLayout.CENTER); -// result.add(presetsPanel, BorderLayout.LINE_START); - return result; - } - - /** The princess panel contains the individual princess settings. */ - private JPanel princessPanel() { - JPanel result = new JPanel(); - result.setLayout(new BoxLayout(result, BoxLayout.PAGE_AXIS)); - result.add(behaviorSection()); -// result.add(retreatSection()); - result.add(createButtonPanel()); - return result; - } - - private JPanel nameSection() { - JPanel result = new JPanel(); - result.setLayout(new BoxLayout(result, BoxLayout.PAGE_AXIS)); - result.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0)); - UIUtil.Content panContent = new UIUtil.Content(); - panContent.setLayout(new BoxLayout(panContent, BoxLayout.PAGE_AXIS)); - result.add(panContent); - - var namePanel = new JPanel(); - nameField.setToolTipText(Messages.getString("BotConfigDialog.namefield.tooltip")); - // When the dialog configures an existing player, the name must not be changed - nameField.setText(getAutoResolveBehaviorSettingName()); - nameField.setEnabled(false); - nameLabel.setLabelFor(nameField); - nameLabel.setDisplayedMnemonic(KeyEvent.VK_N); - namePanel.add(nameLabel); - namePanel.add(nameField); - - panContent.add(namePanel); - return result; - } - - /** The presets panel has a list of behavior presets for Princess. */ - private JPanel presetsPanel() { - var result = new JPanel(); - result.setLayout(new BoxLayout(result, BoxLayout.PAGE_AXIS)); - result.setBorder(new EmptyBorder(0, 10, 0, 20)); - - chooseLabel.setAlignmentX(CENTER_ALIGNMENT); - chooseLabel.setDisplayedMnemonic(KeyEvent.VK_P); - chooseLabel.setLabelFor(presetsList); - var headerPanel = new UIUtil.FixedYPanel(); - headerPanel.add(chooseLabel); - - presetsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - presetsList.addListSelectionListener(this); - presetsList.setCellRenderer(new PresetsRenderer()); - presetsList.addMouseListener(presetsMouseListener); - - result.add(headerPanel); - result.add(Box.createVerticalStrut(10)); - result.add(presetsList); - - return result; - } - - private JPanel behaviorSection() { - JPanel result = new UIUtil.OptionPanel("BotConfigDialog.behaviorSection"); - UIUtil.Content panContent = new UIUtil.Content(); - panContent.setLayout(new BoxLayout(panContent, BoxLayout.PAGE_AXIS)); - result.add(panContent); - - panContent.add(buildSlider(braverySlidebar, Messages.getString("BotConfigDialog.braverySliderMin"), - Messages.getString("BotConfigDialog.braverySliderMax"), - Messages.getString("BotConfigDialog.braveryTooltip"), - Messages.getString("BotConfigDialog.braverySliderTitle"))); - panContent.add(Box.createVerticalStrut(7)); - - panContent.add( - buildSlider(selfPreservationSlidebar, Messages.getString("BotConfigDialog.selfPreservationSliderMin"), - Messages.getString("BotConfigDialog.selfPreservationSliderMax"), - Messages.getString("BotConfigDialog.selfPreservationTooltip"), - Messages.getString("BotConfigDialog.selfPreservationSliderTitle"))); - panContent.add(Box.createVerticalStrut(7)); - - panContent.add(buildSlider(aggressionSlidebar, Messages.getString("BotConfigDialog.aggressionSliderMin"), - Messages.getString("BotConfigDialog.aggressionSliderMax"), - Messages.getString("BotConfigDialog.aggressionTooltip"), - Messages.getString("BotConfigDialog.aggressionSliderTitle"))); - panContent.add(Box.createVerticalStrut(7)); - - panContent.add(buildSlider(herdingSlidebar, Messages.getString("BotConfigDialog.herdingSliderMin"), - Messages.getString("BotConfigDialog.herdingSliderMax"), - Messages.getString("BotConfigDialog.herdingToolTip"), - Messages.getString("BotConfigDialog.herdingSliderTitle"))); - panContent.add(Box.createVerticalStrut(7)); - - panContent.add(buildSlider(fallShameSlidebar, Messages.getString("BotConfigDialog.fallShameSliderMin"), - Messages.getString("BotConfigDialog.fallShameSliderMax"), - Messages.getString("BotConfigDialog.fallShameToolTip"), - Messages.getString("BotConfigDialog.fallShameSliderTitle"))); - - var buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10)); - buttonPanel.setAlignmentX(SwingConstants.CENTER); - result.add(buttonPanel); - -// savePreset.addActionListener(this); -// savePreset.setMnemonic(KeyEvent.VK_S); -// savePreset.setToolTipText(Messages.getString("BotConfigDialog.saveTip")); -// buttonPanel.add(savePreset); -// saveNewPreset.addActionListener(this); -// saveNewPreset.setMnemonic(KeyEvent.VK_A); -// saveNewPreset.setToolTipText(Messages.getString("BotConfigDialog.saveNewTip")); -// buttonPanel.add(saveNewPreset); - - return result; - } - - - private JPanel retreatSection() { - JPanel result = new UIUtil.OptionPanel("BotConfigDialog.retreatSection"); - UIUtil.Content panContent = new UIUtil.Content(); - panContent.setLayout(new BoxLayout(panContent, BoxLayout.PAGE_AXIS)); - result.add(panContent); - - autoFleeCheck.setToolTipText(Messages.getString("BotConfigDialog.autoFleeTooltip")); - autoFleeCheck.addActionListener(this); - autoFleeCheck.setMnemonic(KeyEvent.VK_F); - - fleeEdgeCombo.removeItem(CardinalEdge.NONE); - fleeEdgeCombo.setToolTipText(Messages.getString("BotConfigDialog.homeEdgeTooltip")); - fleeEdgeCombo.setSelectedIndex(0); - fleeEdgeCombo.addActionListener(this); - - forcedWithdrawalCheck.setToolTipText(Messages.getString("BotConfigDialog.forcedWithdrawalTooltip")); - forcedWithdrawalCheck.addActionListener(this); - forcedWithdrawalCheck.setMnemonic(KeyEvent.VK_W); - - withdrawEdgeCombo.removeItem(CardinalEdge.NONE); - withdrawEdgeCombo.setToolTipText(Messages.getString("BotConfigDialog.retreatEdgeTooltip")); - withdrawEdgeCombo.setSelectedIndex(0); - - var firstLine = new JPanel(new FlowLayout(FlowLayout.LEFT)); - var secondLine = new JPanel(new FlowLayout(FlowLayout.LEFT)); - firstLine.add(forcedWithdrawalCheck); - firstLine.add(Box.createHorizontalStrut(20)); - firstLine.add(withdrawEdgeLabel); - firstLine.add(withdrawEdgeCombo); - secondLine.add(autoFleeCheck); - secondLine.add(Box.createHorizontalStrut(20)); - secondLine.add(fleeEdgeLabel); - secondLine.add(fleeEdgeCombo); - panContent.add(firstLine); - panContent.add(Box.createVerticalStrut(5)); - panContent.add(secondLine); - - return result; - } - - protected void updatePresetFields() { - selfPreservationSlidebar.setValue(autoResolveBehavior.getSelfPreservationIndex()); - aggressionSlidebar.setValue(autoResolveBehavior.getHyperAggressionIndex()); - fallShameSlidebar.setValue(autoResolveBehavior.getFallShameIndex()); - herdingSlidebar.setValue(autoResolveBehavior.getHerdMentalityIndex()); - braverySlidebar.setValue(autoResolveBehavior.getBraveryIndex()); - } - - private void updateDialogFields() { - updatePresetFields(); - - forcedWithdrawalCheck.setSelected(autoResolveBehavior.isForcedWithdrawal()); - withdrawEdgeCombo.setSelectedItem(autoResolveBehavior.getRetreatEdge()); - - autoFleeCheck.setSelected(autoResolveBehavior.shouldAutoFlee()); - fleeEdgeCombo.setSelectedItem(autoResolveBehavior.getDestinationEdge()); - - updateEnabledStates(); - } - - /** Updates all necessary enabled states of buttons/dropdowns. */ - private void updateEnabledStates() { - fleeEdgeLabel.setEnabled(autoFleeCheck.isSelected()); - fleeEdgeCombo.setEnabled(autoFleeCheck.isSelected()); - withdrawEdgeLabel.setEnabled(forcedWithdrawalCheck.isSelected()); - withdrawEdgeCombo.setEnabled(forcedWithdrawalCheck.isSelected()); -// savePreset.setEnabled(isChangedPreset()); - } - - /** - * Returns true if a preset is selected and is different from the current slider - * settings. - */ - private boolean isChangedPreset() { - return (chosenPreset != null) - && (chosenPreset.getSelfPreservationIndex() != selfPreservationSlidebar.getValue() - || chosenPreset.getHyperAggressionIndex() != aggressionSlidebar.getValue() - || chosenPreset.getFallShameIndex() != fallShameSlidebar.getValue() - || chosenPreset.getHerdMentalityIndex() != herdingSlidebar.getValue() - || chosenPreset.getBraveryIndex() != braverySlidebar.getValue()); - } - - private JPanel buildSlider(JSlider thisSlider, String minMsgProperty, - String maxMsgProperty, String toolTip, String title) { - TitledBorder border = BorderFactory.createTitledBorder(title); - border.setTitlePosition(TitledBorder.TOP); - border.setTitleJustification(TitledBorder.CENTER); - var result = new UIUtil.TipPanel(); - result.setBorder(border); - result.setLayout(new BoxLayout(result, BoxLayout.PAGE_AXIS)); - result.setToolTipText(toolTip); - thisSlider.setToolTipText(toolTip); - thisSlider.setPaintLabels(false); - thisSlider.setSnapToTicks(true); - thisSlider.addChangeListener(this); - - var panLabels = new JPanel(); - panLabels.setLayout(new BoxLayout(panLabels, BoxLayout.LINE_AXIS)); - panLabels.add(new JLabel(minMsgProperty, SwingConstants.LEFT)); - panLabels.add(Box.createHorizontalGlue()); - panLabels.add(new JLabel(maxMsgProperty, SwingConstants.RIGHT)); - - result.add(panLabels); - result.add(thisSlider); - result.revalidate(); - return result; - } - - protected JPanel createButtonPanel() { - JPanel result = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); - - butOK.addActionListener((l) -> { - okAction(); - setVisible(false); - }); - butOK.setMnemonic(KeyEvent.VK_K); - result.add(butOK); - - butCancel.addActionListener(this::cancelActionPerformed); - butCancel.setMnemonic(KeyEvent.VK_C); - result.add(butCancel); - - princessHelpButton.addActionListener(this); - princessHelpButton.setMnemonic(KeyEvent.VK_H); - result.add(princessHelpButton); - - return result; - } - - private void showPrincessHelp() { - new PrincessHelpDialog(getFrame()).setVisible(true); - } - - private void okAction() { + private void updateBehaviorSettings() { + var autoResolveBehaviorSettings = getBehaviorSettings(); try { - savePrincessProperties(); + autoResolveBehaviorSettings.setDescription(campaign.getName() + ":AI"); } catch (PrincessException e) { - logger.error("Error saving AutoResolveBehaviorSettings properties", e); - } - } - - @Override - public void actionPerformed(ActionEvent e) { - if (e.getSource() == princessHelpButton) { - showPrincessHelp(); - } - } - - /** Saves the current Behavior to the currently selected Behavior Preset. */ - private void savePreset() { - writePreset(getAutoResolveBehaviorSettingName()); - } - - /** Removes the given Behavior Preset. */ - private void removePreset(String name) { - behaviorSettingsFactory.removeBehavior(name); - behaviorSettingsFactory.saveBehaviorSettings(false); - updatePresets(); - } - - private void savePrincessProperties() throws PrincessException { - BehaviorSettings tempBehavior = new BehaviorSettings(); - tempBehavior.setFallShameIndex(fallShameSlidebar.getValue()); - tempBehavior.setForcedWithdrawal(forcedWithdrawalCheck.isSelected()); - tempBehavior.setAutoFlee(autoFleeCheck.isSelected()); - tempBehavior.setDestinationEdge(fleeEdgeCombo.getSelectedItem()); - tempBehavior.setRetreatEdge(withdrawEdgeCombo.getSelectedItem()); - tempBehavior.setHyperAggressionIndex(aggressionSlidebar.getValue()); - tempBehavior.setSelfPreservationIndex(selfPreservationSlidebar.getValue()); - tempBehavior.setHerdMentalityIndex(herdingSlidebar.getValue()); - tempBehavior.setBraveryIndex(braverySlidebar.getValue()); - tempBehavior.setDescription(getAutoResolveBehaviorSettingName()); - autoResolveBehavior = tempBehavior; - campaign.setAutoResolveBehaviorSettings(tempBehavior); - savePreset(); - } - - private void writePreset(String name) { - BehaviorSettings newBehavior = new BehaviorSettings(); - try { - newBehavior.setDescription(name); - } catch (PrincessException e1) { - return; - } - newBehavior.setFallShameIndex(fallShameSlidebar.getValue()); - newBehavior.setHyperAggressionIndex(aggressionSlidebar.getValue()); - newBehavior.setSelfPreservationIndex(selfPreservationSlidebar.getValue()); - newBehavior.setHerdMentalityIndex(herdingSlidebar.getValue()); - newBehavior.setBraveryIndex(braverySlidebar.getValue()); - behaviorSettingsFactory.addBehavior(newBehavior); - behaviorSettingsFactory.saveBehaviorSettings(false); - } - - @Override - public void valueChanged(ListSelectionEvent event) { - if (event.getValueIsAdjusting()) { + // This should never happen, but if it does, it is not a critical error. + // We set the auto resolve behavior setting, ignore that its description + // could not be set, log the error and continue. + logger.error("Could not set description for auto resolve behavior settings", e); + campaign.setAutoResolveBehaviorSettings(autoResolveBehaviorSettings); return; } - if (event.getSource() == presetsList) { - presetSelected(); - } - } - - /** Shows a popup menu for a behavior preset, allowing to delete it. */ - private transient MouseListener presetsMouseListener = new MouseAdapter() { - - @Override - public void mouseReleased(MouseEvent e) { - int row = presetsList.locationToIndex(e.getPoint()); - if (e.isPopupTrigger() && (row != -1)) { - ScalingPopup popup = new ScalingPopup(); - String behavior = presetsList.getModel().getElementAt(row); - var deleteItem = new JMenuItem("Delete " + behavior); - deleteItem.addActionListener(event -> removePreset(behavior)); - popup.add(deleteItem); - popup.show(e.getComponent(), e.getX(), e.getY()); - } - } - - @Override - public void mouseClicked(MouseEvent evt) { - if (SwingUtilities.isLeftMouseButton(evt) && evt.getClickCount() == 1) { - presetSelected(); - } - } - }; - - /** - * Called when a Preset is selected. This will often be called twice when - * clicking with the mouse (by the listselectionlistener and the mouselistener). - * In this way the list will react when copying a Preset from another bot and - * then clicking the already selected Preset again. And it will also react to - * keyboard navigation. - */ - private void presetSelected() { - if (presetsList.isSelectionEmpty()) { - chosenPreset = null; - } else { - autoResolveBehavior = behaviorSettingsFactory.getBehavior(presetsList.getSelectedValue()); - chosenPreset = behaviorSettingsFactory.getBehavior(presetsList.getSelectedValue()); + behaviorSettingsFactory.addBehavior(autoResolveBehaviorSettings); + behaviorSettingsFactory.saveBehaviorSettings(false); - if (autoResolveBehavior == null) { - autoResolveBehavior = new BehaviorSettings(); - } - updatePresetFields(); - } - updateEnabledStates(); - } - - /** - * Sets up/Updates the displayed preset list (e.g. after adding or deleting a - * preset) - */ - private void updatePresets() { - presets = new ArrayList<>(Arrays.asList(behaviorSettingsFactory.getBehaviorNames())); - ((AutoResolveBehaviorSettingsDialog.PresetsModel) presetsList.getModel()).fireUpdate(); + campaign.setAutoResolveBehaviorSettings(autoResolveBehaviorSettings); } @Override - public void stateChanged(ChangeEvent e) { - updateEnabledStates(); - } - - private class PresetsModel extends DefaultListModel { - - @Override - public int getSize() { - return presets.size(); - } - - @Override - public String getElementAt(int index) { - return presets.get(index); - } - - /** Call when elements of the list change. */ - private void fireUpdate() { - fireContentsChanged(this, 0, getSize() - 1); - } + protected void okAction() { + super.okAction(); + updateBehaviorSettings(); } - /** - * A renderer for the Behavior Presets list. Adapts the font size to the gui - * scaling and - * colors the special list elements (other bot Configurations and original - * Config). - */ - private class PresetsRenderer extends DefaultListCellRenderer { - - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, - boolean cellHasFocus) { - Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - comp.setFont(UIUtil.getScaledFont()); - String preset = (String) value; - if (preset.startsWith(UIUtil.BOT_MARKER)) { - comp.setForeground(UIUtil.uiLightBlue()); - } - - if (preset.equals(Messages.getString("BotConfigDialog.previousConfig"))) { - comp.setForeground(UIUtil.uiGreen()); - } - - return comp; - } - } } diff --git a/MekHQ/src/mekhq/utilities/MoreObjects.java b/MekHQ/src/mekhq/utilities/MoreObjects.java deleted file mode 100644 index 088b361cad..0000000000 --- a/MekHQ/src/mekhq/utilities/MoreObjects.java +++ /dev/null @@ -1,12 +0,0 @@ -package mekhq.utilities; - -public class MoreObjects { - - private MoreObjects() { - } - - public static O firstNonNull(O first, O second) { - return first != null ? first : second; - } -} - From 0401e140d1d3ad5538a26ce4d5f39802333fb00d Mon Sep 17 00:00:00 2001 From: Coppio Date: Thu, 31 Oct 2024 14:03:28 -0300 Subject: [PATCH 3/8] feat: added help dialog for auto resolve, and injected the help button for the auto resolve in the dialog --- MekHQ/docs/help/en/AutoResolve.html | 4 +- ...toResolveBehaviorSettingsDialog.properties | 4 ++ MekHQ/src/mekhq/gui/CampaignGUI.java | 1 - .../AutoResolveBehaviorSettingsDialog.java | 54 ++++++++++++++++++- ...AutoResolveBehaviorSettingsHelpDialog.java | 32 +++++++++++ 5 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 MekHQ/resources/mekhq/resources/AutoResolveBehaviorSettingsDialog.properties create mode 100644 MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java diff --git a/MekHQ/docs/help/en/AutoResolve.html b/MekHQ/docs/help/en/AutoResolve.html index 6334aecc52..dc043348d6 100644 --- a/MekHQ/docs/help/en/AutoResolve.html +++ b/MekHQ/docs/help/en/AutoResolve.html @@ -13,7 +13,7 @@

Introduction

want to play every single lengthy battle.

How to Use

Auto Resolve is available as a new option in the Scenario panel, the button "Auto Resolve" will setup the game scenario - on MegaMek as normal, and then it adds a Princess bot with the name following the format :AI, it is setup + on MegaMek as normal, and then it adds a Princess bot with the name following the format YourCompany:AI, it is setup as the player is setup, same starting position, color, camouflage and team, and then all the players units change owner to it.

To make the game faster, you can change configuration to not show the reports between phases, and select the skip @@ -23,7 +23,7 @@

Configuring the Auto Resolve Beha

The configuration is accessible in the Manage Campaign menu, in the MekHQ toolbar, there is the entry Auto Resolve Behavior Settings, it will open the Configure Princess Bot window, where you can define the behavior for the auto resolve preset.

-

By default, every campaign now have an auto-resolve default preset, that is named :AI, you can change the +

By default, every campaign now have an auto-resolve default preset, that is named YourCompany:AI, you can change the configuration as you like and select whatever preset you want.

Whenever you hit the "OK" button the current configuration being shown overwrites the auto resolve preset for your company, so if you want to experiment with different behaviors, I suggest that you create new presets and then diff --git a/MekHQ/resources/mekhq/resources/AutoResolveBehaviorSettingsDialog.properties b/MekHQ/resources/mekhq/resources/AutoResolveBehaviorSettingsDialog.properties new file mode 100644 index 0000000000..bf4ced59a4 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/AutoResolveBehaviorSettingsDialog.properties @@ -0,0 +1,4 @@ +AutoResolveBehaviorSettingsDialog.title=Auto Resolve Help +AutoResolveBehaviorSettingsDialog.autoResolveHelpPath=docs/help/en/AutoResolve.html +AutoResolveBehaviorSettingsDialog.help=Auto Resolve Help +AutoResolveBehaviorSettingsDialog.helpTooltip=Open the Auto Resolve Help documentation in a new window \ No newline at end of file diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index 147fd8b65a..a23aa8fe87 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -1019,7 +1019,6 @@ private void initMenu() { .addActionListener(evt -> { var autoResolveBehaviorSettingsDialog = new AutoResolveBehaviorSettingsDialog(getFrame(), getCampaign()); autoResolveBehaviorSettingsDialog.setVisible(true); - autoResolveBehaviorSettingsDialog.setModal(true); autoResolveBehaviorSettingsDialog.pack(); }); diff --git a/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java b/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java index 38cfee7842..eb4ebeb4b0 100644 --- a/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java @@ -2,19 +2,30 @@ import megamek.client.bot.princess.BehaviorSettingsFactory; import megamek.client.bot.princess.PrincessException; +import megamek.client.ui.baseComponents.MMButton; import megamek.client.ui.dialogs.BotConfigDialog; import megamek.logging.MMLogger; +import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.gui.dialog.helpDialogs.AutoResolveBehaviorSettingsHelpDialog; + import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.ResourceBundle; public class AutoResolveBehaviorSettingsDialog extends BotConfigDialog { private final static MMLogger logger = MMLogger.create(AutoResolveBehaviorSettingsDialog.class); + private static final ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.AutoResolveBehaviorSettingsDialog", + MekHQ.getMHQOptions().getLocale()); + private Campaign campaign; private final BehaviorSettingsFactory behaviorSettingsFactory = BehaviorSettingsFactory.getInstance(); + private JButton autoResolveHelpButton; /** * Creates a new instance of AutoResolveBehaviorSettingsDialog. @@ -32,7 +43,38 @@ public AutoResolveBehaviorSettingsDialog(final JFrame frame, final Campaign camp setCampaign(campaign); } - public void setCampaign(final Campaign campaign) { + private JButton getAutoResolveHelpButton() { + return autoResolveHelpButton; + } + + private void setAutoResolveHelpButton(JButton autoResolveHelpButton) { + this.autoResolveHelpButton = autoResolveHelpButton; + } + + @Override + protected Container createCenterPane() { + var result = super.createCenterPane(); + // get last item in the result + + result.add(createAutoResolveHelpButton()); + return result; + } + + protected JPanel createAutoResolveHelpButton() { +// var result = new JPanel(new BorderLayout(5, 0)); + JPanel result = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); + result.setAlignmentX(LEFT_ALIGNMENT); + + setAutoResolveHelpButton(new MMButton("btnNewYear", + resourceMap.getString("AutoResolveBehaviorSettingsDialog.help"), + resourceMap.getString("AutoResolveBehaviorSettingsDialog.helpTooltip"), + this::autoResolveHelpActionPerformed)); + + result.add(getAutoResolveHelpButton()); + return result; + } + + private void setCampaign(final Campaign campaign) { this.campaign = campaign; } @@ -55,6 +97,16 @@ private void updateBehaviorSettings() { campaign.setAutoResolveBehaviorSettings(autoResolveBehaviorSettings); } + protected void autoResolveHelpActionPerformed(ActionEvent evt) { + showAutoResolveHelp(); + } + + private void showAutoResolveHelp() { + var autoResolveHelp = new AutoResolveBehaviorSettingsHelpDialog(getFrame()); + autoResolveHelp.setVisible(true); + autoResolveHelp.setAlwaysOnTop(true); + } + @Override protected void okAction() { super.okAction(); diff --git a/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java b/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java new file mode 100644 index 0000000000..57378a8c94 --- /dev/null +++ b/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java @@ -0,0 +1,32 @@ +package mekhq.gui.dialog.helpDialogs; + +import megamek.client.ui.dialogs.helpDialogs.AbstractHelpDialog; +import mekhq.MekHQ; + +import javax.swing.*; +import java.awt.*; +import java.util.ResourceBundle; + +public class AutoResolveBehaviorSettingsHelpDialog extends AbstractHelpDialog { + + private static final ResourceBundle resourceMap = ResourceBundle.getBundle( + "mekhq.resources.AutoResolveBehaviorSettingsDialog", + MekHQ.getMHQOptions().getLocale()); + + + /** + * Creates a new instance of AutoResolveBehaviorSettingsHelpDialog. + *

+ * This screen opens a help dialog, using the megamek help dialog, which open an HTML file + *

+ * @param frame parent frame + */ + public AutoResolveBehaviorSettingsHelpDialog(final JFrame frame) { + super(frame, resourceMap.getString("AutoResolveBehaviorSettingsDialog.title"), + resourceMap.getString("AutoResolveBehaviorSettingsDialog.autoResolveHelpPath")); + + setMinimumSize(new Dimension(400, 400)); + setModalExclusionType(ModalExclusionType.TOOLKIT_EXCLUDE); + } + +} From 804e23b8ead59cca50a8ab3cd76898b31ada5e90 Mon Sep 17 00:00:00 2001 From: Coppio Date: Thu, 31 Oct 2024 14:19:43 -0300 Subject: [PATCH 4/8] fix: redundant creation of a bot force --- MekHQ/src/mekhq/AtBGameThread.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/MekHQ/src/mekhq/AtBGameThread.java b/MekHQ/src/mekhq/AtBGameThread.java index 3f4f3ae32d..ad63c02cdf 100644 --- a/MekHQ/src/mekhq/AtBGameThread.java +++ b/MekHQ/src/mekhq/AtBGameThread.java @@ -469,8 +469,6 @@ public void run() { private void setupPlayerBotForAutoResolve(Player player) throws InterruptedException, PrincessException { var botName = player.getName() + ":AI"; - var autoResolveBot = new BotForce(); - autoResolveBot.setName(botName); Thread.sleep(MekHQ.getMHQOptions().getStartGameBotClientDelay()); var botClient = new Princess(botName, client.getHost(), client.getPort()); From d68f12b41122542992c2030cb8b00a68b595ca2f Mon Sep 17 00:00:00 2001 From: Coppio Date: Thu, 31 Oct 2024 14:43:45 -0300 Subject: [PATCH 5/8] fix: added a couple of javadocs, changed name of a couple of variables and deleted comments --- MekHQ/src/mekhq/AtBGameThread.java | 10 ++-- MekHQ/src/mekhq/GameThread.java | 57 ++++++++++++++++--- .../AutoResolveBehaviorSettingsDialog.java | 3 - 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/MekHQ/src/mekhq/AtBGameThread.java b/MekHQ/src/mekhq/AtBGameThread.java index ad63c02cdf..23a09b7c89 100644 --- a/MekHQ/src/mekhq/AtBGameThread.java +++ b/MekHQ/src/mekhq/AtBGameThread.java @@ -67,20 +67,20 @@ public class AtBGameThread extends GameThread { * * @param name The name of the player * @param password The password for the game - * @param c The client + * @param client The client * @param app The MekHQ application * @param units The list of units to import into the game * @param scenario The scenario to use for this game * @param autoResolveBehaviorSettings The behavior settings for the auto resolve bot */ - public AtBGameThread(String name, String password, Client c, MekHQ app, List units, + public AtBGameThread(String name, String password, Client client, MekHQ app, List units, AtBScenario scenario, @Nullable BehaviorSettings autoResolveBehaviorSettings) { - this(name, password, c, app, units, scenario, autoResolveBehaviorSettings, true); + this(name, password, client, app, units, scenario, autoResolveBehaviorSettings, true); } - public AtBGameThread(String name, String password, Client c, MekHQ app, List units, + public AtBGameThread(String name, String password, Client client, MekHQ app, List units, AtBScenario scenario, @Nullable BehaviorSettings autoResolveBehaviorSettings, boolean started) { - super(name, password, c, app, units, scenario, started); + super(name, password, client, app, units, scenario, started); this.scenario = Objects.requireNonNull(scenario); this.autoResolveBehaviorSettings = autoResolveBehaviorSettings; } diff --git a/MekHQ/src/mekhq/GameThread.java b/MekHQ/src/mekhq/GameThread.java index 86909aec72..3d21df4915 100644 --- a/MekHQ/src/mekhq/GameThread.java +++ b/MekHQ/src/mekhq/GameThread.java @@ -68,25 +68,68 @@ class GameThread extends Thread implements CloseClientListener { // endregion Variable Declarations // region Constructors - public GameThread(String name, String password, Client c, MekHQ app, List units, Scenario s) { - this(name, password, c, app, units, s, true); + + /** + * GameThread + *

+ * Initializes a new thread for a game. + *

+ * + * @param name The player name + * @param password The game password + * @param client The client + * @param app The MekHQ instance + * @param units The list of units you intend to play with in your side + * @param scenario The scenario that is going to be initialized for the game + */ + public GameThread(String name, String password, Client client, MekHQ app, List units, Scenario scenario) { + this(name, password, client, app, units, scenario, true); } - public GameThread(String name, Client c, MekHQ app, List units, Scenario s, boolean started) { - this(name, "", c, app, units, s, started); + + /** + * GameThread + *

+ * Initializes a new thread for a game. + *

+ * + * @param name The player name + * @param client The client + * @param app The MekHQ instance + * @param units The list of units you intend to play with in your side + * @param scenario The scenario that is going to be initialized for the game + * @param started Whether the game has already started + */ + public GameThread(String name, Client client, MekHQ app, List units, Scenario scenario, boolean started) { + this(name, "", client, app, units, scenario, started); } - public GameThread(String name, String password, Client c, MekHQ app, List units, Scenario s, + + /** + * GameThread + *

+ * Initializes a new thread for a game. + *

+ * + * @param name The player name + * @param password The game password + * @param client The client + * @param app The MekHQ instance + * @param units The list of units you intend to play with in your side + * @param scenario The scenario that is going to be initialized for the game + * @param started Whether the game has already started + */ + public GameThread(String name, String password, Client client, MekHQ app, List units, Scenario scenario, boolean started) { super(name); myname = name.trim(); this.password = password; - this.client = c; + this.client = client; this.app = app; this.units = Objects.requireNonNull(units); this.started = started; this.campaign = app.getCampaign(); - this.scenario = Objects.requireNonNull(s); + this.scenario = Objects.requireNonNull(scenario); } // endregion Constructors diff --git a/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java b/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java index eb4ebeb4b0..1b5c27bf19 100644 --- a/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/AutoResolveBehaviorSettingsDialog.java @@ -54,14 +54,11 @@ private void setAutoResolveHelpButton(JButton autoResolveHelpButton) { @Override protected Container createCenterPane() { var result = super.createCenterPane(); - // get last item in the result - result.add(createAutoResolveHelpButton()); return result; } protected JPanel createAutoResolveHelpButton() { -// var result = new JPanel(new BorderLayout(5, 0)); JPanel result = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); result.setAlignmentX(LEFT_ALIGNMENT); From 48cc7d9a09f4842ec49d332c2ebede0c9cf990fa Mon Sep 17 00:00:00 2001 From: Luana Coppio Date: Tue, 5 Nov 2024 10:18:27 -0300 Subject: [PATCH 6/8] Update AutoResolve.html Added information on current limitations on the auto-resolve --- MekHQ/docs/help/en/AutoResolve.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MekHQ/docs/help/en/AutoResolve.html b/MekHQ/docs/help/en/AutoResolve.html index dc043348d6..0ffa08a74b 100644 --- a/MekHQ/docs/help/en/AutoResolve.html +++ b/MekHQ/docs/help/en/AutoResolve.html @@ -29,6 +29,11 @@

Configuring the Auto Resolve Beha for your company, so if you want to experiment with different behaviors, I suggest that you create new presets and then just select the one you want before hitting "OK" to close the configuration.

Q&A

+

Q: What are its current limitations?

+

Unfortunatelly Princess, being part of an honorable bloodline, she will abide by the Ares Convention and won's ever shoot at + retreating units, this means that any scenario to stop enemies from reaching the other side of the map will fail, and every mission + where she has to reach the other side of the map will succeed, both without firing a single shot. She is also resource heavy, so + any scenario with too many VTOLs or Hovertanks or too many units overall can be slugsh.

Q: Can I change the configuration during the game?

Sure, its a Princess Bot, so all chat commands to change behavior apply

Q: Can I change the configuration for a specific scenario?

@@ -51,4 +56,4 @@

Q&A

is a preset that you are telling MekHQ to use, not a campaign specific configuration.

- \ No newline at end of file + From c8e7f554d3d2f4e42f8413121e7c844e57929ade Mon Sep 17 00:00:00 2001 From: Luana Coppio Date: Mon, 18 Nov 2024 14:16:47 -0300 Subject: [PATCH 7/8] Update AutoResolveBehaviorSettingsHelpDialog.java --- .../helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java b/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java index 57378a8c94..3a4d661813 100644 --- a/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java @@ -16,9 +16,7 @@ public class AutoResolveBehaviorSettingsHelpDialog extends AbstractHelpDialog { /** * Creates a new instance of AutoResolveBehaviorSettingsHelpDialog. - *

* This screen opens a help dialog, using the megamek help dialog, which open an HTML file - *

* @param frame parent frame */ public AutoResolveBehaviorSettingsHelpDialog(final JFrame frame) { From c993836b0c754149a980ff722ae260904039a5ed Mon Sep 17 00:00:00 2001 From: Luana Coppio Date: Tue, 19 Nov 2024 11:21:30 -0300 Subject: [PATCH 8/8] Update AutoResolveBehaviorSettingsHelpDialog.java --- MekHQ/src/mekhq/MekHQ.java | 2 +- MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java | 6 +++--- .../helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/MekHQ/src/mekhq/MekHQ.java b/MekHQ/src/mekhq/MekHQ.java index ed49457617..7689c4b838 100644 --- a/MekHQ/src/mekhq/MekHQ.java +++ b/MekHQ/src/mekhq/MekHQ.java @@ -517,7 +517,7 @@ public void gamePhaseChange(GamePhaseChangeEvent e) { } @Override - public void gameVictory(GameVictoryEvent gve) { + public void gameVictory(PostGameResolution gve) { // Prevent double run if (gameThread.stopRequested()) { return; diff --git a/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java b/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java index 5f4f24c05e..ee8be3e740 100644 --- a/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java +++ b/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java @@ -24,7 +24,7 @@ import megamek.client.Client; import megamek.common.*; import megamek.common.annotations.Nullable; -import megamek.common.event.GameVictoryEvent; +import megamek.common.event.PostGameResolution; import megamek.common.loaders.EntityLoadingException; import megamek.common.options.OptionsConstants; import megamek.logging.MMLogger; @@ -91,7 +91,7 @@ public class ResolveScenarioTracker { Optional unitList = Optional.empty(); Client client; Boolean control; - private GameVictoryEvent victoryEvent; + private PostGameResolution victoryEvent; private static final MMLogger logger = MMLogger.create(ResolveScenarioTracker.class); @@ -2142,7 +2142,7 @@ public boolean isLikelyCaptured() { } } - public void setEvent(GameVictoryEvent gve) { + public void setEvent(PostGameResolution gve) { victoryEvent = gve; } diff --git a/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java b/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java index 3a4d661813..72e19ffbc3 100644 --- a/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/helpDialogs/AutoResolveBehaviorSettingsHelpDialog.java @@ -13,7 +13,6 @@ public class AutoResolveBehaviorSettingsHelpDialog extends AbstractHelpDialog { "mekhq.resources.AutoResolveBehaviorSettingsDialog", MekHQ.getMHQOptions().getLocale()); - /** * Creates a new instance of AutoResolveBehaviorSettingsHelpDialog. * This screen opens a help dialog, using the megamek help dialog, which open an HTML file