package org.saturnclient.ui.screens; import java.util.List; import org.saturnclient.common.provider.Providers; import org.saturnclient.common.ref.game.MinecraftClientRef.MinecraftScreen; import org.saturnclient.config.AnimationConfig; import org.saturnclient.config.Config; import org.saturnclient.config.property.FloatProperty; import org.saturnclient.config.Theme; import org.saturnclient.ui.Element; import org.saturnclient.ui.SaturnScreen; import org.saturnclient.ui.anim.Animation; import org.saturnclient.ui.anim.Fade; import org.saturnclient.ui.anim.SlideFade; import org.saturnclient.ui.anim.SlideY; import org.saturnclient.ui.components.AccountChip; import org.saturnclient.ui.components.Dock; import org.saturnclient.ui.elements.AnimationStagger; import org.saturnclient.ui.elements.Button; import org.saturnclient.ui.elements.IconButton; import org.saturnclient.ui.elements.ImageTexture; import org.saturnclient.ui.elements.Label; import org.saturnclient.ui.resources.Fonts; import org.saturnclient.ui.resources.Textures; /** * Saturn's title screen: the logo over the game's menus in the middle, the * player and a quit button in the top corners, and along the bottom a dock * (Saturn's settings, its mods and cosmetics, then Minecraft's options) and the version. */ public class TitleMenu extends SaturnScreen { // The logo and wordmark as the shift menu has them: the wordmark over the logo's foot private static final int LOGO = 98; private static final int WORDMARK_WIDTH = 98; private static final int WORDMARK_HEIGHT = 10; private static final int WORDMARK_Y = 88; /** From the logo's foot to the menus, close so they read as one block */ private static final int MENUS_GAP = 14; private static final int COLUMN_WIDTH = 280; private static final int BUTTON_HEIGHT = 36; private static final int GAP = 8; /** Space around the account chip, quit button, dock and version, from the screen's edges */ private static final int EDGE = 14; /** The top corners sit a little tighter */ private static final int TOP_EDGE = 10; /** As tall as the account chip beside it */ private static final int QUIT_SIZE = AccountChip.HEIGHT; // The intro comes in in reading order, left to right and top to bottom, one step apart private static final int STEP_ACCOUNT = 0; private static final int STEP_QUIT = 1; private static final int STEP_LOGO = 2; private static final int STEP_MENUS = 3; /** After the two menus */ private static final int STEP_VERSION = 5; private static final int STEP_DOCK = 6; private static final float VERSION_SCALE = 0.5f; private static final float VERSION_ALPHA = 0.6f; public TitleMenu() { super("Title Menu"); } @Override protected FloatProperty getScaleSetting() { return Config.titleScale; } @Override public void ui() { blurBackground = false; // Logo and menus, centered together a little above the middle int menusHeight = BUTTON_HEIGHT * 2 + GAP; int blockHeight = LOGO + MENUS_GAP + menusHeight; int top = (height - blockHeight) / 2 - 20; draw(new ImageTexture(Config.getLogo()) .dimensions(LOGO, LOGO) .position((width - LOGO) / 2, top) .animation(step(new SlideY(AnimationConfig.logo, -20), STEP_LOGO))); draw(new ImageTexture(Textures.LOGO_TEXT) .dimensions(WORDMARK_WIDTH, WORDMARK_HEIGHT) .position((width - WORDMARK_WIDTH) / 2, top + WORDMARK_Y) .animation(step(new Fade(AnimationConfig.logo.duration.value), STEP_LOGO))); AnimationStagger menus = new AnimationStagger(AnimationConfig.mainMenu) .after(stepDelay(STEP_MENUS)); addMenuButton(menus, new Button("Singleplayer", () -> Providers.saturn.getClient().setScreen(MinecraftScreen.SelectWorld)) .dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, 0)); addMenuButton(menus, new Button("Multiplayer", () -> Providers.saturn.getClient().setScreen(MinecraftScreen.Multiplayer)) .dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, BUTTON_HEIGHT + GAP)); draw(menus.position((width - COLUMN_WIDTH) / 2, top + LOGO + MENUS_GAP)); // Top corners: the player, and quitting the game draw(new AccountChip() .position(TOP_EDGE, TOP_EDGE) .animation(step(new SlideFade(AnimationConfig.mainMenu, 8), STEP_ACCOUNT))); draw(new IconButton(Textures.ICON_CLOSE, () -> Providers.saturn.getClient().scheduleStop()) .dimensions(QUIT_SIZE, QUIT_SIZE) .position(width - TOP_EDGE - QUIT_SIZE, TOP_EDGE) .animation(step(new SlideFade(AnimationConfig.mainMenu, 8), STEP_QUIT))); // Along the bottom: the dock, Saturn first (its settings) and Minecraft's options last, and the version Dock dock = new Dock(List.of( new Dock.Item(Textures.LOGO, () -> Providers.saturn.getClient().setScreen(new ClientSettings())), new Dock.Item(Textures.ICON_MODS, () -> Providers.saturn.getClient().setScreen(new ModMenu())), new Dock.Item(Textures.ICON_COSMETICS, () -> Providers.saturn.getClient().setScreen(new CosmeticsMenu())), new Dock.Item(Textures.ICON_SETTINGS, () -> Providers.saturn.getClient().setScreen(MinecraftScreen.Options)))); draw(dock.position((width - dock.width) / 2, height - EDGE - Dock.HEIGHT) .animation(step(new SlideFade(AnimationConfig.mainMenu, -12), STEP_DOCK))); String version = getVersionText(); int versionHeight = (int) (Fonts.getHeight() * VERSION_SCALE); draw(new Label(() -> version, VERSION_SCALE, Theme::getBodyFont, () -> Theme.withAlpha(VERSION_ALPHA, Theme.getMutedFg())) .position(EDGE, height - EDGE - versionHeight) .animation(step(new Fade(AnimationConfig.mainMenu), STEP_VERSION))); } /** * When the intro's {@code step} starts, in ms: the Main Menu stagger apart, * or all at once when staggering is off. */ private static int stepDelay(int step) { return Config.stagger.value ? step * AnimationConfig.mainMenu.stagger.value : 0; } private static Animation step(Animation animation, int step) { animation.delay = stepDelay(step); return animation; } /** * "Saturn 0.1.0 · 1.21.11", from the mod version "0.1.0+1.21.11". */ private static String getVersionText() { String[] version = Providers.saturn.getModVersion().split("\\+", 2); String text = "Saturn " + version[0]; return version.length > 1 ? text + " · " + version[1] : text; } private static void addMenuButton(AnimationStagger stagger, Element button) { stagger.draw(button.animation(new SlideFade(AnimationConfig.mainMenu, -10))); } }