327 lines
14 KiB
Java
327 lines
14 KiB
Java
package net.minecraft.client.gui.screens.inventory;
|
|
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import net.minecraft.client.gui.GuiGraphics;
|
|
import net.minecraft.client.gui.components.Button;
|
|
import net.minecraft.client.gui.components.Button.OnPress;
|
|
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;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public class MerchantScreen extends AbstractContainerScreen<MerchantMenu> {
|
|
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");
|
|
/**
|
|
* The GUI texture for the villager merchant GUI.
|
|
*/
|
|
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");
|
|
/**
|
|
* The integer value corresponding to the currently selected merchant recipe.
|
|
*/
|
|
private int shopItem;
|
|
private final MerchantScreen.TradeOfferButton[] tradeOfferButtons = new MerchantScreen.TradeOfferButton[7];
|
|
int scrollOff;
|
|
private boolean isDragging;
|
|
|
|
public MerchantScreen(MerchantMenu menu, Inventory playerInventory, Component title) {
|
|
super(menu, playerInventory, title);
|
|
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, button -> {
|
|
if (button instanceof MerchantScreen.TradeOfferButton) {
|
|
this.shopItem = ((MerchantScreen.TradeOfferButton)button).getIndex() + this.scrollOff;
|
|
this.postButtonClick();
|
|
}
|
|
}));
|
|
k += 20;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) {
|
|
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;
|
|
guiGraphics.drawString(this.font, component, k, 6, 4210752, false);
|
|
} else {
|
|
guiGraphics.drawString(this.font, this.title, 49 + this.imageWidth / 2 - this.font.width(this.title) / 2, 6, 4210752, false);
|
|
}
|
|
|
|
guiGraphics.drawString(this.font, this.playerInventoryTitle, this.inventoryLabelX, this.inventoryLabelY, 4210752, false);
|
|
int l = this.font.width(TRADES_LABEL);
|
|
guiGraphics.drawString(this.font, TRADES_LABEL, 5 - l / 2 + 48, 6, 4210752, false);
|
|
}
|
|
|
|
@Override
|
|
protected void renderBg(GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) {
|
|
int i = (this.width - this.imageWidth) / 2;
|
|
int j = (this.height - this.imageHeight) / 2;
|
|
guiGraphics.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 = (MerchantOffer)merchantOffers.get(k);
|
|
if (merchantOffer.isOutOfStock()) {
|
|
guiGraphics.blitSprite(RenderType::guiTextured, OUT_OF_STOCK_SPRITE, this.leftPos + 83 + 99, this.topPos + 35, 28, 21);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void renderProgressBar(GuiGraphics guiGraphics, int posX, int posY, MerchantOffer merchantOffer) {
|
|
int i = this.menu.getTraderLevel();
|
|
int j = this.menu.getTraderXp();
|
|
if (i < 5) {
|
|
guiGraphics.blitSprite(RenderType::guiTextured, EXPERIENCE_BAR_BACKGROUND_SPRITE, posX + 136, posY + 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 m = Math.min(Mth.floor(f * (j - k)), 102);
|
|
guiGraphics.blitSprite(RenderType::guiTextured, EXPERIENCE_BAR_CURRENT_SPRITE, 102, 5, 0, 0, posX + 136, posY + 16, m, 5);
|
|
int n = this.menu.getFutureTraderXp();
|
|
if (n > 0) {
|
|
int o = Math.min(Mth.floor(n * f), 102 - m);
|
|
guiGraphics.blitSprite(RenderType::guiTextured, EXPERIENCE_BAR_RESULT_SPRITE, 102, 5, m, 0, posX + 136 + m, posY + 16, o, 5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void renderScroller(GuiGraphics guiGraphics, int posX, int posY, MerchantOffers merchantOffers) {
|
|
int i = merchantOffers.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 m = Math.min(113, this.scrollOff * k);
|
|
if (this.scrollOff == i - 1) {
|
|
m = 113;
|
|
}
|
|
|
|
guiGraphics.blitSprite(RenderType::guiTextured, SCROLLER_SPRITE, posX + 94, posY + 18 + m, 6, 27);
|
|
} else {
|
|
guiGraphics.blitSprite(RenderType::guiTextured, SCROLLER_DISABLED_SPRITE, posX + 94, posY + 18, 6, 27);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
|
super.render(guiGraphics, mouseX, mouseY, partialTick);
|
|
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(guiGraphics, i, j, merchantOffers);
|
|
int m = 0;
|
|
|
|
for (MerchantOffer merchantOffer : merchantOffers) {
|
|
if (!this.canScroll(merchantOffers.size()) || m >= this.scrollOff && m < 7 + this.scrollOff) {
|
|
ItemStack itemStack = merchantOffer.getBaseCostA();
|
|
ItemStack itemStack2 = merchantOffer.getCostA();
|
|
ItemStack itemStack3 = merchantOffer.getCostB();
|
|
ItemStack itemStack4 = merchantOffer.getResult();
|
|
guiGraphics.pose().pushPose();
|
|
guiGraphics.pose().translate(0.0F, 0.0F, 100.0F);
|
|
int n = k + 2;
|
|
this.renderAndDecorateCostA(guiGraphics, itemStack2, itemStack, l, n);
|
|
if (!itemStack3.isEmpty()) {
|
|
guiGraphics.renderFakeItem(itemStack3, i + 5 + 35, n);
|
|
guiGraphics.renderItemDecorations(this.font, itemStack3, i + 5 + 35, n);
|
|
}
|
|
|
|
this.renderButtonArrows(guiGraphics, merchantOffer, i, n);
|
|
guiGraphics.renderFakeItem(itemStack4, i + 5 + 68, n);
|
|
guiGraphics.renderItemDecorations(this.font, itemStack4, i + 5 + 68, n);
|
|
guiGraphics.pose().popPose();
|
|
k += 20;
|
|
m++;
|
|
} else {
|
|
m++;
|
|
}
|
|
}
|
|
|
|
int o = this.shopItem;
|
|
MerchantOffer merchantOfferx = (MerchantOffer)merchantOffers.get(o);
|
|
if (this.menu.showProgressBar()) {
|
|
this.renderProgressBar(guiGraphics, i, j, merchantOfferx);
|
|
}
|
|
|
|
if (merchantOfferx.isOutOfStock() && this.isHovering(186, 35, 22, 21, mouseX, mouseY) && this.menu.canRestock()) {
|
|
guiGraphics.renderTooltip(this.font, DEPRECATED_TOOLTIP, mouseX, mouseY);
|
|
}
|
|
|
|
for (MerchantScreen.TradeOfferButton tradeOfferButton : this.tradeOfferButtons) {
|
|
if (tradeOfferButton.isHoveredOrFocused()) {
|
|
tradeOfferButton.renderToolTip(guiGraphics, mouseX, mouseY);
|
|
}
|
|
|
|
tradeOfferButton.visible = tradeOfferButton.index < this.menu.getOffers().size();
|
|
}
|
|
}
|
|
|
|
this.renderTooltip(guiGraphics, mouseX, mouseY);
|
|
}
|
|
|
|
private void renderButtonArrows(GuiGraphics guiGraphics, MerchantOffer merchantOffers, int posX, int posY) {
|
|
if (merchantOffers.isOutOfStock()) {
|
|
guiGraphics.blitSprite(RenderType::guiTextured, TRADE_ARROW_OUT_OF_STOCK_SPRITE, posX + 5 + 35 + 20, posY + 3, 10, 9);
|
|
} else {
|
|
guiGraphics.blitSprite(RenderType::guiTextured, TRADE_ARROW_SPRITE, posX + 5 + 35 + 20, posY + 3, 10, 9);
|
|
}
|
|
}
|
|
|
|
private void renderAndDecorateCostA(GuiGraphics guiGraphics, ItemStack realCost, ItemStack baseCost, int x, int y) {
|
|
guiGraphics.renderFakeItem(realCost, x, y);
|
|
if (baseCost.getCount() == realCost.getCount()) {
|
|
guiGraphics.renderItemDecorations(this.font, realCost, x, y);
|
|
} else {
|
|
guiGraphics.renderItemDecorations(this.font, baseCost, x, y, baseCost.getCount() == 1 ? "1" : null);
|
|
guiGraphics.renderItemDecorations(this.font, realCost, x + 14, y, realCost.getCount() == 1 ? "1" : null);
|
|
guiGraphics.pose().pushPose();
|
|
guiGraphics.pose().translate(0.0F, 0.0F, 300.0F);
|
|
guiGraphics.blitSprite(RenderType::guiTextured, DISCOUNT_STRIKETHRUOGH_SPRITE, x + 7, y + 12, 9, 2);
|
|
guiGraphics.pose().popPose();
|
|
}
|
|
}
|
|
|
|
private boolean canScroll(int numOffers) {
|
|
return numOffers > 7;
|
|
}
|
|
|
|
@Override
|
|
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
|
|
if (super.mouseScrolled(mouseX, mouseY, scrollX, scrollY)) {
|
|
return true;
|
|
} else {
|
|
int i = this.menu.getOffers().size();
|
|
if (this.canScroll(i)) {
|
|
int j = i - 7;
|
|
this.scrollOff = Mth.clamp((int)(this.scrollOff - scrollY), 0, j);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) {
|
|
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)mouseY - 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(mouseX, mouseY, button, dragX, dragY);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
|
this.isDragging = false;
|
|
int i = (this.width - this.imageWidth) / 2;
|
|
int j = (this.height - this.imageHeight) / 2;
|
|
if (this.canScroll(this.menu.getOffers().size()) && mouseX > i + 94 && mouseX < i + 94 + 6 && mouseY > j + 18 && mouseY <= j + 18 + 139 + 1) {
|
|
this.isDragging = true;
|
|
}
|
|
|
|
return super.mouseClicked(mouseX, mouseY, button);
|
|
}
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
class TradeOfferButton extends Button {
|
|
final int index;
|
|
|
|
public TradeOfferButton(final int x, final int y, final int index, final OnPress onPress) {
|
|
super(x, y, 88, 20, CommonComponents.EMPTY, onPress, DEFAULT_NARRATION);
|
|
this.index = index;
|
|
this.visible = false;
|
|
}
|
|
|
|
public int getIndex() {
|
|
return this.index;
|
|
}
|
|
|
|
public void renderToolTip(GuiGraphics guiGraphics, int mouseX, int mouseY) {
|
|
if (this.isHovered && MerchantScreen.this.menu.getOffers().size() > this.index + MerchantScreen.this.scrollOff) {
|
|
if (mouseX < this.getX() + 20) {
|
|
ItemStack itemStack = ((MerchantOffer)MerchantScreen.this.menu.getOffers().get(this.index + MerchantScreen.this.scrollOff)).getCostA();
|
|
guiGraphics.renderTooltip(MerchantScreen.this.font, itemStack, mouseX, mouseY);
|
|
} else if (mouseX < this.getX() + 50 && mouseX > this.getX() + 30) {
|
|
ItemStack itemStack = ((MerchantOffer)MerchantScreen.this.menu.getOffers().get(this.index + MerchantScreen.this.scrollOff)).getCostB();
|
|
if (!itemStack.isEmpty()) {
|
|
guiGraphics.renderTooltip(MerchantScreen.this.font, itemStack, mouseX, mouseY);
|
|
}
|
|
} else if (mouseX > this.getX() + 65) {
|
|
ItemStack itemStack = ((MerchantOffer)MerchantScreen.this.menu.getOffers().get(this.index + MerchantScreen.this.scrollOff)).getResult();
|
|
guiGraphics.renderTooltip(MerchantScreen.this.font, itemStack, mouseX, mouseY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|