diff --git a/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionBrowserScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionBrowserScreen.java index 05515a4640a..fe2e42d8931 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionBrowserScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionBrowserScreen.java @@ -108,7 +108,7 @@ protected void init() { if (categoryTabWidgets.isEmpty()) for (int i = 0; i < 6; i++) { - CategoryTabWidget categoryTabWidget = new CategoryTabWidget(new ItemStack(Items.SPONGE), this::clickSlot); + CategoryTabWidget categoryTabWidget = new CategoryTabWidget(new ItemStack(Items.SPONGE), this::clickSlot, i); categoryTabWidgets.add(categoryTabWidget); addWidget(categoryTabWidget); // This method only makes it clickable, does not add it to the drawables list // manually rendered in the render method to have it not render behind the durability bars diff --git a/src/main/java/de/hysky/skyblocker/skyblock/auction/widgets/CategoryTabWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/auction/widgets/CategoryTabWidget.java index d96a6cff7cb..75408e04065 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/auction/widgets/CategoryTabWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/auction/widgets/CategoryTabWidget.java @@ -1,20 +1,38 @@ package de.hysky.skyblocker.skyblock.auction.widgets; +import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.skyblock.auction.SlotClickHandler; import de.hysky.skyblocker.utils.render.gui.SideTabButtonWidget; +import de.hysky.skyblocker.utils.render.texture.FallbackedTexture; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.WidgetSprites; +import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.world.item.Item.TooltipContext; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; +import org.jspecify.annotations.Nullable; public class CategoryTabWidget extends SideTabButtonWidget { + @SuppressWarnings("unchecked") + private static final @Nullable FallbackedTexture[] SPRITES = new FallbackedTexture[6]; private final SlotClickHandler slotClick; private int slotId = -1; - public CategoryTabWidget(ItemStack icon, SlotClickHandler slotClick) { - super(0, 0, false, icon); + private static WidgetSprites getSprites(int index) { + FallbackedTexture sprite = SPRITES[index]; + if (sprite == null) return (SPRITES[index] = FallbackedTexture.ofWidgetSprites( + new WidgetSprites( + SkyblockerMod.id("auctions_gui/category_tab_" + (index + 1)), + SkyblockerMod.id("auctions_gui/category_tab_selected" + (index + 1)) + ), + RecipeBookTabButton.SPRITES)).get(); + return sprite.get(); + } + + public CategoryTabWidget(ItemStack icon, SlotClickHandler slotClick, int id) { + super(0, 0, false, getSprites(id), icon); this.slotClick = slotClick; } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyFinderScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyFinderScreen.java index 80b20e79f91..2ee12c32001 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyFinderScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/partyfinder/PartyFinderScreen.java @@ -6,6 +6,7 @@ import de.hysky.skyblocker.annotations.Init; import de.hysky.skyblocker.debug.Debug; import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.render.texture.FallbackedTexture; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; @@ -46,7 +47,9 @@ public class PartyFinderScreen extends Screen { protected static final Logger LOGGER = LoggerFactory.getLogger(PartyFinderScreen.class); - protected static final Identifier BACKGROUND_TEXTURE = Identifier.withDefaultNamespace("social_interactions/background"); + protected static final FallbackedTexture BACKGROUND_TEXTURE = FallbackedTexture.ofGuiSprite( + SkyblockerMod.id("party_finder/background"), + Identifier.withDefaultNamespace("social_interactions/background")); protected static final Identifier SEARCH_ICON_TEXTURE = Identifier.withDefaultNamespace("icon/search"); protected static final Component SEARCH_TEXT = Component.translatable("gui.socialInteractions.search_hint").withStyle(ChatFormatting.ITALIC).withStyle(ChatFormatting.GRAY); public static boolean isInKuudraPartyFinder = false; @@ -284,7 +287,7 @@ public void render(GuiGraphics context, int mouseX, int mouseY, float delta) { public void renderBackground(GuiGraphics context, int mouseX, int mouseY, float delta) { this.renderTransparentBackground(context); int i = partyEntryListWidget.getRowWidth() + 16 + 6; - context.blitSprite(RenderPipelines.GUI_TEXTURED, BACKGROUND_TEXTURE, partyEntryListWidget.getRowLeft() - 8, partyEntryListWidget.getY() - 12 - 8, i, partyEntryListWidget.getBottom() - partyEntryListWidget.getY() + 16 + 12); + context.blitSprite(RenderPipelines.GUI_TEXTURED, BACKGROUND_TEXTURE.get(), partyEntryListWidget.getRowLeft() - 8, partyEntryListWidget.getY() - 12 - 8, i, partyEntryListWidget.getBottom() - partyEntryListWidget.getY() + 16 + 12); } @Override diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockCraftingTableScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockCraftingTableScreen.java index 71652e6b5ae..dd8ab413419 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockCraftingTableScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockCraftingTableScreen.java @@ -3,6 +3,8 @@ import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.skyblock.itemlist.recipebook.SkyblockRecipeBookWidget; import java.time.Duration; + +import de.hysky.skyblocker.utils.render.texture.FallbackedTexture; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.ImageButton; import net.minecraft.client.gui.components.Tooltip; @@ -20,7 +22,12 @@ import net.minecraft.world.item.Items; public class SkyblockCraftingTableScreen extends AbstractContainerScreen { - private static final Identifier TEXTURE = Identifier.withDefaultNamespace("textures/gui/container/crafting_table.png"); + private static final FallbackedTexture TEXTURE = FallbackedTexture.ofTexture( + SkyblockerMod.id("textures/gui/container/skyblock_crafting_table.png"), + Identifier.withDefaultNamespace("textures/gui/container/crafting_table.png")); + private static final FallbackedTexture MIRRORVERSE_TEXTURE = FallbackedTexture.ofTexture( + SkyblockerMod.id("textures/gui/container/skyblock_crafting_table.png"), + Identifier.withDefaultNamespace("textures/gui/container/crafting_table.png")); protected static final WidgetSprites MORE_CRAFTS_TEXTURES = new WidgetSprites( SkyblockerMod.id("quick_craft/more_button"), SkyblockerMod.id("quick_craft/more_button_disabled"), @@ -96,9 +103,9 @@ protected void renderSlot(GuiGraphics context, Slot slot, int mouseX, int mouseY protected void renderBg(GuiGraphics context, float delta, int mouseX, int mouseY) { int i = this.leftPos; int j = (this.height - this.imageHeight) / 2; - context.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, i, j, 0, 0, this.imageWidth, this.imageHeight, 256, 256); + context.blit(RenderPipelines.GUI_TEXTURED, menu.mirrorverse ? MIRRORVERSE_TEXTURE.get() : TEXTURE.get(), i, j, 0, 0, this.imageWidth, this.imageHeight, 256, 256); //4 px of margin to allow some space for custom resource packs that have size differences on the crafting table/inventory textures - if (!menu.mirrorverse) context.blit(RenderPipelines.GUI_TEXTURED, QUICK_CRAFT, i + 143, j - 3, 0, 0, 37, 90, 37, 90); + if (TEXTURE.isUsingFallback() && !menu.mirrorverse) context.blit(RenderPipelines.GUI_TEXTURED, QUICK_CRAFT, i + 143, j - 3, 0, 0, 37, 90, 37, 90); } @Override diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockInventoryScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockInventoryScreen.java index 156c4e91c56..9d3010a80ea 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockInventoryScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockInventoryScreen.java @@ -14,6 +14,7 @@ import de.hysky.skyblocker.skyblock.item.wikilookup.WikiLookupManager; import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.texture.FallbackedTexture; import de.hysky.skyblocker.utils.scheduler.MessageScheduler; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.minecraft.client.Minecraft; @@ -63,6 +64,9 @@ public class SkyblockInventoryScreen extends InventoryScreen implements HoveredI private static final Identifier SLOT_TEXTURE = Identifier.withDefaultNamespace("container/slot"); private static final Identifier EMPTY_SLOT = SkyblockerMod.id("equipment/empty_icon"); private static final Path FOLDER = SkyblockerMod.CONFIG_DIR.resolve("equipment"); + private static final FallbackedTexture BACKGROUND = FallbackedTexture.ofTexture( + SkyblockerMod.id("textures/gui/container/skyblock_inventory.png"), + INVENTORY_LOCATION); private final Slot[] equipmentSlots = new Slot[4]; private ItemStack hoveredItem; @@ -189,7 +193,15 @@ public void removed() { @Override protected void renderBg(GuiGraphics context, float delta, int mouseX, int mouseY) { - super.renderBg(context, delta, mouseX, mouseY); + if (BACKGROUND.isUsingFallback()) { + super.renderBg(context, delta, mouseX, mouseY); + } else { + int x = this.leftPos; + int y = this.topPos; + context.blit(RenderPipelines.GUI_TEXTURED, BACKGROUND.get(), x, y, 0.0F, 0.0F, this.imageWidth, this.imageHeight, 256, 256); + renderEntityInInventoryFollowsMouse(context, x + 26, y + 8, x + 75, y + 78, 30, 0.0625F, mouseX, mouseY, this.minecraft.player); + return; + } for (int i = 0; i < 3; i++) { context.blitSprite(RenderPipelines.GUI_TEXTURED, SLOT_TEXTURE, leftPos + 76, topPos + 7 + i * 18, 18, 18); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/museum/MuseumManager.java b/src/main/java/de/hysky/skyblocker/skyblock/museum/MuseumManager.java index 432bf8cf86b..0acb790a4e6 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/museum/MuseumManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/museum/MuseumManager.java @@ -1,6 +1,7 @@ package de.hysky.skyblocker.skyblock.museum; import com.google.common.collect.Lists; +import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.utils.hoveredItem.HoveredItemStackProvider; import com.mojang.datafixers.util.Either; import de.hysky.skyblocker.skyblock.item.wikilookup.WikiLookupManager; @@ -37,7 +38,7 @@ public class MuseumManager extends AbstractWidget implements HoveredItemStackProvider { private static final Minecraft CLIENT = Minecraft.getInstance(); private static final Font TEXT_RENDERER = CLIENT.font; - private static final Identifier BACKGROUND_TEXTURE = Identifier.withDefaultNamespace("textures/gui/recipe_book.png"); + private static final Identifier BACKGROUND_TEXTURE = SkyblockerMod.id("background"); public static final int BACKGROUND_WIDTH = 147; public static final int BACKGROUND_HEIGHT = 160; public static final int SPACING = 2; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java index 75fd9cf2a0f..734eefc14fb 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java @@ -7,6 +7,7 @@ import de.hysky.skyblocker.mixins.accessors.PopupScreenAccessor; import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.render.gui.AbstractPopupScreen; +import de.hysky.skyblocker.utils.render.texture.FallbackedTexture; import de.hysky.skyblocker.utils.scheduler.MessageScheduler; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -36,6 +37,8 @@ @Environment(value = EnvType.CLIENT) public class QuickNavButton extends AbstractWidget { private static final long TOGGLE_DURATION = 1000; + @SuppressWarnings("unchecked") + private static final @Nullable FallbackedTexture[] TAB_TEXTURES = new FallbackedTexture[14]; private final int index; private final boolean toggled; @@ -178,8 +181,7 @@ public void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delt alpha = Math.min(alpha + 10, 255); } - // Construct the texture identifier based on the index and toggled state - Identifier tabTexture = Identifier.withDefaultNamespace("container/creative_inventory/tab_" + (isTopTab() ? "top" : "bottom") + "_" + (renderInFront ? "selected" : "unselected") + "_" + (index % 7 + 1)); + Identifier tabTexture = getTexture(); // Render the button texture, always with full alpha if it's not rendering in front context.blitSprite(RenderPipelines.GUI_TEXTURED, tabTexture, this.getX(), this.getY(), this.width, this.height, renderInFront ? ARGB.color(alpha, -1) : -1); @@ -190,6 +192,16 @@ public void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delt this.handleCursor(context); } + private Identifier getTexture() { + FallbackedTexture texture = TAB_TEXTURES[index]; + if (texture != null) return texture.get(); + // Construct the texture identifier based on the index and toggled state + return (TAB_TEXTURES[index] = FallbackedTexture.ofGuiSprite( + SkyblockerMod.id("quick_nav/tab_" + (isTopTab() ? "top" : "bottom") + "_" + (renderInFront ? "selected" : "unselected") + "_" + (index % 7 + 1)), + Identifier.withDefaultNamespace("container/creative_inventory/tab_" + (isTopTab() ? "top" : "bottom") + "_" + (renderInFront ? "selected" : "unselected") + "_" + (index % 7 + 1)) + )).get(); + } + @Override protected void updateWidgetNarration(NarrationElementOutput builder) {} } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java index 96452186086..2e2b1a01a21 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java @@ -1,6 +1,8 @@ package de.hysky.skyblocker.skyblock.searchoverlay; +import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.texture.FallbackedTexture; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; @@ -23,7 +25,9 @@ public class OverlayScreen extends Screen { protected static final Identifier SEARCH_ICON_TEXTURE = Identifier.withDefaultNamespace("icon/search"); protected static final Identifier DELETE_ICON_TEXTURE = Identifier.withDefaultNamespace("textures/gui/sprites/pending_invite/reject.png"); - private static final Identifier BACKGROUND_TEXTURE = Identifier.withDefaultNamespace("social_interactions/background"); + private static final FallbackedTexture BACKGROUND_TEXTURE = FallbackedTexture.ofGuiSprite( + SkyblockerMod.id("search_overlay/background"), + Identifier.withDefaultNamespace("social_interactions/background")); private static final int rowHeight = 20; private static final int specialButtonSize = rowHeight; private EditBox searchField; @@ -219,7 +223,7 @@ public void renderBackground(GuiGraphics context, int mouseX, int mouseY, float if (historyButtons.length > 0) { //add space for history label if it could exist maxHeight += (int) (rowHeight * 0.75); } - context.blitSprite(RenderPipelines.GUI_TEXTURED, BACKGROUND_TEXTURE, searchField.getX() - 8, searchField.getY() - 8, (int) (this.width * 0.4) + 16, maxHeight + 16); + context.blitSprite(RenderPipelines.GUI_TEXTURED, BACKGROUND_TEXTURE.get(), searchField.getX() - 8, searchField.getY() - 8, (int) (this.width * 0.4) + 16, maxHeight + 16); } /** diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/SideTabButtonWidget.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/SideTabButtonWidget.java index d13384a7b86..fa702c381e9 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/SideTabButtonWidget.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/SideTabButtonWidget.java @@ -2,6 +2,7 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.ImageButton; +import net.minecraft.client.gui.components.WidgetSprites; import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.renderer.RenderPipelines; @@ -16,14 +17,17 @@ public void setIcon(ItemStack icon) { this.icon = icon.copy(); } - public SideTabButtonWidget(int x, int y, boolean toggled, ItemStack icon) { - super(x, y, 35, 27, RecipeBookTabButton.SPRITES, _ignored -> {}); + public SideTabButtonWidget(int x, int y, boolean toggled, WidgetSprites sprites, ItemStack icon) { + super(x, y, 35, 27, sprites, _ignored -> {}); this.icon = icon.copy(); } + public SideTabButtonWidget(int x, int y, boolean toggled, ItemStack icon) { + this(x, y, toggled, RecipeBookTabButton.SPRITES, icon); + } + @Override public void renderContents(GuiGraphics context, int mouseX, int mouseY, float delta) { - if (sprites == null) return; Identifier identifier = sprites.get(true, this.selected); int x = getX(); if (this.selected) x -= 2; diff --git a/src/main/java/de/hysky/skyblocker/utils/render/texture/FallbackedTexture.java b/src/main/java/de/hysky/skyblocker/utils/render/texture/FallbackedTexture.java new file mode 100644 index 00000000000..3bebce3e4f5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/texture/FallbackedTexture.java @@ -0,0 +1,79 @@ +package de.hysky.skyblocker.utils.render.texture; + +import com.mojang.datafixers.util.Unit; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.annotations.Init; +import net.fabricmc.fabric.api.resource.v1.ResourceLoader; +import net.fabricmc.fabric.api.resource.v1.reloader.ResourceReloaderKeys; +import net.minecraft.client.gui.components.WidgetSprites; +import net.minecraft.data.AtlasIds; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.PackType; +import org.jspecify.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** + * A texture reference that will default back to another one if it doesn't exist. + */ +public interface FallbackedTexture extends Supplier { + List UPDATE_CALLBACKS = new ArrayList<>(); + + boolean isUsingFallback(); + + /** + * @param id the texture that may or may not be specified by a resource pack + * @param fallback the fallback if {@code id} doesn't exist + * @param atlas the to look in, pass null if the texture isn't in any atlas. + * @return a fallbacked texture supplier + */ + static FallbackedTexture of(Identifier id, Identifier fallback, @Nullable Identifier atlas) { + IdentifierTexture identifierTexture = new IdentifierTexture(id, fallback, atlas); + UPDATE_CALLBACKS.add(identifierTexture::update); + return identifierTexture; + } + + /** + * For textures in the {@code gui/sprites} folder. + */ + static FallbackedTexture ofGuiSprite(Identifier id, Identifier fallback) { + return of(id, fallback, AtlasIds.GUI); + } + + /** + * For textures that aren't in any atlas (usually backgrounds) + */ + static FallbackedTexture ofTexture(Identifier id, Identifier fallback) { + return of(id, fallback, null); + } + + /** + * WidgetSprites, used for buttons. + */ + static FallbackedTexture ofWidgetSprites(WidgetSprites sprites, WidgetSprites fallback) { + List textures = new ArrayList<>(4); + WidgetSpritesFallback.Helper helper = new WidgetSpritesFallback.Helper(sprites, fallback); + helper.populate(textures, WidgetSprites::enabled); + helper.populate(textures, WidgetSprites::disabled); + helper.populate(textures, WidgetSprites::enabledFocused); + helper.populate(textures, WidgetSprites::disabledFocused); + WidgetSpritesFallback spritesFallback = new WidgetSpritesFallback(textures.get(0), textures.get(1), textures.get(2), textures.get(3)); + UPDATE_CALLBACKS.add(spritesFallback::update); + return spritesFallback; + } + + + @Init + static void init() { + Identifier id = SkyblockerMod.id("fallback_texture_reloader"); + ResourceLoader.get(PackType.CLIENT_RESOURCES).registerReloader(id, + (sharedState, executor, preparationBarrier, executor2) -> CompletableFuture + .supplyAsync(() -> Unit.INSTANCE, executor) + .thenCompose(preparationBarrier::wait) + .thenAcceptAsync(unit -> UPDATE_CALLBACKS.forEach(Runnable::run))); + ResourceLoader.get(PackType.CLIENT_RESOURCES).addReloaderOrdering(ResourceReloaderKeys.AFTER_VANILLA, id); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/texture/IdentifierTexture.java b/src/main/java/de/hysky/skyblocker/utils/render/texture/IdentifierTexture.java new file mode 100644 index 00000000000..2b86211855e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/texture/IdentifierTexture.java @@ -0,0 +1,48 @@ +package de.hysky.skyblocker.utils.render.texture; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.Resource; +import org.jspecify.annotations.Nullable; + +import java.util.Optional; + +class IdentifierTexture implements FallbackedTexture { + + protected final Identifier texture; + protected final Identifier fallback; + private final @Nullable Identifier atlas; + private Identifier currentTexture; + + protected IdentifierTexture(Identifier texture, Identifier fallback, @Nullable Identifier atlas) { + this.texture = texture; + this.fallback = fallback; + this.atlas = atlas; + this.currentTexture = texture; + if (Minecraft.getInstance().isGameLoadFinished()) update(); + } + + @Override + public boolean isUsingFallback() { + return currentTexture == fallback; + } + + @Override + public Identifier get() { + return currentTexture; + } + + void update() { + Minecraft minecraft = Minecraft.getInstance(); + if (atlas != null) { + TextureAtlas textureAtlas = minecraft.getAtlasManager().getAtlasOrThrow(atlas); + if (textureAtlas.missingSprite().equals(textureAtlas.getSprite(texture))) currentTexture = fallback; + else currentTexture = texture; + } else { + Optional resource = minecraft.getResourceManager().getResource(texture); + if (resource.isEmpty()) currentTexture = fallback; + else currentTexture = texture; + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/texture/WidgetSpritesFallback.java b/src/main/java/de/hysky/skyblocker/utils/render/texture/WidgetSpritesFallback.java new file mode 100644 index 00000000000..7c196593b69 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/texture/WidgetSpritesFallback.java @@ -0,0 +1,61 @@ +package de.hysky.skyblocker.utils.render.texture; + +import net.minecraft.client.gui.components.WidgetSprites; +import net.minecraft.data.AtlasIds; +import net.minecraft.resources.Identifier; + +import java.util.List; +import java.util.function.Function; + +class WidgetSpritesFallback implements FallbackedTexture { + private final IdentifierTexture enabled; + private final IdentifierTexture disabled; + private final IdentifierTexture enabledFocused; + private final IdentifierTexture disabledFocused; + private WidgetSprites sprites; + + WidgetSpritesFallback(IdentifierTexture enabled, IdentifierTexture disabled, IdentifierTexture enabledFocused, IdentifierTexture disabledFocused) { + this.enabled = enabled; + this.disabled = disabled; + this.enabledFocused = enabledFocused; + this.disabledFocused = disabledFocused; + sprites = new WidgetSprites(enabled.get(), disabled.get(), enabledFocused.get(), disabledFocused.get()); + } + + @Override + public boolean isUsingFallback() { + return enabled.isUsingFallback() || disabled.isUsingFallback() || enabledFocused.isUsingFallback() || disabledFocused.isUsingFallback(); + } + + @Override + public WidgetSprites get() { + return sprites; + } + + void update() { + enabled.update(); + disabled.update(); + enabledFocused.update(); + disabledFocused.update(); + sprites = new WidgetSprites(enabled.get(), disabled.get(), enabledFocused.get(), disabledFocused.get()); + } + + /** + * Helper class to avoid having duplicate IdentifierTextures. + */ + record Helper(WidgetSprites sprites, WidgetSprites fallback) { + void populate(List textures, Function identifierFunction) { + Identifier identifier = identifierFunction.apply(sprites); + Identifier fallbackIdentifier = identifierFunction.apply(fallback); + for (int i = 0; i < textures.size(); i++) { + IdentifierTexture identifierTexture = textures.get(i); + if (identifierTexture.texture == identifier && identifierTexture.fallback == fallbackIdentifier) { + textures.add(identifierTexture); + return; + } + } + textures.add(new IdentifierTexture(identifier, fallbackIdentifier, AtlasIds.GUI)); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/texture/package-info.java b/src/main/java/de/hysky/skyblocker/utils/render/texture/package-info.java new file mode 100644 index 00000000000..fb938c7c883 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/texture/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package de.hysky.skyblocker.utils.render.texture; + +import org.jspecify.annotations.NullMarked;