diff --git a/README.md b/README.md index 9947adb..aad4fd5 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Suggest more tweaks in the issue tracker! * Crying Obsidian Nether Portal * Allow Leashing Villagers * Disable Instant Nether Portal In Creative +* Add Note Block Screen ## Changelog [View Changelog](CHANGELOG.md) diff --git a/src/main/java/com/thebrokenrail/slightlyvanilla/ModConfig.java b/src/main/java/com/thebrokenrail/slightlyvanilla/ModConfig.java index 7d0a815..1d09b1c 100644 --- a/src/main/java/com/thebrokenrail/slightlyvanilla/ModConfig.java +++ b/src/main/java/com/thebrokenrail/slightlyvanilla/ModConfig.java @@ -3,6 +3,9 @@ package com.thebrokenrail.slightlyvanilla; import me.sargunvohra.mcmods.autoconfig1u.ConfigData; import me.sargunvohra.mcmods.autoconfig1u.annotation.Config; import me.sargunvohra.mcmods.autoconfig1u.annotation.ConfigEntry; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; @Config(name = SlightlyVanilla.NAMESPACE) public class ModConfig implements ConfigData { @@ -16,6 +19,8 @@ public class ModConfig implements ConfigData { public boolean cryingObsidianNetherPortal = true; public boolean allowLeashingVillagers = true; public boolean disableInstantNetherPortalInCreative = true; + @Environment(EnvType.CLIENT) + public boolean noteBlockScreen = true; public static class ThrowableOption { public boolean player; diff --git a/src/main/java/com/thebrokenrail/slightlyvanilla/client/screen/NoteBlockScreen.java b/src/main/java/com/thebrokenrail/slightlyvanilla/client/screen/NoteBlockScreen.java new file mode 100644 index 0000000..d7bc111 --- /dev/null +++ b/src/main/java/com/thebrokenrail/slightlyvanilla/client/screen/NoteBlockScreen.java @@ -0,0 +1,144 @@ +package com.thebrokenrail.slightlyvanilla.client.screen; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.thebrokenrail.slightlyvanilla.SlightlyVanilla; +import com.thebrokenrail.slightlyvanilla.mixin.ScreenHandlerAccessor; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.SliderWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket; +import net.minecraft.screen.ScreenHandlerContext; +import net.minecraft.state.property.Properties; +import net.minecraft.text.LiteralText; +import net.minecraft.text.TranslatableText; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +import java.util.function.BiFunction; + +@Environment(EnvType.CLIENT) +public class NoteBlockScreen extends Screen { + private static class NoteSliderWidget extends SliderWidget { + private final ScreenHandlerContext context; + + public NoteSliderWidget(ScreenHandlerContext context, int x, int y, int width, int height) { + super(x, y, width, height, LiteralText.EMPTY, (double) getNote(context) / getMax()); + this.context = context; + updateMessage(); + } + + private static double getMax() { + return (double) Properties.NOTE.getValues().size() - 1; + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + private static int getNote(ScreenHandlerContext context) { + return context.run((BiFunction) (world, pos) -> world.getBlockState(pos).get(Properties.NOTE)).get(); + } + + private int toNote() { + return (int) (value * getMax()); + } + + @Override + protected void updateMessage() { + int note = toNote(); + setMessage(new TranslatableText("text." + SlightlyVanilla.NAMESPACE + ".noteblock_screen_slider", note, new TranslatableText("text." + SlightlyVanilla.NAMESPACE + ".noteblock_note." + note))); + } + + @Override + protected void applyValue() { + context.run((world, blockPos) -> { + BlockState target = world.getBlockState(blockPos); + + int currentNote = target.get(Properties.NOTE); + int targetNote = toNote(); + + int amount; + if (targetNote >= currentNote) { + amount = targetNote - currentNote; + } else { + amount = ((int) getMax()) - currentNote + 1 + targetNote; + } + + MinecraftClient client = MinecraftClient.getInstance(); + assert client != null; + assert client.getNetworkHandler() != null; + + BlockHitResult hitResult = new BlockHitResult(Vec3d.ofCenter(blockPos), Direction.UP, blockPos, false); + for (int i = 0; i < amount; i++) { + client.getNetworkHandler().sendPacket(new PlayerInteractBlockC2SPacket(Hand.MAIN_HAND, hitResult)); + } + + world.setBlockState(blockPos, target.with(Properties.NOTE, targetNote)); + }); + } + } + + private static final Identifier TEXTURE = new Identifier(SlightlyVanilla.NAMESPACE, "textures/gui/noteblock.png"); + + private final ScreenHandlerContext context; + + public NoteBlockScreen(ScreenHandlerContext context) { + super(Blocks.NOTE_BLOCK.getName()); + passEvents = true; + this.context = context; + } + + @Override + protected void init() { + super.init(); + int sliderWidth = BACKGROUND_WIDTH - 36; + int sliderHeight = 20; + addButton(new NoteSliderWidget(context, width / 2 - sliderWidth / 2, (height / 2) - (sliderHeight / 2) + 4, sliderWidth, sliderHeight)); + } + + private static final int BACKGROUND_WIDTH = 176; + private static final int BACKGROUND_HEIGHT = 60; + + private static final int TITLE_X_OFFSET = 8; + private static final int TITLE_Y_OFFSEt = 6; + + @Override + public void renderBackground(MatrixStack matrices) { + super.renderBackground(matrices); + + //noinspection deprecation + RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); + assert client != null; + client.getTextureManager().bindTexture(TEXTURE); + int centerX = width / 2; + int centerY = height / 2; + drawTexture(matrices, centerX - (BACKGROUND_WIDTH / 2), centerY - (BACKGROUND_HEIGHT / 2), 0, 0, BACKGROUND_WIDTH, BACKGROUND_HEIGHT); + + int titleX = centerX - (BACKGROUND_WIDTH / 2) + TITLE_X_OFFSET; + int titleY = centerY - (BACKGROUND_HEIGHT / 2) + TITLE_Y_OFFSEt; + textRenderer.draw(matrices, title, titleX, titleY, 4210752); + } + + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + renderBackground(matrices); + super.render(matrices, mouseX, mouseY, delta); + } + + @Override + public void tick() { + super.tick(); + assert client != null; + if (!ScreenHandlerAccessor.callCanUse(context, client.player, Blocks.NOTE_BLOCK)) { + assert client.player != null; + client.player.closeScreen(); + } + } +} diff --git a/src/main/java/com/thebrokenrail/slightlyvanilla/mixin/MixinClientPlayerInteractionManager.java b/src/main/java/com/thebrokenrail/slightlyvanilla/mixin/MixinClientPlayerInteractionManager.java new file mode 100644 index 0000000..48b2df5 --- /dev/null +++ b/src/main/java/com/thebrokenrail/slightlyvanilla/mixin/MixinClientPlayerInteractionManager.java @@ -0,0 +1,39 @@ +package com.thebrokenrail.slightlyvanilla.mixin; + +import com.thebrokenrail.slightlyvanilla.SlightlyVanilla; +import com.thebrokenrail.slightlyvanilla.client.screen.NoteBlockScreen; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.screen.ScreenHandlerContext; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +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.callback.CallbackInfoReturnable; + +@Environment(EnvType.CLIENT) +@Mixin(ClientPlayerInteractionManager.class) +public class MixinClientPlayerInteractionManager { + @Shadow + @Final + private MinecraftClient client; + + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getStackInHand(Lnet/minecraft/util/Hand;)Lnet/minecraft/item/ItemStack;", ordinal = 0), method = "interactBlock", cancellable = true) + public void interactBlock(ClientPlayerEntity player, ClientWorld world, Hand hand, BlockHitResult hitResult, CallbackInfoReturnable info) { + if (!player.isSpectator() && SlightlyVanilla.getConfig().noteBlockScreen && world.getBlockState(hitResult.getBlockPos()).getBlock() == Blocks.NOTE_BLOCK) { + NoteBlockScreen screen = new NoteBlockScreen(ScreenHandlerContext.create(world, hitResult.getBlockPos())); + client.openScreen(screen); + + info.setReturnValue(ActionResult.SUCCESS); + } + } +} diff --git a/src/main/java/com/thebrokenrail/slightlyvanilla/mixin/ScreenHandlerAccessor.java b/src/main/java/com/thebrokenrail/slightlyvanilla/mixin/ScreenHandlerAccessor.java new file mode 100644 index 0000000..cea3cc1 --- /dev/null +++ b/src/main/java/com/thebrokenrail/slightlyvanilla/mixin/ScreenHandlerAccessor.java @@ -0,0 +1,19 @@ +package com.thebrokenrail.slightlyvanilla.mixin; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.block.Block; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.ScreenHandlerContext; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Environment(EnvType.CLIENT) +@Mixin(ScreenHandler.class) +public interface ScreenHandlerAccessor { + @Invoker + static boolean callCanUse(ScreenHandlerContext context, PlayerEntity player, Block block) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/resources/assets/slightlyvanilla/lang/en_us.json b/src/main/resources/assets/slightlyvanilla/lang/en_us.json index 8bc83dc..c534ceb 100644 --- a/src/main/resources/assets/slightlyvanilla/lang/en_us.json +++ b/src/main/resources/assets/slightlyvanilla/lang/en_us.json @@ -12,6 +12,33 @@ "text.autoconfig.slightlyvanilla.option.cryingObsidianNetherPortal": "Crying Obsidian Nether Portal", "text.autoconfig.slightlyvanilla.option.allowLeashingVillagers": "Allow Leashing Villagers", "text.autoconfig.slightlyvanilla.option.disableInstantNetherPortalInCreative": "Disable Instant Nether Portal In Creative", + "text.autoconfig.slightlyvanilla.option.noteBlockScreen": "Add Note Block Screen", "entity.slightlyvanilla.slimeball": "Slimeball", - "entity.slightlyvanilla.spawn_egg": "Spawn Egg" + "entity.slightlyvanilla.spawn_egg": "Spawn Egg", + "text.slightlyvanilla.noteblock_screen_slider": "Note: %s (%s)", + "text.slightlyvanilla.noteblock_note.0": "F#", + "text.slightlyvanilla.noteblock_note.1": "G", + "text.slightlyvanilla.noteblock_note.2": "G#", + "text.slightlyvanilla.noteblock_note.3": "A", + "text.slightlyvanilla.noteblock_note.4": "A#", + "text.slightlyvanilla.noteblock_note.5": "B", + "text.slightlyvanilla.noteblock_note.6": "C", + "text.slightlyvanilla.noteblock_note.7": "C#", + "text.slightlyvanilla.noteblock_note.8": "D", + "text.slightlyvanilla.noteblock_note.9": "D#", + "text.slightlyvanilla.noteblock_note.10": "E", + "text.slightlyvanilla.noteblock_note.11": "F", + "text.slightlyvanilla.noteblock_note.12": "F#", + "text.slightlyvanilla.noteblock_note.13": "G", + "text.slightlyvanilla.noteblock_note.14": "G#", + "text.slightlyvanilla.noteblock_note.15": "A", + "text.slightlyvanilla.noteblock_note.16": "A#", + "text.slightlyvanilla.noteblock_note.17": "B", + "text.slightlyvanilla.noteblock_note.18": "C", + "text.slightlyvanilla.noteblock_note.19": "C#", + "text.slightlyvanilla.noteblock_note.20": "D", + "text.slightlyvanilla.noteblock_note.21": "D#", + "text.slightlyvanilla.noteblock_note.22": "E", + "text.slightlyvanilla.noteblock_note.23": "F", + "text.slightlyvanilla.noteblock_note.24": "F#" } \ No newline at end of file diff --git a/src/main/resources/assets/slightlyvanilla/textures/gui/noteblock.png b/src/main/resources/assets/slightlyvanilla/textures/gui/noteblock.png new file mode 100644 index 0000000..0a70801 Binary files /dev/null and b/src/main/resources/assets/slightlyvanilla/textures/gui/noteblock.png differ diff --git a/src/main/resources/slightlyvanilla.mixins.json b/src/main/resources/slightlyvanilla.mixins.json index 9d8a8a5..ad014fa 100644 --- a/src/main/resources/slightlyvanilla.mixins.json +++ b/src/main/resources/slightlyvanilla.mixins.json @@ -3,16 +3,18 @@ "package": "com.thebrokenrail.slightlyvanilla.mixin", "compatibilityLevel": "JAVA_8", "client": [ - "MixinClientPlayNetworkHandler" + "MixinClientPlayNetworkHandler", + "ScreenHandlerAccessor" ], "mixins": [ - "MixinRespawnAnchorBlock", - "MixinPlayerEntity", + "MixinAbstractTraderEntity", + "MixinClientPlayerInteractionManager", "MixinItem", - "MixinSpawnEggItem", "MixinLootableContainerBlockEntity", "MixinNetherPortalBlockAreaHelper", - "MixinAbstractTraderEntity" + "MixinPlayerEntity", + "MixinRespawnAnchorBlock", + "MixinSpawnEggItem" ], "injectors": { "defaultRequire": 1