Skip to content

Commit 97ca02f

Browse files
authored
Spray an optional edition tag across the MegaMek logo on the splash screen (#8711)
## Summary MegaMek can now say which edition a build is, right on the artwork: a short tag sprayed straight across the MegaMek logo in red spray-paint lettering, outlined in black so it stays readable over the pale wordmark underneath. It follows the `notes=` entry added in #8710, and is driven the same way. Put this in `megamek/resources/Version.properties`: ```properties splashTag=Core Rules Edition ``` and "Core Rules Edition" appears sprayed across the logo, tilted slightly off level. Leave the entry out and the artwork is exactly as it is today. It is deliberately a **separate** entry from `notes=`, so the artwork and the menu can say different things - this build says "Core Rules Release" in the menu and "Core Rules Edition" on the logo - or a build can have one without the other. **This PR ships the tag switched on**, labelling the build as the Core Rules Edition. ## The font The lettering uses the **Rubik Spray Paint** font, under the SIL Open Font License 1.1. It is already on `MegaMek/mm-data` main at `data/fonts/Rubik_Spray_Paint/`, holding `RubikSprayPaint-Regular.ttf` and `OFL.txt`, laid out the same way as `Anta/` and `Jura/`. MegaMek registers everything under `data/fonts` automatically, so there is no loader change here. If the font is ever missing - an un-staged or stale data directory - the tag is still drawn, in the default font, and a warning naming the missing font goes to `megamek.log`. It degrades rather than breaks. ## Changes 1. **A new optional `splashTag` entry in `Version.properties`**, documented in the file next to `notes`. 2. **`Version` reads it**, alongside the other optional entries, reusing the same "absent or blank means nothing configured" handling. Everything added is `static`, so nothing changes about how a `Version` travels in a packet or a save game. 3. **`MegaMekGUI` paints it** onto the splash artwork: it loads the font once when the menu is built, then sizes and places the tag against the logo each time the artwork is drawn. 4. **`drawLogo` now returns the rectangle it drew into**, which is what the tag is positioned against. Its drawing behaviour is unchanged. ### Two things worth knowing for review **The tag is placed against the wordmark plate, not the middle of the logo image.** The obvious approach - centre the tag on the logo - puts it up on the 'Mek's legs, because `mm-logo.png` is a tall composite: a 'Mek standing above the wordmark plate, with the plate filling only the bottom quarter or so. Profiling the image's transparency, the plate is the full-width band running from about three-quarters of the way down to the bottom edge, so the tag is placed against that band instead. **The tag is capped by height as well as width.** Sizing it purely to span the logo's width means a short tag gets a huge font - "BETA" would come out taller than the plate it is supposed to be sprayed across. The size is therefore the smaller of "fits the width" and "fits the plate's height". Everything is derived from the logo's own rectangle, which is itself derived from the window size, so the placement holds at any launcher size rather than being tuned to one screen. ## Files Changed - `megamek/resources/Version.properties` - the new entry, documented, with the Core Rules Edition tag set - `megamek/src/megamek/Version.java` - reads it; `getSplashTag()` and `hasSplashTag()` - `megamek/src/megamek/client/ui/clientGUI/MegaMekGUI.java` - loads the font, sizes and sprays the tag, and `drawLogo` now reports where it drew ## Testing - `compileJava`, `compileTestJava`, `VersionTest` (31/31) and checkstyle all clean. - Run in-game repeatedly while settling the look. The final version was checked against a clean `megamek.log`: no ERROR, FATAL or null-pointer entries, and the expected `[SplashTag] Spraying 'Core Rules Edition' onto the splash artwork in Rubik Spray Paint` line. - The placement was worked out by measuring the logo image's transparency per row to find where the wordmark plate actually sits, rather than by eye. - The sizing and placement were then checked numerically across launcher widths of 800, 1024, 1280, 1600, 1920, 2560 and 3200 pixels, and across tags from 4 to 35 characters ("BETA" through "The Twenty Fifth Anniversary Edition"). In every combination the tag lands on the plate and stays inside the artwork, with between 7 and 46 pixels to spare at the bottom. ## What is NOT proven yet - **No automated test covers the tag.** The checks above were done with a throwaway harness, not a committed test. Making the sizing testable would mean pulling it out of the painting code; I am happy to do that if reviewers would rather have it guarded. - **Only run at the default GUI scale and the default window size** for the visual check. The numeric check above covers other sizes, but I have only *looked* at one. - **Only run with one skin.** The tag is painted onto the splash artwork rather than onto a skinned component, so a skin should not affect it - but that is reasoning, not something I have tried. - **The font has not been through a release build.** It works from a staged data directory, and it is on mm-data main; I have not confirmed it survives packaging into a distributed build.
2 parents ffafdf8 + 404d8f1 commit 97ca02f

3 files changed

Lines changed: 360 additions & 2 deletions

File tree

megamek/resources/Version.properties

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,31 @@ notesColor=Red
6767
# megamek.log. Remember that a large size makes the note taller and pushes the buttons down.
6868
#
6969
notesFontSize=14
70+
#
71+
# Optional tag sprayed straight across the MegaMek logo on the splash artwork, in red spray-paint
72+
# lettering. This is separate from the note above, so the artwork and the menu can say different
73+
# things - or a build can have one without the other.
74+
#
75+
# Leave it out and the artwork is left clean. Keep it SHORT: it is sized to sit across the width of
76+
# the logo, so a long tag comes out small and cramped. Use plain ASCII only.
77+
#
78+
# The lettering needs the "Rubik Spray Paint" font, which ships in mm-data under
79+
# data/fonts/Rubik_Spray_Paint. If that font is missing the tag is still drawn, in the default font,
80+
# and a warning goes to megamek.log.
81+
#
82+
splashTag=Core Rules Edition
83+
#
84+
# Colour of the sprayed tag, from the same list of names as notesColor above: red, orange, yellow,
85+
# green, blue, cyan, magenta, pink, white, black, gray, lightgray, darkgray (grey is accepted too).
86+
# Leave it out and the tag is sprayed in red. An unrecognised name falls back to red and is reported
87+
# in megamek.log. Whichever colour is chosen is left slightly see-through, so the logo shows through
88+
# the paint.
89+
#
90+
#splashTagColor=red
91+
#
92+
# Angle the tag is sprayed at, in degrees. Zero is level. A NEGATIVE angle drops the left-hand end and
93+
# lifts the right-hand end; a POSITIVE angle does the opposite. Leave it out and the tag sits at -2.5,
94+
# just off level, which is enough to stop it reading as a tidy caption. Anything beyond -45 to 45 is
95+
# treated as a mistake, falls back to -2.5, and is reported in megamek.log.
96+
#
97+
#splashTagRotation=-2.5

megamek/src/megamek/Version.java

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,26 @@ public final class Version implements Comparable<Version>, Serializable {
8888
/** Key of the optional point size the build note is drawn at. Absent unless a build note sets one. */
8989
private static final String NOTES_FONT_SIZE_BUNDLE_KEY = "notesFontSize";
9090

91+
/** Key of the optional tag sprayed across the MegaMek logo on the splash art. Absent for ordinary releases. */
92+
private static final String SPLASH_TAG_BUNDLE_KEY = "splashTag";
93+
94+
/** Key of the optional colour name the splash tag is sprayed in. Absent unless a splash tag sets one. */
95+
private static final String SPLASH_TAG_COLOR_BUNDLE_KEY = "splashTagColor";
96+
97+
/** Key of the optional angle, in degrees, the splash tag is sprayed at. Absent unless a splash tag sets one. */
98+
private static final String SPLASH_TAG_ROTATION_BUNDLE_KEY = "splashTagRotation";
99+
100+
/** The angle the splash tag is sprayed at when it does not ask for one: very slightly off level. */
101+
public static final double DEFAULT_SPLASH_TAG_ROTATION = -2.5;
102+
103+
/**
104+
* Bounds accepted for a configured splash tag angle. Anything past this is treated as a mistake rather than
105+
* an intention, since a tag turned much further than this stops reading as paint on a sign and starts
106+
* running off the artwork.
107+
*/
108+
private static final double MINIMUM_SPLASH_TAG_ROTATION = -45.0;
109+
private static final double MAXIMUM_SPLASH_TAG_ROTATION = 45.0;
110+
91111
/**
92112
* The value {@link #getBuildNotesFontSize()} reports when no point size is configured, meaning the build note
93113
* is drawn at whatever size the rest of the menu uses.
@@ -114,6 +134,9 @@ public final class Version implements Comparable<Version>, Serializable {
114134
private static final String BUILD_NOTES = readOptionalNotesFromBundle();
115135
private static final String BUILD_NOTES_COLOR_NAME = readOptionalEntryFromBundle(NOTES_COLOR_BUNDLE_KEY);
116136
private static final int BUILD_NOTES_FONT_SIZE = readOptionalNotesFontSizeFromBundle();
137+
private static final String SPLASH_TAG = readOptionalEntryFromBundle(SPLASH_TAG_BUNDLE_KEY);
138+
private static final String SPLASH_TAG_COLOR_NAME = readOptionalEntryFromBundle(SPLASH_TAG_COLOR_BUNDLE_KEY);
139+
private static final double SPLASH_TAG_ROTATION = readOptionalSplashTagRotationFromBundle();
117140

118141
private int major;
119142
private int minor;
@@ -318,6 +341,36 @@ private static int readOptionalNotesFontSizeFromBundle() {
318341
return parsedFontSize;
319342
}
320343

344+
/**
345+
* Reads the optional {@code splashTagRotation} entry from {@code Version.properties}.
346+
*
347+
* @return the configured angle in degrees, or {@link #DEFAULT_SPLASH_TAG_ROTATION} when the key is absent,
348+
* blank, not a number, or turned further than makes sense on the artwork
349+
*/
350+
private static double readOptionalSplashTagRotationFromBundle() {
351+
final String configuredRotation = readOptionalEntryFromBundle(SPLASH_TAG_ROTATION_BUNDLE_KEY);
352+
if (configuredRotation == null) {
353+
return DEFAULT_SPLASH_TAG_ROTATION;
354+
}
355+
356+
final double parsedRotation = MathUtility.parseDouble(configuredRotation, Double.NaN);
357+
final boolean isNotANumber = Double.isNaN(parsedRotation);
358+
final boolean isTurnedTooFar = (parsedRotation < MINIMUM_SPLASH_TAG_ROTATION)
359+
|| (parsedRotation > MAXIMUM_SPLASH_TAG_ROTATION);
360+
if (isNotANumber || isTurnedTooFar) {
361+
LOGGER.warn("[Version] Version.properties sets splashTagRotation to '{}', which is not an angle "
362+
+ "between {} and {} degrees. The splash tag will be sprayed at {} degrees instead.",
363+
configuredRotation,
364+
MINIMUM_SPLASH_TAG_ROTATION,
365+
MAXIMUM_SPLASH_TAG_ROTATION,
366+
DEFAULT_SPLASH_TAG_ROTATION);
367+
return DEFAULT_SPLASH_TAG_ROTATION;
368+
}
369+
370+
LOGGER.debug("[Version] Splash tag angle {} degrees loaded from Version.properties", parsedRotation);
371+
return parsedRotation;
372+
}
373+
321374
/**
322375
* Reads an optional entry from {@code Version.properties}, treating an absent key and a blank value alike.
323376
*
@@ -398,6 +451,51 @@ public static boolean hasBuildNotesFontSize() {
398451
return BUILD_NOTES_FONT_SIZE != NO_NOTES_FONT_SIZE;
399452
}
400453

454+
/**
455+
* Returns the short tag this build sprays across the MegaMek logo on the splash art, such as the name of the
456+
* edition it is celebrating.
457+
*
458+
* <p>This is separate from {@link #getBuildNotes()} so that the artwork and the menu can say different
459+
* things, or so that a build can have one without the other.</p>
460+
*
461+
* @return the splash tag, or {@code null} when this build has none, which is the case for every ordinary
462+
* release
463+
*/
464+
public static @Nullable String getSplashTag() {
465+
return SPLASH_TAG;
466+
}
467+
468+
/**
469+
* @return {@code true} if this build has a tag to spray across the splash art, otherwise {@code false}
470+
*/
471+
public static boolean hasSplashTag() {
472+
return SPLASH_TAG != null;
473+
}
474+
475+
/**
476+
* Returns the colour the splash tag asks to be sprayed in, as the plain name written in
477+
* {@code Version.properties} rather than as a colour object, for the same reason as
478+
* {@link #getBuildNotesColorName()}: this class is also used well away from the user interface.
479+
*
480+
* @return the configured colour name, or {@code null} when the tag did not ask for a particular colour
481+
*/
482+
public static @Nullable String getSplashTagColorName() {
483+
return SPLASH_TAG_COLOR_NAME;
484+
}
485+
486+
/**
487+
* Returns the angle, in degrees, the splash tag is sprayed at.
488+
*
489+
* <p>Zero is level. A negative angle drops the left-hand end and lifts the right-hand end; a positive angle
490+
* does the opposite. This follows the screen's own sense of rotation, where the vertical axis points
491+
* downwards.</p>
492+
*
493+
* @return the configured angle, or {@link #DEFAULT_SPLASH_TAG_ROTATION} when the tag did not ask for one
494+
*/
495+
public static double getSplashTagRotation() {
496+
return SPLASH_TAG_ROTATION;
497+
}
498+
401499
// region Getters
402500
public int getMajor() {
403501
return major;

0 commit comments

Comments
 (0)