From d07b3f7626ab4b817eaf960964cf02af9a9d1afd Mon Sep 17 00:00:00 2001 From: StavWasPlayZ Date: Sat, 22 Jul 2023 14:40:40 +0300 Subject: [PATCH 1/3] Migrate mod version v3.1 to Fabric --- gradle.properties | 2 +- .../client/config/ModClientConfigs.java | 10 +- .../config/enumType/label/NoteGridLabel.java | 10 +- .../instrument/drum/DrumNoteButton.java | 14 +- .../partial/AbstractInstrumentScreen.java | 70 +----- .../instrument/partial/note/NoteButton.java | 205 ++--------------- .../partial/note/NoteButtonRenderer.java | 215 ++++++++++++++++++ .../partial/note/NoteGridButton.java | 15 +- .../animation/NoteAnimationController.java | 2 +- .../AbstractInstrumentOptionsScreen.java | 92 ++++---- .../instrument/FloralZitherOptionsScreen.java | 6 +- .../GridInstrumentOptionsScreen.java | 8 + .../packets/instrument/InstrumentPacket.java | 8 +- .../packets/instrument/PlayNotePacket.java | 8 +- .../genshinstrument/sound/NoteSound.java | 48 +++- .../LabelUtil.java} | 52 +---- .../genshinstrument/util/ServerUtil.java | 8 +- .../resources/assets/genshinstrument/icon.png | Bin 72267 -> 11817 bytes .../models/item/instrument.json | 6 + .../textures/item/floral_zither.png | Bin 6025 -> 2367 bytes .../textures/item/vintage_lyre.png | Bin 308 -> 541 bytes .../textures/item/windsong_lyre.png | Bin 308 -> 536 bytes src/main/resources/fabric.mod.json | 2 +- 23 files changed, 405 insertions(+), 376 deletions(-) create mode 100644 src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButtonRenderer.java rename src/main/java/com/cstav/genshinstrument/{client/gui/screens/instrument/partial/note/label/AbsGridLabels.java => util/LabelUtil.java} (73%) diff --git a/gradle.properties b/gradle.properties index 3300eab..4ab3cf8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ parchment_mappings=1.20.1:2023.07.02 loader_version=0.14.21 # Mod Properties -mod_version=3.0 +mod_version=3.1 maven_group=com.cstav.genshinstrument archives_base_name=genshinstrument diff --git a/src/main/java/com/cstav/genshinstrument/client/config/ModClientConfigs.java b/src/main/java/com/cstav/genshinstrument/client/config/ModClientConfigs.java index 6c9be5c..9cd4afa 100644 --- a/src/main/java/com/cstav/genshinstrument/client/config/ModClientConfigs.java +++ b/src/main/java/com/cstav/genshinstrument/client/config/ModClientConfigs.java @@ -11,14 +11,14 @@ import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.common.ForgeConfigSpec.BooleanValue; import net.minecraftforge.common.ForgeConfigSpec.Builder; -import net.minecraftforge.common.ForgeConfigSpec.DoubleValue; import net.minecraftforge.common.ForgeConfigSpec.EnumValue; +import net.minecraftforge.common.ForgeConfigSpec.IntValue; @Environment(EnvType.CLIENT) public class ModClientConfigs { public static final ForgeConfigSpec CONFIGS; - public static final DoubleValue PITCH; + public static final IntValue PITCH; public static final EnumValue GRID_LABEL_TYPE; public static final EnumValue CHANNEL_TYPE; public static final BooleanValue STOP_MUSIC_ON_PLAY, EMIT_RING_ANIMATION, SHARED_INSTRUMENT, @@ -33,7 +33,7 @@ public class ModClientConfigs { PITCH = configBuilder.defineInRange("instrument_pitch", - 1, doubleMe(NoteSound.MIN_PITCH), doubleMe(NoteSound.MAX_PITCH) + 1, NoteSound.MIN_PITCH, NoteSound.MAX_PITCH ); GRID_LABEL_TYPE = configBuilder.defineEnum("label_type", NoteGridLabel.KEYBOARD_LAYOUT); CHANNEL_TYPE = configBuilder.defineEnum("channel_type", InstrumentChannelType.MIXED); @@ -56,8 +56,4 @@ public class ModClientConfigs { CONFIGS = configBuilder.build(); } - - private static double doubleMe(final float num) { - return Double.valueOf(Float.toString(num)); - } } diff --git a/src/main/java/com/cstav/genshinstrument/client/config/enumType/label/NoteGridLabel.java b/src/main/java/com/cstav/genshinstrument/client/config/enumType/label/NoteGridLabel.java index 73ee12a..ddea65f 100644 --- a/src/main/java/com/cstav/genshinstrument/client/config/enumType/label/NoteGridLabel.java +++ b/src/main/java/com/cstav/genshinstrument/client/config/enumType/label/NoteGridLabel.java @@ -3,10 +3,10 @@ import com.cstav.genshinstrument.client.gui.screens.instrument.partial.AbstractGridInstrumentScreen; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.NoteButton; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.NoteGridButton; -import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.label.AbsGridLabels; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.label.INoteLabel; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.label.NoteLabelSupplier; import com.cstav.genshinstrument.client.keyMaps.KeyMappings; +import com.cstav.genshinstrument.util.LabelUtil; import net.minecraft.network.chat.Component; @@ -20,15 +20,15 @@ public enum NoteGridLabel implements INoteLabel { KeyMappings.GRID_INSTRUMENT_MAPPINGS[ng(note).column][ng(note).row].getDisplayName() )), NOTE_NAME((note) -> Component.literal( - AbsGridLabels.getCutNoteName(ng(note)) + LabelUtil.getCutNoteName(ng(note)) )), DO_RE_MI((note) -> Component.translatable( - INoteLabel.TRANSLATABLE_PATH + AbsGridLabels.DO_RE_MI[ng(note).row % gs(note).rows()] - ).append(AbsGridLabels.getCutNoteName(ng(note)).substring(1)) + INoteLabel.TRANSLATABLE_PATH + LabelUtil.DO_RE_MI[ng(note).row % gs(note).rows()] + ).append(LabelUtil.getCutNoteName(ng(note)).substring(1)) ), ABC_1((note) -> Component.literal( - AbsGridLabels.ABC[ng(note).row] + (gs(note).columns() - ng(note).column) + LabelUtil.ABC[ng(note).row] + (gs(note).columns() - ng(note).column) )), ABC_2((note) -> Component.literal( ( diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/drum/DrumNoteButton.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/drum/DrumNoteButton.java index 041709f..4d2e134 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/drum/DrumNoteButton.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/drum/DrumNoteButton.java @@ -2,6 +2,7 @@ import com.cstav.genshinstrument.client.config.ModClientConfigs; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.NoteButton; +import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.NoteButtonRenderer; import com.cstav.genshinstrument.networking.buttonidentifier.DrumNoteIdentifier; import net.fabricmc.api.EnvType; @@ -17,9 +18,7 @@ public DrumNoteButton(DrumButtonType btnType, boolean isRight, AratakisGreatAndG super( btnType.getSound(), ModClientConfigs.DRUM_LABEL_TYPE.get().getLabelSupplier(), - btnType.getIndex(), 2, - - drumScreen, 13, .34f, 1.01f + drumScreen ); this.btnType = btnType; @@ -31,5 +30,14 @@ public DrumNoteButton(DrumButtonType btnType, boolean isRight, AratakisGreatAndG public DrumNoteIdentifier getIdentifier() { return new DrumNoteIdentifier(this); } + + + @Override + protected NoteButtonRenderer initNoteRenderer() { + return new NoteButtonRenderer(this, + btnType.getIndex(), 2, + 13, .34f, 1.01f + ); + } } diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/AbstractInstrumentScreen.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/AbstractInstrumentScreen.java index 1017b94..61cc974 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/AbstractInstrumentScreen.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/AbstractInstrumentScreen.java @@ -17,7 +17,6 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -34,11 +33,11 @@ public abstract class AbstractInstrumentScreen extends Screen { /** * The set pitch of all note buttons in this screen */ - private float pitch = ModClientConfigs.PITCH.get().floatValue(); - public float getPitch() { + private int pitch = ModClientConfigs.PITCH.get().intValue(); + public int getPitch() { return pitch; } - public void setPitch(float pitch) { + public void setPitch(int pitch) { this.pitch = NoteSound.clampPitch(pitch); } @@ -153,7 +152,6 @@ public AbstractInstrumentScreen(final InteractionHand hand) { super(CommonComponents.EMPTY); interactionHand = hand; - optionsScreen.setOnCloseRunnable(this::onOptionsClose); } @@ -194,8 +192,7 @@ public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) { return true; } - return ioa() ? optionsScreen.keyPressed(pKeyCode, pScanCode, pModifiers) - : super.keyPressed(pKeyCode, pScanCode, pModifiers); + return super.keyPressed(pKeyCode, pScanCode, pModifiers); } @Override public boolean keyReleased(int pKeyCode, int pScanCode, int pModifiers) { @@ -205,15 +202,11 @@ public boolean keyReleased(int pKeyCode, int pScanCode, int pModifiers) { if (note != null) note.locked = false; - return ioa() ? optionsScreen.keyReleased(pKeyCode, pScanCode, pModifiers) - : super.keyReleased(pKeyCode, pScanCode, pModifiers); + return super.keyReleased(pKeyCode, pScanCode, pModifiers); } @Override public boolean mouseReleased(double pMouseX, double pMouseY, int pButton) { - if (ioa()) - return optionsScreen.mouseReleased(pMouseX, pMouseY, pButton); - unlockFocused(); return super.mouseReleased(pMouseX, pMouseY, pButton); @@ -232,58 +225,13 @@ private void unlockFocused() { ((NoteButton)getFocused()).locked = false; } - - //#region Making the options screen function - private boolean isOptionsActive = false; - - @Override - public void render(GuiGraphics gui, int pMouseX, int pMouseY, float pPartialTick) { - super.render(gui, pMouseX, pMouseY, pPartialTick); - if (ioa()) - optionsScreen.render(gui, pMouseX, pMouseY, pPartialTick); - } - - protected void onOptionsOpen() { - isOptionsActive = true; + public void onOptionsOpen() { setFocused(null); + minecraft.setScreen(optionsScreen); } - protected void onOptionsClose() { - isOptionsActive = false; - } - - - @Override - public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) { - return ioa() ? optionsScreen.mouseClicked(pMouseX, pMouseY, pButton) - : super.mouseClicked(pMouseX, pMouseY, pButton); - } - @Override - public boolean mouseDragged(double pMouseX, double pMouseY, int pButton, double pDragX, double pDragY) { - return ioa() ? optionsScreen.mouseDragged(pMouseX, pMouseY, pButton, pDragX, pDragY) - : super.mouseDragged(pMouseX, pMouseY, pButton, pDragX, pDragY); - } - @Override - public void mouseMoved(double pMouseX, double pMouseY) { - if (ioa()) - optionsScreen.mouseMoved(pMouseX, pMouseY); - else - super.mouseMoved(pMouseX, pMouseY); + public void onOptionsClose() { + minecraft.setScreen(this); } - @Override - public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) { - return ioa() ? optionsScreen.mouseScrolled(pMouseX, pMouseY, pDelta) - : super.mouseScrolled(pMouseX, pMouseY, pDelta); - } - - - /** - * Shorthand for {@link AbstractInstrumentScreen#isOptionsActive isOptionsActive}. - */ - private boolean ioa() { - return isOptionsActive; - } - - //#endregion @Override diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButton.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButton.java index 7356170..0d46773 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButton.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButton.java @@ -1,21 +1,15 @@ package com.cstav.genshinstrument.client.gui.screens.instrument.partial.note; import java.awt.Point; -import java.util.ArrayList; import com.cstav.genshinstrument.client.ClientUtil; -import com.cstav.genshinstrument.client.config.ModClientConfigs; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.AbstractInstrumentScreen; -import com.cstav.genshinstrument.client.gui.screens.instrument.partial.InstrumentThemeLoader; -import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.animation.NoteAnimationController; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.label.NoteLabelSupplier; import com.cstav.genshinstrument.networking.ModPacketHandler; import com.cstav.genshinstrument.networking.buttonidentifier.DefaultNoteButtonIdentifier; import com.cstav.genshinstrument.networking.buttonidentifier.NoteButtonIdentifier; import com.cstav.genshinstrument.networking.packets.instrument.InstrumentPacket; import com.cstav.genshinstrument.sound.NoteSound; -import com.cstav.genshinstrument.util.CommonUtil; -import com.mojang.blaze3d.systems.RenderSystem; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -25,20 +19,10 @@ import net.minecraft.client.gui.narration.NarratedElementType; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.sounds.SoundManager; -import net.minecraft.resources.ResourceLocation; @Environment(EnvType.CLIENT) public abstract class NoteButton extends AbstractButton { - public static final String NOTE_BG_FILENAME = "note_bg.png"; - - private static final double - FLAT_TEXTURE_HEIGHT_MULTIPLIER = 3.7f/1.3f, - FLAT_TEXTURE_WIDTH_MULTIPLIER = 1.7f/1.3f, - SHARP_MULTIPLIER = .8f, - DOUBLE_SHARP_MULTIPLIER = .9f - ; - @SuppressWarnings("resource") public static int getSize() { @@ -54,12 +38,6 @@ public static int getSize() { } - protected final Minecraft minecraft = Minecraft.getInstance(); - - protected final NoteAnimationController noteAnimation = new NoteAnimationController(.15f, 9, this); - protected final ArrayList rings = new ArrayList<>(); - - /** *

