Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read/Write AS/SBF units #5249

Merged
merged 18 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions megamek/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ sourceSets {

dependencies {
implementation 'com.fasterxml.jackson.core:jackson-core:2.14.2'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.2'
implementation 'com.formdev:flatlaf:3.0'
implementation 'com.github.ikkisoft:SerialKiller:master-SNAPSHOT'
implementation 'com.sun.mail:jakarta.mail:2.0.1'
Expand Down
17 changes: 17 additions & 0 deletions megamek/docs/MMU Files/Annihilator ANH-3EX.mmu
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# This is an example MMU file for a single Alpha Strike element
# This file uses YAML formatting. Always leave a space behind the colon
# The Annihilator 3EX is not a canon unit and it is not converted from a Total Warfare unit
# Values that have a default may be omitted
# TMM, Threshold and PV are calculated from the given values
type: ASElement
chassis: Annihilator
model: ANH-3EX # default: empty model text ("")
skill: 5 # default: 4
astype: BM
size: 4
role: Brawler # default: Undetermined
move: 6/4j # never write the inch (") character
damage: 5/3/0 # default: no damage
armor: 12
structure: 10
specials: AC2/2/0, CASE # default: no specials
71 changes: 71 additions & 0 deletions megamek/docs/MMU Files/Example SBF Formation.mmu
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Example full SBF formation

# Most elements are canon units and need not list stats; these are converted from the unit cache
# The two tanks that are not canon have their stats listed, so this MMU file can be loaded without any
# outside information (other files)

# Note that in nested objects, consistent indentation and hyphens are important (YAML formatting)
type: SBFFormation
generalname: Test Formation
units:
- type: SBFUnit
generalname: Command Lance
elements:
- type: ASElement
fullname: Rakshasa MDG-1A
- type: ASElement
fullname: Rifleman RFL-6D
- type: ASElement
fullname: Thunderbolt TDR-9NAIS
- type: SBFUnit
generalname: Strike Lance
elements:
- type: ASElement
fullname: Enforcer III ENF-6M
- type: ASElement
fullname: Shadow Hawk SHD-5D
skill: 5
- type: ASElement
fullname: Hatchetman HCT-6D
- type: ASElement
fullname: Fennec FEC-3C
- type: SBFUnit
generalname: Fire Lance
elements:
- type: ASElement
fullname: Valkyrie VLK-QD1
skill: 3
- type: ASElement
fullname: Osiris OSR-5D
- type: ASElement
fullname: Garm GRM-01A2
- type: ASElement
fullname: Javelin JVN-11A 'Fire Javelin'
- type: SBFUnit
generalname: Tank Lance
elements:
- type: ASElement
fullname: Alacorn Heavy Tank Mk III
- type: ASElement
fullname: Ajax Assault Tank A
skill: 5
- type: ASElement
chassis: Manticore Heavy Tank
model: C-Ph Mk II
astype: CV
size: 3
move: 8t
damage: 5/5/2
armor: 7
structure: 3
specials: "ARS, CASE, CR, IF1, SRCH, SRM1/1, TUR(4/4/2, IF1)"
- type: ASElement
chassis: Manticore Heavy Tank
model: C-Ph Mk II
astype: CV
size: 3
move: 8t
damage: 5/5/2
armor: 7
structure: 3
specials: "ARS, CASE, CR, IF1, SRCH, SRM1/1, TUR(4/4/2, IF1)"
17 changes: 17 additions & 0 deletions megamek/docs/MMU Files/Example SBF Unit.mmu
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# This is an example MMU file for a single SBF Unit
# This file uses YAML formatting. Always leave a space behind the colon
# In most cases, surrounding quotes can be omitted
# This Strategic BattleForce Unit does not list the elements it is built from;
# therefore its values must all be given. It can be used to build a SBF formation
type: "SBFUnit"
generalname: "Heavy Mek Unit"
sbftype: "BM"
size: 3
tmm: 1
move: 4
movemode: "MEK_WALK"
jump: 1
armor: 19
damage: "5/7/5"
specials: "IF4"
pv: 69
35 changes: 35 additions & 0 deletions megamek/docs/MMU Files/Example unit list.mmu
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Example MMU unit list; when listing multiple units of any type side-by-side, each list element
# must be preceded by a hyphen as shown here; also the indentation is important.
# Any number of spaces (no TABs) can be used but it must be consistent.
# Blank lines are optional and have no effect
- type: ASElement
fullname: Atlas AS7-D # This is a canon unit; no stats need be given
# The fullname must be chassis <space> model

- type: ASElement
fullname: Ontos Heavy Tank # This is also a canon unit; the stats are converted from the unit cache
skill: 5 # This element will get a skill of 5 instead of the default 4

- type: ASElement
chassis: Manticore Heavy Tank # This is not a canon unit; therefore its stats must be given
model: C-Ph Mk II
astype: CV
size: 3
move: 8t
damage: 5/5/2
armor: 7
structure: 3
specials: "ARS, CASE, CR, IF1, SRCH, SRM1/1, TUR(4/4/2, IF1)"

- type: "SBFUnit" # Different types of unit or object can be mixed in a file
generalname: "Heavy Mek Unit"
sbftype: "BM"
size: 3
tmm: 1
move: 4
movemode: "MEK_WALK"
jump: 1
armor: 19
damage: "5/7/5"
specials: "IF4"
pv: 69
15 changes: 15 additions & 0 deletions megamek/docs/MMU Files/Gray Death Standard Suit.mmu
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This is an example MMU file for a single Alpha Strike element
# This file uses YAML formatting. Always leave a space behind the colon
# This BA is a canon unit and could be converted from a Total Warfare unit
type: ASElement
chassis: Gray Death Standard Suit
model: "[Laser](Sqd4)" # square brackets must be enclosed in quotes
astype: BA
size: 1
role: Ambusher
move: 6f
damage: 2/0/0
armor: 1
structure: 2
specials: "AM, CAR4, MEC, RCN"
squadsize: 4 # only on BA; optional (only used to display the squad size on the AS Card)
75 changes: 75 additions & 0 deletions megamek/docs/MMU Files/Large Aero AS elements.mmu
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Example large aerospace Alpha Strike elements
# These are canon units and would not normally require stats to be given at all; this is only to show how to define them

# These use the keywords nose, aft and side instead of damage
# The arcs use CAP, SCAP and MSL and also list the arc specials
# Station-keeping movement is represented without "0.", e.g. "2k"

# Quotes can be omitted in most cases (YAML formatting)
- type: "ASElement"
chassis: "DropShuttle"
model: "K-1"
astype: "SC"
size: 1
role: "None"
move: "4p"
nose: "2/2/0*/-"
aft: "1/1/-/-, ENE, PNT1"
side: "0*/0*/-/-, ENE"
armor: 12
structure: 3
specials: "CT52-D2, LECM, LG, SPC, VSTOL"

- type: "ASElement"
chassis: "Aurora"
model: "(Gunship)"
astype: "DA"
size: 1
role: "None"
move: "4a"
nose: "2/3/2/-"
aft: "3/3/2/-"
side: "5/7/6/-, PNT1"
armor: 20
structure: 4
specials: "AT2-D2, CT188-D3, LG, SPC, VSTOL"

- type: "ASElement"
chassis: "Merchant Jumpship"
model: "(2602)"
astype: "JS"
size: 2
move: "2k"
nose: "ENE"
aft: "ENE"
side: "ENE"
armor: 9
structure: 1
specials: "CT657-D2, DT2, KF, SPC, ST2-D2"

- type: "ASElement"
chassis: "Corone Destroyer"
model: "(2915)"
astype: "WS"
size: 1
role: "None"
move: "3"
nose: "5/5/5/-, CAP26/26/26/5"
aft: "1/1/1/-, CAP10/10/10/-"
side: "3/3/3/-, CAP21/21/21/3"
armor: 109
structure: 47
specials: "AT20-D3, CK36-D1, CRW2, DT4, HPG, KF, LF, MT45-D2, SPC"

- type: "ASElement"
chassis: "Olympus Recharge Station"
model: "(2942)"
astype: "SS"
size: 3
move: "2k"
nose: "22/22/10/3, PNT4"
aft: "22/22/10/3, PNT4"
side: "11/11/5/2, PNT2"
armor: 28
structure: 1
specials: "AT10-D2, CK943-D1, CRW3, DT4, SPC, ST10-D2"
36 changes: 36 additions & 0 deletions megamek/src/megamek/client/ui/dialogs/ASStatsDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,19 @@
import megamek.common.alphaStrike.AlphaStrikeHelper;
import megamek.common.alphaStrike.cardDrawer.ASCardPrinter;
import megamek.common.alphaStrike.conversion.ASConverter;
import megamek.common.jacksonadapters.MMUWriter;

import java.util.List;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;

/**
* This non-modal dialog shows stats of one or more AlphaStrike elements in the form of a table.
Expand All @@ -47,6 +53,7 @@ public class ASStatsDialog extends AbstractDialog {
private final JButton clipBoardButton = new JButton(Messages.getString("CASCardPanel.copyCard"));
private final JButton copyStatsButton = new JButton(Messages.getString("CASCardPanel.copyStats"));
private final JButton printButton = new JButton(Messages.getString("CASCardPanel.printCard"));
private final JButton saveButton = new JButton(Messages.getString("Save.text"));
private final JScrollPane scrollPane = new JScrollPane();
private final JPanel centerPanel = new JPanel();
private ASStatsTablePanel tablePanel;
Expand Down Expand Up @@ -77,6 +84,9 @@ protected Container createCenterPane() {
optionsPanel.add(clipBoardButton);
optionsPanel.add(copyStatsButton);
optionsPanel.add(printButton);
optionsPanel.add(saveButton);
saveButton.addActionListener(e -> save());
saveButton.setFont(UIUtil.getScaledFont());
clipBoardButton.addActionListener(e -> copyToClipboard());
copyStatsButton.addActionListener(e -> copyStats());
printButton.addActionListener(ev -> printCards());
Expand Down Expand Up @@ -161,6 +171,32 @@ private StringBuilder dataLine(AlphaStrikeElement element) {
return dataLine;
}

private void save() {
List<AlphaStrikeElement> elements = entities.stream().filter(ASConverter::canConvert)
.map(e -> ASConverter.convert(e, true))
.collect(Collectors.toList());
if (elements.isEmpty()) {
return;
}
var fileChooser = new JFileChooser(".");
fileChooser.setDialogTitle(Messages.getString("Save.text"));
fileChooser.setFileFilter(new FileNameExtensionFilter("MUL files", "mmu"));
fileChooser.setSelectedFile(new File(elements.get(0).generalName() + ".mmu"));
int returnVal = fileChooser.showSaveDialog(getParent());
if ((returnVal != JFileChooser.APPROVE_OPTION) || (fileChooser.getSelectedFile() == null)) {
return;
}

File unitFile = fileChooser.getSelectedFile();

try {
new MMUWriter().writeMMUFileFullStats(unitFile, elements);
} catch (IOException | IllegalArgumentException e) {
JOptionPane.showMessageDialog(getParent(), "The MMU file could not be written. "
+ e.getMessage());
}
}

private void adaptToGUIScale() {
UIUtil.adjustDialog(this, UIUtil.FONT_SCALE1);
}
Expand Down
34 changes: 33 additions & 1 deletion megamek/src/megamek/client/ui/dialogs/SBFStatsDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,23 @@
import megamek.codeUtilities.StringUtility;
import megamek.common.Game;
import megamek.common.force.Force;
import megamek.common.jacksonadapters.MMUWriter;
import megamek.common.strategicBattleSystems.SBFFormation;
import megamek.common.strategicBattleSystems.SBFFormationConverter;
import megamek.common.strategicBattleSystems.SBFRecordSheetBook;
import megamek.common.strategicBattleSystems.SBFUnit;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Vector;
import java.util.stream.Collectors;
Expand All @@ -54,9 +59,10 @@ public class SBFStatsDialog extends AbstractDialog {

private final Collection<Force> forceList;
private final Game game;
private Collection<SBFFormation> formations;
private List<SBFFormation> formations;
private final MMToggleButton elementsToggle = new MMToggleButton(Messages.getString("SBFStatsDialog.showElements"));
private final JButton clipBoardButton = new JButton(Messages.getString("SBFStatsDialog.copy"));
private final JButton saveButton = new JButton(Messages.getString("Save.text"));
private final JButton printButton = new JButton(Messages.getString("SBFStatsDialog.print"));
private final JLabel headerFontLabel = new JLabel(Messages.getString("SBFStatsDialog.headerFont"));
private JComboBox<String> headerFontChooser;
Expand Down Expand Up @@ -92,6 +98,7 @@ protected Container createCenterPane() {
optionsPanel.add(Box.createHorizontalStrut(25));
optionsPanel.add(elementsToggle);
optionsPanel.add(clipBoardButton);
optionsPanel.add(saveButton);

headerFontChooser = new JComboBox<>(new Vector<>(FontHandler.getAvailableNonSymbolFonts()));
headerFontChooser.addItem("");
Expand All @@ -116,6 +123,8 @@ protected Container createCenterPane() {
elementsToggle.setFont(UIUtil.getScaledFont());
clipBoardButton.addActionListener(e -> copyToClipboard());
clipBoardButton.setFont(UIUtil.getScaledFont());
saveButton.addActionListener(e -> save());
saveButton.setFont(UIUtil.getScaledFont());
printButton.addActionListener(e -> printRecordSheets());
printButton.setFont(UIUtil.getScaledFont());
headerFontLabel.setFont(UIUtil.getScaledFont());
Expand Down Expand Up @@ -177,6 +186,29 @@ private void copyToClipboard() {
clipboard.setContents(stringSelection, null);
}

private void save() {
if (formations.isEmpty()) {
return;
}
var fileChooser = new JFileChooser(".");
fileChooser.setDialogTitle(Messages.getString("Save.text"));
fileChooser.setFileFilter(new FileNameExtensionFilter("MUL files", "mmu"));
fileChooser.setSelectedFile(new File(formations.get(0).generalName() + ".mmu"));
int returnVal = fileChooser.showSaveDialog(getParent());
if ((returnVal != JFileChooser.APPROVE_OPTION) || (fileChooser.getSelectedFile() == null)) {
return;
}

File unitFile = fileChooser.getSelectedFile();

try {
new MMUWriter().writeMMUFile(unitFile, formations);
} catch (IOException | IllegalArgumentException e) {
JOptionPane.showMessageDialog(getParent(), "The MMU file could not be written. "
+ e.getMessage());
}
}

private String clipboardString(Collection<SBFFormation> formations) {
StringBuilder result = new StringBuilder();
result.append("SBF Formation").append(COLUMN_SEPARATOR);
Expand Down
Loading
Loading