diff --git a/src/main/java/de/shiewk/bedrockdeathscreen/BedrockDeathScreen.java b/src/main/java/de/shiewk/bedrockdeathscreen/BedrockDeathScreenMod.java similarity index 68% rename from src/main/java/de/shiewk/bedrockdeathscreen/BedrockDeathScreen.java rename to src/main/java/de/shiewk/bedrockdeathscreen/BedrockDeathScreenMod.java index ed0b077..12e4208 100644 --- a/src/main/java/de/shiewk/bedrockdeathscreen/BedrockDeathScreen.java +++ b/src/main/java/de/shiewk/bedrockdeathscreen/BedrockDeathScreenMod.java @@ -2,7 +2,7 @@ package de.shiewk.bedrockdeathscreen; import net.fabricmc.api.ModInitializer; -public class BedrockDeathScreen implements ModInitializer { +public class BedrockDeathScreenMod implements ModInitializer { @Override public void onInitialize() { diff --git a/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/BedrockDeathScreen.java b/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/BedrockDeathScreen.java new file mode 100644 index 0000000..a7499d5 --- /dev/null +++ b/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/BedrockDeathScreen.java @@ -0,0 +1,181 @@ +package de.shiewk.bedrockdeathscreen.client.screen; + +import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.booleans.BooleanConsumer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.*; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.screen.ScreenTexts; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvent; +import net.minecraft.text.ClickEvent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector2d; + +import java.awt.*; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +public class BedrockDeathScreen extends DeathScreen { + + private int ticksSinceDeath = 0; + private Text scoreText = Text.empty(); + private Text message; + private static final Text menuMessage = Text.translatable("deathScreen.titleScreen"); + private Text respawnMessage; + private final boolean hardcore; + private final Identifier SOUND = Identifier.of("minecraft", "ui.button.click"); + + public BedrockDeathScreen(@Nullable Text message, boolean isHardcore) { + super(message, isHardcore); + this.message = message; + this.hardcore = isHardcore; + } + + 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 getTotalDelta(0) > 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 getTotalDelta(0) > 1050 && + mouseX > startX && + mouseX < endX && + mouseY > startY && + mouseY < endY; + } + + private void renderRespawnButton(DrawContext context, int mouxeX, int mouseY, float delta, 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); + + final MatrixStack matrices = context.getMatrices(); + matrices.push(); + matrices.scale(1.25f, 1.25f, 1.25f); + + context.drawCenteredTextWithShadow(textRenderer, respawnMessage, (int) (width/2/1.25), (int) (height/1.25 - height/1.25/3 - 1), textColor); + + matrices.pop(); + } + + private void renderMenuButton(DrawContext context, int mouxeX, int mouseY, float delta, 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, (int) (width/2) - (textRenderer.getWidth(menuMessage)/2), (int) (height - height/3 - 1 + 31), textColor, false); + } + + private float getTotalDelta(float delta){ + return ticksSinceDeath * 50f + delta; + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + final float totalDelta = getTotalDelta(delta); + context.fill(0, 0, width, height, new Color(0, 0, 0, (int) Math.min(80, totalDelta/4f)).getRGB()); + if (totalDelta > 750.0f){ + final int backOpacity = (int) Math.min(255, (totalDelta - 750f) / 10f); + context.fill(0, 0, width, height, new Color(155, 0, 0, (int) (backOpacity/2.5)).getRGB()); + + final int textOpacity = (int) Math.min(255, (totalDelta - 750f) / 3f); + if (textOpacity > 0){ + context.getMatrices().push(); + context.getMatrices().scale(2F, 2F, 2F); + context.drawCenteredTextWithShadow(this.textRenderer, this.title, (int) (this.width / 2 / 2), (int) (this.height / 3.5 / 2 - 10), new Color(255, 255, 255, textOpacity).getRGB()); + context.getMatrices().pop(); + context.drawCenteredTextWithShadow(this.textRenderer, this.message, this.width / 2, (int) (this.height / 3.5), new Color(255, 255, 255, textOpacity).getRGB()); + } + } + if (totalDelta > 1250f){ + float respawnOpacity = (int) Math.min(255, (totalDelta - 1250f) / 3f); + renderRespawnButton(context, mouseX, mouseY, delta, respawnOpacity); + } + if (totalDelta > 1750f){ + float menuOpacity = (int) Math.min(255, (totalDelta - 1750f) / 3f); + renderMenuButton(context, mouseX, mouseY, delta, menuOpacity); + } + } + + @Override + protected void init() { + this.respawnMessage = this.hardcore ? Text.translatable("deathScreen.spectate") : Text.translatable("deathScreen.respawn"); + this.scoreText = Text.translatable("deathScreen.score.value", new Object[]{Text.literal(Integer.toString(this.client.player.getScore())).formatted(Formatting.YELLOW)}); + } + + @Override + public void tick() { + super.tick(); + ticksSinceDeath++; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (hoversRespawn((int) mouseX, (int) mouseY)){ + ticksSinceDeath = 0; + this.client.player.requestRespawn(); + this.client.player.playSoundToPlayer(SoundEvent.of(SOUND), SoundCategory.MASTER, 1f, 1f); + return true; + } + return false; + } + + @Override + public boolean shouldCloseOnEsc() { + return true; + } +} diff --git a/src/main/java/de/shiewk/bedrockdeathscreen/mixin/MixinClientPlayNetworkHandler.java b/src/main/java/de/shiewk/bedrockdeathscreen/mixin/MixinClientPlayNetworkHandler.java new file mode 100644 index 0000000..87de40e --- /dev/null +++ b/src/main/java/de/shiewk/bedrockdeathscreen/mixin/MixinClientPlayNetworkHandler.java @@ -0,0 +1,32 @@ +package de.shiewk.bedrockdeathscreen.mixin; + +import com.mojang.brigadier.ParseResults; +import de.shiewk.bedrockdeathscreen.client.screen.BedrockDeathScreen; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.DeathScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.command.CommandSource; +import net.minecraft.entity.Entity; +import net.minecraft.network.packet.s2c.play.DeathMessageS2CPacket; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ClientPlayNetworkHandler.class) +public abstract class MixinClientPlayNetworkHandler { + + @Shadow private ClientWorld world; + + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setScreen(Lnet/minecraft/client/gui/screen/Screen;)V"), method = "onDeathMessage", cancellable = true) + private void onDeathScreenShow(DeathMessageS2CPacket packet, CallbackInfo ci){ + MinecraftClient.getInstance().setScreen(new BedrockDeathScreen(packet.message(), this.world.getLevelProperties().isHardcore())); + ci.cancel(); + } +} diff --git a/src/main/resources/bedrockdeathscreen.mixins.json b/src/main/resources/bedrockdeathscreen.mixins.json index 08ea77c..f8eb60f 100644 --- a/src/main/resources/bedrockdeathscreen.mixins.json +++ b/src/main/resources/bedrockdeathscreen.mixins.json @@ -6,6 +6,7 @@ "mixins": [ ], "client": [ + "MixinClientPlayNetworkHandler" ], "injectors": { "defaultRequire": 1 diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index cf8bf55..fca6584 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -18,7 +18,7 @@ "de.shiewk.bedrockdeathscreen.client.BedrockDeathScreenClient" ], "main": [ - "de.shiewk.bedrockdeathscreen.BedrockDeathScreen" + "de.shiewk.bedrockdeathscreen.BedrockDeathScreenMod" ] }, "mixins": [