From 4e82c974a7217824eab084fe43ce4385b6aae52d Mon Sep 17 00:00:00 2001 From: vicisacat Date: Sat, 20 Apr 2024 16:04:25 +0200 Subject: start event notifications --- .../java/de/hysky/skyblocker/SkyblockerMod.java | 1 + .../skyblocker/skyblock/EventNotifications.java | 103 +++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/EventNotifications.java (limited to 'src') diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index a9aab3b6..abf09032 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -177,6 +177,7 @@ public class SkyblockerMod implements ClientModInitializer { Kuudra.init(); RenderHelper.init(); FancyStatusBars.init(); + EventNotifications.init(); containerSolverManager.init(); statusBarTracker.init(); BeaconHighlighter.init(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/EventNotifications.java b/src/main/java/de/hysky/skyblocker/skyblock/EventNotifications.java new file mode 100644 index 00000000..5f3bae90 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/EventNotifications.java @@ -0,0 +1,103 @@ +package de.hysky.skyblocker.skyblock; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mojang.logging.LogUtils; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.utils.Http; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.toast.SystemToast; +import net.minecraft.text.Text; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +public class EventNotifications { + private static final Logger LOGGER = LogUtils.getLogger(); + + private static long currentTime = System.currentTimeMillis() / 1000; + + public static void init() { + Scheduler.INSTANCE.scheduleCyclic(EventNotifications::timeUpdate, 20); + + SkyblockEvents.JOIN.register(EventNotifications::refreshEvents); + } + + private static final Map> events = new Object2ObjectOpenHashMap<>(); + + public static void refreshEvents() { + CompletableFuture.supplyAsync(() -> { + try { + JsonArray jsonElements = SkyblockerMod.GSON.fromJson(Http.sendGetRequest("https://hysky.de/api/calendar"), JsonArray.class); + return jsonElements.asList().stream().map(JsonElement::getAsJsonObject).toList(); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to download warps list", e); + } + return List.of(); + }).thenAccept(eventsList -> { + events.clear(); + for (JsonObject object : eventsList) { + if (object.get("timestamp").getAsLong() + object.get("duration").getAsInt() < currentTime) continue; + SkyblockEvent skyblockEvent = SkyblockEvent.of(object); + events.computeIfAbsent(object.get("event").getAsString(), s -> new LinkedList<>()).add(skyblockEvent); + } + + for (Map.Entry> entry : events.entrySet()) { + entry.getValue().sort(Comparator.comparingLong(SkyblockEvent::start)); // Sort just in case it's not in order for some reason in API + LOGGER.info("Next {} is at {}", entry.getKey(), entry.getValue().peekFirst()); + } + }); + } + + + + private static void timeUpdate() { + long newTime = System.currentTimeMillis() / 1000; + LOGGER.info(String.valueOf(newTime)); + for (Map.Entry> entry : events.entrySet()) { + LinkedList nextEvents = entry.getValue(); + SkyblockEvent skyblockEvent = nextEvents.peekFirst(); + if (skyblockEvent == null) continue; + if (newTime > skyblockEvent.start() + skyblockEvent.duration()) { + nextEvents.pollFirst(); + skyblockEvent = nextEvents.peekFirst(); + if (skyblockEvent == null) continue; + } + if (currentTime + 60 < skyblockEvent.start() && newTime + 60 >= skyblockEvent.start()) { + MinecraftClient.getInstance().getToastManager().add(new SystemToast( + SystemToast.Type.PERIODIC_NOTIFICATION, + Text.literal(entry.getKey()), + Text.literal("Starting soon!"))); + } + } + currentTime = newTime; + } + + public record SkyblockEvent(long start, int duration, String[] extras, @Nullable String warpCommand) { + public static SkyblockEvent of(JsonObject jsonObject) { + String location = jsonObject.get("location").getAsString(); + location = location.isBlank() ? null: location; + return new SkyblockEvent(jsonObject.get("timestamp").getAsLong(), + jsonObject.get("duration").getAsInt(), + jsonObject.get("extras").getAsJsonArray().asList().stream().map(JsonElement::getAsString).toArray(String[]::new), + location); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("start", start) + .append("duration", duration) + .append("extras", extras) + .append("warpCommand", warpCommand) + .toString(); + } + } +} -- cgit From cc4823e3646573d052ea36fef1ce6d9f9e9b90fd Mon Sep 17 00:00:00 2001 From: vicisacat Date: Sat, 20 Apr 2024 21:08:48 +0200 Subject: some more progress --- .../java/de/hysky/skyblocker/SkyblockerMod.java | 1 + .../skyblocker/skyblock/EventNotifications.java | 103 --------------------- .../skyblock/events/EventNotifications.java | 102 ++++++++++++++++++++ .../skyblocker/skyblock/events/EventToast.java | 76 +++++++++++++++ .../resources/assets/skyblocker/lang/en_us.json | 2 + .../textures/gui/sprites/notification.png | Bin 0 -> 157 bytes .../textures/gui/sprites/notification.png.mcmeta | 10 ++ 7 files changed, 191 insertions(+), 103 deletions(-) delete mode 100644 src/main/java/de/hysky/skyblocker/skyblock/EventNotifications.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/events/EventToast.java create mode 100644 src/main/resources/assets/skyblocker/textures/gui/sprites/notification.png create mode 100644 src/main/resources/assets/skyblocker/textures/gui/sprites/notification.png.mcmeta (limited to 'src') diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index abf09032..19eb395a 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -26,6 +26,7 @@ import de.hysky.skyblocker.skyblock.end.EnderNodes; import de.hysky.skyblocker.skyblock.end.TheEnd; import de.hysky.skyblocker.skyblock.entity.MobBoundingBoxes; import de.hysky.skyblocker.skyblock.fancybars.FancyStatusBars; +import de.hysky.skyblocker.skyblock.events.EventNotifications; import de.hysky.skyblocker.skyblock.garden.FarmingHud; import de.hysky.skyblocker.skyblock.garden.LowerSensitivity; import de.hysky.skyblocker.skyblock.garden.VisitorHelper; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/EventNotifications.java b/src/main/java/de/hysky/skyblocker/skyblock/EventNotifications.java deleted file mode 100644 index 5f3bae90..00000000 --- a/src/main/java/de/hysky/skyblocker/skyblock/EventNotifications.java +++ /dev/null @@ -1,103 +0,0 @@ -package de.hysky.skyblocker.skyblock; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mojang.logging.LogUtils; -import de.hysky.skyblocker.SkyblockerMod; -import de.hysky.skyblocker.events.SkyblockEvents; -import de.hysky.skyblocker.utils.Http; -import de.hysky.skyblocker.utils.scheduler.Scheduler; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.toast.SystemToast; -import net.minecraft.text.Text; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; - -import java.util.*; -import java.util.concurrent.CompletableFuture; - -public class EventNotifications { - private static final Logger LOGGER = LogUtils.getLogger(); - - private static long currentTime = System.currentTimeMillis() / 1000; - - public static void init() { - Scheduler.INSTANCE.scheduleCyclic(EventNotifications::timeUpdate, 20); - - SkyblockEvents.JOIN.register(EventNotifications::refreshEvents); - } - - private static final Map> events = new Object2ObjectOpenHashMap<>(); - - public static void refreshEvents() { - CompletableFuture.supplyAsync(() -> { - try { - JsonArray jsonElements = SkyblockerMod.GSON.fromJson(Http.sendGetRequest("https://hysky.de/api/calendar"), JsonArray.class); - return jsonElements.asList().stream().map(JsonElement::getAsJsonObject).toList(); - } catch (Exception e) { - LOGGER.error("[Skyblocker] Failed to download warps list", e); - } - return List.of(); - }).thenAccept(eventsList -> { - events.clear(); - for (JsonObject object : eventsList) { - if (object.get("timestamp").getAsLong() + object.get("duration").getAsInt() < currentTime) continue; - SkyblockEvent skyblockEvent = SkyblockEvent.of(object); - events.computeIfAbsent(object.get("event").getAsString(), s -> new LinkedList<>()).add(skyblockEvent); - } - - for (Map.Entry> entry : events.entrySet()) { - entry.getValue().sort(Comparator.comparingLong(SkyblockEvent::start)); // Sort just in case it's not in order for some reason in API - LOGGER.info("Next {} is at {}", entry.getKey(), entry.getValue().peekFirst()); - } - }); - } - - - - private static void timeUpdate() { - long newTime = System.currentTimeMillis() / 1000; - LOGGER.info(String.valueOf(newTime)); - for (Map.Entry> entry : events.entrySet()) { - LinkedList nextEvents = entry.getValue(); - SkyblockEvent skyblockEvent = nextEvents.peekFirst(); - if (skyblockEvent == null) continue; - if (newTime > skyblockEvent.start() + skyblockEvent.duration()) { - nextEvents.pollFirst(); - skyblockEvent = nextEvents.peekFirst(); - if (skyblockEvent == null) continue; - } - if (currentTime + 60 < skyblockEvent.start() && newTime + 60 >= skyblockEvent.start()) { - MinecraftClient.getInstance().getToastManager().add(new SystemToast( - SystemToast.Type.PERIODIC_NOTIFICATION, - Text.literal(entry.getKey()), - Text.literal("Starting soon!"))); - } - } - currentTime = newTime; - } - - public record SkyblockEvent(long start, int duration, String[] extras, @Nullable String warpCommand) { - public static SkyblockEvent of(JsonObject jsonObject) { - String location = jsonObject.get("location").getAsString(); - location = location.isBlank() ? null: location; - return new SkyblockEvent(jsonObject.get("timestamp").getAsLong(), - jsonObject.get("duration").getAsInt(), - jsonObject.get("extras").getAsJsonArray().asList().stream().map(JsonElement::getAsString).toArray(String[]::new), - location); - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .append("start", start) - .append("duration", duration) - .append("extras", extras) - .append("warpCommand", warpCommand) - .toString(); - } - } -} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java b/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java new file mode 100644 index 00000000..1d5761a5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java @@ -0,0 +1,102 @@ +package de.hysky.skyblocker.skyblock.events; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mojang.logging.LogUtils; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.utils.Http; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.toast.SystemToast; +import net.minecraft.text.Text; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +public class EventNotifications { + private static final Logger LOGGER = LogUtils.getLogger(); + + private static long currentTime = System.currentTimeMillis() / 1000; + + public static void init() { + Scheduler.INSTANCE.scheduleCyclic(EventNotifications::timeUpdate, 20); + + SkyblockEvents.JOIN.register(EventNotifications::refreshEvents); + } + + private static final Map> events = new Object2ObjectOpenHashMap<>(); + + public static void refreshEvents() { + CompletableFuture.supplyAsync(() -> { + try { + JsonArray jsonElements = SkyblockerMod.GSON.fromJson(Http.sendGetRequest("https://hysky.de/api/calendar"), JsonArray.class); + return jsonElements.asList().stream().map(JsonElement::getAsJsonObject).toList(); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to download warps list", e); + } + return List.of(); + }).thenAccept(eventsList -> { + events.clear(); + for (JsonObject object : eventsList) { + if (object.get("timestamp").getAsLong() + object.get("duration").getAsInt() < currentTime) continue; + SkyblockEvent skyblockEvent = SkyblockEvent.of(object); + events.computeIfAbsent(object.get("event").getAsString(), s -> new LinkedList<>()).add(skyblockEvent); + } + + for (Map.Entry> entry : events.entrySet()) { + entry.getValue().sort(Comparator.comparingLong(SkyblockEvent::start)); // Sort just in case it's not in order for some reason in API + //LOGGER.info("Next {} is at {}", entry.getKey(), entry.getValue().peekFirst()); + } + }); + } + + + + private static void timeUpdate() { + long newTime = System.currentTimeMillis() / 1000; + for (Map.Entry> entry : events.entrySet()) { + LinkedList nextEvents = entry.getValue(); + SkyblockEvent skyblockEvent = nextEvents.peekFirst(); + if (skyblockEvent == null) continue; + if (newTime > skyblockEvent.start() + skyblockEvent.duration()) { + nextEvents.pollFirst(); + skyblockEvent = nextEvents.peekFirst(); + if (skyblockEvent == null) continue; + } + if (currentTime + 60 < skyblockEvent.start() && newTime + 60 >= skyblockEvent.start()) { + MinecraftClient.getInstance().getToastManager().add(new SystemToast( + SystemToast.Type.PERIODIC_NOTIFICATION, + Text.literal(entry.getKey()), + Text.literal("Starting soon!"))); + } + } + currentTime = newTime; + } + + public record SkyblockEvent(long start, int duration, String[] extras, @Nullable String warpCommand) { + public static SkyblockEvent of(JsonObject jsonObject) { + String location = jsonObject.get("location").getAsString(); + location = location.isBlank() ? null: location; + return new SkyblockEvent(jsonObject.get("timestamp").getAsLong(), + jsonObject.get("duration").getAsInt(), + jsonObject.get("extras").getAsJsonArray().asList().stream().map(JsonElement::getAsString).toArray(String[]::new), + location); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("start", start) + .append("duration", duration) + .append("extras", extras) + .append("warpCommand", warpCommand) + .toString(); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/events/EventToast.java b/src/main/java/de/hysky/skyblocker/skyblock/events/EventToast.java new file mode 100644 index 00000000..4a0d139b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/events/EventToast.java @@ -0,0 +1,76 @@ +package de.hysky.skyblocker.skyblock.events; + +import de.hysky.skyblocker.SkyblockerMod; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.toast.Toast; +import net.minecraft.client.toast.ToastManager; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.ScreenTexts; +import net.minecraft.text.MutableText; +import net.minecraft.text.OrderedText; +import net.minecraft.text.Text; +import net.minecraft.util.Colors; +import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; + +import java.util.List; + +public class EventToast implements Toast { + private static final Identifier TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "notification"); + + private final long eventStartTime; + + private final List message; + private final ItemStack icon; + + public EventToast(long eventStartTime, String name, ItemStack icon) { + this.eventStartTime = eventStartTime; + MutableText formatted = Text.translatable("skyblocker.events.startsSoon", Text.literal(name).formatted(Formatting.YELLOW)).formatted(Formatting.WHITE); + message = MinecraftClient.getInstance().textRenderer.wrapLines(formatted, 200); + this.icon = icon; + + } + @Override + public Visibility draw(DrawContext context, ToastManager manager, long startTime) { + context.drawGuiTexture(TEXTURE, 0, 0, getWidth(), getHeight()); + + long currentTime = System.currentTimeMillis() / 1000; + int timeTillEvent = (int) (eventStartTime - currentTime); + + int seconds = timeTillEvent % 60; + int minutes = (timeTillEvent/60) % 60; + int hours = (timeTillEvent/3600) % 24; + + MutableText time = Text.empty(); + if (hours > 0) { + time.append(hours + "h").append(" "); + } + if (hours > 0 || minutes > 0) { + time.append(minutes + "m").append(" "); + } + time.append(seconds + "s"); + + int y = 4; + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + for (OrderedText orderedText : message) { + context.drawText(textRenderer, orderedText, 30, y, Colors.WHITE, false); + y += textRenderer.fontHeight; + } + context.drawText(textRenderer, time, 30, y, Colors.LIGHT_YELLOW, false); + + context.drawItemWithoutEntity(icon, 8, getHeight()/2 - 8); + return startTime > 5_000 ? Visibility.HIDE: Visibility.SHOW; + } + + @Override + public int getWidth() { + return 200 + 30 + 5; + } + + @Override + public int getHeight() { + return 8 + 9 + message.size()*9; + } +} diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 57abdc46..8b549ac7 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -761,6 +761,8 @@ "skyblocker.itemProtection.noItemUuid": "§cYou must be holding an item that has a uuid in order to protect it!", "skyblocker.itemProtection.unableToProtect": "§cUnable to protect this item :( (Are you in skyblock?, are you holding an item?)", + "skyblocker.events.startsSoon": "%s starts soon!", + "skyblocker.tips.enabled": "§aEnabled Tips.", "skyblocker.tips.disabled": "§cDisabled Tips.", "skyblocker.tips.clickEnable": "§a[Click to Enable Tips]", diff --git a/src/main/resources/assets/skyblocker/textures/gui/sprites/notification.png b/src/main/resources/assets/skyblocker/textures/gui/sprites/notification.png new file mode 100644 index 00000000..8f272cd7 Binary files /dev/null and b/src/main/resources/assets/skyblocker/textures/gui/sprites/notification.png differ diff --git a/src/main/resources/assets/skyblocker/textures/gui/sprites/notification.png.mcmeta b/src/main/resources/assets/skyblocker/textures/gui/sprites/notification.png.mcmeta new file mode 100644 index 00000000..c514d54c --- /dev/null +++ b/src/main/resources/assets/skyblocker/textures/gui/sprites/notification.png.mcmeta @@ -0,0 +1,10 @@ +{ + "gui": { + "scaling": { + "type": "nine_slice", + "width": 160, + "height": 32, + "border": 4 + } + } +} \ No newline at end of file -- cgit From 2617ae503efa5b13b84c8fd5530f219ebf28d427 Mon Sep 17 00:00:00 2001 From: vicisacat Date: Sun, 21 Apr 2024 23:16:20 +0200 Subject: working notifications, probably I ain't waiting 50 hours waiting for notifications --- .../skyblock/events/EventNotifications.java | 63 ++++++++++++++++--- .../skyblocker/skyblock/events/EventToast.java | 54 ++++++++-------- .../skyblock/events/JacobEventToast.java | 71 ++++++++++++++++++++++ src/main/java/de/hysky/skyblocker/utils/Utils.java | 17 ++++++ .../utils/config/DurationController.java | 70 +++++++++++++++++++++ .../utils/config/DurationControllerWidget.java | 38 ++++++++++++ 6 files changed, 280 insertions(+), 33 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/config/DurationController.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/config/DurationControllerWidget.java (limited to 'src') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java b/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java index 1d5761a5..ad85e7da 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java @@ -3,15 +3,19 @@ package de.hysky.skyblocker.skyblock.events; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.logging.LogUtils; import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.events.SkyblockEvents; import de.hysky.skyblocker.utils.Http; import de.hysky.skyblocker.utils.scheduler.Scheduler; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.toast.SystemToast; -import net.minecraft.text.Text; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; import org.apache.commons.lang3.builder.ToStringBuilder; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -24,10 +28,42 @@ public class EventNotifications { private static long currentTime = System.currentTimeMillis() / 1000; + public static final Map eventIcons = new Object2ObjectOpenHashMap<>(); + + static { + eventIcons.put("Dark Auction", new ItemStack(Items.NETHER_BRICK)); + eventIcons.put("Bonus Fishing Festival", new ItemStack(Items.FISHING_ROD)); + eventIcons.put("Bonus Mining Fiesta", new ItemStack(Items.IRON_PICKAXE)); + eventIcons.put("Jacob's Farming Contest", new ItemStack(Items.IRON_HOE)); + eventIcons.put("New Year Celebration", new ItemStack(Items.CAKE)); + eventIcons.put("Election Over!", new ItemStack(Items.JUKEBOX)); + eventIcons.put("Election Booth Opens", new ItemStack(Items.JUKEBOX)); + eventIcons.put("Spooky Festival", new ItemStack(Items.JACK_O_LANTERN)); + eventIcons.put("Season of Jerry", new ItemStack(Items.SNOWBALL)); + eventIcons.put("Jerry's Workshop Opens", new ItemStack(Items.SNOW_BLOCK)); + eventIcons.put("Traveling Zoo", new ItemStack(Items.HAY_BLOCK)); // change to the custom head one day + } + public static void init() { Scheduler.INSTANCE.scheduleCyclic(EventNotifications::timeUpdate, 20); SkyblockEvents.JOIN.register(EventNotifications::refreshEvents); + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register( + ClientCommandManager.literal("skyblocker").then( + ClientCommandManager.literal("ye").then( + ClientCommandManager.argument("time", IntegerArgumentType.integer(6)).executes(context -> { + MinecraftClient.getInstance().getToastManager().add( + new EventToast(System.currentTimeMillis() / 1000 + context.getArgument("time", int.class), "Jacob's or something idk", new ItemStack(Items.PAPER)) + ); + return 0; + }) + ).executes(context -> { + MinecraftClient.getInstance().getToastManager().add( + new JacobEventToast(System.currentTimeMillis() / 1000 + 60, "Jacob's farming contest", new String[]{"Cactus","Cocoa Beans","Pumpkin"}) + ); + return 0;}) + ) + )); } private static final Map> events = new Object2ObjectOpenHashMap<>(); @@ -59,6 +95,9 @@ public class EventNotifications { private static void timeUpdate() { + List reminderTimes = SkyblockerConfigManager.get().general.eventNotifications.reminderTimes; + if (reminderTimes.isEmpty()) return; + long newTime = System.currentTimeMillis() / 1000; for (Map.Entry> entry : events.entrySet()) { LinkedList nextEvents = entry.getValue(); @@ -69,11 +108,21 @@ public class EventNotifications { skyblockEvent = nextEvents.peekFirst(); if (skyblockEvent == null) continue; } - if (currentTime + 60 < skyblockEvent.start() && newTime + 60 >= skyblockEvent.start()) { - MinecraftClient.getInstance().getToastManager().add(new SystemToast( - SystemToast.Type.PERIODIC_NOTIFICATION, - Text.literal(entry.getKey()), - Text.literal("Starting soon!"))); + String eventName = entry.getKey(); + + for (Integer reminderTime : reminderTimes) { + if (currentTime + reminderTime < skyblockEvent.start() && newTime + reminderTime >= skyblockEvent.start()) { + if (eventName.equals("Jacob's Farming Contest")) { + MinecraftClient.getInstance().getToastManager().add( + new JacobEventToast(skyblockEvent.start(), eventName, skyblockEvent.extras()) + ); + } + else { + MinecraftClient.getInstance().getToastManager().add( + new EventToast(skyblockEvent.start(), eventName, eventIcons.getOrDefault(eventName, new ItemStack(Items.PAPER))) + ); + } + } } } currentTime = newTime; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/events/EventToast.java b/src/main/java/de/hysky/skyblocker/skyblock/events/EventToast.java index 4a0d139b..bd44c0a5 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/events/EventToast.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/events/EventToast.java @@ -1,13 +1,13 @@ package de.hysky.skyblocker.skyblock.events; import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.utils.Utils; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.toast.Toast; import net.minecraft.client.toast.ToastManager; import net.minecraft.item.ItemStack; -import net.minecraft.screen.ScreenTexts; import net.minecraft.text.MutableText; import net.minecraft.text.OrderedText; import net.minecraft.text.Text; @@ -18,17 +18,20 @@ import net.minecraft.util.Identifier; import java.util.List; public class EventToast implements Toast { - private static final Identifier TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "notification"); + protected static final Identifier TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "notification"); private final long eventStartTime; - private final List message; - private final ItemStack icon; + protected final List message; + protected final int messageWidth; + protected final ItemStack icon; public EventToast(long eventStartTime, String name, ItemStack icon) { this.eventStartTime = eventStartTime; MutableText formatted = Text.translatable("skyblocker.events.startsSoon", Text.literal(name).formatted(Formatting.YELLOW)).formatted(Formatting.WHITE); - message = MinecraftClient.getInstance().textRenderer.wrapLines(formatted, 200); + TextRenderer renderer = MinecraftClient.getInstance().textRenderer; + message = renderer.wrapLines(formatted, 150); + messageWidth = message.stream().mapToInt(renderer::getWidth).max().orElse(150); this.icon = icon; } @@ -36,41 +39,40 @@ public class EventToast implements Toast { public Visibility draw(DrawContext context, ToastManager manager, long startTime) { context.drawGuiTexture(TEXTURE, 0, 0, getWidth(), getHeight()); - long currentTime = System.currentTimeMillis() / 1000; - int timeTillEvent = (int) (eventStartTime - currentTime); + int y = 7; + y = 2 + drawMessage(context, 30, y, Colors.WHITE); + drawTimer(context, 30, y); - int seconds = timeTillEvent % 60; - int minutes = (timeTillEvent/60) % 60; - int hours = (timeTillEvent/3600) % 24; - - MutableText time = Text.empty(); - if (hours > 0) { - time.append(hours + "h").append(" "); - } - if (hours > 0 || minutes > 0) { - time.append(minutes + "m").append(" "); - } - time.append(seconds + "s"); + context.drawItemWithoutEntity(icon, 8, getHeight()/2 - 8); + return startTime > 5_000 ? Visibility.HIDE: Visibility.SHOW; + } - int y = 4; + protected int drawMessage(DrawContext context, int x, int y, int color) { TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; for (OrderedText orderedText : message) { - context.drawText(textRenderer, orderedText, 30, y, Colors.WHITE, false); + context.drawText(textRenderer, orderedText, x, y, color, false); y += textRenderer.fontHeight; } - context.drawText(textRenderer, time, 30, y, Colors.LIGHT_YELLOW, false); + return y; + } - context.drawItemWithoutEntity(icon, 8, getHeight()/2 - 8); - return startTime > 5_000 ? Visibility.HIDE: Visibility.SHOW; + protected void drawTimer(DrawContext context, int x, int y) { + long currentTime = System.currentTimeMillis() / 1000; + int timeTillEvent = (int) (eventStartTime - currentTime); + + Text time = timeTillEvent < 0 ? Text.literal("Starts now!"): Utils.getDurationText(timeTillEvent); + + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + context.drawText(textRenderer, time, x, y, Colors.LIGHT_YELLOW, false); } @Override public int getWidth() { - return 200 + 30 + 5; + return messageWidth + 30 + 6; } @Override public int getHeight() { - return 8 + 9 + message.size()*9; + return 12 + 2 + (message.size()+1)*9; } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java b/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java new file mode 100644 index 00000000..4f9c3fd7 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java @@ -0,0 +1,71 @@ +package de.hysky.skyblocker.skyblock.events; + +import com.mojang.blaze3d.platform.GlStateManager; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.toast.ToastManager; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.Colors; +import net.minecraft.util.math.MathHelper; + +import java.util.HashMap; +import java.util.Map; + +public class JacobEventToast extends EventToast{ + + private final String[] crops; + + private static final Map cropItems = new HashMap<>(); + + static { + cropItems.put("Wheat", new ItemStack(Items.WHEAT)); + cropItems.put("Mushroom", new ItemStack(Items.RED_MUSHROOM)); + cropItems.put("Pumpkin", new ItemStack(Items.CARVED_PUMPKIN)); + cropItems.put("Melon", new ItemStack(Items.MELON)); + cropItems.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE)); + cropItems.put("Cactus", new ItemStack(Items.CACTUS)); + cropItems.put("Carrot", new ItemStack(Items.CARROT)); + cropItems.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS)); + cropItems.put("Potato", new ItemStack(Items.POTATO)); + cropItems.put("Nether Wart", new ItemStack(Items.NETHER_WART)); + } + + public JacobEventToast(long eventStartTime, String name, String[] crops) { + super(eventStartTime, name, new ItemStack(Items.IRON_HOE)); + this.crops = crops; + } + + @Override + public Visibility draw(DrawContext context, ToastManager manager, long startTime) { + context.drawGuiTexture(TEXTURE, 0, 0, getWidth(), getHeight()); + + int y = 7; + TextRenderer textRenderer = manager.getClient().textRenderer; + if (startTime < 3_000){ + int k = MathHelper.floor(MathHelper.clamp((3_000 - startTime) / 200.0f, 0.0f, 1.0f) * 255.0f) << 24 | 0x4000000; + y = 2 + drawMessage(context, 30, y, 0xFFFFFF | k); + } else { + int k = (~MathHelper.floor(MathHelper.clamp((startTime - 3_000) / 200.0f, 0.0f, 1.0f) * 255.0f)) << 24 | 0x4000000; + + + String s = "Crops:"; + int x = 30 + textRenderer.getWidth(s) + 4; + context.drawText(textRenderer, s, 30, 7 + (16 - textRenderer.fontHeight)/2, Colors.WHITE, false); + for (int i = 0; i < crops.length; i++) { + context.drawItem(cropItems.get(crops[i]), x + i * (16 + 8), 7); + } + context.fill(30, 6, 30 + messageWidth, 22, 400, 0x212121 | k); + y += textRenderer.fontHeight * message.size(); + } + drawTimer(context, 30, y); + + context.drawItemWithoutEntity(icon, 8, getHeight()/2 - 8); + return startTime > 5_000 ? Visibility.HIDE: Visibility.SHOW; + } + + @Override + public int getHeight() { + return Math.max(super.getHeight(), 32); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 62a3b897..8316bb9c 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -19,6 +19,7 @@ import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.PlayerListEntry; import net.minecraft.scoreboard.*; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import org.jetbrains.annotations.NotNull; @@ -355,6 +356,22 @@ public class Utils { } } + public static Text getDurationText(int timeInSeconds) { + int seconds = timeInSeconds % 60; + int minutes = (timeInSeconds/60) % 60; + int hours = (timeInSeconds/3600) % 24; + + MutableText time = Text.empty(); + if (hours > 0) { + time.append(hours + "h").append(" "); + } + if (hours > 0 || minutes > 0) { + time.append(minutes + "m").append(" "); + } + time.append(seconds + "s"); + return time; + } + private static void updateFromPlayerList(MinecraftClient client) { if (client.getNetworkHandler() == null) { return; diff --git a/src/main/java/de/hysky/skyblocker/utils/config/DurationController.java b/src/main/java/de/hysky/skyblocker/utils/config/DurationController.java new file mode 100644 index 00000000..09edcf3c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/config/DurationController.java @@ -0,0 +1,70 @@ +package de.hysky.skyblocker.utils.config; + +import de.hysky.skyblocker.utils.Utils; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.gui.AbstractWidget; +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.gui.controllers.string.IStringController; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public record DurationController(Option option) implements IStringController { + + private static final Pattern secondsPattern = Pattern.compile("(^|\\s)(\\d+)s(\\s|$)"); + private static final Pattern minutesPattern = Pattern.compile("(^|\\s)(\\d+)m(\\s|$)"); + private static final Pattern hoursPattern = Pattern.compile("(^|\\s)(\\d+)h(\\s|$)"); + + @Override + public String getString() { + return Utils.getDurationText(option.pendingValue()).getString(); + } + + + @Override + public void setFromString(String value) { + Matcher hoursMatcher = hoursPattern.matcher(value); + Matcher minutesMatcher = minutesPattern.matcher(value); + Matcher secondsMatcher = secondsPattern.matcher(value); + + int result = 0; + if (hoursMatcher.find()) { + result += Integer.parseInt(hoursMatcher.group(2)) * 3600; + } + if (minutesMatcher.find()) { + result += Integer.parseInt(minutesMatcher.group(2)) * 60; + } + if (secondsMatcher.find()) { + result += Integer.parseInt(secondsMatcher.group(2)); + } + option.requestSet(result); + } + + + @Override + public boolean isInputValid(String s) { + Matcher hoursMatcher = hoursPattern.matcher(s); + Matcher minutesMatcher = minutesPattern.matcher(s); + Matcher secondsMatcher = secondsPattern.matcher(s); + + int hoursCount = 0; + while (hoursMatcher.find()) hoursCount++; + int minutesCount = 0; + while (minutesMatcher.find()) minutesCount++; + int secondsCount = 0; + while (secondsMatcher.find()) secondsCount++; + + if (hoursCount == 0 && minutesCount == 0 && secondsCount == 0) return false; + if (hoursCount > 1 || minutesCount > 1 || secondsCount > 1) return false; + s = s.replaceAll(hoursPattern.pattern(), ""); + s = s.replaceAll(minutesPattern.pattern(), ""); + s = s.replaceAll(secondsPattern.pattern(), ""); + return s.isBlank(); + } + + @Override + public AbstractWidget provideWidget(YACLScreen screen, Dimension widgetDimension) { + return new DurationControllerWidget(this, screen, widgetDimension); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/config/DurationControllerWidget.java b/src/main/java/de/hysky/skyblocker/utils/config/DurationControllerWidget.java new file mode 100644 index 00000000..f25cd088 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/config/DurationControllerWidget.java @@ -0,0 +1,38 @@ +package de.hysky.skyblocker.utils.config; + +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.gui.controllers.string.IStringController; +import dev.isxander.yacl3.gui.controllers.string.StringControllerElement; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.function.Consumer; + +public class DurationControllerWidget extends StringControllerElement { + + public DurationControllerWidget(IStringController control, YACLScreen screen, Dimension dim) { + super(control, screen, dim, false); + } + + @Override + public void unfocus() { + if (control.isInputValid(inputField)) super.unfocus(); + else modifyInput(stringBuilder -> stringBuilder.replace(0, stringBuilder.length(), control.getString())); + } + + @Override + public boolean modifyInput(Consumer consumer) { + StringBuilder temp = new StringBuilder(inputField); + consumer.accept(temp); + inputField = temp.toString(); + return true; + } + + @Override + protected Text getValueText() { + Text valueText = super.getValueText(); + boolean inputValid = control.isInputValid(valueText.getString()); + return valueText.copy().formatted(inputValid ? Formatting.WHITE: Formatting.RED); + } +} -- cgit From 84ffa6d5f466a52b7927070447f71c816bcc6c94 Mon Sep 17 00:00:00 2001 From: vicisacat Date: Sun, 21 Apr 2024 23:17:39 +0200 Subject: unused import :D --- src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java b/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java index 4f9c3fd7..12590d09 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java @@ -1,6 +1,5 @@ package de.hysky.skyblocker.skyblock.events; -import com.mojang.blaze3d.platform.GlStateManager; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.toast.ToastManager; -- cgit From c645892f94f9017b3685733c04520c47beedfed9 Mon Sep 17 00:00:00 2001 From: vicisacat Date: Tue, 23 Apr 2024 13:51:45 +0200 Subject: upcoming events tab!!! so cool --- .../auction/widgets/CategoryTabWidget.java | 28 +--- .../skyblock/events/EventNotifications.java | 8 +- .../skyblock/events/JacobEventToast.java | 2 +- .../skyblocker/skyblock/itemlist/ItemListTab.java | 89 +++++++++++ .../skyblock/itemlist/ItemListWidget.java | 128 +++++++++------- .../skyblock/itemlist/SearchResultsWidget.java | 15 +- .../skyblock/itemlist/UpcomingEventsTab.java | 168 +++++++++++++++++++++ .../utils/render/gui/SideTabButtonWidget.java | 39 +++++ 8 files changed, 400 insertions(+), 77 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListTab.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/UpcomingEventsTab.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/SideTabButtonWidget.java (limited to 'src') 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 02dbc132..a0b5f0b9 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,42 +1,26 @@ package de.hysky.skyblocker.skyblock.auction.widgets; import de.hysky.skyblocker.skyblock.auction.SlotClickHandler; +import de.hysky.skyblocker.utils.render.gui.SideTabButtonWidget; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ButtonTextures; -import net.minecraft.client.gui.widget.ToggleButtonWidget; import net.minecraft.client.item.TooltipType; import net.minecraft.item.Item.TooltipContext; import net.minecraft.item.ItemStack; -import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; -public class CategoryTabWidget extends ToggleButtonWidget { - private static final ButtonTextures TEXTURES = new ButtonTextures(new Identifier("recipe_book/tab"), new Identifier("recipe_book/tab_selected")); - - public void setIcon(@NotNull ItemStack icon) { - this.icon = icon.copy(); - } - - private @NotNull ItemStack icon; +public class CategoryTabWidget extends SideTabButtonWidget { private final SlotClickHandler slotClick; private int slotId = -1; public CategoryTabWidget(@NotNull ItemStack icon, SlotClickHandler slotClick) { - super(0, 0, 35, 27, false); - this.icon = icon.copy(); // copy prevents item disappearing on click + super(0, 0, false, icon); this.slotClick = slotClick; - setTextures(TEXTURES); } @Override public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { - if (textures == null) return; - Identifier identifier = textures.get(true, this.toggled); - int x = getX(); - if (toggled) x -= 2; - context.drawGuiTexture(identifier, x, this.getY(), this.width, this.height); - context.drawItem(icon, x + 9, getY() + 5); + super.renderWidget(context, mouseX, mouseY, delta); if (isMouseOver(mouseX, mouseY)) { context.getMatrices().push(); @@ -52,8 +36,8 @@ public class CategoryTabWidget extends ToggleButtonWidget { @Override public void onClick(double mouseX, double mouseY) { - if (this.toggled || slotId == -1) return; + if (isToggled() || slotId == -1) return; + super.onClick(mouseX, mouseY); slotClick.click(slotId); - this.setToggled(true); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java b/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java index ad85e7da..d2f99040 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/events/EventNotifications.java @@ -28,13 +28,15 @@ public class EventNotifications { private static long currentTime = System.currentTimeMillis() / 1000; + public static final String JACOBS = "Jacob's Farming Contest"; + public static final Map eventIcons = new Object2ObjectOpenHashMap<>(); static { eventIcons.put("Dark Auction", new ItemStack(Items.NETHER_BRICK)); eventIcons.put("Bonus Fishing Festival", new ItemStack(Items.FISHING_ROD)); eventIcons.put("Bonus Mining Fiesta", new ItemStack(Items.IRON_PICKAXE)); - eventIcons.put("Jacob's Farming Contest", new ItemStack(Items.IRON_HOE)); + eventIcons.put(JACOBS, new ItemStack(Items.IRON_HOE)); eventIcons.put("New Year Celebration", new ItemStack(Items.CAKE)); eventIcons.put("Election Over!", new ItemStack(Items.JUKEBOX)); eventIcons.put("Election Booth Opens", new ItemStack(Items.JUKEBOX)); @@ -68,6 +70,10 @@ public class EventNotifications { private static final Map> events = new Object2ObjectOpenHashMap<>(); + public static Map> getEvents() { + return events; + } + public static void refreshEvents() { CompletableFuture.supplyAsync(() -> { try { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java b/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java index 12590d09..a7a5f9c0 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/events/JacobEventToast.java @@ -15,7 +15,7 @@ public class JacobEventToast extends EventToast{ private final String[] crops; - private static final Map cropItems = new HashMap<>(); + public static final Map cropItems = new HashMap<>(); static { cropItems.put("Wheat", new ItemStack(Items.WHEAT)); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListTab.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListTab.java new file mode 100644 index 00000000..4109246d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListTab.java @@ -0,0 +1,89 @@ +package de.hysky.skyblocker.skyblock.itemlist; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.List; + +public class ItemListTab extends ItemListWidget.TabContainerWidget { + + private SearchResultsWidget results; + private final MinecraftClient client; + private TextFieldWidget searchField; + + public ItemListTab(int x, int y, MinecraftClient client, TextFieldWidget searchField) { + super(x, y, Text.literal("Item List Tab")); + this.client = client; + this.searchField = searchField; + if (ItemRepository.filesImported()) { + this.results = new SearchResultsWidget(this.client, x - 9, y - 9 ); + this.results.updateSearchResult(searchField == null ? "": this.searchField.getText()); + } + } + + @Override + public List children() { + return List.of(results, searchField); + } + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + MatrixStack matrices = context.getMatrices(); + matrices.push(); + matrices.translate(0.0D, 0.0D, 100.0D); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + int x = getX(); + int y = getY(); + + // all coordinates offseted -9 + if (!ItemRepository.filesImported() && !this.searchField.isFocused() && this.searchField.getText().isEmpty()) { + Text hintText = (Text.literal("Loading...")).formatted(Formatting.ITALIC).formatted(Formatting.GRAY); + context.drawTextWithShadow(this.client.textRenderer, hintText, x + 16, y + 7, -1); + } else if (!this.searchField.isFocused() && this.searchField.getText().isEmpty()) { + Text hintText = (Text.translatable("gui.recipebook.search_hint")).formatted(Formatting.ITALIC).formatted(Formatting.GRAY); + context.drawTextWithShadow(this.client.textRenderer, hintText, x + 16, y + 7, -1); + } else { + this.searchField.render(context, mouseX, mouseY, delta); + } + if (ItemRepository.filesImported()) { + if (results == null) { + this.results = new SearchResultsWidget(this.client, x - 9, y - 9); + } + this.results.updateSearchResult(this.searchField.getText()); + this.results.render(context, mouseX, mouseY, delta); + } + matrices.pop(); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) {} + + public void setSearchField(TextFieldWidget searchField) { + this.searchField = searchField; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (!visible) return false; + if (this.searchField.mouseClicked(mouseX, mouseY, button)) { + this.results.closeRecipeView(); + this.searchField.setFocused(true); + return true; + } else { + this.searchField.setFocused(false); + return this.results.mouseClicked(mouseX, mouseY, button); + } + } + + @Override + public void drawTooltip(DrawContext context, int mouseX, int mouseY) { + if (this.results != null) this.results.drawTooltip(context, mouseX, mouseY); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListWidget.java index 6120528c..4a258b9e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListWidget.java @@ -1,103 +1,127 @@ package de.hysky.skyblocker.skyblock.itemlist; -import com.mojang.blaze3d.systems.RenderSystem; - import de.hysky.skyblocker.mixins.accessors.RecipeBookWidgetAccessor; +import de.hysky.skyblocker.utils.render.gui.SideTabButtonWidget; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.objects.ObjectObjectImmutablePair; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.ContainerWidget; import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; import net.minecraft.screen.AbstractRecipeScreenHandler; import net.minecraft.text.Text; -import net.minecraft.util.Formatting; + +import java.util.ArrayList; +import java.util.List; @Environment(value = EnvType.CLIENT) public class ItemListWidget extends RecipeBookWidget { private int parentWidth; private int parentHeight; private int leftOffset; - private TextFieldWidget searchField; - private SearchResultsWidget results; + + private TabContainerWidget currentTabContent; + private final List> tabs = new ArrayList<>(2); + private ItemListTab itemListTab; public ItemListWidget() { super(); } - public void updateSearchResult() { - this.results.updateSearchResult(((RecipeBookWidgetAccessor) this).getSearchText()); - } - @Override public void initialize(int parentWidth, int parentHeight, MinecraftClient client, boolean narrow, AbstractRecipeScreenHandler craftingScreenHandler) { super.initialize(parentWidth, parentHeight, client, narrow, craftingScreenHandler); this.parentWidth = parentWidth; this.parentHeight = parentHeight; this.leftOffset = narrow ? 0 : 86; - this.searchField = ((RecipeBookWidgetAccessor) this).getSearchField(); - int x = (this.parentWidth - 147) / 2 - this.leftOffset; - int y = (this.parentHeight - 166) / 2; - if (ItemRepository.filesImported()) { - this.results = new SearchResultsWidget(this.client, x, y); - this.updateSearchResult(); - } + TextFieldWidget searchField = ((RecipeBookWidgetAccessor) this).getSearchField(); + int x = (parentWidth - 147) / 2 - leftOffset; + int y = (parentHeight - 166) / 2; + + // Init all the tabs, content and the tab button on the left + tabs.clear(); + itemListTab = new ItemListTab(x + 9, y + 9, this.client, searchField); + + SideTabButtonWidget itemListTabButton = new SideTabButtonWidget(x - 30, y + 3, true, new ItemStack(Items.CRAFTING_TABLE)); + itemListTabButton.setTooltip(Tooltip.of(Text.literal("Item List"))); + tabs.add(new ObjectObjectImmutablePair<>( + itemListTabButton, + this.currentTabContent = this.itemListTab)); + + SideTabButtonWidget eventsTabButtonWidget = new SideTabButtonWidget(x - 30, y + 3 + 27, false, new ItemStack(Items.CLOCK)); + eventsTabButtonWidget.setTooltip(Tooltip.of(Text.literal("Upcoming Events"))); + tabs.add(new ObjectObjectImmutablePair<>( + eventsTabButtonWidget, + new UpcomingEventsTab(x + 9, y + 9, this.client) + )); + + } + + @Override + public void reset() { + super.reset(); + if (itemListTab != null) itemListTab.setSearchField(((RecipeBookWidgetAccessor) this).getSearchField()); } @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { if (this.isOpen()) { - MatrixStack matrices = context.getMatrices(); - matrices.push(); - matrices.translate(0.0D, 0.0D, 100.0D); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - this.searchField = ((RecipeBookWidgetAccessor) this).getSearchField(); int i = (this.parentWidth - 147) / 2 - this.leftOffset; int j = (this.parentHeight - 166) / 2; + // Draw the texture context.drawTexture(TEXTURE, i, j, 1, 1, 147, 166); - this.searchField = ((RecipeBookWidgetAccessor) this).getSearchField(); - - if (!ItemRepository.filesImported() && !this.searchField.isFocused() && this.searchField.getText().isEmpty()) { - Text hintText = (Text.literal("Loading...")).formatted(Formatting.ITALIC).formatted(Formatting.GRAY); - context.drawTextWithShadow(this.client.textRenderer, hintText, i + 25, j + 14, -1); - } else if (!this.searchField.isFocused() && this.searchField.getText().isEmpty()) { - Text hintText = (Text.translatable("gui.recipebook.search_hint")).formatted(Formatting.ITALIC).formatted(Formatting.GRAY); - context.drawTextWithShadow(this.client.textRenderer, hintText, i + 25, j + 14, -1); - } else { - this.searchField.render(context, mouseX, mouseY, delta); - } - if (ItemRepository.filesImported()) { - if (results == null) { - int x = (this.parentWidth - 147) / 2 - this.leftOffset; - int y = (this.parentHeight - 166) / 2; - this.results = new SearchResultsWidget(this.client, x, y); - } - this.updateSearchResult(); - this.results.render(context, mouseX, mouseY, delta); + // Draw the tab's content + if (currentTabContent != null) currentTabContent.render(context, mouseX, mouseY, delta); + // Draw the tab buttons + for (Pair tab : tabs) { + tab.left().render(context, mouseX, mouseY, delta); } - matrices.pop(); + } } @Override public void drawTooltip(DrawContext context, int x, int y, int mouseX, int mouseY) { - if (this.isOpen() && ItemRepository.filesImported() && results != null) { - this.results.drawTooltip(context, mouseX, mouseY); + if (this.isOpen() && currentTabContent != null) { + this.currentTabContent.drawTooltip(context, mouseX, mouseY); } } @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (this.isOpen() && this.client.player != null && !this.client.player.isSpectator() && ItemRepository.filesImported() && this.searchField != null && results != null) { - if (this.searchField.mouseClicked(mouseX, mouseY, button)) { - this.results.closeRecipeView(); - this.searchField.setFocused(true); - return true; - } else { - this.searchField.setFocused(false); - return this.results.mouseClicked(mouseX, mouseY, button); + if (this.isOpen() && this.client.player != null && !this.client.player.isSpectator()) { + // check if a tab is clicked + for (Pair tab : tabs) { + if (tab.first().mouseClicked(mouseX, mouseY, button) && currentTabContent != tab.right()) { + for (Pair tab2 : tabs) { + tab2.first().setToggled(false); + } + tab.first().setToggled(true); + currentTabContent = tab.right(); + return true; + } } + // click the tab content + if (currentTabContent != null) return currentTabContent.mouseClicked(mouseX, mouseY, button); + else return false; } else return false; } + + /** + * A container widget but with a fixed width and height and a drawTooltip method to implement + */ + public abstract static class TabContainerWidget extends ContainerWidget { + + public TabContainerWidget(int x, int y, Text text) { + super(x, y, 131, 150, text); + } + + public abstract void drawTooltip(DrawContext context, int mouseX, int mouseY); + } } \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java index 1ef352e3..48d3a8f6 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java @@ -6,6 +6,7 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.Element; import net.minecraft.client.gui.screen.ButtonTextures; import net.minecraft.client.gui.widget.ToggleButtonWidget; import net.minecraft.component.DataComponentTypes; @@ -23,7 +24,7 @@ import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class SearchResultsWidget implements Drawable { +public class SearchResultsWidget implements Drawable, Element { private static final ButtonTextures PAGE_FORWARD_TEXTURES = new ButtonTextures(new Identifier("recipe_book/page_forward"), new Identifier("recipe_book/page_forward_highlighted")); private static final ButtonTextures PAGE_BACKWARD_TEXTURES = new ButtonTextures(new Identifier("recipe_book/page_backward"), new Identifier("recipe_book/page_backward_highlighted")); private static final int COLS = 5; @@ -225,4 +226,16 @@ public class SearchResultsWidget implements Drawable { return false; } + private boolean focused = false; + + @Override + public void setFocused(boolean focused) { + this.