Skip to content

Add processing of multiple fluff images (continues #5490) - #8664

Draft
HammerGS wants to merge 30 commits into
mainfrom
Implement-multiple-fluff-images
Draft

Add processing of multiple fluff images (continues #5490)#8664
HammerGS wants to merge 30 commits into
mainfrom
Implement-multiple-fluff-images

Conversation

@HammerGS

@HammerGS HammerGS commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

This continues @SJuliez's work in #5490, rebuilt on current main. That PR had been open since
May 2024 and had fallen 2430 commits behind, so this is the same feature brought up to date with
the conflict resolved.

What it does for players: a unit can now have more than one fluff image, and the unit readout
panel gets < / > buttons to page through them. Where fluff images are stored also becomes more
flexible:

  • Main folder, as today. fluff/Mek/Atlas AS7-D.png still matches that exact model, and
    fluff/Mek/Atlas.png still matches any Atlas. Unchanged by this PR.
  • Chassis subfolder. Everything in fluff/Mek/Atlas/ matches any Atlas, whatever the files are
    called.
  • Model subfolder. Everything in fluff/Mek/Atlas/Atlas AS7-D/ matches only that model. A unit
    with an empty model matches the folder ---empty---.

Worked example: drop three images into data/images/fluff/Mek/Atlas/. Open any Atlas in the unit
selector and all three are available, browsable with the < and > buttons, instead of only the
single image that would previously have matched.

If a fluff image ships alongside a *data.yaml file (the convention used by the painted-minis image
packs), the panel reads title, author and an insignia entry from it and shows them under the
image, so artists get credited in-app.

Relationship to #5490

Same feature, same author's commits (history preserved). Opened as a new PR because #5490 lives on
a fork branch that had drifted too far to update in place. #5490 can be closed in favour of this
once maintainers are happy with the approach.

What changed relative to #5490

  1. Merged current main (2430 commits).
  2. Resolved the one conflict, in FluffImageHelper. Both sides had rewritten the same search
    method for different reasons. main had turned findFluffFile() into a loop over
    getFluffPaths() so Battlefield Support Assets search their own Asset folder and then fall
    back to the folder of their corresponding TW unit type; Add processing of multiple fluff images (WIP) #5490 had turned it into a multi-result
    findFluffFiles() that also searches chassis/model subfolders. Both behaviours are kept: the
    chassis-folder search now runs inside the per-fluff-path loop, for the user dir and the internal
    dir alike, and main's hud.png record-sheet fallback stays last.
  3. Used a LinkedHashSet for the candidate set instead of the HashSet in Add processing of multiple fluff images (WIP) #5490. Both sides
    deliberately order candidates most-specific-first, and getFluffImage() returns the first entry
    — a plain HashSet would silently randomise which image a unit shows.
  4. Reverted EXTENSIONS_FLUFF_IMAGE_FORMATS to String[]. Add processing of multiple fluff images (WIP) #5490 changed this public constant
    to List<String>, which breaks the MegaMekLab build (Arrays.stream(...) on it in FluffTab
    and IconView) and would be a silent public-API change for a constant MML and MekHQ consume.
    The internal membership test now uses a private Set derived from the array.

Files Changed

  • megamek/client/ui/util/FluffImageHelper.java — multi-result fluff search, chassis/model
    subfolder lookup, FluffImageRecord, merged with main's asset-folder search.
  • megamek/client/ui/dialogs/unitSelectorDialogs/EntityReadoutPanel.java< / > browsing,
    info label under the image, placeholder for units with no fluff.
  • megamek/client/ui/FluffImageTooltip.java — new; reads the *data.yaml sidecar files.

Testing

  • ./gradlew :megamek:compileJava and :megamek:compileTestJava green.
  • FluffImageHelperAssetTest passes — this is main's test for the Battlefield Support Asset
    folder fallback, i.e. exactly the behaviour the conflict resolution had to preserve.
  • checkstyleMain green (re-run after the checklist pass).
  • Confirmed the public EXTENSIONS_FLUFF_IMAGE_FORMATS constant is now byte-identical to main,
    so MegaMekLab is unaffected.

Review checklist pass

The WIP items carried over from #5490 have now been addressed (commit 48faefe):

  • User-facing strings moved into messages.properties (Unit:, Artist:, Insignia:, the image
    load error), and the browse buttons gained localized tooltips. The separating space after each
    label is added in code so translators do not have to carry a trailing-space escape.
  • FluffImageTooltip given the current full MegaMek license header; its class javadoc no longer
    refers to the long-renamed MechViewPanel.
  • MMLogger replaces log4j LogManager, with [FluffImages] diagnostic logging on the paths that
    previously failed silently: an unreadable fluff image, a missing placeholder, and a unit with no
    fluff at all.
  • Abbreviated identifiers renamed — catch (… e/ex) to exception, lambda e/p to
    event/entry/candidate, Box p to readoutAndFluffPanel.
  • yamlMapper to YAML_MAPPER; the yaml suffix, tooltip width and yaml node names are now named
    constants.
  • UIUtil.scaleForGUI(...) on the gap under the image; CENTER_ALIGNMENT/TOP_ALIGNMENT instead
    of raw 0.5f/0.
  • Wildcard java.util.* and java.awt.* imports replaced with explicit ones; the now-unused
    BoxLayout and FixedXPanel imports dropped.
  • @Nullable and javadoc added where null is legitimate, including @param tags on
    FluffImageRecord; javadoc literals wrapped in {@code ...}.

Three genuine bugs surfaced during that pass and are fixed in the same commit:

  1. reset() never cleared the fluff image. It cleared the leftover fluffImageComponent field
    rather than the label actually on screen. That field is removed, and reset() now clears the
    image, the info line, the record list and the browse buttons.
  2. Stale browse buttons. Selecting a unit with sprites-only enabled, or clearing the selection,
    left the < / > buttons enabled from the previously shown unit.
  3. The tooltip was built twice per image change in changeFluffImageIndex.

Documentation defect found while setting up test data

The class javadoc on getFluffInChassisDirs states that in the main fluff/[unittype]/ folder
"the filename may now contain additional information after an underscore (atlas_xyz.jpg matches for
any Atlas mek)". That behaviour does not exist. The main folder is matched by
findMatchingFiles, which does file.getName().equalsIgnoreCase(nameCandidate + extension) - an
exact match, with no underscore or prefix handling anywhere in the class. The claim appears to
describe an intention that was never implemented. It needs either implementing or removing from the
javadoc before this merges; an earlier revision of this description repeated the claim and has been
corrected.

What is NOT proven yet

  • No runtime playtest. The build and unit tests are green, but I have not launched a game and
    exercised the actual fluff browsing, so the < / > buttons, the placeholder image and the
    yaml info label are unverified in the running client.
  • The *data.yaml tooltip path is entirely unexercised — it needs one of the painted-minis
    image packs present to do anything, and no test or manual run has confirmed it parses.
  • fluff_placeholder.png is referenced but not shipped in this PR; units with no fluff image
    will fall back to a null image until that asset is added to mm-data.
  • Record-sheet printing (MML) is untouched but not re-tested. getRecordSheetFluffImage() now
    goes through the multi-result path and returns the first candidate; the ordering is preserved by
    the LinkedHashSet, but no record sheet has actually been printed to confirm it.
  • No new unit tests for the chassis/model subfolder matching rules. Those rules are the heart of
    the feature and deserve coverage before this is merged.

Continues #5490. Original work by @SJuliez.

SJuliez and others added 27 commits May 20, 2024 11:40
MegaMek
+ Fix 5476: Update SV armor slots to 2 or 1 for Rating E or F respectively

MegaMekLab
+ Fix ##1514: Fix Atrocious table shading render speed

MekHQ
+ PR #4106: Use tabs in customize scenario dialog
+ PR #4105: Updated Post-Scenario Logging for Prisoners
+ PR #4104: Fixed Post-Scenario Tracking System's Handling of Multiple Personnel in autoAwards
+ PR #4102: Fixed Award Tier Count Calculations in PersonViewPanel
+ PR #4093: Added Negotiation and Scrounge Skill Settings for Administrator Personnel
+ PR #4054: Added Life Paths Campaign Options Tab, Added Education Module
            Very Important to read the documentation on this feature (See Docs folder)
# Conflicts:
#	megamek/docs/history.txt
Destrier has Sponson Equipment in Inconsistent Location #5865
# Conflicts:
#	megamek/data/mechfiles/vehicles/3145/Davion/Destrier Siege Vehicle.blk
#	megamek/src/megamek/client/ui/swing/MekViewPanel.java
#	megamek/src/megamek/client/ui/swing/util/FluffImageHelper.java
# Conflicts:
#	megamek/docs/AI and MegaMek Suite (Not Princess Stuff).txt
#	megamek/docs/history.txt
#	megamek/src/megamek/client/ui/FluffImageTooltip.java
#	megamek/src/megamek/client/ui/swing/MekViewPanel.java
#	megamek/src/megamek/client/ui/util/FluffImageHelper.java
- Fix getExtension variable bug (filename vs f)
- Remove test code: use get(0) instead of random image selection
- Fix double setText on imageInfoLabel (removed redundant call)
- Add null safety to FluffImageRecord.getImage() for null file
- Add null parent check in FluffImageTooltip.getYamlFile()
- Add Files.walk depth limits for performance
- Fix JavaDoc: return descriptions now match actual behavior
- Remove commented-out code blocks
- Remove unused prepareLabelText method
- Fix path construction for PLACEHOLDER_IMAGE_NAME
- Add bounds checks in isSuitableYamlFile()
Brings the multi-fluff branch up to date with main (2430 commits).

Conflict resolved in FluffImageHelper: main had reworked the single-result
findFluffFile() to search an ordered list of fluff directories via
getFluffPaths(), so Battlefield Support Assets can fall back to the folder of
their corresponding TW unit type. The branch had reworked the same method into
a multi-result findFluffFiles() that also searches chassis/model subdirectories.

Both are kept: findFluffFiles() now runs the branch's chassis-directory search
inside main's per-fluff-path loop, for the user dir and the internal dir alike,
and keeps main's hud.png record-sheet fallback as the last candidate.
The candidate set is a LinkedHashSet rather than a HashSet so the
most-specific-art-first ordering that both sides rely on is preserved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…uilds

The multi-fluff work changed this public constant from String[] to List<String>
to get a contains() check for the new chassis-directory scan. MegaMekLab calls
Arrays.stream() on it in FluffTab and IconView, so the type swap breaks the MML
build, and MegaMek's deprecation policy does not allow silently changing a
public API that MML and MekHQ consume.

Restore the constant to main's String[] and derive a private Set for the
membership test instead, which also makes the lookup O(1).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 6, 2026 20:32
Style and diagnosability pass over the fluff image work, no intended behaviour
change beyond the three fixes noted at the end.

- Externalise the user-facing strings ("Unit:", "Artist:", "Insignia:", the
  image load error) into messages.properties, and give the browse buttons
  localized tooltips. The separating space after each label is added in code so
  the translated strings do not need a trailing-space escape.
- Give FluffImageTooltip the current full MegaMek license header and correct its
  class javadoc, which still referred to the long-renamed MechViewPanel.
- Replace log4j LogManager with MMLogger in FluffImageTooltip and
  FluffImageHelper, and add [FluffImages] diagnostic logging where failures were
  previously silent: an unreadable fluff image, a missing placeholder image and
  a unit with no fluff at all.
- Rename abbreviated identifiers: catch parameters e/ex to exception, lambda
  parameters e/p to event/entry/candidate, and Box p to readoutAndFluffPanel.
- yamlMapper becomes YAML_MAPPER, and the yaml suffix, tooltip width and node
  names become named constants.
- Scale the gap under the fluff image with UIUtil.scaleForGUI, and use
  CENTER_ALIGNMENT/TOP_ALIGNMENT instead of raw 0.5f/0.
- Replace the wildcard java.util and java.awt imports with explicit ones and
  drop the now-unused BoxLayout and FixedXPanel imports.
- Add @nullable and javadoc (including @PARAM tags on FluffImageRecord) where
  null is a legitimate value, and wrap javadoc literals in {@code ...}.

Three real fixes came out of the pass:

- reset() cleared the leftover fluffImageComponent field rather than the label
  actually on screen, so resetting the panel never cleared the fluff image. That
  field is now removed and reset() clears the image, the info line, the record
  list and the browse buttons.
- Selecting a unit with sprites-only enabled, or with no unit at all, left the
  browse buttons enabled from the previously shown unit.
- changeFluffImageIndex built the tooltip twice per image change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@HammerGS HammerGS added AI Assisted Development This project/code contains AI use under the supervision of a human developer. With Human testing. GUI/UX User interface and user experience labels Aug 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds support for multiple fluff images per unit, including directory-based discovery rules and UI controls to page through available images, plus optional YAML sidecar metadata for artist/title/insignia.

Changes:

  • Update fluff image discovery to return multiple candidates (including chassis/model subfolders) and preserve deterministic selection order.
  • Add EntityReadoutPanel UI controls (</>) to browse images and show metadata under the image.
  • Introduce FluffImageTooltip to parse *data.yaml sidecar files and render localized tooltip content.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

File Description
megamek/src/megamek/client/ui/util/FluffImageHelper.java Adds multi-result fluff search, chassis/model directory scanning, and a lazy-loading FluffImageRecord.
megamek/src/megamek/client/ui/dialogs/unitSelectorDialogs/EntityReadoutPanel.java Adds image browsing controls, placeholder handling, and wiring for tooltip/metadata display.
megamek/src/megamek/client/ui/FluffImageTooltip.java New YAML sidecar parser to generate localized HTML tooltip content for fluff images.
megamek/resources/megamek/client/messages.properties Adds localization keys for new readout panel tooltips/errors and tooltip labels.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread megamek/src/megamek/client/ui/dialogs/unitSelectorDialogs/EntityReadoutPanel.java Outdated
Comment thread megamek/src/megamek/client/ui/FluffImageTooltip.java Outdated
Comment thread megamek/src/megamek/client/ui/FluffImageTooltip.java Outdated
Comment thread megamek/src/megamek/client/ui/util/FluffImageHelper.java
Comment thread megamek/src/megamek/client/ui/util/FluffImageHelper.java
Comment thread megamek/src/megamek/client/ui/util/FluffImageHelper.java Outdated
- The readout panel opened on the second fluff image. setFluffImage() reset the
  index to 0 and then called showNextFluffImage(), which steps by one before
  displaying, so a unit with two or more images showed image 2 first and the "<"
  button went back to image 1. Single-image units wrapped around to 0 and looked
  correct, which is why it went unnoticed. Now shows the first image.

- Tighten the chassis and model directory checks from exists() to isDirectory(),
  so a stray file named like a chassis is not scanned as a directory.

- Use Files.list instead of Files.walk(dir, 1) for the shallow directory scan,
  which also drops the directory entry itself and the Objects::toString and
  File::new round trip.

- Sort the images found in a chassis or model directory by file name. Neither
  Files.list nor Files.walk guarantees an order, so without this the image a
  unit shows first was down to the file system.

- Pad the tooltip style colour to six hex digits. A dark tooltip foreground
  colour such as pure blue produced "ff", which is not a valid CSS colour, so
  the label text fell back to the default colour.

- Extract the "---empty---" model directory name into a constant.

Adds FluffImageHelperChassisDirTest covering the chassis and model subdirectory
rules: model directory wins over chassis directory, the chassis directory is
used when no model directory matches, empty models match the ---empty---
directory, clan chassis variant directories resolve, non-image files and stray
files named like a chassis are ignored, and repeated calls return the same
deterministic order.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Assisted Development This project/code contains AI use under the supervision of a human developer. With Human testing. GUI/UX User interface and user experience

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants