package net.minecraft.client.gui.screens.inventory; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; import net.minecraft.client.renderer.RenderType; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ServerboundSelectTradePacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.entity.npc.VillagerData; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MerchantMenu; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.trading.MerchantOffer; import net.minecraft.world.item.trading.MerchantOffers; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) public class MerchantScreen extends AbstractContainerScreen { private static final ResourceLocation OUT_OF_STOCK_SPRITE = ResourceLocation.withDefaultNamespace("container/villager/out_of_stock"); private static final ResourceLocation EXPERIENCE_BAR_BACKGROUND_SPRITE = ResourceLocation.withDefaultNamespace("container/villager/experience_bar_background"); private static final ResourceLocation EXPERIENCE_BAR_CURRENT_SPRITE = ResourceLocation.withDefaultNamespace("container/villager/experience_bar_current"); private static final ResourceLocation EXPERIENCE_BAR_RESULT_SPRITE = ResourceLocation.withDefaultNamespace("container/villager/experience_bar_result"); private static final ResourceLocation SCROLLER_SPRITE = ResourceLocation.withDefaultNamespace("container/villager/scroller"); private static final ResourceLocation SCROLLER_DISABLED_SPRITE = ResourceLocation.withDefaultNamespace("container/villager/scroller_disabled"); private static final ResourceLocation TRADE_ARROW_OUT_OF_STOCK_SPRITE = ResourceLocation.withDefaultNamespace("container/villager/trade_arrow_out_of_stock"); private static final ResourceLocation TRADE_ARROW_SPRITE = ResourceLocation.withDefaultNamespace("container/villager/trade_arrow"); private static final ResourceLocation DISCOUNT_STRIKETHRUOGH_SPRITE = ResourceLocation.withDefaultNamespace("container/villager/discount_strikethrough"); private static final ResourceLocation VILLAGER_LOCATION = ResourceLocation.withDefaultNamespace("textures/gui/container/villager.png"); private static final int TEXTURE_WIDTH = 512; private static final int TEXTURE_HEIGHT = 256; private static final int MERCHANT_MENU_PART_X = 99; private static final int PROGRESS_BAR_X = 136; private static final int PROGRESS_BAR_Y = 16; private static final int SELL_ITEM_1_X = 5; private static final int SELL_ITEM_2_X = 35; private static final int BUY_ITEM_X = 68; private static final int LABEL_Y = 6; private static final int NUMBER_OF_OFFER_BUTTONS = 7; private static final int TRADE_BUTTON_X = 5; private static final int TRADE_BUTTON_HEIGHT = 20; private static final int TRADE_BUTTON_WIDTH = 88; private static final int SCROLLER_HEIGHT = 27; private static final int SCROLLER_WIDTH = 6; private static final int SCROLL_BAR_HEIGHT = 139; private static final int SCROLL_BAR_TOP_POS_Y = 18; private static final int SCROLL_BAR_START_X = 94; private static final Component TRADES_LABEL = Component.translatable("merchant.trades"); private static final Component DEPRECATED_TOOLTIP = Component.translatable("merchant.deprecated"); private int shopItem; private final MerchantScreen.TradeOfferButton[] tradeOfferButtons = new MerchantScreen.TradeOfferButton[7]; int scrollOff; private boolean isDragging; public MerchantScreen(MerchantMenu p_99123_, Inventory p_99124_, Component p_99125_) { super(p_99123_, p_99124_, p_99125_); this.imageWidth = 276; this.inventoryLabelX = 107; } private void postButtonClick() { this.menu.setSelectionHint(this.shopItem); this.menu.tryMoveItems(this.shopItem); this.minecraft.getConnection().send(new ServerboundSelectTradePacket(this.shopItem)); } @Override protected void init() { super.init(); int i = (this.width - this.imageWidth) / 2; int j = (this.height - this.imageHeight) / 2; int k = j + 16 + 2; for (int l = 0; l < 7; l++) { this.tradeOfferButtons[l] = this.addRenderableWidget(new MerchantScreen.TradeOfferButton(i + 5, k, l, p_99174_ -> { if (p_99174_ instanceof MerchantScreen.TradeOfferButton) { this.shopItem = ((MerchantScreen.TradeOfferButton)p_99174_).getIndex() + this.scrollOff; this.postButtonClick(); } })); k += 20; } } @Override protected void renderLabels(GuiGraphics p_283337_, int p_282009_, int p_283691_) { int i = this.menu.getTraderLevel(); if (i > 0 && i <= 5 && this.menu.showProgressBar()) { Component component = Component.translatable("merchant.title", this.title, Component.translatable("merchant.level." + i)); int j = this.font.width(component); int k = 49 + this.imageWidth / 2 - j / 2; p_283337_.drawString(this.font, component, k, 6, 4210752, false); } else { p_283337_.drawString(this.font, this.title, 49 + this.imageWidth / 2 - this.font.width(this.title) / 2, 6, 4210752, false); } p_283337_.drawString(this.font, this.playerInventoryTitle, this.inventoryLabelX, this.inventoryLabelY, 4210752, false); int l = this.font.width(TRADES_LABEL); p_283337_.drawString(this.font, TRADES_LABEL, 5 - l / 2 + 48, 6, 4210752, false); } @Override protected void renderBg(GuiGraphics p_283072_, float p_281275_, int p_282312_, int p_282984_) { int i = (this.width - this.imageWidth) / 2; int j = (this.height - this.imageHeight) / 2; p_283072_.blit(RenderType::guiTextured, VILLAGER_LOCATION, i, j, 0.0F, 0.0F, this.imageWidth, this.imageHeight, 512, 256); MerchantOffers merchantoffers = this.menu.getOffers(); if (!merchantoffers.isEmpty()) { int k = this.shopItem; if (k < 0 || k >= merchantoffers.size()) { return; } MerchantOffer merchantoffer = merchantoffers.get(k); if (merchantoffer.isOutOfStock()) { p_283072_.blitSprite(RenderType::guiTextured, OUT_OF_STOCK_SPRITE, this.leftPos + 83 + 99, this.topPos + 35, 28, 21); } } } private void renderProgressBar(GuiGraphics p_281426_, int p_283008_, int p_283085_, MerchantOffer p_282094_) { int i = this.menu.getTraderLevel(); int j = this.menu.getTraderXp(); if (i < 5) { p_281426_.blitSprite(RenderType::guiTextured, EXPERIENCE_BAR_BACKGROUND_SPRITE, p_283008_ + 136, p_283085_ + 16, 102, 5); int k = VillagerData.getMinXpPerLevel(i); if (j >= k && VillagerData.canLevelUp(i)) { int l = 102; float f = 102.0F / (VillagerData.getMaxXpPerLevel(i) - k); int i1 = Math.min(Mth.floor(f * (j - k)), 102); p_281426_.blitSprite(RenderType::guiTextured, EXPERIENCE_BAR_CURRENT_SPRITE, 102, 5, 0, 0, p_283008_ + 136, p_283085_ + 16, i1, 5); int j1 = this.menu.getFutureTraderXp(); if (j1 > 0) { int k1 = Math.min(Mth.floor(j1 * f), 102 - i1); p_281426_.blitSprite(RenderType::guiTextured, EXPERIENCE_BAR_RESULT_SPRITE, 102, 5, i1, 0, p_283008_ + 136 + i1, p_283085_ + 16, k1, 5); } } } } private void renderScroller(GuiGraphics p_283030_, int p_283154_, int p_281664_, MerchantOffers p_282877_) { int i = p_282877_.size() + 1 - 7; if (i > 1) { int j = 139 - (27 + (i - 1) * 139 / i); int k = 1 + j / i + 139 / i; int l = 113; int i1 = Math.min(113, this.scrollOff * k); if (this.scrollOff == i - 1) { i1 = 113; } p_283030_.blitSprite(RenderType::guiTextured, SCROLLER_SPRITE, p_283154_ + 94, p_281664_ + 18 + i1, 6, 27); } else { p_283030_.blitSprite(RenderType::guiTextured, SCROLLER_DISABLED_SPRITE, p_283154_ + 94, p_281664_ + 18, 6, 27); } } @Override public void render(GuiGraphics p_283487_, int p_281994_, int p_282099_, float p_281815_) { super.render(p_283487_, p_281994_, p_282099_, p_281815_); MerchantOffers merchantoffers = this.menu.getOffers(); if (!merchantoffers.isEmpty()) { int i = (this.width - this.imageWidth) / 2; int j = (this.height - this.imageHeight) / 2; int k = j + 16 + 1; int l = i + 5 + 5; this.renderScroller(p_283487_, i, j, merchantoffers); int i1 = 0; for (MerchantOffer merchantoffer : merchantoffers) { if (!this.canScroll(merchantoffers.size()) || i1 >= this.scrollOff && i1 < 7 + this.scrollOff) { ItemStack itemstack = merchantoffer.getBaseCostA(); ItemStack itemstack1 = merchantoffer.getCostA(); ItemStack itemstack2 = merchantoffer.getCostB(); ItemStack itemstack3 = merchantoffer.getResult(); p_283487_.pose().pushPose(); p_283487_.pose().translate(0.0F, 0.0F, 100.0F); int j1 = k + 2; this.renderAndDecorateCostA(p_283487_, itemstack1, itemstack, l, j1); if (!itemstack2.isEmpty()) { p_283487_.renderFakeItem(itemstack2, i + 5 + 35, j1); p_283487_.renderItemDecorations(this.font, itemstack2, i + 5 + 35, j1); } this.renderButtonArrows(p_283487_, merchantoffer, i, j1); p_283487_.renderFakeItem(itemstack3, i + 5 + 68, j1); p_283487_.renderItemDecorations(this.font, itemstack3, i + 5 + 68, j1); p_283487_.pose().popPose(); k += 20; i1++; } else { i1++; } } int k1 = this.shopItem; MerchantOffer merchantoffer1 = merchantoffers.get(k1); if (this.menu.showProgressBar()) { this.renderProgressBar(p_283487_, i, j, merchantoffer1); } if (merchantoffer1.isOutOfStock() && this.isHovering(186, 35, 22, 21, p_281994_, p_282099_) && this.menu.canRestock()) { p_283487_.renderTooltip(this.font, DEPRECATED_TOOLTIP, p_281994_, p_282099_); } for (MerchantScreen.TradeOfferButton merchantscreen$tradeofferbutton : this.tradeOfferButtons) { if (merchantscreen$tradeofferbutton.isHoveredOrFocused()) { merchantscreen$tradeofferbutton.renderToolTip(p_283487_, p_281994_, p_282099_); } merchantscreen$tradeofferbutton.visible = merchantscreen$tradeofferbutton.index < this.menu.getOffers().size(); } } this.renderTooltip(p_283487_, p_281994_, p_282099_); } private void renderButtonArrows(GuiGraphics p_283020_, MerchantOffer p_281926_, int p_282752_, int p_282179_) { if (p_281926_.isOutOfStock()) { p_283020_.blitSprite(RenderType::guiTextured, TRADE_ARROW_OUT_OF_STOCK_SPRITE, p_282752_ + 5 + 35 + 20, p_282179_ + 3, 10, 9); } else { p_283020_.blitSprite(RenderType::guiTextured, TRADE_ARROW_SPRITE, p_282752_ + 5 + 35 + 20, p_282179_ + 3, 10, 9); } } private void renderAndDecorateCostA(GuiGraphics p_281357_, ItemStack p_283466_, ItemStack p_282046_, int p_282403_, int p_283601_) { p_281357_.renderFakeItem(p_283466_, p_282403_, p_283601_); if (p_282046_.getCount() == p_283466_.getCount()) { p_281357_.renderItemDecorations(this.font, p_283466_, p_282403_, p_283601_); } else { p_281357_.renderItemDecorations(this.font, p_282046_, p_282403_, p_283601_, p_282046_.getCount() == 1 ? "1" : null); p_281357_.renderItemDecorations(this.font, p_283466_, p_282403_ + 14, p_283601_, p_283466_.getCount() == 1 ? "1" : null); p_281357_.pose().pushPose(); p_281357_.pose().translate(0.0F, 0.0F, 300.0F); p_281357_.blitSprite(RenderType::guiTextured, DISCOUNT_STRIKETHRUOGH_SPRITE, p_282403_ + 7, p_283601_ + 12, 9, 2); p_281357_.pose().popPose(); } } private boolean canScroll(int p_99141_) { return p_99141_ > 7; } @Override public boolean mouseScrolled(double p_99127_, double p_99128_, double p_99129_, double p_298933_) { if (super.mouseScrolled(p_99127_, p_99128_, p_99129_, p_298933_)) { return true; } else { int i = this.menu.getOffers().size(); if (this.canScroll(i)) { int j = i - 7; this.scrollOff = Mth.clamp((int)(this.scrollOff - p_298933_), 0, j); } return true; } } @Override public boolean mouseDragged(double p_99135_, double p_99136_, int p_99137_, double p_99138_, double p_99139_) { int i = this.menu.getOffers().size(); if (this.isDragging) { int j = this.topPos + 18; int k = j + 139; int l = i - 7; float f = ((float)p_99136_ - j - 13.5F) / (k - j - 27.0F); f = f * l + 0.5F; this.scrollOff = Mth.clamp((int)f, 0, l); return true; } else { return super.mouseDragged(p_99135_, p_99136_, p_99137_, p_99138_, p_99139_); } } @Override public boolean mouseClicked(double p_99131_, double p_99132_, int p_99133_) { this.isDragging = false; int i = (this.width - this.imageWidth) / 2; int j = (this.height - this.imageHeight) / 2; if (this.canScroll(this.menu.getOffers().size()) && p_99131_ > i + 94 && p_99131_ < i + 94 + 6 && p_99132_ > j + 18 && p_99132_ <= j + 18 + 139 + 1) { this.isDragging = true; } return super.mouseClicked(p_99131_, p_99132_, p_99133_); } @OnlyIn(Dist.CLIENT) class TradeOfferButton extends Button { final int index; public TradeOfferButton(final int p_99205_, final int p_99206_, final int p_99207_, final Button.OnPress p_99208_) { super(p_99205_, p_99206_, 88, 20, CommonComponents.EMPTY, p_99208_, DEFAULT_NARRATION); this.index = p_99207_; this.visible = false; } public int getIndex() { return this.index; } public void renderToolTip(GuiGraphics p_281313_, int p_283342_, int p_283060_) { if (this.isHovered && MerchantScreen.this.menu.getOffers().size() > this.index + MerchantScreen.this.scrollOff) { if (p_283342_ < this.getX() + 20) { ItemStack itemstack = MerchantScreen.this.menu.getOffers().get(this.index + MerchantScreen.this.scrollOff).getCostA(); p_281313_.renderTooltip(MerchantScreen.this.font, itemstack, p_283342_, p_283060_); } else if (p_283342_ < this.getX() + 50 && p_283342_ > this.getX() + 30) { ItemStack itemstack2 = MerchantScreen.this.menu.getOffers().get(this.index + MerchantScreen.this.scrollOff).getCostB(); if (!itemstack2.isEmpty()) { p_281313_.renderTooltip(MerchantScreen.this.font, itemstack2, p_283342_, p_283060_); } } else if (p_283342_ > this.getX() + 65) { ItemStack itemstack1 = MerchantScreen.this.menu.getOffers().get(this.index + MerchantScreen.this.scrollOff).getResult(); p_281313_.renderTooltip(MerchantScreen.this.font, itemstack1, p_283342_, p_283060_); } } } } }