/* * Decompiled with CFR 0.152. */ package net.minecraft.client.gui.screens.recipebook; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.narration.NarratedElementType; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.recipebook.RecipeBookPage; import net.minecraft.client.gui.screens.recipebook.RecipeCollection; import net.minecraft.client.gui.screens.recipebook.SlotSelectTime; import net.minecraft.client.input.MouseButtonInfo; import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import net.minecraft.util.context.ContextMap; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.display.RecipeDisplayEntry; import net.minecraft.world.item.crafting.display.RecipeDisplayId; public class RecipeButton extends AbstractWidget { private static final Identifier SLOT_MANY_CRAFTABLE_SPRITE = Identifier.withDefaultNamespace("recipe_book/slot_many_craftable"); private static final Identifier SLOT_CRAFTABLE_SPRITE = Identifier.withDefaultNamespace("recipe_book/slot_craftable"); private static final Identifier SLOT_MANY_UNCRAFTABLE_SPRITE = Identifier.withDefaultNamespace("recipe_book/slot_many_uncraftable"); private static final Identifier SLOT_UNCRAFTABLE_SPRITE = Identifier.withDefaultNamespace("recipe_book/slot_uncraftable"); private static final float ANIMATION_TIME = 15.0f; private static final int BACKGROUND_SIZE = 25; private static final Component MORE_RECIPES_TOOLTIP = Component.translatable("gui.recipebook.moreRecipes"); private RecipeCollection collection = RecipeCollection.EMPTY; private List selectedEntries = List.of(); private boolean allRecipesHaveSameResultDisplay; private final SlotSelectTime slotSelectTime; private float animationTime; public RecipeButton(SlotSelectTime slotSelectTime) { super(0, 0, 25, 25, CommonComponents.EMPTY); this.slotSelectTime = slotSelectTime; } public void init(RecipeCollection collection, boolean isFiltering, RecipeBookPage page, ContextMap resolutionContext) { this.collection = collection; List fittingRecipes = collection.getSelectedRecipes(isFiltering ? RecipeCollection.CraftableStatus.CRAFTABLE : RecipeCollection.CraftableStatus.ANY); this.selectedEntries = fittingRecipes.stream().map(entry -> new ResolvedEntry(entry.id(), entry.resultItems(resolutionContext))).toList(); this.allRecipesHaveSameResultDisplay = RecipeButton.allRecipesHaveSameResultDisplay(this.selectedEntries); List newlyShownRecipes = fittingRecipes.stream().map(RecipeDisplayEntry::id).filter(page.getRecipeBook()::willHighlight).toList(); if (!newlyShownRecipes.isEmpty()) { newlyShownRecipes.forEach(page::recipeShown); this.animationTime = 15.0f; } } private static boolean allRecipesHaveSameResultDisplay(List entries) { Iterator itemsIterator = entries.stream().flatMap(e -> e.displayItems().stream()).iterator(); if (!itemsIterator.hasNext()) { return true; } ItemStack firstItem = (ItemStack)itemsIterator.next(); while (itemsIterator.hasNext()) { ItemStack nextItem = (ItemStack)itemsIterator.next(); if (ItemStack.isSameItemSameComponents(firstItem, nextItem)) continue; return false; } return true; } public RecipeCollection getCollection() { return this.collection; } @Override public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float a) { boolean shouldAnimate; Identifier sprite = this.collection.hasCraftable() ? (this.hasMultipleRecipes() ? SLOT_MANY_CRAFTABLE_SPRITE : SLOT_CRAFTABLE_SPRITE) : (this.hasMultipleRecipes() ? SLOT_MANY_UNCRAFTABLE_SPRITE : SLOT_UNCRAFTABLE_SPRITE); boolean bl = shouldAnimate = this.animationTime > 0.0f; if (shouldAnimate) { float squeeze = 1.0f + 0.1f * (float)Math.sin(this.animationTime / 15.0f * (float)Math.PI); graphics.pose().pushMatrix(); graphics.pose().translate((float)(this.getX() + 8), (float)(this.getY() + 12)); graphics.pose().scale(squeeze, squeeze); graphics.pose().translate((float)(-(this.getX() + 8)), (float)(-(this.getY() + 12))); this.animationTime -= a; } graphics.blitSprite(RenderPipelines.GUI_TEXTURED, sprite, this.getX(), this.getY(), this.width, this.height); ItemStack currentItemStack = this.getDisplayStack(); int offset = 4; if (this.hasMultipleRecipes() && this.allRecipesHaveSameResultDisplay) { graphics.renderItem(currentItemStack, this.getX() + offset + 1, this.getY() + offset + 1, 0); --offset; } graphics.renderFakeItem(currentItemStack, this.getX() + offset, this.getY() + offset); if (shouldAnimate) { graphics.pose().popMatrix(); } } private boolean hasMultipleRecipes() { return this.selectedEntries.size() > 1; } public boolean isOnlyOption() { return this.selectedEntries.size() == 1; } public RecipeDisplayId getCurrentRecipe() { int index = this.slotSelectTime.currentIndex() % this.selectedEntries.size(); return this.selectedEntries.get((int)index).id; } public ItemStack getDisplayStack() { int currentIndex = this.slotSelectTime.currentIndex(); int entryCount = this.selectedEntries.size(); int offsetIndex = currentIndex / entryCount; int entryIndex = currentIndex - entryCount * offsetIndex; return this.selectedEntries.get(entryIndex).selectItem(offsetIndex); } public List getTooltipText(ItemStack displayStack) { ArrayList texts = new ArrayList(Screen.getTooltipFromItem(Minecraft.getInstance(), displayStack)); if (this.hasMultipleRecipes()) { texts.add(MORE_RECIPES_TOOLTIP); } return texts; } @Override public void updateWidgetNarration(NarrationElementOutput output) { output.add(NarratedElementType.TITLE, (Component)Component.translatable("narration.recipe", this.getDisplayStack().getHoverName())); if (this.hasMultipleRecipes()) { output.add(NarratedElementType.USAGE, Component.translatable("narration.button.usage.hovered"), Component.translatable("narration.recipe.usage.more")); } else { output.add(NarratedElementType.USAGE, (Component)Component.translatable("narration.button.usage.hovered")); } } @Override public int getWidth() { return 25; } @Override protected boolean isValidClickButton(MouseButtonInfo buttonInfo) { return buttonInfo.button() == 0 || buttonInfo.button() == 1; } private record ResolvedEntry(RecipeDisplayId id, List displayItems) { public ItemStack selectItem(int index) { if (this.displayItems.isEmpty()) { return ItemStack.EMPTY; } int offset = index % this.displayItems.size(); return this.displayItems.get(offset); } } }