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 a7499d5..2036093 100644 --- a/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/BedrockDeathScreen.java +++ b/src/main/java/de/shiewk/bedrockdeathscreen/client/screen/BedrockDeathScreen.java @@ -1,29 +1,20 @@ 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.client.gui.screen.ConfirmScreen; +import net.minecraft.client.gui.screen.DeathScreen; +import net.minecraft.client.gui.screen.MessageScreen; +import net.minecraft.client.gui.screen.TitleScreen; 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 { @@ -31,9 +22,12 @@ public class BedrockDeathScreen extends DeathScreen { private Text scoreText = Text.empty(); private Text message; 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; public BedrockDeathScreen(@Nullable Text message, boolean isHardcore) { super(message, isHardcore); @@ -86,13 +80,7 @@ public class BedrockDeathScreen extends DeathScreen { 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(); + context.drawCenteredTextWithShadow(textRenderer, respawnMessage, (int) (width/2), (int) (height - height/3), textColor); } private void renderMenuButton(DrawContext context, int mouxeX, int mouseY, float delta, float opacity){ @@ -120,7 +108,53 @@ public class BedrockDeathScreen extends DeathScreen { context.drawText(textRenderer, menuMessage, (int) (width/2) - (textRenderer.getWidth(menuMessage)/2), (int) (height - height/3 - 1 + 31), textColor, false); } - private float getTotalDelta(float delta){ + private void renderConfirmRespawnButton(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 = 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, (int) (width/2) - (textRenderer.getWidth(respawnMessage)/2), (int) (height - height/3 - 1 + 30), textColor, false); + } + + private void renderConfirmQuitButton(DrawContext context, int mouxeX, int mouseY, float delta){ + 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, (int) (width/2) - (textRenderer.getWidth(menuMessage)/2), (int) (height - height/3), textColor, false); + } + + public float getTotalDelta(float delta){ return ticksSinceDeath * 50f + delta; } @@ -141,14 +175,21 @@ public class BedrockDeathScreen extends DeathScreen { 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); + if (!confirmingExit){ + 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); + } + } else { + context.drawCenteredTextWithShadow(this.textRenderer, BedrockDeathScreen.confirmQuitText, this.width / 2, (int) (this.height - this.height / 3 - 9 - 12), new Color(255, 255, 255, 255).getRGB()); + renderConfirmRespawnButton(context, mouseX, mouseY, delta, 255f); + renderConfirmQuitButton(context, mouseX, mouseY, delta); } + } @Override @@ -163,19 +204,77 @@ public class BedrockDeathScreen extends DeathScreen { ticksSinceDeath++; } + private void respawn(){ + ticksSinceDeath = 0; + this.client.player.requestRespawn(); + playUISound(); + } + + private void playUISound() { + this.client.player.playSoundToPlayer(SoundEvent.of(SOUND), SoundCategory.MASTER, 1f, 1f); + } + @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); + 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; } - @Override - public boolean shouldCloseOnEsc() { - return true; + private void quitLevel(){ + if (this.client.world != null) { + this.client.world.disconnect(); + } + + this.client.disconnect(new MessageScreen(Text.translatable("menu.savingLevel"))); + this.client.setScreen(new TitleScreen()); + } + + private void onTitleScreenButtonClicked(){ + if (this.hardcore) { + this.quitLevel(); + } else { + final MutableText titleScreenText = Text.translatable("deathScreen.titleScreen"); + ConfirmScreen confirmScreen = new TitleScreenConfirmScreen((confirmed) -> { + if (confirmed) { + this.quitLevel(); + } else { + respawn(); + } + + }, confirmQuitText, ScreenTexts.EMPTY, titleScreenText, Text.translatable("deathScreen.respawn")); + this.client.setScreen(confirmScreen); + confirmScreen.disableButtons(20); + } + } + + public float calcCamPitch() { + return 20; + } + + public float calcCamYaw(){ + return deg < 180 ? deg : deg-360; + } + + public float calcCameraOffset(float delta){ + final double e = Math.E; + final double del = getTotalDelta(delta) / 600d; + final double ep = Math.pow(e, del); + return (float) (ep / (1d + ep) * 7d + 2d); } } diff --git a/src/main/java/de/shiewk/bedrockdeathscreen/mixin/MixinCamera.java b/src/main/java/de/shiewk/bedrockdeathscreen/mixin/MixinCamera.java new file mode 100644 index 0000000..0a7a990 --- /dev/null +++ b/src/main/java/de/shiewk/bedrockdeathscreen/mixin/MixinCamera.java @@ -0,0 +1,47 @@ +package de.shiewk.bedrockdeathscreen.mixin; + +import de.shiewk.bedrockdeathscreen.client.screen.BedrockDeathScreen; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.Camera; +import net.minecraft.entity.Entity; +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.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(Camera.class) +public class MixinCamera { + + @Shadow private float lastTickDelta; + + @Redirect(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;getYaw(F)F")) + public float onYawGet(Entity instance, float tickDelta){ + if (MinecraftClient.getInstance().currentScreen instanceof BedrockDeathScreen bedrockDeathScreen){ + return bedrockDeathScreen.calcCamYaw(); + } else { + return instance.getYaw(tickDelta); + } + } + + @Redirect(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;getPitch(F)F")) + public float onPitchGet(Entity instance, float tickDelta){ + if (MinecraftClient.getInstance().currentScreen instanceof BedrockDeathScreen bedrockDeathScreen){ + return bedrockDeathScreen.calcCamPitch(); + } else { + return instance.getPitch(tickDelta); + } + } + + @ModifyConstant(method = "update", constant = @Constant(floatValue = 4.0f)) + public float onUpdateThirdPerson(float constant){ + final MinecraftClient client = MinecraftClient.getInstance(); + if (client.currentScreen instanceof BedrockDeathScreen bedrockDeathScreen){ + final float delta = client.getTickDelta(); + return bedrockDeathScreen.calcCameraOffset(delta); + } else { + return 4.0f; + } + } +} diff --git a/src/main/java/de/shiewk/bedrockdeathscreen/mixin/MixinGameRenderer.java b/src/main/java/de/shiewk/bedrockdeathscreen/mixin/MixinGameRenderer.java new file mode 100644 index 0000000..d4f1ef5 --- /dev/null +++ b/src/main/java/de/shiewk/bedrockdeathscreen/mixin/MixinGameRenderer.java @@ -0,0 +1,51 @@ +package de.shiewk.bedrockdeathscreen.mixin; + +import de.shiewk.bedrockdeathscreen.client.screen.BedrockDeathScreen; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.world.BlockView; +import org.joml.Matrix4f; +import org.spongepowered.asm.mixin.Final; +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.CallbackInfoReturnable; + +@Mixin(GameRenderer.class) +public abstract class MixinGameRenderer { + + @Shadow @Final MinecraftClient client; + + @Shadow private float fovMultiplier; + + @Shadow public abstract void tick(); + + @Shadow protected abstract void renderHand(Camera camera, float tickDelta, Matrix4f matrix4f); + + @Inject(at = @At("TAIL"), method = "getFov", cancellable = true) + public void onFovGet(Camera camera, float tickDelta, boolean changingFov, CallbackInfoReturnable cir){ + if (client.currentScreen instanceof BedrockDeathScreen bedrockDeathScreen){ + cir.setReturnValue((double) Math.min(60 + bedrockDeathScreen.getTotalDelta(client.getTickDelta()) / (405d), 80)); + } + } + + @Redirect(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/Camera;update(Lnet/minecraft/world/BlockView;Lnet/minecraft/entity/Entity;ZZF)V")) + public void onCameraUpdate(Camera instance, BlockView area, Entity focusedEntity, boolean thirdPerson, boolean inverseView, float tickDelta){ + if (client.currentScreen instanceof BedrockDeathScreen bedrockDeathScreen){ + instance.update(area, focusedEntity, true, false, tickDelta); + } else { + instance.update(area, focusedEntity, thirdPerson, inverseView, tickDelta); + } + } + + @Redirect(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;renderHand(Lnet/minecraft/client/render/Camera;FLorg/joml/Matrix4f;)V")) + public void onCameraUpdate(GameRenderer instance, Camera camera, float tickDelta, Matrix4f matrix4f){ + if (!(instance.getClient().currentScreen instanceof BedrockDeathScreen)){ + renderHand(camera, tickDelta, matrix4f); + } + } +} diff --git a/src/main/resources/bedrockdeathscreen.mixins.json b/src/main/resources/bedrockdeathscreen.mixins.json index f8eb60f..4023863 100644 --- a/src/main/resources/bedrockdeathscreen.mixins.json +++ b/src/main/resources/bedrockdeathscreen.mixins.json @@ -6,7 +6,9 @@ "mixins": [ ], "client": [ - "MixinClientPlayNetworkHandler" + "MixinCamera", + "MixinClientPlayNetworkHandler", + "MixinGameRenderer" ], "injectors": { "defaultRequire": 1 diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index fca6584..f11aa51 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -27,6 +27,6 @@ "depends": { "fabricloader": ">=${loader_version}", "fabric": "*", - "minecraft": "${minecraft_version}" + "minecraft": ">=${minecraft_version}" } }