diff --git a/src/main/java/org/saturnclient/ui/ElementRenderer.java b/src/main/java/org/saturnclient/ui/ElementRenderer.java index ec2cd96..c863d6c 100644 --- a/src/main/java/org/saturnclient/ui/ElementRenderer.java +++ b/src/main/java/org/saturnclient/ui/ElementRenderer.java @@ -26,6 +26,11 @@ public class ElementRenderer { for (Element element : elements) { element.playAnimationFrame(elapsed); + // Not faded in yet + if (element.opacity <= 0) { + continue; + } + renderScope.getMatrixStack().push(); renderScope.setOpacity(element.opacity); renderScope.getMatrixStack().translate(element.x, element.y); diff --git a/src/main/java/org/saturnclient/ui/anim/Fade.java b/src/main/java/org/saturnclient/ui/anim/Fade.java index 9a5ecef..9663e42 100644 --- a/src/main/java/org/saturnclient/ui/anim/Fade.java +++ b/src/main/java/org/saturnclient/ui/anim/Fade.java @@ -16,12 +16,12 @@ public class Fade extends Animation { @Override public void tick(double progress, Element element) { - element.opacity = Math.max(0.1f, targetOpacity * (float) progress); + element.opacity = targetOpacity * (float) progress; } @Override public void init(Element element) { targetOpacity = element.opacity; - element.opacity = 0.1f; + element.opacity = 0; } } diff --git a/src/main/java/org/saturnclient/ui/elements/AnimationStagger.java b/src/main/java/org/saturnclient/ui/elements/AnimationStagger.java index 80dba99..c27c897 100644 --- a/src/main/java/org/saturnclient/ui/elements/AnimationStagger.java +++ b/src/main/java/org/saturnclient/ui/elements/AnimationStagger.java @@ -13,6 +13,8 @@ import org.saturnclient.ui.RenderScope; public class AnimationStagger extends Element { protected List children = new ArrayList<>(); public int delay; + /** When the first child starts, in ms */ + public int start = 0; public AnimationStagger(int delay) { this.delay = delay; @@ -22,13 +24,21 @@ public class AnimationStagger extends Element { this.delay = config.stagger.value; } + /** + * Holds the children back until {@code start} ms after the screen opens. + */ + public AnimationStagger after(int start) { + this.start = start; + return this; + } + public void draw(Element element) { if (!Config.stagger.value) { element.animation = null; } if (element.animation != null) { - element.animation.delay = delay * children.size(); + element.animation.delay = start + delay * children.size(); } ElementRenderer.draw(children, element); diff --git a/src/main/java/org/saturnclient/ui/screens/TitleMenu.java b/src/main/java/org/saturnclient/ui/screens/TitleMenu.java index 6d7e040..86b4caf 100644 --- a/src/main/java/org/saturnclient/ui/screens/TitleMenu.java +++ b/src/main/java/org/saturnclient/ui/screens/TitleMenu.java @@ -10,6 +10,7 @@ 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; @@ -17,14 +18,16 @@ 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, and - * along the bottom the player, a dock of Saturn's screens and the version. + * 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 of + * Saturn's screens and 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 @@ -37,8 +40,16 @@ public class TitleMenu extends SaturnScreen { private static final int COLUMN_WIDTH = 280; private static final int BUTTON_HEIGHT = 36; private static final int GAP = 8; - private static final int EDGE = 28; - private static final float VERSION_SCALE = 0.62f; + /** Space around the account chip, quit button, dock and version, from the screen's edges */ + private static final int EDGE = 14; + private static final int QUIT_SIZE = 32; + + // The intro, in ms: the logo, then the menus, then what's around the edges + private static final int MENUS_DELAY = 250; + private static final int EDGES_DELAY = 450; + private static final int EDGES_STAGGER = 60; + private static final float VERSION_SCALE = 0.5f; + private static final float VERSION_ALPHA = 0.6f; public TitleMenu() { super("Title Menu"); @@ -54,7 +65,7 @@ public class TitleMenu extends SaturnScreen { blurBackground = false; // Logo and menus, centered together a little above the middle - int menusHeight = BUTTON_HEIGHT * 3 + GAP * 2; + int menusHeight = BUTTON_HEIGHT * 2 + GAP; int blockHeight = LOGO + MENUS_GAP + menusHeight; int top = (height - blockHeight) / 2 - 20; @@ -67,57 +78,57 @@ public class TitleMenu extends SaturnScreen { .position((width - WORDMARK_WIDTH) / 2, top + WORDMARK_Y) .animation(new Fade(AnimationConfig.logo.duration.value))); - int rowHeight = BUTTON_HEIGHT + GAP; - int halfWidth = (COLUMN_WIDTH - GAP) / 2; - AnimationStagger menus = new AnimationStagger(AnimationConfig.mainMenu); + AnimationStagger menus = new AnimationStagger(AnimationConfig.mainMenu).after(MENUS_DELAY); 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, rowHeight)); - addMenuButton(menus, new Button("Options", - () -> Providers.saturn.getClient().setScreen(MinecraftScreen.Options)) - .dimensions(halfWidth, BUTTON_HEIGHT).position(0, rowHeight * 2)); - addMenuButton(menus, new Button("Quit", - () -> Providers.saturn.getClient().scheduleStop()) - .dimensions(halfWidth, BUTTON_HEIGHT).position(COLUMN_WIDTH - halfWidth, rowHeight * 2)); + .dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, BUTTON_HEIGHT + GAP)); draw(menus.position((width - COLUMN_WIDTH) / 2, top + LOGO + MENUS_GAP)); - // Along the bottom: the player, Saturn's screens, the version + // Top corners: the player, and quitting the game draw(new AccountChip() - .position(EDGE, height - EDGE - AccountChip.HEIGHT) - .animation(new Fade(AnimationConfig.mainMenu))); + .position(EDGE, EDGE) + .animation(delayed(new SlideFade(AnimationConfig.mainMenu, 8), EDGES_DELAY))); + draw(new IconButton(Textures.ICON_CLOSE, () -> Providers.saturn.getClient().scheduleStop()) + .dimensions(QUIT_SIZE, QUIT_SIZE) + .position(width - EDGE - QUIT_SIZE, EDGE + (AccountChip.HEIGHT - QUIT_SIZE) / 2) + .animation(delayed(new SlideFade(AnimationConfig.mainMenu, 8), EDGES_DELAY))); + // Along the bottom: the dock of Saturn's screens and Minecraft's options, and the version Dock dock = new Dock(List.of( 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_HUD_EDITOR, - () -> Providers.saturn.getClient().setScreen(new HudEditor())), + new Dock.Item(Textures.LOGO, () -> Providers.saturn.getClient().setScreen(new ClientSettings())), new Dock.Item(Textures.ICON_SETTINGS, - () -> Providers.saturn.getClient().setScreen(new ClientSettings())))); + () -> Providers.saturn.getClient().setScreen(MinecraftScreen.Options)))); draw(dock.position((width - dock.width) / 2, height - EDGE - Dock.HEIGHT) - .animation(new SlideFade(AnimationConfig.mainMenu, 12))); + .animation(delayed(new SlideFade(AnimationConfig.mainMenu, -12), EDGES_DELAY + EDGES_STAGGER))); String version = getVersionText(); - int versionWidth = (int) (Fonts.getWidth(version, Theme.getBodyFont()) * VERSION_SCALE); int versionHeight = (int) (Fonts.getHeight() * VERSION_SCALE); - draw(new Label(() -> version, VERSION_SCALE, Theme::getBodyFont, Theme::getMutedFg) - .position(width - EDGE - versionWidth, height - EDGE - (AccountChip.HEIGHT + versionHeight) / 2) - .animation(new Fade(AnimationConfig.mainMenu))); + draw(new Label(() -> version, VERSION_SCALE, Theme::getBodyFont, + () -> Theme.withAlpha(VERSION_ALPHA, Theme.getMutedFg())) + .position(EDGE, height - EDGE - versionHeight) + .animation(delayed(new Fade(AnimationConfig.mainMenu), EDGES_DELAY + EDGES_STAGGER * 2))); + } + + private static Animation delayed(Animation animation, int delay) { + animation.delay = delay; + return animation; } /** - * "Saturn Client 0.1.0 · Minecraft 1.21.11", from the mod version - * "0.1.0+1.21.11". + * "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 Client " + version[0]; - return version.length > 1 ? text + " · Minecraft " + version[1] : text; + String text = "Saturn " + version[0]; + return version.length > 1 ? text + " · " + version[1] : text; } private static void addMenuButton(AnimationStagger stagger, Element button) {