package net.minecraft.client.gui.screens.achievement; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Comparator; import java.util.List; import java.util.Set; 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.AbstractWidget; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.LoadingDotsWidget; import net.minecraft.client.gui.components.ObjectSelectionList; import net.minecraft.client.gui.layouts.HeaderAndFooterLayout; import net.minecraft.client.gui.layouts.LinearLayout; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.achievement.StatsScreen.ItemStatisticsList.ItemRow; import net.minecraft.client.gui.screens.achievement.StatsScreen.ItemStatisticsList.ItemRowComparator; import net.minecraft.client.gui.screens.achievement.StatsScreen.MobsStatisticsList.MobRow; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.resources.language.I18n; import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ServerboundClientCommandPacket; import net.minecraft.network.protocol.game.ServerboundClientCommandPacket.Action; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvents; import net.minecraft.stats.Stat; import net.minecraft.stats.StatType; import net.minecraft.stats.Stats; import net.minecraft.stats.StatsCounter; import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Block; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public class StatsScreen extends Screen { private static final Component TITLE = Component.translatable("gui.stats"); static final ResourceLocation SLOT_SPRITE = ResourceLocation.withDefaultNamespace("container/slot"); static final ResourceLocation HEADER_SPRITE = ResourceLocation.withDefaultNamespace("statistics/header"); static final ResourceLocation SORT_UP_SPRITE = ResourceLocation.withDefaultNamespace("statistics/sort_up"); static final ResourceLocation SORT_DOWN_SPRITE = ResourceLocation.withDefaultNamespace("statistics/sort_down"); private static final Component PENDING_TEXT = Component.translatable("multiplayer.downloadingStats"); static final Component NO_VALUE_DISPLAY = Component.translatable("stats.none"); private static final Component GENERAL_BUTTON = Component.translatable("stat.generalButton"); private static final Component ITEMS_BUTTON = Component.translatable("stat.itemsButton"); private static final Component MOBS_BUTTON = Component.translatable("stat.mobsButton"); protected final Screen lastScreen; private static final int LIST_WIDTH = 280; private static final int PADDING = 5; private static final int FOOTER_HEIGHT = 58; private HeaderAndFooterLayout layout = new HeaderAndFooterLayout(this, 33, 58); @Nullable private StatsScreen.GeneralStatisticsList statsList; @Nullable StatsScreen.ItemStatisticsList itemStatsList; @Nullable private StatsScreen.MobsStatisticsList mobsStatsList; final StatsCounter stats; @Nullable private ObjectSelectionList activeList; /** * When true, the game will be paused when the gui is shown */ private boolean isLoading = true; public StatsScreen(Screen lastScreen, StatsCounter stats) { super(TITLE); this.lastScreen = lastScreen; this.stats = stats; } @Override protected void init() { this.layout.addToContents(new LoadingDotsWidget(this.font, PENDING_TEXT)); this.minecraft.getConnection().send(new ServerboundClientCommandPacket(Action.REQUEST_STATS)); } public void initLists() { this.statsList = new StatsScreen.GeneralStatisticsList(this.minecraft); this.itemStatsList = new StatsScreen.ItemStatisticsList(this.minecraft); this.mobsStatsList = new StatsScreen.MobsStatisticsList(this.minecraft); } public void initButtons() { HeaderAndFooterLayout headerAndFooterLayout = new HeaderAndFooterLayout(this, 33, 58); headerAndFooterLayout.addTitleHeader(TITLE, this.font); LinearLayout linearLayout = headerAndFooterLayout.addToFooter(LinearLayout.vertical()).spacing(5); linearLayout.defaultCellSetting().alignHorizontallyCenter(); LinearLayout linearLayout2 = linearLayout.addChild(LinearLayout.horizontal()).spacing(5); linearLayout2.addChild(Button.builder(GENERAL_BUTTON, buttonx -> this.setActiveList(this.statsList)).width(120).build()); Button button = linearLayout2.addChild(Button.builder(ITEMS_BUTTON, buttonx -> this.setActiveList(this.itemStatsList)).width(120).build()); Button button2 = linearLayout2.addChild(Button.builder(MOBS_BUTTON, buttonx -> this.setActiveList(this.mobsStatsList)).width(120).build()); linearLayout.addChild(Button.builder(CommonComponents.GUI_DONE, buttonx -> this.onClose()).width(200).build()); if (this.itemStatsList != null && this.itemStatsList.children().isEmpty()) { button.active = false; } if (this.mobsStatsList != null && this.mobsStatsList.children().isEmpty()) { button2.active = false; } this.layout = headerAndFooterLayout; this.layout.visitWidgets(guiEventListener -> { AbstractWidget var10000 = this.addRenderableWidget(guiEventListener); }); this.repositionElements(); } @Override protected void repositionElements() { this.layout.arrangeElements(); if (this.activeList != null) { this.activeList.updateSize(this.width, this.layout); } } @Override public void onClose() { this.minecraft.setScreen(this.lastScreen); } public void onStatsUpdated() { if (this.isLoading) { this.initLists(); this.setActiveList(this.statsList); this.initButtons(); this.setInitialFocus(); this.isLoading = false; } } @Override public boolean isPauseScreen() { return !this.isLoading; } public void setActiveList(@Nullable ObjectSelectionList activeList) { if (this.activeList != null) { this.removeWidget(this.activeList); } if (activeList != null) { this.addRenderableWidget(activeList); this.activeList = activeList; this.repositionElements(); } } static String getTranslationKey(Stat stat) { return "stat." + stat.getValue().toString().replace(':', '.'); } @Environment(EnvType.CLIENT) class GeneralStatisticsList extends ObjectSelectionList { public GeneralStatisticsList(final Minecraft minecraft) { super(minecraft, StatsScreen.this.width, StatsScreen.this.height - 33 - 58, 33, 14); ObjectArrayList> objectArrayList = new ObjectArrayList<>(Stats.CUSTOM.iterator()); objectArrayList.sort(Comparator.comparing(statx -> I18n.get(StatsScreen.getTranslationKey(statx)))); for (Stat stat : objectArrayList) { this.addEntry(new net.minecraft.client.gui.screens.achievement.StatsScreen.GeneralStatisticsList.Entry(this, stat)); } } @Override public int getRowWidth() { return 280; } } @Environment(EnvType.CLIENT) class ItemStatisticsList extends ObjectSelectionList { private static final int SLOT_BG_SIZE = 18; private static final int SLOT_STAT_HEIGHT = 22; private static final int SLOT_BG_Y = 1; private static final int SORT_NONE = 0; private static final int SORT_DOWN = -1; private static final int SORT_UP = 1; private final ResourceLocation[] iconSprites = new ResourceLocation[]{ ResourceLocation.withDefaultNamespace("statistics/block_mined"), ResourceLocation.withDefaultNamespace("statistics/item_broken"), ResourceLocation.withDefaultNamespace("statistics/item_crafted"), ResourceLocation.withDefaultNamespace("statistics/item_used"), ResourceLocation.withDefaultNamespace("statistics/item_picked_up"), ResourceLocation.withDefaultNamespace("statistics/item_dropped") }; protected final List> blockColumns; protected final List> itemColumns; protected final Comparator itemStatSorter = new ItemRowComparator(this); @Nullable protected StatType sortColumn; protected int headerPressed = -1; protected int sortOrder; public ItemStatisticsList(final Minecraft minecraft) { super(minecraft, StatsScreen.this.width, StatsScreen.this.height - 33 - 58, 33, 22, 22); this.blockColumns = Lists.>newArrayList(); this.blockColumns.add(Stats.BLOCK_MINED); this.itemColumns = Lists.>newArrayList(Stats.ITEM_BROKEN, Stats.ITEM_CRAFTED, Stats.ITEM_USED, Stats.ITEM_PICKED_UP, Stats.ITEM_DROPPED); Set set = Sets.newIdentityHashSet(); for (Item item : BuiltInRegistries.ITEM) { boolean bl = false; for (StatType statType : this.itemColumns) { if (statType.contains(item) && StatsScreen.this.stats.getValue(statType.get(item)) > 0) { bl = true; } } if (bl) { set.add(item); } } for (Block block : BuiltInRegistries.BLOCK) { boolean bl = false; for (StatType statTypex : this.blockColumns) { if (statTypex.contains(block) && StatsScreen.this.stats.getValue(statTypex.get(block)) > 0) { bl = true; } } if (bl) { set.add(block.asItem()); } } set.remove(Items.AIR); for (Item item : set) { this.addEntry(new ItemRow(this, item)); } } int getColumnX(int index) { return 75 + 40 * index; } @Override protected void renderHeader(GuiGraphics guiGraphics, int x, int y) { if (!this.minecraft.mouseHandler.isLeftPressed()) { this.headerPressed = -1; } for (int i = 0; i < this.iconSprites.length; i++) { ResourceLocation resourceLocation = this.headerPressed == i ? StatsScreen.SLOT_SPRITE : StatsScreen.HEADER_SPRITE; guiGraphics.blitSprite(RenderType::guiTextured, resourceLocation, x + this.getColumnX(i) - 18, y + 1, 18, 18); } if (this.sortColumn != null) { int i = this.getColumnX(this.getColumnIndex(this.sortColumn)) - 36; ResourceLocation resourceLocation = this.sortOrder == 1 ? StatsScreen.SORT_UP_SPRITE : StatsScreen.SORT_DOWN_SPRITE; guiGraphics.blitSprite(RenderType::guiTextured, resourceLocation, x + i, y + 1, 18, 18); } for (int i = 0; i < this.iconSprites.length; i++) { int j = this.headerPressed == i ? 1 : 0; guiGraphics.blitSprite(RenderType::guiTextured, this.iconSprites[i], x + this.getColumnX(i) - 18 + j, y + 1 + j, 18, 18); } } @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { boolean bl = super.mouseClicked(mouseX, mouseY, button); return !bl && this.clickedHeader( (int)(mouseX - (this.getX() + this.width / 2.0 - this.getRowWidth() / 2.0)), (int)(mouseY - this.getY()) + (int)this.scrollAmount() - 4 ) ? true : bl; } protected boolean clickedHeader(int x, int y) { this.headerPressed = -1; for (int i = 0; i < this.iconSprites.length; i++) { int j = x - this.getColumnX(i); if (j >= -36 && j <= 0) { this.headerPressed = i; break; } } if (this.headerPressed >= 0) { this.sortByColumn(this.getColumn(this.headerPressed)); this.minecraft.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); return true; } else { return false; } } @Override public int getRowWidth() { return 280; } private StatType getColumn(int index) { return index < this.blockColumns.size() ? (StatType)this.blockColumns.get(index) : (StatType)this.itemColumns.get(index - this.blockColumns.size()); } private int getColumnIndex(StatType statType) { int i = this.blockColumns.indexOf(statType); if (i >= 0) { return i; } else { int j = this.itemColumns.indexOf(statType); return j >= 0 ? j + this.blockColumns.size() : -1; } } @Override protected void renderDecorations(GuiGraphics guiGraphics, int mouseX, int mouseY) { if (mouseY >= this.getY() && mouseY <= this.getBottom()) { ItemRow itemRow = this.getHovered(); int i = this.getRowLeft(); if (itemRow != null) { if (mouseX < i || mouseX > i + 18) { return; } Item item = itemRow.getItem(); guiGraphics.renderTooltip(StatsScreen.this.font, item.getName(), mouseX, mouseY, item.components().get(DataComponents.TOOLTIP_STYLE)); } else { Component component = null; int j = mouseX - i; for (int k = 0; k < this.iconSprites.length; k++) { int l = this.getColumnX(k); if (j >= l - 18 && j <= l) { component = this.getColumn(k).getDisplayName(); break; } } if (component != null) { guiGraphics.renderTooltip(StatsScreen.this.font, component, mouseX, mouseY); } } } } protected void sortByColumn(StatType statType) { if (statType != this.sortColumn) { this.sortColumn = statType; this.sortOrder = -1; } else if (this.sortOrder == -1) { this.sortOrder = 1; } else { this.sortColumn = null; this.sortOrder = 0; } this.children().sort(this.itemStatSorter); } } @Environment(EnvType.CLIENT) class MobsStatisticsList extends ObjectSelectionList { public MobsStatisticsList(final Minecraft minecraft) { super(minecraft, StatsScreen.this.width, StatsScreen.this.height - 33 - 58, 33, 9 * 4); for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { if (StatsScreen.this.stats.getValue(Stats.ENTITY_KILLED.get(entityType)) > 0 || StatsScreen.this.stats.getValue(Stats.ENTITY_KILLED_BY.get(entityType)) > 0 ) { this.addEntry(new MobRow(this, entityType)); } } } @Override public int getRowWidth() { return 280; } } }