Returns the identifier of this button.

* You may use the {@link DefaultNoteButtonIdentifier default implementation} if you're too lazy. @@ -68,52 +46,28 @@ public NoteButtonIdentifier getIdentifier() { return new DefaultNoteButtonIdentifier(getSound()); } + + public final AbstractInstrumentScreen instrumentScreen; + protected final Minecraft minecraft = Minecraft.getInstance(); private NoteSound sound; - public final AbstractInstrumentScreen instrumentScreen; + private NoteLabelSupplier labelSupplier; - protected final int noteTextureRow, rowsInNoteTexture; - protected final ResourceLocation rootLocation, - noteLocation, noteBgLocation, accidentalsLocation; + private NoteButtonRenderer noteRenderer; + protected abstract NoteButtonRenderer initNoteRenderer(); - private NoteLabelSupplier labelSupplier; - private int noteTextureWidth = 56; - //FIXME Actually figure out a formula instead of guessing - private float randomAssMultiplier1 = .9f, randomAssMultiplier2 = 1.025f; public NoteButton(NoteSound sound, - NoteLabelSupplier labelSupplier, int noteTextureRow, int rowsInNoteTexture, - AbstractInstrumentScreen instrumentScreen) { - super(0, 0, getSize(), getSize(), null); + NoteLabelSupplier labelSupplier, AbstractInstrumentScreen instrumentScreen) { + super(0, 0, getSize(), getSize(), null); this.sound = sound; - this.labelSupplier = labelSupplier; - this.instrumentScreen = instrumentScreen; - - this.noteTextureRow = noteTextureRow; - this.rowsInNoteTexture = rowsInNoteTexture; - - - rootLocation = instrumentScreen.getResourceFromRoot("note"); - - noteLocation = instrumentScreen.getNotesLocation(); - noteBgLocation = getResourceFromRoot(NOTE_BG_FILENAME); - accidentalsLocation = getResourceFromRoot("accidentals.png"); - } - public NoteButton(NoteSound sound, - NoteLabelSupplier labelSupplier, int noteTextureRow, int rowsInNoteTexture, - AbstractInstrumentScreen instrumentScreen, int noteTextureWidth, - float randomAssMultiplier1, float randomAssMultiplier2) { - this(sound, labelSupplier, noteTextureRow, rowsInNoteTexture, instrumentScreen); - - this.noteTextureWidth = noteTextureWidth; - this.randomAssMultiplier1 = randomAssMultiplier1; - this.randomAssMultiplier2 = randomAssMultiplier2; } + public void setLabelSupplier(final NoteLabelSupplier labelSupplier) { this.labelSupplier = labelSupplier; updateNoteLabel(); @@ -135,18 +89,9 @@ public void setSound(NoteSound sound) { if (getIdentifier() instanceof DefaultNoteButtonIdentifier) ((DefaultNoteButtonIdentifier)getIdentifier()).setSound(sound); } - - - /** - * @param path The resource to obtain from this note's directory - * @see {@link AbstractInstrumentScreen#getResourceFrom(ResourceLocation, String)} - */ - protected ResourceLocation getResourceFromRoot(final String path) { - return CommonUtil.getResourceFrom(rootLocation, path); - } + private int initX, initY; - private int textX, textY; /** * Initializes the button's initial position. * This is done for the animations to work properly - for them to stick to the same position. @@ -155,8 +100,8 @@ public void initPos() { initX = getX(); initY = getY(); - textX = getX() + width/2; - textY = getY() + height/2 + 7; + noteRenderer.setLabelX(getX() + width/2); + noteRenderer.setLabelY(getY() + height/2 + 7); } public int getInitX() { @@ -181,133 +126,21 @@ public NoteNotation getNotation() { public void init() { + noteRenderer = initNoteRenderer(); initPos(); setLabelSupplier(labelSupplier); } public boolean isPlaying() { - return noteAnimation.isPlaying(); + return noteRenderer.noteAnimation.isPlaying(); } - //#region Rendering - @Override public void renderWidget(GuiGraphics gui, int mouseX, int mouseY, float partialTick) { - RenderSystem.enableBlend(); - RenderSystem.enableDepthTest(); - - rings.removeIf((ring) -> !ring.isPlaying()); - rings.forEach((ring) -> ring.render(gui)); - - renderNoteButton(gui, instrumentScreen.getThemeLoader()); - - noteAnimation.update(); - } - protected void renderNoteButton(GuiGraphics gui, final InstrumentThemeLoader themeLoader) { - renderNote(gui, themeLoader); - renderSymbol(gui, themeLoader); - renderLabel(gui, themeLoader); - - renderAccidentals(gui); + noteRenderer.renderNoteButton(gui, instrumentScreen.getThemeLoader()); } - protected void renderNote(final GuiGraphics gui, final InstrumentThemeLoader themeLoader) { - // width = full color, width * 2 = border, 0 = normal - int blitOffset = - isPlaying() ? - foreignPlaying ? - (width * 2) - : width - : isHoveredOrFocused() ? - (width * 2) - : 0; - - gui.blit(noteBgLocation, - this.getX(), this.getY(), - blitOffset, 0, - width, height, - width*3, height - ); - } - // "Symbol" so that I can call the above "Note" - protected void renderSymbol(final GuiGraphics gui, final InstrumentThemeLoader themeLoader) { - final int noteWidth = width/2, noteHeight = height/2; - - ClientUtil.setShaderColor((isPlaying() && !foreignPlaying) - ? themeLoader.getPressedNoteTheme() - : themeLoader.getLabelTheme() - ); - - gui.blit(noteLocation, - this.getX() + noteWidth/2, this.getY() + noteHeight/2, - //NOTE: I have no clue whatsoever how on earth these 1.025 and .9 multipliers actually work. - // Like seriously wtf why fkuaherjgaeorg i hate maths - //NOTE: Moved said numbers to the randomAss vars - noteWidth * noteTextureRow * randomAssMultiplier2, 0, - noteWidth, noteHeight, - (int)(noteWidth * (noteTextureWidth / rowsInNoteTexture) * randomAssMultiplier1), height/2 - ); - - ClientUtil.resetShaderColor(); - } - protected void renderLabel(final GuiGraphics gui, final InstrumentThemeLoader themeLoader) { - //FIXME: All text rendered this way are making their way to the top of - // the render stack, for some reason - gui.drawCenteredString( - minecraft.font, getMessage(), - textX, textY, - ((isPlaying() && !foreignPlaying) - ? themeLoader.getPressedNoteTheme() - : themeLoader.getNoteTheme() - ).getRGB() - ); - } - protected void renderAccidentals(final GuiGraphics gui) { - switch (getNotation()) { - case NONE: break; - - case FLAT: - renderAccidental(gui, 0); - break; - case SHARP: - renderAccidental(gui, 1); - break; - case DOUBLE_FLAT: - renderAccidental(gui, 0, -6, -3); - renderAccidental(gui, 0, 5, 2); - break; - case DOUBLE_SHARP: - renderAccidental(gui, 2, -1, 0); - break; - - } - } - - protected void renderAccidental(final GuiGraphics gui, int index) { - renderAccidental(gui, index, 0, 0); - } - protected void renderAccidental(GuiGraphics gui, int index, int offsetX, int offsetY) { - final int textureWidth = (int)(width * FLAT_TEXTURE_WIDTH_MULTIPLIER * ( - (index == 1) ? SHARP_MULTIPLIER : (index == 2) ? DOUBLE_SHARP_MULTIPLIER : 1 - )), - textureHeight = (int)(height * FLAT_TEXTURE_HEIGHT_MULTIPLIER * ( - (index == 1) ? SHARP_MULTIPLIER : (index == 2) ? DOUBLE_SHARP_MULTIPLIER : 1 - )); - - final int spritePartHeight = textureHeight/3; - - gui.blit(accidentalsLocation, - getX() - 9 + offsetX, getY() - 6 + offsetY, - // Handle sharp imperfections - isPlaying() ? textureWidth/2 : 0, (spritePartHeight) * index - index, - textureWidth/2, spritePartHeight + ((index == 1) ? 3 : 0), - textureWidth - (((index != 0) && isPlaying()) ? 1 : 0), textureHeight - ); - } - - //#endregion - public boolean locked = false; public void play() { @@ -334,14 +167,8 @@ public void onPress() { play(); } - private boolean foreignPlaying = false; - public void playNoteAnimation(final boolean isForeign) { - foreignPlaying = isForeign; - - noteAnimation.play(isForeign); - if (ModClientConfigs.EMIT_RING_ANIMATION.get()) - rings.add(new NoteRing(this, isForeign)); + noteRenderer.playNoteAnimation(isForeign); } diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButtonRenderer.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButtonRenderer.java new file mode 100644 index 0000000..c36d2ab --- /dev/null +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButtonRenderer.java @@ -0,0 +1,215 @@ +package com.cstav.genshinstrument.client.gui.screens.instrument.partial.note; + +import java.util.ArrayList; + +import com.cstav.genshinstrument.client.ClientUtil; +import com.cstav.genshinstrument.client.config.ModClientConfigs; +import com.cstav.genshinstrument.client.gui.screens.instrument.partial.AbstractInstrumentScreen; +import com.cstav.genshinstrument.client.gui.screens.instrument.partial.InstrumentThemeLoader; +import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.animation.NoteAnimationController; +import com.cstav.genshinstrument.util.CommonUtil; +import com.mojang.blaze3d.systems.RenderSystem; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.resources.ResourceLocation; + +public class NoteButtonRenderer { + private static final Minecraft MINECRAFT = Minecraft.getInstance(); + + private static final double + FLAT_TEXTURE_HEIGHT_MULTIPLIER = 3.7f/1.3f, + FLAT_TEXTURE_WIDTH_MULTIPLIER = 1.7f/1.3f, + SHARP_MULTIPLIER = .8f, + DOUBLE_SHARP_MULTIPLIER = .9f + ; + + public final NoteButton noteButton; + protected final AbstractInstrumentScreen instrumentScreen; + + // Resources + protected final ResourceLocation rootLocation, + noteLocation, noteBgLocation, accidentalsLocation; + + // Texture properties + protected final int noteTextureRow, rowsInNoteTexture; + private final int noteTextureWidth; + //FIXME Actually figure out a formula instead of guessing + private final float randomAssMultiplier1, randomAssMultiplier2; + + // Animations + public final NoteAnimationController noteAnimation; + private boolean foreignPlaying = false; + protected final ArrayList rings = new ArrayList<>(); + + + public NoteButtonRenderer(NoteButton noteButton, int noteTextureRow, int rowsInNoteTexture, + int noteTextureWidth, float randomAssMultiplier1, float randomAssMultiplier2) { + this.noteButton = noteButton; + instrumentScreen = noteButton.instrumentScreen; + + noteAnimation = new NoteAnimationController(.15f, 9, noteButton); + + + this.noteTextureRow = noteTextureRow; + this.rowsInNoteTexture = rowsInNoteTexture; + + + rootLocation = instrumentScreen.getResourceFromRoot("note"); + + noteLocation = instrumentScreen.getNotesLocation(); + noteBgLocation = getResourceFromRoot("note_bg.png"); + accidentalsLocation = getResourceFromRoot("accidentals.png"); + + + + this.noteTextureWidth = noteTextureWidth; + this.randomAssMultiplier1 = randomAssMultiplier1; + this.randomAssMultiplier2 = randomAssMultiplier2; + } + + + + public void render(GuiGraphics gui, int mouseX, int mouseY, float partialTick, InstrumentThemeLoader themeLoader) { + RenderSystem.enableBlend(); + RenderSystem.enableDepthTest(); + + rings.removeIf((ring) -> !ring.isPlaying()); + rings.forEach((ring) -> ring.render(gui)); + + + renderNoteButton(gui, themeLoader); + renderNote(gui, themeLoader); + renderLabel(gui, themeLoader); + + renderAccidentals(gui, themeLoader); + + + noteAnimation.update(); + } + + protected void renderNoteButton(final GuiGraphics gui, final InstrumentThemeLoader themeLoader) { + // width = full color, width * 2 = border, 0 = normal + int blitOffset = + noteButton.isPlaying() ? + foreignPlaying ? + (noteButton.getWidth() * 2) + : noteButton.getWidth() + : noteButton.isHoveredOrFocused() ? + (noteButton.getWidth() * 2) + : 0; + + gui.blit(noteBgLocation, + noteButton.getX(), noteButton.getY(), + blitOffset, 0, + noteButton.getWidth(), noteButton.getHeight(), + noteButton.getWidth()*3, noteButton.getHeight() + ); + } + + protected void renderNote(final GuiGraphics gui, final InstrumentThemeLoader themeLoader) { + final int noteWidth = noteButton.getWidth()/2, noteHeight = noteButton.getHeight()/2; + + ClientUtil.setShaderColor((noteButton.isPlaying() && !foreignPlaying) + ? themeLoader.getPressedNoteTheme() + : themeLoader.getLabelTheme() + ); + + gui.blit(noteLocation, + noteButton.getX() + noteWidth/2, noteButton.getY() + noteHeight/2, + //NOTE: I have no clue whatsoever how on earth these 1.025 and .9 multipliers actually work. + // Like seriously wtf why fkuaherjgaeorg i hate maths + //NOTE: Moved said numbers to the randomAss vars + noteWidth * noteTextureRow * randomAssMultiplier2, 0, + noteWidth, noteHeight, + (int)(noteWidth * (noteTextureWidth / rowsInNoteTexture) * randomAssMultiplier1), noteButton.getHeight()/2 + ); + + ClientUtil.resetShaderColor(); + } + + + // Labels act junky when the notes are pressed, so just cache their initial and fixed location + private int labelX, labelY; + public void setLabelX(int labelX) { + this.labelX = labelX; + } + public void setLabelY(int labelY) { + this.labelY = labelY; + } + + protected void renderLabel(final GuiGraphics gui, final InstrumentThemeLoader themeLoader) { + gui.drawCenteredString( + MINECRAFT.font, noteButton.getMessage(), + labelX, labelY, + ((noteButton.isPlaying() && !foreignPlaying) + ? themeLoader.getPressedNoteTheme() + : themeLoader.getNoteTheme() + ).getRGB() + ); + } + + + protected void renderAccidentals(final GuiGraphics gui, final InstrumentThemeLoader themeLoader) { + switch (noteButton.getNotation()) { + case NONE: break; + + case FLAT: + renderAccidental(gui, 0); + break; + case SHARP: + renderAccidental(gui, 1); + break; + case DOUBLE_FLAT: + renderAccidental(gui, 0, -6, -3); + renderAccidental(gui, 0, 5, 2); + break; + case DOUBLE_SHARP: + renderAccidental(gui, 2, -1, 0); + break; + + } + } + + protected void renderAccidental(final GuiGraphics gui, int index) { + renderAccidental(gui, index, 0, 0); + } + protected void renderAccidental(GuiGraphics gui, int index, int offsetX, int offsetY) { + // Handle sharp imperfections + final int textureWidth = (int)(noteButton.getWidth() * FLAT_TEXTURE_WIDTH_MULTIPLIER * ( + (index == 1) ? SHARP_MULTIPLIER : (index == 2) ? DOUBLE_SHARP_MULTIPLIER : 1 + )), + textureHeight = (int)(noteButton.getHeight() * FLAT_TEXTURE_HEIGHT_MULTIPLIER * ( + (index == 1) ? SHARP_MULTIPLIER : (index == 2) ? DOUBLE_SHARP_MULTIPLIER : 1 + )); + + final int spritePartHeight = textureHeight/3; + + gui.blit(accidentalsLocation, + noteButton.getX() - 9 + offsetX, noteButton.getY() - 6 + offsetY, + noteButton.isPlaying() ? textureWidth/2 : 0, (spritePartHeight) * index - index, + + textureWidth/2, spritePartHeight + ((index == 1) ? 3 : 0), + textureWidth - (((index != 0) && noteButton.isPlaying()) ? 1 : 0), textureHeight + ); + } + + + public void playNoteAnimation(final boolean isForeign) { + foreignPlaying = isForeign; + + noteAnimation.play(isForeign); + if (ModClientConfigs.EMIT_RING_ANIMATION.get()) + rings.add(new NoteRing(noteButton, isForeign)); + } + + + /** + * @param path The resource to obtain from this note's directory + * @see {@link AbstractInstrumentScreen#getResourceFrom(ResourceLocation, String)} + */ + protected ResourceLocation getResourceFromRoot(final String path) { + return CommonUtil.getResourceFrom(rootLocation, path); + } + +} \ No newline at end of file diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteGridButton.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteGridButton.java index bc6e00e..734bc98 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteGridButton.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteGridButton.java @@ -2,10 +2,10 @@ import com.cstav.genshinstrument.client.config.ModClientConfigs; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.AbstractGridInstrumentScreen; -import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.label.AbsGridLabels; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.label.NoteLabelSupplier; import com.cstav.genshinstrument.networking.buttonidentifier.NoteGridButtonIdentifier; import com.cstav.genshinstrument.sound.NoteSound; +import com.cstav.genshinstrument.util.LabelUtil; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -17,7 +17,7 @@ public class NoteGridButton extends NoteButton { public NoteGridButton(int row, int column, NoteSound sound, NoteLabelSupplier labelSupplier, AbstractGridInstrumentScreen instrumentScreen) { - super(sound, labelSupplier, row, instrumentScreen.rows(), instrumentScreen); + super(sound, labelSupplier, instrumentScreen); this.row = row; this.column = column; @@ -31,8 +31,17 @@ public NoteGridButtonIdentifier getIdentifier() { @Override public NoteNotation getNotation() { return ModClientConfigs.ACCURATE_ACCIDENTALS.get() - ? NoteNotation.getNotation(AbsGridLabels.getNoteName(this)) + ? NoteNotation.getNotation(LabelUtil.getNoteName(this)) : NoteNotation.NONE; } + + @Override + protected NoteButtonRenderer initNoteRenderer() { + return new NoteButtonRenderer(this, + row, ((AbstractGridInstrumentScreen)instrumentScreen).rows(), + 57, .9f, 1.025f + ); + } + } \ No newline at end of file diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/animation/NoteAnimationController.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/animation/NoteAnimationController.java index 1801578..3079d33 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/animation/NoteAnimationController.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/animation/NoteAnimationController.java @@ -9,7 +9,7 @@ @Environment(EnvType.CLIENT) public class NoteAnimationController extends AnimationController { - // The size of this button in double + /** The size of this button in double */ protected double dSize; protected NoteButton button; public NoteAnimationController(float duration, float targetValue, final NoteButton button) { diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java index 4f14836..4c58d3a 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java @@ -10,14 +10,13 @@ import com.cstav.genshinstrument.client.config.enumType.label.NoteGridLabel; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.AbstractInstrumentScreen; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.NoteButton; -import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.label.AbsGridLabels; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.label.INoteLabel; import com.cstav.genshinstrument.sound.NoteSound; +import com.cstav.genshinstrument.util.LabelUtil; import com.ibm.icu.text.DecimalFormat; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractSliderButton; import net.minecraft.client.gui.components.Button; @@ -39,8 +38,6 @@ public abstract class AbstractInstrumentOptionsScreen extends Screen { private static final String SOUND_CHANNEL_KEY = "button.genshinstrument.audioChannels", STOP_MUSIC_KEY = "button.genshinstrument.stop_music_on_play"; - public static final double PITCH_STEP = .05; - protected final HashMap APPLIED_OPTIONS = new HashMap<>(); @@ -86,7 +83,6 @@ protected int getButtonHeight() { protected final Screen lastScreen; protected final boolean isOverlay; - private Runnable onCloseRunnable; protected final @Nullable INoteLabel[] labels; protected final @Nullable INoteLabel currLabel; @@ -116,10 +112,6 @@ public AbstractInstrumentOptionsScreen(final Screen lastScreen) { currLabel = ModClientConfigs.GRID_LABEL_TYPE.get(); } - public void setOnCloseRunnable(final Runnable onCloseRunnable) { - this.onCloseRunnable = onCloseRunnable; - } - @Override protected void init() { @@ -172,31 +164,27 @@ protected void initAudioSection(final GridLayout grid, final RowHelper rowHelper final DecimalFormat format = new DecimalFormat("0.00"); { + pitch = getPitch(); updateMessage(); } + private int pitch; + @Override protected void updateMessage() { this.setMessage( - Component.translatable("button.genshinstrument.pitch").append( - ": " + format.format(getPitch()) - + " ("+AbsGridLabels.getNoteName(getPitch(), AbstractInstrumentScreen.DEFAULT_NOTE_LAYOUT, 0)+")" - ) + Component.translatable("button.genshinstrument.pitch").append(": " + + LabelUtil.getNoteName(pitch, AbstractInstrumentScreen.DEFAULT_NOTE_LAYOUT, 0) + + " ("+format.format(NoteSound.getPitchByNoteOffset(pitch))+")" + ) ); } @Override protected void applyValue() { - onPitchChanged(this, divisibleBy5(Mth.clampedLerp(NoteSound.MIN_PITCH, NoteSound.MAX_PITCH, value))); - } - - private static double divisibleBy5(double number) { - if (number > (NoteSound.MAX_PITCH - .001)) - return NoteSound.MAX_PITCH; - - final double factor = Math.round(1 / PITCH_STEP * 100) / 100; - return (int)(number * factor) / factor; + pitch = (int)Mth.clampedLerp(NoteSound.MIN_PITCH, NoteSound.MAX_PITCH, value); + onPitchChanged(this, pitch); } }; rowHelper.addChild(pitchSlider); @@ -261,14 +249,14 @@ protected void initVisualsSection(final GridLayout grid, final RowHelper rowHelp protected void initOptionsGrid(final GridLayout grid, final RowHelper rowHelper) { initAudioSection(grid, rowHelper); - rowHelper.addChild(SpacerElement.height(15), 2); + rowHelper.addChild(SpacerElement.height(7), 2); initVisualsSection(grid, rowHelper); } - private float getPitch() { + private int getPitch() { return (!isOverlay) - ? ModClientConfigs.PITCH.get().floatValue() + ? ModClientConfigs.PITCH.get().intValue() : instrumentScreen.getPitch(); } @@ -280,14 +268,11 @@ protected void onLabelChanged(final CycleButton button, final INoteL queueToSave("note_label", () -> saveLabel(label)); } - protected void saveLabel(final INoteLabel newLabel) { - if (newLabel instanceof NoteGridLabel) - ModClientConfigs.GRID_LABEL_TYPE.set((NoteGridLabel)newLabel); - } + protected abstract void saveLabel(final INoteLabel newLabel); - protected void onPitchChanged(final AbstractSliderButton slider, final double pitch) { + protected void onPitchChanged(final AbstractSliderButton slider, final int pitch) { if (isOverlay) { - instrumentScreen.setPitch((float)pitch); + instrumentScreen.setPitch(pitch); instrumentScreen.notesIterable().forEach(NoteButton::updateNoteLabel); queueToSave("pitch", () -> savePitch(pitch)); @@ -295,7 +280,7 @@ protected void onPitchChanged(final AbstractSliderButton slider, final double pi ModClientConfigs.PITCH.set(pitch); } } - protected void savePitch(final double newPitch) { + protected void savePitch(final int newPitch) { ModClientConfigs.PITCH.set(newPitch); } @@ -323,8 +308,11 @@ protected void onAccurateAccidentalsChanged(final CycleButton button, f @Override public void render(GuiGraphics gui, int pMouseX, int pMouseY, float pPartialTick) { + if (isOverlay) + instrumentScreen.render(gui, pMouseX, pMouseY, pPartialTick); + + renderBackground(gui); - gui.drawCenteredString(font, title, width/2, 20, Color.WHITE.getRGB()); super.render(gui, pMouseX, pMouseY, pPartialTick); @@ -333,17 +321,12 @@ public void render(GuiGraphics gui, int pMouseX, int pMouseY, float pPartialTick @Override public void onClose() { - if (!isOverlay) { - if (lastScreen != null) - Minecraft.getInstance().setScreen(lastScreen); - else - super.onClose(); - } - onSave(); - if (onCloseRunnable != null) - onCloseRunnable.run(); + if (isOverlay) + instrumentScreen.onOptionsClose(); + else + super.onClose(); } protected void onSave() { for (final Runnable runnable : APPLIED_OPTIONS.values()) @@ -353,6 +336,31 @@ protected void onSave() { } + + @Override + public boolean isPauseScreen() { + return false; + } + + + // Make pressing notes possible with keyboard + @Override + public boolean keyPressed(int p_96552_, int p_96553_, int p_96554_) { + if (isOverlay && p_96552_ != 256) + instrumentScreen.keyPressed(p_96552_, p_96553_, p_96554_); + + return super.keyPressed(p_96552_, p_96553_, p_96554_); + } + @Override + public boolean keyReleased(int p_94715_, int p_94716_, int p_94717_) { + if (isOverlay && p_94715_ != 256) + instrumentScreen.keyReleased(p_94715_, p_94716_, p_94717_); + + return super.keyReleased(p_94715_, p_94716_, p_94717_); + } + + + /** * Tooltip is being annoying and not rpelacing my args. * So, fine, I'll do it myself. diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/FloralZitherOptionsScreen.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/FloralZitherOptionsScreen.java index b4d0775..a9b598a 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/FloralZitherOptionsScreen.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/FloralZitherOptionsScreen.java @@ -18,7 +18,7 @@ @Environment(EnvType.CLIENT) public class FloralZitherOptionsScreen extends GridInstrumentOptionsScreen { private static final String SOUND_TYPE_KEY = "button.genshinstrument.zither.soundType"; - private final static int SPACE_BEFORE = 50, SPACER_HEIGHT = 0, PADDING = 20; + private final static int SPACE_BEFORE = 30, SPACER_HEIGHT = 13; public FloralZitherOptionsScreen(final FloralZitherScreen screen) { super(screen); @@ -49,7 +49,7 @@ protected void initOptionsGrid(GridLayout grid, RowHelper rowHelper) { getBigButtonWidth(), getButtonHeight() , Component.translatable(SOUND_TYPE_KEY), this::onSoundTypeChange); - rowHelper.addChild(soundTypeButton, 2, rowHelper.newCellSettings().paddingTop(PADDING)); + rowHelper.addChild(soundTypeButton, 2); } @Override @@ -58,7 +58,7 @@ public void render(GuiGraphics gui, int pMouseX, int pMouseY, float pPartialTick gui.drawCenteredString(font, Component.translatable("label.genshinstrument.zither_options"), - width/2, heightBefore + SPACE_BEFORE + SPACER_HEIGHT + width/2, heightBefore + SPACE_BEFORE , Color.WHITE.getRGB()); } diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/GridInstrumentOptionsScreen.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/GridInstrumentOptionsScreen.java index 639b9c0..47881a1 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/GridInstrumentOptionsScreen.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/GridInstrumentOptionsScreen.java @@ -3,6 +3,7 @@ import com.cstav.genshinstrument.client.config.ModClientConfigs; import com.cstav.genshinstrument.client.config.enumType.label.NoteGridLabel; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.AbstractGridInstrumentScreen; +import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.label.INoteLabel; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -33,7 +34,14 @@ public NoteGridLabel getCurrentLabel() { return ModClientConfigs.GRID_LABEL_TYPE.get(); } + @Override + protected void saveLabel(final INoteLabel newLabel) { + if (newLabel instanceof NoteGridLabel) + ModClientConfigs.GRID_LABEL_TYPE.set((NoteGridLabel)newLabel); + } + + @Override protected void initVisualsSection(GridLayout grid, RowHelper rowHelper) { final CycleButton renderBackground = CycleButton.booleanBuilder(CommonComponents.OPTION_ON, CommonComponents.OPTION_OFF) diff --git a/src/main/java/com/cstav/genshinstrument/networking/packets/instrument/InstrumentPacket.java b/src/main/java/com/cstav/genshinstrument/networking/packets/instrument/InstrumentPacket.java index 7db5813..28aca9e 100644 --- a/src/main/java/com/cstav/genshinstrument/networking/packets/instrument/InstrumentPacket.java +++ b/src/main/java/com/cstav/genshinstrument/networking/packets/instrument/InstrumentPacket.java @@ -20,12 +20,12 @@ public class InstrumentPacket implements ModPacket { private final NoteSound sound; private final InteractionHand hand; - private final float pitch; + private final int pitch; private final ResourceLocation instrumentId; private final NoteButtonIdentifier noteIdentifier; - public InstrumentPacket(NoteSound sound, float pitch, InteractionHand hand, + public InstrumentPacket(NoteSound sound, int pitch, InteractionHand hand, ResourceLocation instrumentId, NoteButtonIdentifier noteIdentifier) { this.sound = sound; this.hand = hand; @@ -37,7 +37,7 @@ public InstrumentPacket(NoteSound sound, float pitch, InteractionHand hand, public InstrumentPacket(FriendlyByteBuf buf) { sound = NoteSound.readFromNetwork(buf); hand = buf.readEnum(InteractionHand.class); - pitch = buf.readFloat(); + pitch = buf.readInt(); instrumentId = buf.readResourceLocation(); noteIdentifier = NoteButtonIdentifier.readIdentifier(buf); @@ -47,7 +47,7 @@ public InstrumentPacket(FriendlyByteBuf buf) { public void write(final FriendlyByteBuf buf) { sound.writeToNetwork(buf); buf.writeEnum(hand); - buf.writeFloat(pitch); + buf.writeInt(pitch); buf.writeResourceLocation(instrumentId); noteIdentifier.writeToNetwork(buf); diff --git a/src/main/java/com/cstav/genshinstrument/networking/packets/instrument/PlayNotePacket.java b/src/main/java/com/cstav/genshinstrument/networking/packets/instrument/PlayNotePacket.java index b185875..b16bcc0 100644 --- a/src/main/java/com/cstav/genshinstrument/networking/packets/instrument/PlayNotePacket.java +++ b/src/main/java/com/cstav/genshinstrument/networking/packets/instrument/PlayNotePacket.java @@ -21,14 +21,14 @@ public class PlayNotePacket implements ModPacket { private final BlockPos blockPos; private final NoteSound sound; - private final float pitch; + private final int pitch; private final ResourceLocation instrumentId; private final NoteButtonIdentifier noteIdentifier; private final Optional playerUUID; private final Optional hand; - public PlayNotePacket(BlockPos pos, NoteSound sound, float pitch, ResourceLocation instrumentId, + public PlayNotePacket(BlockPos pos, NoteSound sound, int pitch, ResourceLocation instrumentId, NoteButtonIdentifier noteIdentifier, Optional playerUUID, Optional hand) { this.blockPos = pos; @@ -43,7 +43,7 @@ public PlayNotePacket(BlockPos pos, NoteSound sound, float pitch, ResourceLocati public PlayNotePacket(FriendlyByteBuf buf) { blockPos = buf.readBlockPos(); sound = NoteSound.readFromNetwork(buf); - pitch = buf.readFloat(); + pitch = buf.readInt(); instrumentId = buf.readResourceLocation(); noteIdentifier = NoteButtonIdentifier.readIdentifier(buf); @@ -55,7 +55,7 @@ public PlayNotePacket(FriendlyByteBuf buf) { public void write(FriendlyByteBuf buf) { buf.writeBlockPos(blockPos); sound.writeToNetwork(buf); - buf.writeFloat(pitch); + buf.writeInt(pitch); buf.writeResourceLocation(instrumentId); noteIdentifier.writeToNetwork(buf); diff --git a/src/main/java/com/cstav/genshinstrument/sound/NoteSound.java b/src/main/java/com/cstav/genshinstrument/sound/NoteSound.java index a914db2..aafda75 100644 --- a/src/main/java/com/cstav/genshinstrument/sound/NoteSound.java +++ b/src/main/java/com/cstav/genshinstrument/sound/NoteSound.java @@ -9,6 +9,7 @@ import com.cstav.genshinstrument.event.InstrumentPlayedEvent.ByPlayer.ByPlayerArgs; import com.cstav.genshinstrument.event.InstrumentPlayedEvent.InstrumentPlayedEventArgs; import com.cstav.genshinstrument.networking.buttonidentifier.NoteButtonIdentifier; +import com.cstav.genshinstrument.util.LabelUtil; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -43,7 +44,10 @@ public class NoteSound { */ public static final double LOCAL_RANGE = STEREO_RANGE; - public static final float MIN_PITCH = .5f, MAX_PITCH = 1.9f; + public static final int + MIN_PITCH = -LabelUtil.NOTES_PER_SCALE, + MAX_PITCH = LabelUtil.NOTES_PER_SCALE + ; @@ -119,14 +123,13 @@ private static boolean metInstrumentVolume() { /** * A method for packets to use for playing this note on the client's end. - * If {@link Minecraft#player this player} is the same as the gives player, - * the method will only stop the client's background music per preference. + * Will also stop the client's background music per preference. * @param playerUUID The UUID of the player who initiated the sound. Null for when it wasn't a player. * @param hand The hand of the player who initiated the sound. Null for when it wasn't a player. * @param pos The position at which the sound was fired from */ @Environment(EnvType.CLIENT) - public void playAtPos(float pitch, UUID playerUUID, InteractionHand hand, + public void playAtPos(int pitch, UUID playerUUID, InteractionHand hand, ResourceLocation instrumentId, NoteButtonIdentifier buttonIdentifier, BlockPos pos) { final Minecraft minecraft = Minecraft.getInstance(); @@ -155,16 +158,22 @@ public void playAtPos(float pitch, UUID playerUUID, InteractionHand hand, if (player.getUUID().equals(playerUUID)) return; + + + final float mcPitch = getPitchByNoteOffset(clampPitch(pitch)); if (distanceFromPlayer > LOCAL_RANGE) level.playLocalSound(pos, getByPreference(distanceFromPlayer), SoundSource.RECORDS, - 1, NoteSound.clampPitch(pitch) + 1, mcPitch , false); else - playLocally(pitch); + playLocally(mcPitch); } + /** + * Plays this sound locally. Treats the given {@code pitch} as a Minecraft pitch. + */ @Environment(EnvType.CLIENT) public void playLocally(final float pitch) { Minecraft.getInstance().getSoundManager().play(new SimpleSoundInstance( @@ -174,10 +183,33 @@ public void playLocally(final float pitch) { 0, 0, 0, true )); } + /** + *

Plays this note locally.

+ * Treats the given {@code pitch} as a note offset pitch, + * thus performs a conversion from note offset pitch to Minecraft pitch. + * @see NoteSound#getPitchByNoteOffset + */ + @Environment(EnvType.CLIENT) + public void playLocally(final int pitch) { + playLocally(getPitchByNoteOffset(clampPitch(pitch))); + } - public static float clampPitch(final float pitch) { - return Mth.clamp(pitch, MIN_PITCH, MAX_PITCH); + /** + * Clams the given {@code pitch} between the set range + */ + public static int clampPitch(final int pitch) { + return (int)Mth.clamp(pitch, MIN_PITCH, MAX_PITCH); + } + /** + * Converts the given note offset to Minecraft pitch. + * @apiNote Formula taken from + * + * Specy's Genshin music app + * + */ + public static float getPitchByNoteOffset(final int pitch) { + return (float)Math.pow(2, (double)pitch/LabelUtil.NOTES_PER_SCALE); } diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/label/AbsGridLabels.java b/src/main/java/com/cstav/genshinstrument/util/LabelUtil.java similarity index 73% rename from src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/label/AbsGridLabels.java rename to src/main/java/com/cstav/genshinstrument/util/LabelUtil.java index 3d602d1..1873c0f 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/label/AbsGridLabels.java +++ b/src/main/java/com/cstav/genshinstrument/util/LabelUtil.java @@ -1,32 +1,25 @@ -package com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.label; +package com.cstav.genshinstrument.util; import static java.util.Map.entry; -import java.util.LinkedHashMap; -import java.util.Map.Entry; +import java.util.Map; import com.cstav.genshinstrument.client.config.ModClientConfigs; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.AbstractGridInstrumentScreen; import com.cstav.genshinstrument.client.gui.screens.instrument.partial.note.NoteGridButton; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; - -@Environment(EnvType.CLIENT) -public abstract class AbsGridLabels { +public abstract class LabelUtil { public static final String[] DO_RE_MI = { "do", "re", "mi", "fa", "so", "la", "ti" }, - PITCHES = { - "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" - }, ABC = { "C", "D", "E", "F", "G", "A", "B" } ; + // Pitch system implementation // Literally could not have been done w/o Specy's instrument app, oh my gosh their method is genius // Either that or I'm just too much of a novice on music theory @@ -37,7 +30,7 @@ public abstract class AbsGridLabels { * Genshin Music app configs * */ - public static final LinkedHashMap NOTE_SCALES = ofEntries( + public static final Map NOTE_SCALES = Map.ofEntries( entry("Cb", strArr("Cb", "Dbb", "Db", "Ebb", "Eb", "Fb", "Gbb", "Gb", "Abb", "Ab", "Bbb", "Bb")), entry("C", strArr("C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B")), entry("C#", strArr("C#", "D", "D#", "E", "E#", "F#", "G", "G#", "A", "A#", "B", "B#")), @@ -60,31 +53,18 @@ public abstract class AbsGridLabels { entry("B", strArr("B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#")), entry("B#", strArr("B#", "C#", "C##", "D#", "D##", "E#", "F#", "F##", "G#", "G##", "A#", "A##")) ); + public static final int NOTES_PER_SCALE = NOTE_SCALES.get("C").length; + private static String[] strArr(final String... arr) { return arr; } - @SafeVarargs - public static LinkedHashMap ofEntries(Entry... entries) { - final LinkedHashMap map = new LinkedHashMap<>(); - for (Entry entry : entries) - map.put(entry.getKey(), entry.getValue()); - - return map; - } + - public static String getNoteName(final float pitch, final String[] noteLayout, final int offset) { + public static String getNoteName(final int pitch, final String[] noteLayout, final int offset) { final String baseNote = noteLayout[wrapAround(offset, noteLayout.length)]; - //NOTE: Assuming pitch step is always .05 - //TODO: Figure a formula for this 2 - final int pitchStep = perfectConv( - ((2) * 10) * (pitch - 1) - // To account for the fact that a pitch step is 1/16th and we are doing jumps of 1/20th, - // divide by the following amount - / 1.6f - ); final String[] scale = NOTE_SCALES.get(baseNote); - return scale[(doublyPyWrap(pitchStep, scale.length))]; + return scale[(doublyPyWrap(pitch, scale.length))]; } public static String getNoteName(final NoteGridButton gridButton) { final AbstractGridInstrumentScreen screen = (AbstractGridInstrumentScreen) gridButton.instrumentScreen; @@ -95,7 +75,7 @@ public static String getNoteName(final NoteGridButton gridButton) { } public static String getCutNoteName(final NoteGridButton gridButton) { - String result = AbsGridLabels.getNoteName(gridButton); + String result = LabelUtil.getNoteName(gridButton); if (ModClientConfigs.ACCURATE_ACCIDENTALS.get()) result = String.valueOf(result.charAt(0)); @@ -103,14 +83,6 @@ public static String getCutNoteName(final NoteGridButton gridButton) { return result; } - - /** - * Perfects {@code num} to its actual int value by an accuricity of 0.001 - */ - private static int perfectConv(final float num) { - return (int)(num + ((num >= 0) ? .001f : -.001f)); - } - /** * Provides a similar behaviour to python's indexing, * where negatives are counted backwards. @@ -131,7 +103,7 @@ private static int wrapAround(int index, final int arrLength) { return index; } /** - * Performs both {@link AbsGridLabels#pyWrap} and {@link AbsGridLabels#wrapAround} + * Performs both {@link LabelUtil#pyWrap} and {@link LabelUtil#wrapAround} */ private static int doublyPyWrap(int index, final int arrLength) { return wrapAround(pyWrap(index, arrLength), arrLength); diff --git a/src/main/java/com/cstav/genshinstrument/util/ServerUtil.java b/src/main/java/com/cstav/genshinstrument/util/ServerUtil.java index 7a97f27..a6cf391 100644 --- a/src/main/java/com/cstav/genshinstrument/util/ServerUtil.java +++ b/src/main/java/com/cstav/genshinstrument/util/ServerUtil.java @@ -39,7 +39,7 @@ public class ServerUtil { * @param pitch The pitch of the sound to initiate */ public static void sendPlayNotePackets(ServerPlayer player, InteractionHand hand, - NoteSound sound, ResourceLocation instrumentId, float pitch) { + NoteSound sound, ResourceLocation instrumentId, int pitch) { sendPlayNotePackets(player, hand, sound, instrumentId, new DefaultNoteButtonIdentifier(sound), pitch); } @@ -52,7 +52,7 @@ public static void sendPlayNotePackets(ServerPlayer player, InteractionHand hand * @param pitch The pitch of the sound to initiate */ public static void sendPlayNotePackets(ServerPlayer player, InteractionHand hand, - NoteSound sound, ResourceLocation instrumentId, NoteButtonIdentifier noteIdentifier, float pitch) { + NoteSound sound, ResourceLocation instrumentId, NoteButtonIdentifier noteIdentifier, int pitch) { for (final Player listener : noteListeners(player.level(), player.blockPosition())) ModPacketHandler.sendToClient( @@ -86,7 +86,7 @@ public static void sendPlayNotePackets(ServerPlayer player, InteractionHand hand * @param instrumentId The ID of the instrument initiating the sound * @param pitch The pitch of the sound to initiate */ - public static void sendPlayNotePackets(Level level, BlockPos pos, NoteSound sound, ResourceLocation instrumentId, float pitch) { + public static void sendPlayNotePackets(Level level, BlockPos pos, NoteSound sound, ResourceLocation instrumentId, int pitch) { sendPlayNotePackets(level, pos, sound, instrumentId, new DefaultNoteButtonIdentifier(sound), pitch); } /** @@ -100,7 +100,7 @@ public static void sendPlayNotePackets(Level level, BlockPos pos, NoteSound soun * @param pitch The pitch of the sound to initiate */ public static void sendPlayNotePackets(Level level, BlockPos pos, NoteSound sound, - ResourceLocation instrumentId, NoteButtonIdentifier noteIdentifier, float pitch) { + ResourceLocation instrumentId, NoteButtonIdentifier noteIdentifier, int pitch) { for (final Player listener : noteListeners(level, pos)) ModPacketHandler.sendToClient( diff --git a/src/main/resources/assets/genshinstrument/icon.png b/src/main/resources/assets/genshinstrument/icon.png index e342366fb5336adaea5515ddc47fb7faa5f67297..348058d4cca1ec66806861b9e9d18b5f01330216 100644 GIT binary patch literal 11817 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 Pn}-e_wJNtb9rJ$xN0rO@ literal 72267 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%0AWP|RjU>{8B5q~bYKCqaB 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 VOm}#f3se^{s*{)F3H!g2{{_-Gc%uLS diff --git a/src/main/resources/assets/genshinstrument/textures/item/vintage_lyre.png b/src/main/resources/assets/genshinstrument/textures/item/vintage_lyre.png index 7e277c99ebc4bd738e31bac36c1858bf400fae68..a8028ada9ea48ce3e2ec48dc76466c55ebebe47b 100644 GIT binary patch literal 541 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%Lfm0X`wFq4s(K)>?j+no%xBA$Gb!wmKp9x=scPJ{Ibc&PFkA#=*9_;g0(L z)|vq}T3%+V(XJ-39%j+5#&I5|A$EEZP6k1?THdCrF>Ypw-WI3!Z99K-pO>j>oQK(w zZEHPERReAHLmUmlo%Dl&Cfn)wS!(#(>Kxp*);lT8JvPwOL?zHp_d!jdkCo=GwM%@h zH2tl#{H--SOjJC~)SN=S{Vg>Dtu;N2mA%Z=9D+UV0^L1KRosk~U5ykS^%Q`faGCLg z2S_=T1o;L3rwlM0s@~`VG>fyqBeIx*fp0$uGg`0)s{jROdAc};XaxKAweuY^;Bh&u z6>utUsY3*7)VF&D|Nn=_EDETasIx!)beDsT(!`$55Ub?W;)y42vmV!)b}A}l)z)2i zGnm$zY24v^z!>XSWy8GPf6^`97c6a3s`Hb&1Xs)W|Iz%ub;eoSmn@DuF8FLr;SIB2 z7Los?e%+C>=kE_UJF50A|NJzoLvY7k`HObSA8Pjcu0OoK-e)ya4dYxVz%vE=-UE-Y-<{lgP zpeE2c!q>xCg@J*AWsUH5ASF-|w_PK)kP3bOQ#V*bAsL#E4X7BfTPDj@5 zP-O`6wDJLVb~S?foqZxl$_eP?flVw>$Vle>Wi6?@C*|&#kiD@pZBJywJeAjiPu4zkNR2RFTE^cvM?EIS8IhD~f z%Oe>W81k1cn+T-rOM?7@|C0#nQPnf_snv)B&EhQZh%9Dc;5z`qj9J$@Ie~&RJzX3_ zG=hEm+WDFcBwAh{YOG$cLbR}{`r)^G|2OWL)>h#A@5}FXxmpwYPNW1YP1lb&8l-ee zba&R(R~s`-qH}j&X077W_{DsHwJk@+uKD5&m95HA!9FHldj7HEGm5rG9KEtgF_qzQB-gQu&X%Q~loCIDLF|0w_f delta 251 zcmbQivW01aW4%FuPl)S|zKr$l$$KYd@0pY}Ip6Ehw7mUOa*k|UyR0E@*V-lCNn!4> zfe&f|og;kbRYo!}Fofm2+zq4zN`m}?|Dyti2g_FJG} zp{I*uh(@q)-)*tO3LGqs+z}vkx$GA4@21Pg^kTvrO_iI6bt3jV+~&p^}fW8Vf#`|fO}8TY%dAGxQ$@ZIcSnM42Y ocoy1N9F<64|Dp4Kr11gyByVocEJLOnK-(ESUHx3vIVCg!0DKv5AOHXW diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 641b71c..0e56149 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "id": "genshinstrument", - "version": "3.0", + "version": "1", "name": "Genshin Instruments", "description": "A mod that brings Genshin Impact's instruments to Minecraft!\nThis is my first ever published mod, and I am quite proud of it :')\nHope you enjoy it as much as I do!!", "authors": [ From 70b2416edd8140c83da5429927a19d119e3c0fc8 Mon Sep 17 00:00:00 2001 From: StavWasPlayZ Date: Sat, 22 Jul 2023 15:07:09 +0300 Subject: [PATCH 2/3] Fix screen render issues --- .../instrument/partial/note/NoteButton.java | 2 +- .../AbstractInstrumentOptionsScreen.java | 21 +++++++++++++++---- src/main/resources/fabric.mod.json | 2 +- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButton.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButton.java index 0d46773..4108695 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButton.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/note/NoteButton.java @@ -138,7 +138,7 @@ public boolean isPlaying() { @Override public void renderWidget(GuiGraphics gui, int mouseX, int mouseY, float partialTick) { - noteRenderer.renderNoteButton(gui, instrumentScreen.getThemeLoader()); + noteRenderer.render(gui, mouseX, mouseY, partialTick, instrumentScreen.getThemeLoader()); } diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java index 4c58d3a..4c1775b 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java @@ -17,6 +17,7 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractSliderButton; import net.minecraft.client.gui.components.Button; @@ -308,10 +309,13 @@ protected void onAccurateAccidentalsChanged(final CycleButton button, f @Override public void render(GuiGraphics gui, int pMouseX, int pMouseY, float pPartialTick) { - if (isOverlay) - instrumentScreen.render(gui, pMouseX, pMouseY, pPartialTick); - - + if (isOverlay) { + instrumentScreen.render(gui, Integer.MAX_VALUE, Integer.MAX_VALUE, pPartialTick); + // Push the options screen infront + gui.pose().translate(0, 0, 1); + } + + renderBackground(gui); gui.drawCenteredString(font, title, width/2, 20, Color.WHITE.getRGB()); @@ -359,6 +363,15 @@ public boolean keyReleased(int p_94715_, int p_94716_, int p_94717_) { return super.keyReleased(p_94715_, p_94716_, p_94717_); } + // Also resizing + @Override + public void resize(Minecraft minecraft, int width, int height) { + if (isOverlay) + instrumentScreen.resize(minecraft, width, height); + + super.resize(minecraft, width, height); + } + /** diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 0e56149..9ee8f25 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "id": "genshinstrument", - "version": "1", + "version": "3.1", "name": "Genshin Instruments", "description": "A mod that brings Genshin Impact's instruments to Minecraft!\nThis is my first ever published mod, and I am quite proud of it :')\nHope you enjoy it as much as I do!!", "authors": [ From ee5fcc4619ac3d0f8af0780af2f6e2b34cf0442c Mon Sep 17 00:00:00 2001 From: StavWasPlayZ Date: Sun, 23 Jul 2023 00:53:00 +0300 Subject: [PATCH 3/3] Implement changes from Forge --- .../partial/AbstractInstrumentScreen.java | 21 +++++++++++++- .../AbstractInstrumentOptionsScreen.java | 21 +++++++------- .../genshinstrument/event/ClientEvents.java | 28 +++++++++---------- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/AbstractInstrumentScreen.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/AbstractInstrumentScreen.java index 61cc974..d4c39d4 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/AbstractInstrumentScreen.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/instrument/partial/AbstractInstrumentScreen.java @@ -2,6 +2,7 @@ import java.util.Map; import java.util.NoSuchElementException; +import java.util.Optional; import com.cstav.genshinstrument.client.config.ModClientConfigs; import com.cstav.genshinstrument.client.gui.screens.instrument.GenshinConsentScreen; @@ -17,6 +18,7 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -145,7 +147,7 @@ public ResourceLocation getResourceFromRoot(final String path) { } - protected final AbstractInstrumentOptionsScreen optionsScreen = initInstrumentOptionsScreen(); + public final AbstractInstrumentOptionsScreen optionsScreen = initInstrumentOptionsScreen(); public final InteractionHand interactionHand; public AbstractInstrumentScreen(final InteractionHand hand) { @@ -241,6 +243,23 @@ public void onClose() { super.onClose(); } + + + /** + * @return The current instrument screen, if present + */ + public static Optional getCurrentScreen(final Minecraft minecraft) { + if (minecraft.screen instanceof AbstractInstrumentScreen) + return Optional.of((AbstractInstrumentScreen)minecraft.screen); + + if (minecraft.screen instanceof AbstractInstrumentOptionsScreen) { + final AbstractInstrumentOptionsScreen instrumentOptionsScreen = (AbstractInstrumentOptionsScreen)minecraft.screen; + if (instrumentOptionsScreen.isOverlay) + return Optional.of(instrumentOptionsScreen.instrumentScreen); + } + + return Optional.empty(); + } @Override diff --git a/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java b/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java index 4c1775b..4be3dc9 100644 --- a/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java +++ b/src/main/java/com/cstav/genshinstrument/client/gui/screens/options/instrument/AbstractInstrumentOptionsScreen.java @@ -83,12 +83,12 @@ protected int getButtonHeight() { protected final Screen lastScreen; - protected final boolean isOverlay; + public final boolean isOverlay; protected final @Nullable INoteLabel[] labels; protected final @Nullable INoteLabel currLabel; - protected final @Nullable AbstractInstrumentScreen instrumentScreen; + public final @Nullable AbstractInstrumentScreen instrumentScreen; public AbstractInstrumentOptionsScreen(@Nullable AbstractInstrumentScreen screen) { super(Component.translatable("button.genshinstrument.instrumentOptions")); @@ -349,18 +349,19 @@ public boolean isPauseScreen() { // Make pressing notes possible with keyboard @Override - public boolean keyPressed(int p_96552_, int p_96553_, int p_96554_) { - if (isOverlay && p_96552_ != 256) - instrumentScreen.keyPressed(p_96552_, p_96553_, p_96554_); + public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) { + // Only pass when it is a note key + if (isOverlay && (instrumentScreen.getNoteByKey(pKeyCode) != null)) + instrumentScreen.keyPressed(pKeyCode, pScanCode, pModifiers); - return super.keyPressed(p_96552_, p_96553_, p_96554_); + return super.keyPressed(pKeyCode, pScanCode, pModifiers); } @Override - public boolean keyReleased(int p_94715_, int p_94716_, int p_94717_) { - if (isOverlay && p_94715_ != 256) - instrumentScreen.keyReleased(p_94715_, p_94716_, p_94717_); + public boolean keyReleased(int pKeyCode, int pScanCode, int pModifiers) { + if (isOverlay && (instrumentScreen.getNoteByKey(pKeyCode) != null)) + instrumentScreen.keyReleased(pKeyCode, pScanCode, pModifiers); - return super.keyReleased(p_94715_, p_94716_, p_94717_); + return super.keyReleased(pKeyCode, pScanCode, pModifiers); } // Also resizing diff --git a/src/main/java/com/cstav/genshinstrument/event/ClientEvents.java b/src/main/java/com/cstav/genshinstrument/event/ClientEvents.java index 34f58f0..5a06679 100644 --- a/src/main/java/com/cstav/genshinstrument/event/ClientEvents.java +++ b/src/main/java/com/cstav/genshinstrument/event/ClientEvents.java @@ -5,7 +5,7 @@ import com.cstav.genshinstrument.event.InstrumentPlayedEvent.ByPlayer.ByPlayerArgs; import com.cstav.genshinstrument.event.InstrumentPlayedEvent.InstrumentPlayedEventArgs; import com.cstav.genshinstrument.item.InstrumentItem; -import com.cstav.genshinstrument.util.ServerUtil; +import com.cstav.genshinstrument.sound.NoteSound; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -41,30 +41,30 @@ public static void onWorldTick(Minecraft mc) { public static void onInstrumentPlayed(final InstrumentPlayedEventArgs args) { if (!args.isClientSide) return; + if (!ModClientConfigs.SHARED_INSTRUMENT.get()) + return; // If this sound was produced by a player, and that player is ourselves - omit. if ((args instanceof ByPlayerArgs) && ((ByPlayerArgs)(args)).player.equals(MINECRAFT.player)) return; - if (!ModClientConfigs.SHARED_INSTRUMENT.get()) + // Only show play notes in the local range + if (!args.pos.closerThan(MINECRAFT.player.blockPosition(), NoteSound.LOCAL_RANGE)) return; - if (!(MINECRAFT.screen instanceof AbstractInstrumentScreen)) - return; + AbstractInstrumentScreen.getCurrentScreen(MINECRAFT).ifPresent((screen) -> { + // The produced instrument sound has to match the current screen's sounds + if (!screen.getInstrumentId().equals(args.instrumentId)) + return; - final AbstractInstrumentScreen screen = (AbstractInstrumentScreen) MINECRAFT.screen; - // The instrument sound produced has to match the current screen's sounds - if (!screen.getInstrumentId().equals(args.instrumentId)) - return; - // Only show the played note in a close distance - if (!args.pos.closerThan(MINECRAFT.player.blockPosition(), ServerUtil.PLAY_DISTANCE / 3)) - return; + try { + screen.getNoteButton(args.noteIdentifier).playNoteAnimation(true); - try { - screen.getNoteButton(args.noteIdentifier).playNoteAnimation(true); - } catch (Exception e) {} + } catch (Exception e) {} + }); } + }