From 6177e513494fe66feabd830a3b8307fb8587ef67 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Mon, 23 Dec 2024 02:09:17 +0100 Subject: Add Garden Plots Widget (#929) * Add Garden Plots Widget * small fix * make for loop more boring * exclusion zones * rebase oopsie daisy and JEI exclusion zone * ctrl+shift+up arrow * Fix init merge conflicts * merge and port * 1.21.4 --------- Co-authored-by: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> --- .../compatibility/emi/SkyblockerEMIPlugin.java | 11 + .../compatibility/jei/SkyblockerJEIPlugin.java | 24 ++ .../rei/SkyblockerREIClientPlugin.java | 18 ++ .../config/categories/FarmingCategory.java | 21 +- .../skyblocker/config/configs/FarmingConfig.java | 6 + .../skyblocker/mixins/InventoryScreenMixin.java | 45 +++- .../skyblock/garden/GardenPlotsWidget.java | 299 +++++++++++++++++++++ 7 files changed, 419 insertions(+), 5 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/garden/GardenPlotsWidget.java (limited to 'src/main/java/de') diff --git a/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java b/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java index 9d5a2a46..d8d2d28c 100644 --- a/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java +++ b/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java @@ -1,14 +1,20 @@ package de.hysky.skyblocker.compatibility.emi; import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.Location; +import de.hysky.skyblocker.utils.Utils; import dev.emi.emi.api.EmiPlugin; import dev.emi.emi.api.EmiRegistry; import dev.emi.emi.api.recipe.EmiRecipeCategory; import dev.emi.emi.api.render.EmiTexture; import dev.emi.emi.api.stack.Comparison; import dev.emi.emi.api.stack.EmiStack; +import dev.emi.emi.api.widget.Bounds; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.item.Items; import net.minecraft.util.Identifier; @@ -29,5 +35,10 @@ public class SkyblockerEMIPlugin implements EmiPlugin { registry.addCategory(SKYBLOCK); registry.addWorkstation(SKYBLOCK, EmiStack.of(Items.CRAFTING_TABLE)); ItemRepository.getRecipesStream().map(SkyblockEmiRecipe::new).forEach(registry::addRecipe); + registry.addExclusionArea(InventoryScreen.class, (screen, consumer) -> { + if (!SkyblockerConfigManager.get().farming.garden.gardenPlotsWidget || !Utils.getLocation().equals(Location.GARDEN)) return; + HandledScreenAccessor accessor = (HandledScreenAccessor) screen; + consumer.accept(new Bounds(accessor.getX() + accessor.getBackgroundWidth() + 4, accessor.getY(), 104, 127)); + }); } } diff --git a/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockerJEIPlugin.java b/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockerJEIPlugin.java index ee54c3b5..cf35208b 100644 --- a/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockerJEIPlugin.java +++ b/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockerJEIPlugin.java @@ -1,23 +1,33 @@ package de.hysky.skyblocker.compatibility.jei; import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.utils.Location; +import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.gui.handlers.IGuiContainerHandler; +import mezz.jei.api.registration.IGuiHandlerRegistration; import mezz.jei.api.registration.IRecipeCategoryRegistration; import mezz.jei.api.registration.IRecipeRegistration; import mezz.jei.api.registration.ISubtypeRegistration; import mezz.jei.library.ingredients.subtypes.SubtypeInterpreters; import mezz.jei.library.load.registration.SubtypeRegistration; import mezz.jei.library.plugins.vanilla.crafting.CraftingCategoryExtension; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; +import net.minecraft.client.util.math.Rect2i; import net.minecraft.item.ItemStack; import net.minecraft.recipe.*; import net.minecraft.recipe.book.CraftingRecipeCategory; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; +import java.util.Collections; +import java.util.List; import java.util.Map; @JeiPlugin @@ -45,6 +55,11 @@ public class SkyblockerJEIPlugin implements IModPlugin { registration.addRecipeCategories(skyblockCraftingRecipeCategory); } + @Override + public void registerGuiHandlers(@NotNull IGuiHandlerRegistration registration) { + registration.addGuiContainerHandler(InventoryScreen.class, new InventoryContainerHandler()); + } + @Override public void registerRecipes(@NotNull IRecipeRegistration registration) { //FIXME no clue what to replace any of this with, we can't use items as that does not work @@ -63,4 +78,13 @@ public class SkyblockerJEIPlugin implements IModPlugin { ), "abc", "def", "ghi"), recipe.getResult())) ).toList());*/ } + + private static class InventoryContainerHandler implements IGuiContainerHandler { + @Override + public @NotNull List getGuiExtraAreas(@NotNull InventoryScreen containerScreen) { + if (!SkyblockerConfigManager.get().farming.garden.gardenPlotsWidget || !Utils.getLocation().equals(Location.GARDEN)) return List.of(); + HandledScreenAccessor accessor = (HandledScreenAccessor) containerScreen; + return Collections.singletonList(new Rect2i(accessor.getX() + accessor.getBackgroundWidth() + 4, accessor.getY(), 104, 127)); + } + } } diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java index 7ed322a0..dccc3b78 100644 --- a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java @@ -1,15 +1,24 @@ package de.hysky.skyblocker.compatibility.rei; import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.utils.Location; +import de.hysky.skyblocker.utils.Utils; +import me.shedaniel.math.Rectangle; import me.shedaniel.rei.api.client.plugins.REIClientPlugin; import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; +import me.shedaniel.rei.api.client.registry.screen.ExclusionZones; import me.shedaniel.rei.api.common.category.CategoryIdentifier; import me.shedaniel.rei.api.common.util.EntryStacks; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.item.Items; +import java.util.List; + /** * REI integration */ @@ -31,4 +40,13 @@ public class SkyblockerREIClientPlugin implements REIClientPlugin { public void registerEntries(EntryRegistry entryRegistry) { entryRegistry.addEntries(ItemRepository.getItemsStream().map(EntryStacks::of).toList()); } + + @Override + public void registerExclusionZones(ExclusionZones zones) { + zones.register(InventoryScreen.class, screen -> { + if (!SkyblockerConfigManager.get().farming.garden.gardenPlotsWidget || !Utils.getLocation().equals(Location.GARDEN)) return List.of(); + HandledScreenAccessor accessor = (HandledScreenAccessor) screen; + return List.of(new Rectangle(accessor.getX() + accessor.getBackgroundWidth() + 4, accessor.getY(), 104, 127)); + }); + } } diff --git a/src/main/java/de/hysky/skyblocker/config/categories/FarmingCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/FarmingCategory.java index 6ebbc23e..929256c6 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/FarmingCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/FarmingCategory.java @@ -5,10 +5,7 @@ import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.skyblock.garden.FarmingHudWidget; import de.hysky.skyblocker.skyblock.tabhud.config.WidgetsConfigurationScreen; import de.hysky.skyblocker.utils.Location; -import dev.isxander.yacl3.api.ButtonOption; -import dev.isxander.yacl3.api.ConfigCategory; -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.OptionGroup; +import dev.isxander.yacl3.api.*; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; @@ -62,6 +59,22 @@ public class FarmingCategory { newValue -> config.farming.garden.lockMouseGroundOnly = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.farming.garden.gardenPlotsWidget")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.farming.garden.gardenPlotsWidget.@Tooltip"))) + .binding(defaults.farming.garden.gardenPlotsWidget, + () -> config.farming.garden.gardenPlotsWidget, + newValue -> config.farming.garden.gardenPlotsWidget = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.farming.garden.closeScreenOnPlotClick")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.farming.garden.closeScreenOnPlotClick.@Tooltip"))) + .binding(defaults.farming.garden.closeScreenOnPlotClick, + () -> config.farming.garden.closeScreenOnPlotClick, + newValue -> config.farming.garden.closeScreenOnPlotClick = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) .build()) .build(); } diff --git a/src/main/java/de/hysky/skyblocker/config/configs/FarmingConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/FarmingConfig.java index b2faf005..568165a7 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/FarmingConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/FarmingConfig.java @@ -22,6 +22,12 @@ public class FarmingConfig { @SerialEntry public boolean lockMouseGroundOnly = false; + + @SerialEntry + public boolean gardenPlotsWidget = true; + + @SerialEntry + public boolean closeScreenOnPlotClick = false; } public static class FarmingHud { diff --git a/src/main/java/de/hysky/skyblocker/mixins/InventoryScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/InventoryScreenMixin.java index 39da111a..b35d79b2 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/InventoryScreenMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/InventoryScreenMixin.java @@ -10,15 +10,36 @@ import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.garden.GardenPlotsWidget; import de.hysky.skyblocker.skyblock.itemlist.recipebook.SkyblockRecipeBookWidget; +import de.hysky.skyblocker.utils.Location; import de.hysky.skyblocker.utils.Utils; import net.minecraft.client.gui.DrawContext; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.client.gui.screen.ingame.StatusEffectsDisplay; import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.PlayerScreenHandler; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(InventoryScreen.class) -public abstract class InventoryScreenMixin { +public abstract class InventoryScreenMixin extends HandledScreen { + + @Unique + private GardenPlotsWidget gardenPlotsWidget; + @Unique + private ButtonWidget deskButton; + + public InventoryScreenMixin(PlayerScreenHandler handler, PlayerInventory inventory, Text title) { + super(handler, inventory, title); + } + @ModifyArg(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/RecipeBookScreen;(Lnet/minecraft/screen/AbstractRecipeScreenHandler;Lnet/minecraft/client/gui/screen/recipebook/RecipeBookWidget;Lnet/minecraft/entity/player/PlayerInventory;Lnet/minecraft/text/Text;)V")) private static RecipeBookWidget skyblocker$replaceRecipeBook(RecipeBookWidget original, @Local(argsOnly = true) PlayerEntity player) { @@ -40,4 +61,26 @@ public abstract class InventoryScreenMixin { private boolean skyblocker$markStatusEffectsHidden(boolean original) { return Utils.isOnSkyblock() ? !SkyblockerConfigManager.get().uiAndVisuals.hideStatusEffectOverlay : original; } + + @Inject(method = "onRecipeBookToggled", at = @At("TAIL")) + private void skyblocker$moveGardenPlotsWdiget(CallbackInfo ci) { + if (Utils.getLocation().equals(Location.GARDEN) && gardenPlotsWidget != null) { + gardenPlotsWidget.setPosition(x + backgroundWidth + 4, y); + if (deskButton != null) deskButton.setPosition(gardenPlotsWidget.getX() + 4, y + 108); + } + } + + @Inject(method = "init", at = @At("TAIL")) + private void skyblocker$addGardenPlotsWidget(CallbackInfo ci) { + if (Utils.getLocation().equals(Location.GARDEN) && SkyblockerConfigManager.get().farming.garden.gardenPlotsWidget) { + gardenPlotsWidget = new GardenPlotsWidget(x + backgroundWidth + 4, y); + deskButton = ButtonWidget.builder(Text.translatable("skyblocker.gardenPlots.openDesk"), button -> MessageScheduler.INSTANCE.sendMessageAfterCooldown("/desk")) + .dimensions(gardenPlotsWidget.getX() + 7, y + 108, 60, 15) + .build(); + // make desk button get selected before the widget but render after the widget + addSelectableChild(deskButton); + addDrawableChild(gardenPlotsWidget); + addDrawable(deskButton); + } + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/garden/GardenPlotsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/garden/GardenPlotsWidget.java new file mode 100644 index 00000000..4e4af42b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/garden/GardenPlotsWidget.java @@ -0,0 +1,299 @@ +package de.hysky.skyblocker.skyblock.garden; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.mojang.serialization.JsonOps; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.annotations.Init; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import it.unimi.dsi.fastutil.ints.*; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.screen.slot.Slot; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public class GardenPlotsWidget extends ClickableWidget { + + private static final Logger LOGGER = LoggerFactory.getLogger("Garden Plots"); + private static final Path FOLDER = SkyblockerMod.CONFIG_DIR.resolve("garden_plots"); + + ////////////////////////// + // STATIC SHENANIGANS + ////////////////////////// + + public static final Int2IntMap GARDEN_PLOT_TO_SLOT = Int2IntMaps.unmodifiable(new Int2IntOpenHashMap(Map.ofEntries( + Map.entry(1, 7), + Map.entry(2, 11), + Map.entry(3, 13), + Map.entry(4, 17), + Map.entry(5, 6), + Map.entry(6, 8), + Map.entry(7, 16), + Map.entry(8, 18), + Map.entry(9, 2), + Map.entry(10, 10), + Map.entry(11, 14), + Map.entry(12, 22), + Map.entry(13, 1), + Map.entry(14, 3), + Map.entry(15, 5), + Map.entry(16, 9), + Map.entry(17, 15), + Map.entry(18, 19), + Map.entry(19, 21), + Map.entry(20, 23), + Map.entry(21, 0), + Map.entry(22, 4), + Map.entry(23, 20), + Map.entry(24, 24) + ))); + + private static final GardenPlot[] gardenPlots = new GardenPlot[25]; + + @Init + public static void init() { + ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + if (screen instanceof GenericContainerScreen containerScreen && screen.getTitle().getString().trim().equals("Configure Plots")) { + ScreenEvents.remove(screen).register(ignored -> { + GenericContainerScreenHandler screenHandler = containerScreen.getScreenHandler(); + // Take plot icons and names + for (int row = 0; row < 5; row++) for (int i = row * 9 + 2; i < row * 9 + 7; i++) { + if (i == 22) continue; // Barn icon + Slot slot = screenHandler.slots.get(i); + ItemStack stack = slot.getStack(); + if (stack.isEmpty() || stack.isOf(Items.RED_STAINED_GLASS_PANE) || stack.isOf(Items.OAK_BUTTON) || stack.isOf(Items.BLACK_STAINED_GLASS_PANE)) + continue; + gardenPlots[(i / 9) * 5 + (i % 9 - 2)] = new GardenPlot(stack.getItem(), stack.getName().getString().split("-", 2)[1].trim()); + } + + }); + } + }); + + SkyblockEvents.PROFILE_CHANGE.register(((prevProfileId, profileId) -> { + if (!prevProfileId.isEmpty()) + CompletableFuture.runAsync(() -> save(prevProfileId)).thenRun(() -> load(profileId)); + else load(profileId); + })); + + ClientLifecycleEvents.CLIENT_STOPPING.register(client1 -> { + String profileId = Utils.getProfileId(); + if (!profileId.isBlank()) { + CompletableFuture.runAsync(() -> save(profileId)); + } + }); + } + + private static void save(String profileId) { + try { + Files.createDirectories(FOLDER); + } catch (IOException e) { + LOGGER.error("[Skyblocker] Failed to create folder for garden plots!", e); + } + Path resolve = FOLDER.resolve(profileId + ".json"); + + try (BufferedWriter writer = Files.newBufferedWriter(resolve)) { + JsonArray elements = new JsonArray(); + Arrays.stream(gardenPlots).map(gardenPlot -> { + if (gardenPlot == null) return null; + JsonObject jsonObject = new JsonObject(); + jsonObject.add("icon", Item.ENTRY_CODEC.encodeStart(JsonOps.INSTANCE, gardenPlot.item.getRegistryEntry()).getOrThrow()); + jsonObject.addProperty("name", gardenPlot.name); + return jsonObject; + }).forEach(elements::add); + + SkyblockerMod.GSON.toJson(elements, writer); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to save Garden Plots data", e); + } + } + + private static void load(String profileId) { + Path resolve = FOLDER.resolve(profileId + ".json"); + CompletableFuture.supplyAsync(() -> { + try (BufferedReader reader = Files.newBufferedReader(resolve)) { + return SkyblockerMod.GSON.fromJson(reader, JsonArray.class).asList().stream().map(jsonElement -> { + if (jsonElement == null || jsonElement.isJsonNull()) return null; + JsonObject jsonObject = jsonElement.getAsJsonObject(); + return new GardenPlot(Item.ENTRY_CODEC.decode(JsonOps.INSTANCE, jsonObject.get("icon")).getOrThrow().getFirst().value(), jsonObject.get("name").getAsString()); + } + ).toArray(GardenPlot[]::new); + } catch (NoSuchFileException ignored) { + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to load Equipment data", e); + } + return new GardenPlot[25]; + // Schedule on main thread to avoid any async weirdness + }).thenAccept(newPlots -> MinecraftClient.getInstance().execute(() -> System.arraycopy(newPlots, 0, gardenPlots, 0, Math.min(newPlots.length, 25)))); + } + + ///////////////////////////// + // THE WIDGET ITSELF + ///////////////////////////// + + private static final Identifier BACKGROUND_TEXTURE = Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/garden_plots.png"); + private static final MutableText GROSS_PEST_TEXT = Text.translatable("skyblocker.gardenPlots.pests").formatted(Formatting.RED, Formatting.BOLD); + private static final MutableText TP_TEXT = Text.translatable("skyblocker.gardenPlots.tp").formatted(Formatting.YELLOW, Formatting.BOLD); + + private final ItemStack[] items; + private int hoveredSlot = -1; + private long updateFromTabTime = System.currentTimeMillis(); + private final IntList infectedPlots = new IntArrayList(8); + + public GardenPlotsWidget(int x, int y) { + super(x, y, 104, 127, Text.translatable("skyblocker.gardenPlots")); + items = Arrays.stream(gardenPlots).map(gardenPlot -> { + if (gardenPlot == null) return null; + ItemStack itemStack = new ItemStack(gardenPlot.item()); + itemStack.set(DataComponentTypes.ITEM_NAME, Text.literal(gardenPlot.name()).formatted(Formatting.GREEN, Formatting.BOLD)); + return itemStack; + }).toArray(ItemStack[]::new); + items[12] = new ItemStack(Items.LODESTONE); + items[12].set(DataComponentTypes.ITEM_NAME, Text.literal("The Barn")); + updateInfestedFromTab(); + } + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + MatrixStack matrices = context.getMatrices(); + matrices.push(); + matrices.translate(getX(), getY(), 0); + + context.drawTexture(RenderLayer::getGuiTextured, BACKGROUND_TEXTURE, 0, 0, 0, 0, getWidth(), getHeight(), getWidth(), getHeight()); + + context.drawText(textRenderer, getMessage(), 8, 6, 4210752, false); + + hoveredSlot = -1; + long timeMillis = System.currentTimeMillis(); + for (int i = 0; i < items.length; i++) { + ItemStack item = items[i]; + if (item == null) continue; + + + int slotX = 7 + (i % 5) * 18; + int slotY = 17 + (i / 5) * 18; + boolean hovered = slotX + getX() <= mouseX && mouseX < slotX + getX() + 18 && slotY + getY() <= mouseY && mouseY < slotY + getY() + 18; + + if (hovered) { + context.fill(slotX + 1, slotY + 1, slotX + 17, slotY + 17, 0xAA_FF_FF_FF); + matrices.push(); + matrices.translate(slotX, slotY, 100.f); + matrices.scale(1.125f, 1.125f, 1.125f); + context.drawItem(item, 0, 0); + matrices.pop(); + hoveredSlot = i; + } else + context.drawItem(item, slotX + 1, slotY + 1); + + boolean infested = infectedPlots.contains(i); + if (infested && (timeMillis & 512) != 0) { + context.drawBorder(slotX + 1, slotY + 1, 16, 16, 0xFF_FF0000); + } + + // tooltip + if (hovered) { + List tooltip = infested ? + List.of( + Text.translatable("skyblocker.gardenPlots.plot", item.getName()), + GROSS_PEST_TEXT, + Text.empty(), + TP_TEXT) : + + i == 12 ? + List.of( + item.getName(), + Text.empty(), + TP_TEXT) : + + List.of( + Text.translatable("skyblocker.gardenPlots.plot", item.getName()), + Text.empty(), + TP_TEXT + ); + context.drawTooltip(textRenderer, tooltip, mouseX - getX(), mouseY - getY()); + } + } + + matrices.pop(); + + + if (timeMillis - updateFromTabTime > 3000) { + updateFromTabTime = timeMillis; + updateInfestedFromTab(); + } + } + + private void updateInfestedFromTab() { + infectedPlots.clear(); + for (int i = 0; i < PlayerListMgr.getPlayerStringList().size(); i++) { + String string = PlayerListMgr.getPlayerStringList().get(i); + if (string.startsWith("Plots:")) { + String[] split = string.split(":")[1].split(","); + for (String s : split) { + try { + infectedPlots.add(GARDEN_PLOT_TO_SLOT.getOrDefault(Integer.parseInt(s.strip()), -1)); + } catch (NumberFormatException ignored) {} + } + break; + } + } + } + + @Override + public void onClick(double mouseX, double mouseY) { + super.onClick(mouseX, mouseY); + if (hoveredSlot == -1) return; + + if (SkyblockerConfigManager.get().farming.garden.closeScreenOnPlotClick && MinecraftClient.getInstance().currentScreen != null) + MinecraftClient.getInstance().currentScreen.close(); + + if (hoveredSlot == 12) MessageScheduler.INSTANCE.sendMessageAfterCooldown("/warp garden"); + else MessageScheduler.INSTANCE.sendMessageAfterCooldown("/plottp " + gardenPlots[hoveredSlot].name); + } + + @Override + protected boolean isValidClickButton(int button) { + return super.isValidClickButton(button) && hoveredSlot != -1; + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + } + + private record GardenPlot(Item item, String name) { + } +} -- cgit