From b6614da3250ba0f70537896603b1070d15e72527 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sat, 12 Sep 2020 22:46:06 -0400 Subject: [PATCH] Add Note Block Screen Tweak --- README.md | 1 + .../slightlyvanilla/ModConfig.java | 5 + .../client/screen/NoteBlockScreen.java | 144 ++++++++++++++++++ .../MixinClientPlayerInteractionManager.java | 39 +++++ .../mixin/ScreenHandlerAccessor.java | 19 +++ .../assets/slightlyvanilla/lang/en_us.json | 29 +++- .../textures/gui/noteblock.png | Bin 0 -> 8720 bytes .../resources/slightlyvanilla.mixins.json | 12 +- 8 files changed, 243 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/thebrokenrail/slightlyvanilla/client/screen/NoteBlockScreen.java create mode 100644 src/main/java/com/thebrokenrail/slightlyvanilla/mixin/MixinClientPlayerInteractionManager.java create mode 100644 src/main/java/com/thebrokenrail/slightlyvanilla/mixin/ScreenHandlerAccessor.java create mode 100644 src/main/resources/assets/slightlyvanilla/textures/gui/noteblock.png 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 0000000000000000000000000000000000000000..0a70801040fae7e91d46e7686a638f8cd1a1da25 GIT binary patch literal 8720 zcmb7}b#PqWj^Mk^%yAquL(I(F=9tN z+1=WE>Yl5TZmD#BlKwdgQBsgXMZ!k{005{m(jYJZ0Q@5Y0SIt^K05Y=CIA3Isk^F% zBiO)&)Yjg{#LUu|)X~k>nAF(S?2plPc@6T}KBpztf1@f?6RK4c&9Zl`i{VE*)OXoj z(^)}5MF+lSx~a~%p`h%Bj3XE6WfTjLvVa3)d-?1m?UHjypX%Cr|(B} zztzXjV~XD&pd5-s&9^O3~zLI1l|p@ z2WFHxms>eZu=p_i9g;!z^Y^Z z>usy?#DcuZF9X*SPL#l&jW^{Cz2{V6^D-uCHohNAp-xtQH7J*m=aClMV9mshZmx^d ziOxbOm$eM4vehW z;P_1lRW+Na#PEq($QHZE%UqbW;w|ghxHDu-Iu_sv$^=lkwt_daYV%~R4<8}qP0=3A&%j3i-Yf+WI+Gc(p_+~adEjSx*b;R&vuTtQcmJ{_or*QA0 z`Sr=y3z9>9%kz}OXW5@f;`Xrh1-O!9Rb|P3dhXQGwGs`K6EYYbxCo8SL{})bORgrH z5ZSqDveKoOX^OI}izN=-^?6h{jhXx3W*32}#6dh1g+EBP!!uvKg4ul^@5sgxjyC=$ zrD7mL>#QFMwHrVzC|$~OX4Js2f;BFV!4A~@x!85R?}Gq_{~1_dl|1?CD#g-rCPC1V z3D9HiCFNdc7)wD(my||-5>p@~)I1~=p*{0d?`XKR)LX^9C*IFOR`^>^=mSako1fIp zF@ZRGQIH`2^;o0&$J5fh2%MFhX7?93S7&(kS4&p2TyVU+90QMhoM=jY&UWFd_iwG^ z(Z$8>V!{hP9hxhTIzfM}fYdP5iJW2K^!t*51R#jo1TpmRVo%4qf?`f4*t6sLTJ3-g z;niH{<|{erBRiKEO%`pF&oS0q%yg`3NIp2~Ik>-n4kBs;;~sZNVhL{|f5yeI2c$FU#1X+h$N(Jy<-8flW+ts8gIy zhTWlMY6jw%XVHKz&-D05R@DNe!&)o=F3WhzvKrgvr~`#6DGcge9e z@t-Qk{Ag$Nnm-+MLA(ldvUiERj-L=M&91o0rVexaCj?E zt}*frZHL0Pevey0Eu6?^OC{@a{VjK4-|Yh)Pr{Bi+?{B;-XFblVu>h%nS+@jPrf&A zIH`l9)f8W$8q`7i#G@sxP)Oh0QKf7{>M?~d=fHRacQdAy`>Qv$le8vBL})v=8G6+F zyBXRpEi|sBr$eN>M#JOB#*!o@IM`g(A_~*jy0vUn>6v(|^=j^Q9&?PF6$mEIh?8%5 zcpYodreWL4?g$rM%QDOUTISJrRpc*(xVU=ykI+PEqBZleQP3nx9npkzASV01J1u zX+xN@lhjI_ zR84IQ|8g7{OdOEByzg(2s>(Y_x&oA&_@G9=q5HZ}MP15JWqhL3>EGUfOM%HrcfHL3 z@=K5}rF5r%23+*|L=1 zW9~fIP5whWY}sQ^drMT2nhvCCrI=+AIcJFr=JtmsJqXT?zqxEcbYpNz-kYM$78jD@ z{Hi48?>LC9aczmpU3(-=heW{^nf)oi$xv;^PyV<~*y9~!02ZTO^qn28ls5yT4mrN; z=xT_*zcykges%SUw8M-fcPB zZi(0ROMHnWW0l!UQYnI)B*c!y{t`0zJl%3Ds@Qb}pDmqvue;)7!Jx4VIW-b`j3}Q^ zF_Q<8o}AO7b{FhXvQmRs1SU|)3Ym9PPSX-SZYn_auJnt}#~In?fh9^yY7UD>RUO}n zV4Uwd%Z3o(tA|x%P+k{0MoCI^V%!FAPfiWA{g6rBhzUpS4YEzKhl{Ua5~I-wfFJ~q zJZLJ{BFMTDH-QQ^eR%DAcA&c-1f5mhhgH+{MAZ8rsx_kMJ7OJTmZ(G?J@Luk_PH zeCe{oYj%yEE?ry+K-b+9WKSSqV=qZPXhaZCZWL}Hmi(f;e
    ?LvO*Z+%Q$`6b=Z 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