Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6ffa15a
Add Battlefield Support Asset editing and printing
pavelbraginskiy Jul 15, 2026
b592fd9
Use matching mm-data branch in PR CI
pavelbraginskiy Jul 16, 2026
00cd6ac
Address BFS review feedback
pavelbraginskiy Jul 16, 2026
4f7bb1a
Hide Asset overlay in neutral sprite previews
pavelbraginskiy Jul 16, 2026
9ab0270
Merge branch 'main' into bfs-assets
exeea Jul 18, 2026
4f47c98
Merge branch 'main' into bfs-assets
exeea Jul 19, 2026
b71e5b6
Merge remote-tracking branch 'origin/main' into bfs-assets
pavelbraginskiy Jul 21, 2026
2dc138a
Merge remote-tracking branch 'origin/main' into bfs-assets
pavelbraginskiy Jul 23, 2026
b64dd6a
Handle linked assets inside zip archives
pavelbraginskiy Jul 23, 2026
5d14f9c
Harden Battlefield Support editor refresh
pavelbraginskiy Jul 23, 2026
764224e
Preserve dirty state when linked Asset save fails
pavelbraginskiy Jul 24, 2026
56d4120
safer check for matching data branch retrieval
Jul 26, 2026
3548b41
bookmark fix for multipage print of BFS assets
Jul 26, 2026
19d7773
Use record sheet font for Battlefield Support cards
pavelbraginskiy Jul 26, 2026
30f4bc7
Merge fork/bfs-assets into bfs-assets
pavelbraginskiy Jul 26, 2026
41955cb
handle tabs recovery and unit memento
Jul 26, 2026
5b1f4b7
Merge branch 'bfs-assets' of https://github.com/pavelbraginskiy/megam…
Jul 26, 2026
6162709
handle load of bfs when cache is invalid
Jul 26, 2026
1bbc815
Fix Battlefield Support printing review issues
pavelbraginskiy Jul 27, 2026
a2321c0
Merge remote-tracking branch 'origin/main' into bfs-assets
pavelbraginskiy Aug 2, 2026
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
18 changes: 17 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,26 @@ jobs:
fail-fast: false

steps:
- name: Find the Right Data Branch
id: find_data
shell: bash {0}
run: |
if git ls-remote --exit-code --heads \
${{ github.event.pull_request.head.repo.owner.html_url }}/mm-data.git \
${{ github.event.pull_request.head.ref }} > /dev/null 2>&1
then
echo "dataRepo=${{ github.event.pull_request.head.repo.owner.login }}/mm-data" >> $GITHUB_OUTPUT
echo "dataBranch=${{ github.event.pull_request.head.ref }}" >> $GITHUB_OUTPUT
else
echo "dataRepo=MegaMek/mm-data" >> $GITHUB_OUTPUT
echo "dataBranch=main" >> $GITHUB_OUTPUT
fi

- name: Checkout Data Repo
uses: actions/checkout@v7
with:
repository: megamek/mm-data
repository: ${{ steps.find_data.outputs.dataRepo }}
ref: ${{ steps.find_data.outputs.dataBranch }}
path: mm-data

