aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de
diff options
context:
space:
mode:
authorviciscat <51047087+viciscat@users.noreply.github.com>2024-12-23 02:09:17 +0100
committerGitHub <noreply@github.com>2024-12-23 09:09:17 +0800
commit6177e513494fe66feabd830a3b8307fb8587ef67 (patch)
tree4e248671ca88ee47471ddaad48ca467d2f66be4f /src/main/java/de
parent7e7020b518671325e8249fcee2dbfa3ec9e25c31 (diff)
downloadSkyblocker-6177e513494fe66feabd830a3b8307fb8587ef67.tar.gz
Skyblocker-6177e513494fe66feabd830a3b8307fb8587ef67.tar.bz2
Skyblocker-6177e513494fe66feabd830a3b8307fb8587ef67.zip
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>
Diffstat (limited to 'src/main/java/de')
-rw-r--r--src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java11
-rw-r--r--src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockerJEIPlugin.java24
-rw-r--r--src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java18
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/FarmingCategory.java21
-rw-r--r--src/main/java/de/hysky/skyblocker/config/configs/FarmingConfig.java6
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/InventoryScreenMixin.java45
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/garden/GardenPlotsWidget.java299
7 files changed, 419 insertions, 5 deletions
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
@@ -46,6 +56,11 @@ public class SkyblockerJEIPlugin implements IModPlugin {
}
@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
/*registration.getIngredientManager().addIngredientsAtRuntime(VanillaTypes.ITEM_STACK, ItemRepository.getItems());
@@ -63,4 +78,13 @@ public class SkyblockerJEIPlugin implements IModPlugin {
), "abc", "def", "ghi"), recipe.getResult()))
).toList());*/
}
+
+ private static class InventoryContainerHandler implements IGuiContainerHandler<InventoryScreen> {
+ @Override
+ public @NotNull List<Rect2i> 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.<Boolean>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.<Boolean>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<PlayerScreenHandler> {
+
+ @Unique
+ private GardenPlotsWidget gardenPlotsWidget;
+ @Unique
+ private ButtonWidget deskButton;
+
+ public InventoryScreenMixin(PlayerScreenHandler handler, PlayerInventory inventory, Text title) {
+ super(handler, inventory, title);
+ }
+
@ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/RecipeBookScreen;<init>(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<Text> 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) {
+ }
+}