package net.minecraft.client.gui.screens.inventory; 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.renderer.RenderType; import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvents; import net.minecraft.util.Mth; import net.minecraft.util.context.ContextMap; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.StonecutterMenu; import net.minecraft.world.item.crafting.StonecutterRecipe; import net.minecraft.world.item.crafting.SelectableRecipe.SingleInputEntry; import net.minecraft.world.item.crafting.SelectableRecipe.SingleInputSet; import net.minecraft.world.item.crafting.display.SlotDisplay; import net.minecraft.world.item.crafting.display.SlotDisplayContext; @Environment(EnvType.CLIENT) public class StonecutterScreen extends AbstractContainerScreen { private static final ResourceLocation SCROLLER_SPRITE = ResourceLocation.withDefaultNamespace("container/stonecutter/scroller"); private static final ResourceLocation SCROLLER_DISABLED_SPRITE = ResourceLocation.withDefaultNamespace("container/stonecutter/scroller_disabled"); private static final ResourceLocation RECIPE_SELECTED_SPRITE = ResourceLocation.withDefaultNamespace("container/stonecutter/recipe_selected"); private static final ResourceLocation RECIPE_HIGHLIGHTED_SPRITE = ResourceLocation.withDefaultNamespace("container/stonecutter/recipe_highlighted"); private static final ResourceLocation RECIPE_SPRITE = ResourceLocation.withDefaultNamespace("container/stonecutter/recipe"); private static final ResourceLocation BG_LOCATION = ResourceLocation.withDefaultNamespace("textures/gui/container/stonecutter.png"); private static final int SCROLLER_WIDTH = 12; private static final int SCROLLER_HEIGHT = 15; private static final int RECIPES_COLUMNS = 4; private static final int RECIPES_ROWS = 3; private static final int RECIPES_IMAGE_SIZE_WIDTH = 16; private static final int RECIPES_IMAGE_SIZE_HEIGHT = 18; private static final int SCROLLER_FULL_HEIGHT = 54; private static final int RECIPES_X = 52; private static final int RECIPES_Y = 14; private float scrollOffs; /** * Is {@code true} if the player clicked on the scroll wheel in the GUI. */ private boolean scrolling; /** * The index of the first recipe to display. * The number of recipes displayed at any time is 12 (4 recipes per row, and 3 rows). If the player scrolled down one row, this value would be 4 (representing the index of the first slot on the second row). */ private int startIndex; private boolean displayRecipes; public StonecutterScreen(StonecutterMenu menu, Inventory playerInventory, Component title) { super(menu, playerInventory, title); menu.registerUpdateListener(this::containerChanged); this.titleLabelY--; } @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { super.render(guiGraphics, mouseX, mouseY, partialTick); this.renderTooltip(guiGraphics, mouseX, mouseY); } @Override protected void renderBg(GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) { int i = this.leftPos; int j = this.topPos; guiGraphics.blit(RenderType::guiTextured, BG_LOCATION, i, j, 0.0F, 0.0F, this.imageWidth, this.imageHeight, 256, 256); int k = (int)(41.0F * this.scrollOffs); ResourceLocation resourceLocation = this.isScrollBarActive() ? SCROLLER_SPRITE : SCROLLER_DISABLED_SPRITE; guiGraphics.blitSprite(RenderType::guiTextured, resourceLocation, i + 119, j + 15 + k, 12, 15); int l = this.leftPos + 52; int m = this.topPos + 14; int n = this.startIndex + 12; this.renderButtons(guiGraphics, mouseX, mouseY, l, m, n); this.renderRecipes(guiGraphics, l, m, n); } @Override protected void renderTooltip(GuiGraphics guiGraphics, int x, int y) { super.renderTooltip(guiGraphics, x, y); if (this.displayRecipes) { int i = this.leftPos + 52; int j = this.topPos + 14; int k = this.startIndex + 12; SingleInputSet singleInputSet = this.menu.getVisibleRecipes(); for (int l = this.startIndex; l < k && l < singleInputSet.size(); l++) { int m = l - this.startIndex; int n = i + m % 4 * 16; int o = j + m / 4 * 18 + 2; if (x >= n && x < n + 16 && y >= o && y < o + 18) { ContextMap contextMap = SlotDisplayContext.fromLevel(this.minecraft.level); SlotDisplay slotDisplay = ((SingleInputEntry)singleInputSet.entries().get(l)).recipe().optionDisplay(); guiGraphics.renderTooltip(this.font, slotDisplay.resolveForFirstStack(contextMap), x, y); } } } } private void renderButtons(GuiGraphics guiGraphics, int mouseX, int mouseY, int x, int y, int lastVisibleElementIndex) { for (int i = this.startIndex; i < lastVisibleElementIndex && i < this.menu.getNumberOfVisibleRecipes(); i++) { int j = i - this.startIndex; int k = x + j % 4 * 16; int l = j / 4; int m = y + l * 18 + 2; ResourceLocation resourceLocation; if (i == this.menu.getSelectedRecipeIndex()) { resourceLocation = RECIPE_SELECTED_SPRITE; } else if (mouseX >= k && mouseY >= m && mouseX < k + 16 && mouseY < m + 18) { resourceLocation = RECIPE_HIGHLIGHTED_SPRITE; } else { resourceLocation = RECIPE_SPRITE; } guiGraphics.blitSprite(RenderType::guiTextured, resourceLocation, k, m - 1, 16, 18); } } private void renderRecipes(GuiGraphics guiGraphics, int x, int y, int startIndex) { SingleInputSet singleInputSet = this.menu.getVisibleRecipes(); ContextMap contextMap = SlotDisplayContext.fromLevel(this.minecraft.level); for (int i = this.startIndex; i < startIndex && i < singleInputSet.size(); i++) { int j = i - this.startIndex; int k = x + j % 4 * 16; int l = j / 4; int m = y + l * 18 + 2; SlotDisplay slotDisplay = ((SingleInputEntry)singleInputSet.entries().get(i)).recipe().optionDisplay(); guiGraphics.renderItem(slotDisplay.resolveForFirstStack(contextMap), k, m); } } @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { this.scrolling = false; if (this.displayRecipes) { int i = this.leftPos + 52; int j = this.topPos + 14; int k = this.startIndex + 12; for (int l = this.startIndex; l < k; l++) { int m = l - this.startIndex; double d = mouseX - (i + m % 4 * 16); double e = mouseY - (j + m / 4 * 18); if (d >= 0.0 && e >= 0.0 && d < 16.0 && e < 18.0 && this.menu.clickMenuButton(this.minecraft.player, l)) { Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_STONECUTTER_SELECT_RECIPE, 1.0F)); this.minecraft.gameMode.handleInventoryButtonClick(this.menu.containerId, l); return true; } } i = this.leftPos + 119; j = this.topPos + 9; if (mouseX >= i && mouseX < i + 12 && mouseY >= j && mouseY < j + 54) { this.scrolling = true; } } return super.mouseClicked(mouseX, mouseY, button); } @Override public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) { if (this.scrolling && this.isScrollBarActive()) { int i = this.topPos + 14; int j = i + 54; this.scrollOffs = ((float)mouseY - i - 7.5F) / (j - i - 15.0F); this.scrollOffs = Mth.clamp(this.scrollOffs, 0.0F, 1.0F); this.startIndex = (int)(this.scrollOffs * this.getOffscreenRows() + 0.5) * 4; return true; } else { return super.mouseDragged(mouseX, mouseY, button, dragX, dragY); } } @Override public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) { if (super.mouseScrolled(mouseX, mouseY, scrollX, scrollY)) { return true; } else { if (this.isScrollBarActive()) { int i = this.getOffscreenRows(); float f = (float)scrollY / i; this.scrollOffs = Mth.clamp(this.scrollOffs - f, 0.0F, 1.0F); this.startIndex = (int)(this.scrollOffs * i + 0.5) * 4; } return true; } } private boolean isScrollBarActive() { return this.displayRecipes && this.menu.getNumberOfVisibleRecipes() > 12; } protected int getOffscreenRows() { return (this.menu.getNumberOfVisibleRecipes() + 4 - 1) / 4 - 3; } /** * Called every time this screen's container is changed (is marked as dirty). */ private void containerChanged() { this.displayRecipes = this.menu.hasInputItem(); if (!this.displayRecipes) { this.scrollOffs = 0.0F; this.startIndex = 0; } } }