- name: Checking out MegaMekLab
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ PrintQueueDialog.Export.text=Export Record Sheets
PrintQueueDialog.Export.toolTipText=Export record sheets for the selected units
PrintQueueDialog.Cancel.text=Cancel
PrintQueueDialog.Cancel.toolTipText=Cancel without printing
PrintQueueDialog.assetTag=[BFS Asset]
ForceBuildDialog.windowName.text=Force Builder
ForceBuildDialog.cell.C3=C3:
ForceBuildDialog.cell.c3i=c3i:
Expand Down
94 changes: 93 additions & 1 deletion megameklab/resources/megameklab/resources/Views.properties
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
# Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
# InMediaRes Productions, LLC.
#
# MechWarrior Copyright Microsoft Corporation. MegaMek was created under
# MechWarrior Copyright Microsoft Corporation. MegaMekLab was created under
# Microsoft's "Game Content Usage Rules"
# <https://www.xbox.com/en-US/developers/rules> and it is not endorsed by or
# affiliated with Microsoft.
Expand Down Expand Up @@ -63,6 +63,98 @@ BasicInfoView.txtManualBV.tooltip=Alternative to the calculated Battle Value.
BasicInfoView.cbTechBase.values=Inner Sphere,Clan,Mixed Inner Sphere,Mixed Clan
BasicInfoView.txtRole.text=Role:
BasicInfoView.txtRole.tooltip=The combat role of this unit.
BasicInfoView.battlefieldSupportAsset.text=Battlefield Support Asset:
BasicInfoView.battlefieldSupportAsset.tooltip=Whether this unit has a linked Battlefield Support Asset. When checked, an Asset tab appears for editing the asset's stats and card.
BattlefieldSupportCardListPanel.empty.text=No Battlefield Support Assets in the current selection.
BFSStatusBar.addToForce.text=Add to Force
BFSStatusBar.addToForce.tooltip=Add this asset to the current force.
BFSStatusBar.refresh.text=Refresh UI
BFSStatusBar.refresh.tooltip=Refresh the UI, possibly repairing it if it is in a broken state.
BFSStatusBar.cost.text=Cost: %s BV / %s BSP
BFSStatusBar.cost.tooltip=The asset's base (non-veteran) cost, in Battle Value and Battlefield Support Points (1 BSP = 20 BV).
BFSLinkedFiles.delete.message=This unit no longer has a Battlefield Support Asset, but an asset data file still exists:\n%s\n\nDelete this file?
BFSLinkedFiles.delete.title=Remove Asset Data
BFSLinkedFiles.deleteFailed.message=Could not delete %s
BFSLinkedFiles.deleteFailed.title=Delete Failed
BFSStructureTab.source.tooltip=The publication(s) this asset appears in.
BFSStructureTab.veteran.text=Veteran variant
BFSStructureTab.movement.title=Movement
BFSStructureTab.combat.title=Combat
BFSStructureTab.damage.text=Damage (per hit x hits):
BFSStructureTab.range.text=Range (S/M/L):
BFSStructureTab.skill.text=Skill:
BFSStructureTab.veteranSkill.text=Veteran skill:
BFSStructureTab.destroyCheck.text=Destroy check:
BFSStructureTab.threshold.text=Threshold:
BFSStructureTab.cost.title=Cost
BFSStructureTab.cost.text=Cost (BSP):
BFSStructureTab.veteranCost.text=Veteran cost (BSP):
BFSStructureTab.battleValue.text=In Battle Value:
BFSStructureTab.cardOverrides.title=Card Overrides
BFSStructureTab.cardTitle.text=Card title (optional):
BFSStructureTab.cardSubtitle.text=Card subtitle (optional):
BFSStructureTab.mp.text=MP:
BFSStructureTab.tmm.text=TMM:
BFSStructureTab.identity.title=Identity
BFSStructureTab.chassis.text=Chassis:
BFSStructureTab.model.text=Model:
BFSStructureTab.introYear.text=Intro year:
BFSStructureTab.techBase.text=Tech base:
BFSStructureTab.source.text=Source:
BFSStructureTab.assetType.text=Asset type:
BFSStructureTab.role.text=Role:
BFSStructureTab.mode.text=Mode:
BFSStructureTab.specials.title=Specials
BFSStructureTab.removeSelected.text=Remove Selected
BFSStructureTab.add.text=Add
BFSStructureTab.addCustom.text=Add Custom
BFSStructureTab.art.title=Art
BFSStructureTab.fluffFile.text=Fluff from File...
BFSStructureTab.fluffCache.text=Fluff from Cache...
BFSStructureTab.clearFluff.text=Clear Fluff
BFSStructureTab.spriteFile.text=Sprite from File...
BFSStructureTab.spriteCache.text=Sprite from Cache...
BFSStructureTab.clearSprite.text=Clear Sprite
BFSStructureTab.spritePreview.text=Sprite preview:
BFSStructureTab.motive.foot=Foot
BFSStructureTab.motive.jump=Jump
BFSStructureTab.motive.motorized=Motorized
BFSStructureTab.motive.mechanized=Mechanized (%s)
BFSStructureTab.bv.text=BV %s
BFSStructureTab.veteranBv.text=\ (Veteran BV %s)
BFSStructureTab.invalidArtillery.message=Artillery must be one of Long Tom (LT), Sniper (S) or Thumper (T).
BFSStructureTab.invalidArtillery.title=Invalid Artillery
BFSStructureTab.missingValue.message=The %s special requires a number (for example %s2).
BFSStructureTab.missingValue.title=Missing Value
BFSStructureTab.immobileRequired.message=Emplacements are required to keep the Immobile special.
BFSStructureTab.immobileRequired.title=Cannot Remove Special
BFSStructureTab.assetType.VEHICLE=Vehicle
BFSStructureTab.assetType.CONV_INFANTRY=Conventional Infantry
BFSStructureTab.assetType.BATTLE_ARMOR=Battle Armor
BFSStructureTab.assetType.EMPLACEMENT=Emplacement
BFSStructureTab.artillery.THUMPER=Thumper
BFSStructureTab.artillery.SNIPER=Sniper
BFSStructureTab.artillery.LONG_TOM=Long Tom
BFSStructureTab.special.NO_TURRET=No Turret
BFSStructureTab.special.NIMBLE=Nimble
BFSStructureTab.special.IMMOBILE=Immobile
BFSStructureTab.special.INDIRECT_FIRE=Indirect Fire
BFSStructureTab.special.SPOTTER=Spotter
BFSStructureTab.special.TAG=Target Acquisition Gear
BFSStructureTab.special.COMMANDER=Commander
BFSStructureTab.special.APC=Armored Personnel Carrier
BFSStructureTab.special.AMS=Anti-Missile System
BFSStructureTab.special.ARTILLERY=Artillery
BFSStructureTab.special.SWARM=Swarm
BFSStructureTab.special.ARROW_IV=Arrow IV
BFSStructureTab.special.ECM=Electronic Countermeasures
BFSStructureTab.special.TARGETING_COMPUTER=Targeting Computer
BFSStructureTab.special.ANTI_INFANTRY=Anti-Infantry
BFSStructureTab.special.CRIT_SEEKER=Crit-Seeker
BFSStructureTab.special.MASH=Mobile Army Surgical Hospital
BFSStructureTab.special.MECHANIZED=Mechanized
BFSStructureTab.special.PROBE=Active Probe
BFSStructureTab.special.REFLECTIVE_ARMOR=Reflective Armor
MekChassisView.baseType.values=Standard,Industrial,LAM,QuadVee
MekChassisView.motiveType.values=Biped,Quad,Tripod
MekChassisView.lamType.values=Standard,Bimodal
Expand Down
25 changes: 24 additions & 1 deletion megameklab/src/megameklab/MegaMekLab.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,17 @@
import megamek.SuiteConstants;
import megamek.client.ui.clientGUI.GUIPreferences;
import megamek.client.ui.dialogs.LicensingDialog;
import megamek.client.ui.panels.battlefieldSupport.ConfigurableBFSCardPanel;
import megamek.client.ui.preferences.SuitePreferences;
import megamek.client.ui.util.UIUtil;
import megamek.common.battlefieldSupport.cardDrawer.BattlefieldSupportCard;
import megamek.common.equipment.EquipmentType;
import megamek.common.loaders.MekFileParser;
import megamek.common.loaders.MekSummaryCache;
import megamek.common.net.marshalling.SanityInputFilter;
import megamek.common.units.Entity;
import megamek.logging.MMLogger;
import megameklab.printing.RecordSheetOptions;
import megameklab.ui.MegaMekLabTabbedUI;
import megameklab.ui.PopupMessages;
import megameklab.ui.StartupGUI;
Expand Down Expand Up @@ -221,6 +224,7 @@ private static void startup(String[] args) {
MekSummaryCache.getInstance();
CConfig.load();
UnitUtil.loadFonts();
registerCardDefaults();

MegaMek.getMMPreferences().loadFromFile(SuiteConstants.MM_PREFERENCES_FILE);
getMMLPreferences().loadFromFile(SuiteConstants.MML_PREFERENCES_FILE);
Expand Down Expand Up @@ -274,6 +278,24 @@ private static void setLookAndFeel() {
}
}

