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 @@ + + +
+ +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.
+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
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.
+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
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: 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.
++ * 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+ * 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