From b7957807b578437eda554d67fff0cbe992c38184 Mon Sep 17 00:00:00 2001 From: Shiewk Date: Sun, 25 May 2025 14:27:30 +0200 Subject: [PATCH] Rework death screen buttons Also makes buttons visually pressed (#3) --- .../client/screen/BedrockDeathScreen.java | 253 ++++++------------ .../components/BedrockDeathScreenButton.java | 142 ++++++++++ 2 files changed, 223 insertions(+), 172 deletions(-) create mode 100644 src/main/java/de/shiewk/bedrockdeathscreen/client/screen/components/BedrockDeathScreenButton.java diff --git a/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/BedrockDeathScreen.java b/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/BedrockDeathScreen.java index 92e7a80..8a67b77 100644 --- a/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/BedrockDeathScreen.java +++ b/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/BedrockDeathScreen.java @@ -1,15 +1,13 @@ package de.shiewk.bedrockdeathscreen.client.screen; +import de.shiewk.bedrockdeathscreen.client.screen.components.BedrockDeathScreenButton; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.DeathScreen; import net.minecraft.client.gui.screen.MessageScreen; import net.minecraft.client.gui.screen.TitleScreen; -import net.minecraft.sound.SoundCategory; -import net.minecraft.sound.SoundEvent; import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; -import net.minecraft.util.Identifier; import net.minecraft.util.Util; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; @@ -25,13 +23,14 @@ public class BedrockDeathScreen extends DeathScreen { private Text scoreText = Text.empty(); private static final Text menuMessage = Text.translatable("deathScreen.titleScreen"); private static final MutableText confirmQuitText = Text.translatable("deathScreen.quit.confirm"); - private Text respawnMessage; private final boolean hardcore; - private final Identifier SOUND = Identifier.of("minecraft", "ui.button.click"); private final int deg = (int) (Math.random() * 360); private boolean confirmingExit = false; private boolean wasHoveringButtons = false; + private BedrockDeathScreenButton respawnButton; + private BedrockDeathScreenButton menuButton; + public BedrockDeathScreen(@Nullable Text message, boolean isHardcore) { super(message, isHardcore); this.message = message; @@ -39,142 +38,16 @@ public class BedrockDeathScreen extends DeathScreen { screenCreationTime = Util.getMeasuringTimeNano(); } - private boolean hoversRespawn(int mouseX, int mouseY){ - int startX = width/2 - 75; - int startY = height - height / 3 - 9; - int endX = width/2 + 75; - int endY = height - height / 3 + 13; - return getTotalScreenTime() > 1050 && - mouseX > startX && - mouseX < endX && - mouseY > startY && - mouseY < endY; - } - - private boolean hoversMenuButton(int mouseX, int mouseY){ - final int startX = width/2 - 75; - final int startY = height - height / 3 - 9 + 30; - final int endX = width/2 + 75; - final int endY = height - height / 3 + 13 + 30; - return getTotalScreenTime() > 1050 && - mouseX > startX && - mouseX < endX && - mouseY > startY && - mouseY < endY; - } - - private void renderRespawnButton(DrawContext context, int mouxeX, int mouseY, float opacity){ - if ((int) opacity == 0){ - return; - } - final int primary = new Color(60, 133, 39, (int) opacity).getRGB(); - final int secondary = new Color(29, 77, 19, (int) opacity).getRGB(); - final int gaccent = new Color(79, 145, 60, (int) opacity).getRGB(); - final int black = new Color(0, 0, 0, (int) opacity).getRGB(); - final int textColor = new Color(255, 255, 255, (int) opacity).getRGB(); - final boolean mouseHover = hoversRespawn(mouxeX, mouseY); - - final int startX = width/2 - 75; - final int startY = height - height / 3 - 9; - final int endX = width/2 + 75; - final int endY = height - height / 3 + 13; - - context.fill(startX+2, startY+2, endX-2, endY-1, (mouseHover ? secondary : primary)); - context.drawBorder(startX+1, startY+1, (endX-1) - (startX+1), (endY) - (startY+1), gaccent); - context.fill(startX+1, endY, endX-1, endY+3, secondary); - context.drawBorder(startX, startY, (endX) - (startX), (endY+4) - (startY), black); - - context.drawCenteredTextWithShadow(textRenderer, respawnMessage, width/2, height - height/3 - 1, textColor); - } - - private void renderMenuButton(DrawContext context, int mouxeX, int mouseY, float opacity){ - if ((int) opacity == 0){ - return; - } - final int primary = new Color(208, 209, 212, (int) opacity).getRGB(); - final int secondary = new Color(88, 88, 90, (int) opacity).getRGB(); - final int buttonAccent = new Color(177, 178, 181, (int) opacity).getRGB(); - final int gaccent = new Color(227, 227, 229, (int) opacity).getRGB(); - final int black = new Color(0, 0, 0, (int) opacity).getRGB(); - final int textColor = new Color(30, 30, 30, (int) opacity).getRGB(); - final boolean mouseHover = hoversMenuButton(mouxeX, mouseY); - - final int startX = width/2 - 75; - final int startY = height - height / 3 - 9 + 30; - final int endX = width/2 + 75; - final int endY = height - height / 3 + 13 + 30; - - context.fill(startX+2, startY+2, endX-2, endY-1, (mouseHover ? buttonAccent : primary)); - context.drawBorder(startX+1, startY+1, (endX-1) - (startX+1), (endY) - (startY+1), gaccent); - context.fill(startX+1, endY, endX-1, endY+3, secondary); - context.drawBorder(startX, startY, (endX) - (startX), (endY+4) - (startY), black); - - context.drawText(textRenderer, menuMessage, (width/2) - (textRenderer.getWidth(menuMessage)/2), height - height/3 - 1 + 31, textColor, false); - } - - private void renderConfirmRespawnButton(DrawContext context, int mouxeX, int mouseY){ - final int primary = new Color(60, 133, 39, (int) (float) 255.0).getRGB(); - final int secondary = new Color(29, 77, 19, (int) (float) 255.0).getRGB(); - final int gaccent = new Color(79, 145, 60, (int) (float) 255.0).getRGB(); - final int black = new Color(0, 0, 0, (int) (float) 255.0).getRGB(); - final int textColor = new Color(255, 255, 255, (int) (float) 255.0).getRGB(); - final boolean mouseHover = hoversMenuButton(mouxeX, mouseY); - - final int startX = width/2 - 75; - final int startY = height - height / 3 - 9 + 30; - final int endX = width/2 + 75; - final int endY = height - height / 3 + 13 + 30; - - context.fill(startX+2, startY+2, endX-2, endY-1, (mouseHover ? secondary : primary)); - context.drawBorder(startX+1, startY+1, (endX-1) - (startX+1), (endY) - (startY+1), gaccent); - context.fill(startX+1, endY, endX-1, endY+3, secondary); - context.drawBorder(startX, startY, (endX) - (startX), (endY+4) - (startY), black); - - context.drawText(textRenderer, respawnMessage, (width/2) - (textRenderer.getWidth(respawnMessage)/2), height - height/3 - 1 + 30, textColor, false); - } - - private void renderConfirmQuitButton(DrawContext context, int mouxeX, int mouseY){ - final int primary = new Color(208, 209, 212, (int) (float) 255.0).getRGB(); - final int secondary = new Color(88, 88, 90, (int) (float) 255.0).getRGB(); - final int buttonAccent = new Color(177, 178, 181, (int) (float) 255.0).getRGB(); - final int gaccent = new Color(227, 227, 229, (int) (float) 255.0).getRGB(); - final int black = new Color(0, 0, 0, (int) (float) 255.0).getRGB(); - final int textColor = new Color(30, 30, 30, (int) (float) 255.0).getRGB(); - final boolean mouseHover = hoversRespawn(mouxeX, mouseY); - - final int startX = width/2 - 75; - final int startY = height - height / 3 - 9; - final int endX = width/2 + 75; - final int endY = height - height / 3 + 13; - - context.fill(startX+2, startY+2, endX-2, endY-1, (mouseHover ? buttonAccent : primary)); - context.drawBorder(startX+1, startY+1, (endX-1) - (startX+1), (endY) - (startY+1), gaccent); - context.fill(startX+1, endY, endX-1, endY+3, secondary); - context.drawBorder(startX, startY, (endX) - (startX), (endY+4) - (startY), black); - - context.drawText(textRenderer, menuMessage, (width/2) - (textRenderer.getWidth(menuMessage)/2), height - height/3 - 1, textColor, false); - } - public float getTotalScreenTime(){ return (Util.getMeasuringTimeNano() - screenCreationTime) / 1000000f; } @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { - boolean hoveringButton = hoversMenuButton(mouseX, mouseY) || hoversRespawn(mouseX, mouseY); - if (hoveringButton != wasHoveringButtons){ - wasHoveringButtons = hoveringButton; - assert client != null; - long window = client.getWindow().getHandle(); - if (hoveringButton){ - GLFW.glfwSetCursor(window, CURSOR_HAND); - } else { - GLFW.glfwSetCursor(window, 0); - } - } final float totalScreenTime = getTotalScreenTime(); context.fill(0, 0, width, height, new Color(0, 0, 0, (int) Math.min(80, totalScreenTime/4f)).getRGB()); + if (totalScreenTime > 750.0f){ final int backOpacity = (int) Math.min(255, (totalScreenTime - 750f) / 10f); context.fill(0, 0, width, height, new Color(155, 0, 0, (int) (backOpacity/2.5)).getRGB()); @@ -194,66 +67,88 @@ public class BedrockDeathScreen extends DeathScreen { context.drawCenteredTextWithShadow(this.textRenderer, this.scoreText, this.width / 2, (int) (this.height / 3.5) + 12, new Color(255, 255, 255, scoreTextOpacity).getRGB()); } } - if (!confirmingExit){ - if (totalScreenTime > 1259f){ - float respawnOpacity = (int) Math.min(255, (totalScreenTime - 1250f) / 3f); - renderRespawnButton(context, mouseX, mouseY, respawnOpacity); - } - if (totalScreenTime > 1759f){ - float menuOpacity = (int) Math.min(255, (totalScreenTime - 1750f) / 3f); - renderMenuButton(context, mouseX, mouseY, menuOpacity); - } - } else { + + if (confirmingExit){ context.drawCenteredTextWithShadow(this.textRenderer, BedrockDeathScreen.confirmQuitText, this.width / 2, this.height - this.height / 3 - 9 - 12, new Color(255, 255, 255, 255).getRGB()); - renderConfirmRespawnButton(context, mouseX, mouseY); - renderConfirmQuitButton(context, mouseX, mouseY); + } + respawnButton.render(context, mouseX, mouseY, delta); + menuButton.render(context, mouseX, mouseY, delta); + + boolean hoveringButton = menuButton.isHovered() || respawnButton.isHovered(); + if (hoveringButton != wasHoveringButtons){ + wasHoveringButtons = hoveringButton; + assert client != null; + long window = client.getWindow().getHandle(); + if (hoveringButton){ + GLFW.glfwSetCursor(window, CURSOR_HAND); + } else { + GLFW.glfwSetCursor(window, 0); + } } } @Override protected void init() { - this.respawnMessage = this.hardcore ? Text.translatable("deathScreen.spectate") : Text.translatable("deathScreen.respawn"); + Text respawnMessage = this.hardcore ? Text.translatable("deathScreen.spectate") : Text.translatable("deathScreen.respawn"); if (this.client != null && this.client.player != null) { this.scoreText = Text.translatable("deathScreen.score.value", Text.literal(Integer.toString(this.client.player.getScore())).formatted(Formatting.YELLOW)); } + respawnButton = new BedrockDeathScreenButton( + width / 2 - 75, + height - height / 3 - 9, + 150, + 22, + respawnMessage, + textRenderer, + 0x3c8527, + 0x1d4d13, + 0x4f913c, + 0x1d4d13, + 0xffffff, + 0x1d4d13, + 0x4a7142, + true, + () -> (int) Math.min(255, (getTotalScreenTime() - 1250f) / 3f), + this::respawn + ); + menuButton = new BedrockDeathScreenButton( + width / 2 - 75, + height - height / 3 + 21, + 150, + 22, + menuMessage, + textRenderer, + 0xd0d1d4, + 0x58585a, + 0xe3e3e5, + 0xb1b2b5, + 0x1e1e1e, + 0xb1b2b5, + 0xe0e0e1, + false, + () -> (int) Math.min(255, (getTotalScreenTime() - 1750f) / 3f), + this::clickQuitButton + ); } private void respawn(){ if (this.client != null && this.client.player != null) { this.client.player.requestRespawn(); } - playUISound(); - } - - private void playUISound() { - if (this.client != null) { - if (this.client.player != null) { - this.client.player.playSoundToPlayer(SoundEvent.of(SOUND), SoundCategory.MASTER, .75f, 1f); - } - } } @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (hoversRespawn((int) mouseX, (int) mouseY)){ - if (!confirmingExit){ - respawn(); - } else { - playUISound(); - quitLevel(); - } - return true; - } else if (hoversMenuButton((int) mouseX, (int) mouseY)){ - if (!confirmingExit){ - playUISound(); - this.confirmingExit = true; - } else { - respawn(); - } - return true; - } - return false; + if (respawnButton.mouseClicked(mouseX, mouseY, button)) return true; + return menuButton.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + respawnButton.mouseReleased(mouseX, mouseY, button); + menuButton.mouseReleased(mouseX, mouseY, button); + return true; } private void quitLevel(){ @@ -296,4 +191,18 @@ public class BedrockDeathScreen extends DeathScreen { GLFW.glfwSetCursor(window, 0); super.removed(); } + + private void clickQuitButton() { + if (confirmingExit) { + this.quitLevel(); + } else { + confirmingExit = true; + int y = menuButton.getY(); + menuButton.setY(respawnButton.getY()); + respawnButton.setY(y); + + menuButton.resetClickedState(); + respawnButton.resetClickedState(); + } + } } diff --git a/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/components/BedrockDeathScreenButton.java b/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/components/BedrockDeathScreenButton.java new file mode 100644 index 0000000..3a16e6d --- /dev/null +++ b/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/components/BedrockDeathScreenButton.java @@ -0,0 +1,142 @@ +package de.shiewk.bedrockdeathscreen.client.screen.components; + +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.sound.SoundManager; +import net.minecraft.text.Text; + +import java.util.function.IntSupplier; + +public class BedrockDeathScreenButton extends ClickableWidget { + + public static final int colorBlack = 0x000000; + public final TextRenderer textRenderer; + public final int colorPrimary; + public final int colorSecondary; + public final int colorBorder; + public final int colorHover; + public final int colorText; + public final int colorPrimaryPressed; + public final int colorBorderPressed; + public final boolean textShadow; + public final IntSupplier opacitySupplier; + public final Runnable clickAction; + + private boolean pressed = false; + private boolean clicked = false; + + public BedrockDeathScreenButton( + int x, int y, int width, int height, + Text message, TextRenderer textRenderer, + int colorPrimary, int colorSecondary, + int colorBorder, int colorHover, + int colorText, int colorPrimaryPressed, int colorBorderPressed, boolean textShadow, + IntSupplier opacitySupplier, Runnable clickAction + ) { + super(x, y, width, height, message); + this.textRenderer = textRenderer; + this.colorPrimary = colorPrimary; + this.colorSecondary = colorSecondary; + this.colorBorder = colorBorder; + this.colorHover = colorHover; + this.colorText = colorText; + this.colorPrimaryPressed = colorPrimaryPressed; + this.colorBorderPressed = colorBorderPressed; + this.textShadow = textShadow; + this.opacitySupplier = opacitySupplier; + this.clickAction = clickAction; + } + + public int changeColorOpacity(int color, int opacity){ + return (color & 0x00ffffff) | (opacity << 24); + } + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float deltaTicks) { + final boolean mouseHover = isHovered(); + + final int opacity = getOpacity(); + if (opacity < 4) return; + final int startX = getX(); + final int startY = getY(); + final int endX = getX() + getWidth(); + final int endY = getY() + getHeight(); + + boolean shouldAppearPressed = pressed || clicked; + if (shouldAppearPressed){ + context.fill(startX+2, startY+4, endX-2, endY+2, changeColorOpacity(colorPrimaryPressed, opacity)); + context.drawBorder(startX+1, startY+3, (endX-1) - (startX+1), (endY+1) - (startY+1), changeColorOpacity(colorBorderPressed, opacity)); + context.drawBorder(startX, startY+2, endX - startX, (endY+2) - startY, changeColorOpacity(colorBlack, opacity)); + } else { + context.fill(startX+2, startY+2, endX-2, endY-1, (mouseHover ? changeColorOpacity(colorHover, opacity) : changeColorOpacity(colorPrimary, opacity))); + context.drawBorder(startX+1, startY+1, (endX-1) - (startX+1), (endY) - (startY+1), changeColorOpacity(colorBorder, opacity)); + context.fill(startX+1, endY, endX-1, endY+3, changeColorOpacity(colorSecondary, opacity)); + context.drawBorder(startX, startY, endX - startX, (endY+4) - startY, changeColorOpacity(colorBlack, opacity)); + } + + if (textShadow){ + context.drawCenteredTextWithShadow( + textRenderer, + getMessage(), + startX+getWidth()/2, + endY - getHeight()/2 - (shouldAppearPressed ? 1 : 3), + changeColorOpacity(colorText, opacity) + ); + } else { + context.drawText( + textRenderer, + getMessage(), + startX + (getWidth()/2) - (textRenderer.getWidth(getMessage())/2), + endY - getHeight()/2 - (shouldAppearPressed ? 1 : 3), + changeColorOpacity(colorText, opacity), + false + ); + } + } + + @Override + public void onClick(double mouseX, double mouseY) { + pressed = true; + } + + @Override + public void onRelease(double mouseX, double mouseY) { + if (pressed && hovered) { + clicked = true; + clickAction.run(); + } + pressed = false; + } + + @Override + public void playDownSound(SoundManager soundManager) { + if (!clicked) super.playDownSound(soundManager); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) {} + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (getOpacity() > 0){ + return super.mouseClicked(mouseX, mouseY, button); + } else { + return false; + } + } + + public void resetClickedState() { + clicked = false; + } + + public int getOpacity(){ + return opacitySupplier.getAsInt(); + } + + @Override + public boolean isHovered() { + return super.isHovered() && getOpacity() > 0; + } +}