/**
* Registers the record-sheet-derived font and color defaults used by every {@link ConfigurableBFSCardPanel} created
* in this MegaMekLab process (the asset editor and the unit selector's card preview). Plain MegaMek does not call
* this, so its own card previews keep their built-in black-and-white/sans-serif defaults.
*/
private static void registerCardDefaults() {
ConfigurableBFSCardPanel.setDefaultProviders(
// The user's configured record-sheet font, or blank when unset - never a baked-in family, since not
// every machine has it. A blank value leaves the card on Java's default sans-serif.
() -> CConfig.getParam(CConfig.RS_FONT),
() -> switch (new RecordSheetOptions().useColor()) {
case ALL -> BattlefieldSupportCard.ColorMode.ALL;
case LOGO_ONLY -> BattlefieldSupportCard.ColorMode.LOGO_ONLY;
case NONE -> BattlefieldSupportCard.ColorMode.NONE;
},
() -> new RecordSheetOptions().getDamageColorAwt());
}

public static void updateGuiScaling() {
System.setProperty("flatlaf.uiScale", Double.toString(GUIPreferences.getInstance().getGUIScale()));
setLookAndFeel();
Expand Down Expand Up @@ -321,7 +343,8 @@ private static boolean openUnitFile(String filePath, boolean noStartup) {
if (!file.exists()) {
throw new IllegalArgumentException("File not found: " + filePath);
}
if (file.getName().toLowerCase().endsWith(".blk") || file.getName().toLowerCase().endsWith(".mtf")) {
if (file.getName().toLowerCase().endsWith(".blk") || file.getName().toLowerCase().endsWith(".mtf")
|| file.getName().toLowerCase().endsWith(".bfs")) {
LOGGER.info("Opening file: {}", filePath);
Entity e = new MekFileParser(file).getEntity();
if (!UnitUtil.validateUnit(e).isBlank()) {
Expand Down
Loading
Loading