From 19a1df637df912fc2a99170e7b74abed6338ac5a Mon Sep 17 00:00:00 2001 From: Wanja <105125277+Manchick0@users.noreply.github.com> Date: Mon, 30 Dec 2024 02:20:17 +0100 Subject: Add Speed Presets for configurable speed settings (#1111) * Add Speed Presets for configurable speed settings Introduced a Speed Presets system with customizable presets for the rancher boots. Added a GUI to manage presets and integrated support for the `setmaxspeed` command, and the sign-editor to use these presets. Besides all of that, refactored item protection rendering logic to show it everywhere, where applicable, and render it in front of the item, as well as updated the texture being used. * Reverted the proposed changes to the protected item texture I apologize for the changes, since I didn't know that the texture was applied elsewhere. This commit reverts all the changes to the size of the texture and the way it is rendered, to hopefully prevent any issues. * Apply suggestions from code review Co-authored-by: Kevin <92656833+kevinthegreat1@users.noreply.github.com> * Refactor modification checks for SpeedPreset entries. Removed the `hasBeenModified` flag, and replaced it with direct comparisons of initial and current values. * Clean up pointless changes * Refactor SpeedPresets to use Object2IntMap. Replaced LinkedHashMap with Object2IntMap for efficiency and consistency in managing speed presets. Adjusted related methods and logic to accommodate the new data structure. Updated SpeedPresetListWidget to handle changes and comparisons using the revised map implementation. * Refactor SpeedPresets --------- Co-authored-by: Kevin <92656833+kevinthegreat1@users.noreply.github.com> --- .../config/categories/GeneralCategory.java | 18 +++ .../skyblocker/config/configs/GeneralConfig.java | 9 ++ .../mixins/CommandTreeS2CPacketMixin.java | 2 + .../skyblocker/mixins/HandledScreenMixin.java | 11 +- .../skyblocker/mixins/SignEditScreenMixin.java | 59 +++++-- .../speedPreset/SpeedPresetListWidget.java | 180 +++++++++++++++++++++ .../skyblock/speedPreset/SpeedPresets.java | 146 +++++++++++++++++ .../skyblock/speedPreset/SpeedPresetsScreen.java | 75 +++++++++ 8 files changed, 476 insertions(+), 24 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresetListWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresets.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresetsScreen.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java index b041a9fe..944e9d2e 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java @@ -8,6 +8,7 @@ import de.hysky.skyblocker.config.configs.GeneralConfig; import de.hysky.skyblocker.skyblock.item.slottext.SlotTextMode; import de.hysky.skyblocker.skyblock.item.tooltip.adders.CraftPriceTooltip; import de.hysky.skyblocker.skyblock.shortcut.ShortcutsConfigScreen; +import de.hysky.skyblocker.skyblock.speedPreset.SpeedPresetsScreen; import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder; import net.minecraft.client.MinecraftClient; @@ -58,6 +59,23 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) + .group(OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.general.speedPresets")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.general.speedPresets.enableSpeedPresets")) + .binding(defaults.general.speedPresets.enableSpeedPresets, + () -> config.general.speedPresets.enableSpeedPresets, + newValue -> config.general.speedPresets.enableSpeedPresets = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(ButtonOption.createBuilder() + .name(Text.translatable("skyblocker.config.general.speedPresets.config")) + .text(Text.translatable("text.skyblocker.open")) + .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new SpeedPresetsScreen(screen))) + .build()) + .build()) + //Shortcuts .group(OptionGroup.createBuilder() .name(Text.translatable("skyblocker.config.general.shortcuts")) diff --git a/src/main/java/de/hysky/skyblocker/config/configs/GeneralConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/GeneralConfig.java index 912dd769..bb66625a 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/GeneralConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/GeneralConfig.java @@ -22,6 +22,9 @@ public class GeneralConfig { @SerialEntry public boolean acceptReparty = true; + @SerialEntry + public SpeedPresets speedPresets = new SpeedPresets(); + @SerialEntry public Shortcuts shortcuts = new Shortcuts(); @@ -68,6 +71,12 @@ public class GeneralConfig { @SerialEntry public Object2ObjectOpenHashMap customAnimatedDyes = new Object2ObjectOpenHashMap<>(); + public static class SpeedPresets { + + @SerialEntry + public boolean enableSpeedPresets = true; + } + public static class Shortcuts { @SerialEntry public boolean enableShortcuts = true; diff --git a/src/main/java/de/hysky/skyblocker/mixins/CommandTreeS2CPacketMixin.java b/src/main/java/de/hysky/skyblocker/mixins/CommandTreeS2CPacketMixin.java index b96d5a5f..83605ca9 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/CommandTreeS2CPacketMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/CommandTreeS2CPacketMixin.java @@ -7,6 +7,7 @@ import com.mojang.brigadier.tree.LiteralCommandNode; import de.hysky.skyblocker.skyblock.SackItemAutocomplete; import de.hysky.skyblocker.skyblock.ViewstashAutocomplete; import de.hysky.skyblocker.skyblock.WarpAutocomplete; +import de.hysky.skyblocker.skyblock.speedPreset.SpeedPresets; import de.hysky.skyblocker.utils.Utils; import net.minecraft.command.CommandSource; import org.spongepowered.asm.mixin.Mixin; @@ -18,6 +19,7 @@ public class CommandTreeS2CPacketMixin { public CommandNode modifyCommandSuggestions(CommandNode original) { if (Utils.isOnHypixel() && original instanceof LiteralCommandNode literalCommandNode) { return switch (literalCommandNode.getLiteral()) { + case String s when s.equals("setmaxspeed") -> SpeedPresets.getCommandNode(); case String s when s.equals("warp") && WarpAutocomplete.commandNode != null -> WarpAutocomplete.commandNode; case String s when s.equals("getfromsacks") && SackItemAutocomplete.longCommandNode != null -> SackItemAutocomplete.longCommandNode; case String s when s.equals("gfs") && SackItemAutocomplete.shortCommandNode != null -> SackItemAutocomplete.shortCommandNode; diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java index c833d06c..d092de6e 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java @@ -2,9 +2,8 @@ package de.hysky.skyblocker.mixins; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; -import com.mojang.blaze3d.systems.RenderSystem; -import de.hysky.skyblocker.SkyblockerMod; +import com.mojang.blaze3d.systems.RenderSystem; import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.InventorySearch; @@ -36,7 +35,6 @@ import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.SlotActionType; import net.minecraft.text.Text; -import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; @@ -63,9 +61,6 @@ public abstract class HandledScreenMixin extends Screen @Unique private static final int OUT_OF_BOUNDS_SLOT = -999; - @Unique - private static final Identifier ITEM_PROTECTION = Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/item_protection.png"); - @Unique private static final Set FILLER_ITEMS = Set.of( " ", // Empty menu item @@ -335,10 +330,10 @@ public abstract class HandledScreenMixin extends Screen private void skyblocker$drawOnItem(DrawContext context, Slot slot, CallbackInfo ci) { if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds) ItemRarityBackgrounds.tryDraw(slot.getStack(), context, slot.x, slot.y); - // Item protection + // Item Protection if (ItemProtection.isItemProtected(slot.getStack())) { RenderSystem.enableBlend(); - context.drawTexture(RenderLayer::getGuiTextured, ITEM_PROTECTION, slot.x, slot.y, 0, 0, 16, 16, 16, 16); + context.drawTexture(RenderLayer::getGuiTextured, ItemProtection.ITEM_PROTECTION_TEX, slot.x, slot.y, 0, 0, 16, 16, 16, 16); RenderSystem.disableBlend(); } // Search diff --git a/src/main/java/de/hysky/skyblocker/mixins/SignEditScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/SignEditScreenMixin.java index 04342f37..14769a76 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/SignEditScreenMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/SignEditScreenMixin.java @@ -3,9 +3,13 @@ package de.hysky.skyblocker.mixins; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.calculators.SignCalculator; +import de.hysky.skyblocker.skyblock.speedPreset.SpeedPresets; import de.hysky.skyblocker.utils.Utils; import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -15,32 +19,55 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.llamalad7.mixinextras.sugar.Local; -import java.util.Objects; - @Mixin(AbstractSignEditScreen.class) -public abstract class SignEditScreenMixin { +public abstract class SignEditScreenMixin extends Screen { + @Shadow @Final private String[] messages; - @Inject(method = "render", at = @At("HEAD")) + protected SignEditScreenMixin(Text title) { + super(title); + } + + @Inject(method = "render", at = @At("HEAD")) private void skyblocker$render(CallbackInfo ci, @Local(argsOnly = true) DrawContext context) { - //if the sign is being used to enter number send it to the sign calculator - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().uiAndVisuals.inputCalculator.enabled && Objects.equals(messages[1], "^^^^^^^^^^^^^^^")) { - SignCalculator.renderCalculator(context, messages[0], context.getScaledWindowWidth() / 2, 55); - } + if (Utils.isOnSkyblock()) { + var config = SkyblockerConfigManager.get(); + if (messages[1].equals("^^^^^^") && config.general.speedPresets.enableSpeedPresets) { + var presets = SpeedPresets.getInstance(); + if (presets.hasPreset(messages[0])) { + context.drawCenteredTextWithShadow(this.textRenderer, Text.literal(String.format("%s ยป %d", messages[0], presets.getPreset(messages[0]))).formatted(Formatting.GREEN), + context.getScaledWindowWidth() / 2, 55, 0xFFFFFFFF); + } + } + //if the sign is being used to enter number send it to the sign calculator + if (messages[1].equals("^^^^^^^^^^^^^^^") && config.uiAndVisuals.inputCalculator.enabled) { + SignCalculator.renderCalculator(context, messages[0], context.getScaledWindowWidth() / 2, 55); + } + } } @Inject(method = "finishEditing", at = @At("HEAD")) private void skyblocker$finishEditing(CallbackInfo ci) { - //if the sign is being used to enter number get number from calculator for if maths has been done - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().uiAndVisuals.inputCalculator.enabled && Objects.equals(messages[1], "^^^^^^^^^^^^^^^")) { - boolean isPrice = messages[2].contains("price"); - String value = SignCalculator.getNewValue(isPrice); - if (value.length() >= 15) { - value = value.substring(0, 15); - } - messages[0] = value; + var config = SkyblockerConfigManager.get(); + if (Utils.isOnSkyblock()) { + //if the sign is being used to enter the speed cap, retrieve the value from speed presets. + if (messages[1].equals("^^^^^^") && config.general.speedPresets.enableSpeedPresets) { + var presets = SpeedPresets.getInstance(); + if (presets.hasPreset(messages[0])) { + messages[0] = String.valueOf(presets.getPreset(messages[0])); + } + } + //if the sign is being used to enter number get number from calculator for if maths has been done + if (messages[1].equals("^^^^^^^^^^^^^^^") && config.uiAndVisuals.inputCalculator.enabled) { + boolean isPrice = messages[2].contains("price"); + String value = SignCalculator.getNewValue(isPrice); + if (value.length() >= 15) { + value = value.substring(0, 15); + } + messages[0] = value; + } } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresetListWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresetListWidget.java new file mode 100644 index 00000000..bfe22a6c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresetListWidget.java @@ -0,0 +1,180 @@ +package de.hysky.skyblocker.skyblock.speedPreset; + +import it.unimi.dsi.fastutil.objects.ObjectIntPair; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.Selectable; +import net.minecraft.client.gui.widget.*; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class SpeedPresetListWidget extends ElementListWidget { + + private static final Pattern NUMBER = Pattern.compile("^-?\\d+(\\.\\d+)?$"); + // Alphanumeric sequence that doesn't start with a number. + private static final Pattern TITLE = Pattern.compile("^[a-zA-Z][a-zA-Z0-9_]*$"); + + public SpeedPresetListWidget(int width, int height, int y) { + super(MinecraftClient.getInstance(), width, height, y, 25); + var presets = SpeedPresets.getInstance(); + addEntry(new TitleEntry()); + if (presets.getPresetCount() > 0) + presets.forEach((title, speed) -> + this.addEntry(new SpeedPresetEntry(title, String.valueOf(speed)))); + else + this.addEntry(new SpeedPresetEntry("", "")); + } + + @Override + public int getRowWidth() { + return super.getRowWidth() + 104; + } + + public boolean hasBeenChanged() { + var presets = SpeedPresets.getInstance(); + // If there are fewer children than presets, some were removed, and all further checks are pointless + if (children().size() < presets.getPresetCount()) return true; + var childrenMap = this.children().stream() + .filter(SpeedPresetEntry.class::isInstance) + .map(SpeedPresetEntry.class::cast) + .map(SpeedPresetEntry::getMapping) + .filter(Objects::nonNull) + .collect(Collectors.toMap(ObjectIntPair::key, ObjectIntPair::valueInt)); + return !presets.arePresetsEqual(childrenMap); + } + + public void updatePosition() { + children().forEach(AbstractEntry::updatePosition); + } + + public void newEntry() { + var entry = new SpeedPresetEntry("", ""); + this.addEntry(entry); + this.centerScrollOn(entry); + this.setSelected(entry); + this.setFocused(entry); + } + + public void save() { + var presets = SpeedPresets.getInstance(); + presets.clear(); + children().stream().filter(SpeedPresetEntry.class::isInstance).map(SpeedPresetEntry.class::cast).forEach(SpeedPresetEntry::save); + presets.savePresets(); // Write down the changes. + } + + public abstract static class AbstractEntry extends ElementListWidget.Entry { + + protected void updatePosition() {} + + @Override + public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + this.children().forEach(child -> { + if (child instanceof Widget widget) + widget.setY(y); + if (child instanceof Drawable drawable) + drawable.render(context, mouseX, mouseY, tickDelta); + }); + } + } + + public class TitleEntry extends AbstractEntry { + + @Override + public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + // The line height is 25, the height of a single character is always 9. + // 25 - 9 = 16, 16 / 2 = 8, therefore the Y-offset should be 8. + context.drawCenteredTextWithShadow(client.textRenderer, Text.translatable("skyblocker.config.general.speedPresets.config.title"), width / 2 - 50, y + 8, 0xFFFFFF); + context.drawCenteredTextWithShadow(client.textRenderer, Text.translatable("skyblocker.config.general.speedPresets.config.speed"), width / 2 + 50, y + 8, 0xFFFFFF); + } + + @Override + public List selectableChildren() { + return List.of(); + } + + @Override + public List children() { + return List.of(); + } + } + + public class SpeedPresetEntry extends AbstractEntry { + + protected final TextFieldWidget titleInput; + protected final TextFieldWidget speedInput; + protected final ButtonWidget removeButton; + + public SpeedPresetEntry(String title, String speed) { + var client = SpeedPresetListWidget.this.client; + + // All Xs and Ys are then set using the initPosition() method. + this.titleInput = new TextFieldWidget(client.textRenderer, 0, 0, 120, 20, Text.empty()); + this.titleInput.setTextPredicate(str -> str.isEmpty() || TITLE.matcher(str).matches()); + this.titleInput.setText(title); + this.titleInput.setMaxLength(16); + this.titleInput.setPlaceholder(Text.literal("newPreset").formatted(Formatting.DARK_GRAY)); + this.speedInput = new TextFieldWidget(client.textRenderer, 0, 0, 50, 20, Text.empty()); + + this.speedInput.setTextPredicate(str -> str.isEmpty() || NUMBER.matcher(str).matches()); + this.speedInput.setText(speed); + this.speedInput.setMaxLength(3); + this.speedInput.setPlaceholder(Text.literal("0").formatted(Formatting.DARK_GRAY)); + + this.removeButton = ButtonWidget.builder(Text.literal("-"), + (btn) -> SpeedPresetListWidget.this.removeEntry(this)) + .dimensions(0, 0, 20, 20) + .build(); + + this.updatePosition(); + } + + @Override + public List selectableChildren() { + return List.of(titleInput, speedInput, removeButton); + } + + @Override + public List children() { + return List.of(titleInput, speedInput, removeButton); + } + + public void save() { + var mapping = getMapping(); + if (mapping != null) + SpeedPresets.getInstance().setPreset(mapping.key(), mapping.valueInt()); + } + + protected boolean isEmpty() { + return titleInput.getText().isEmpty() && speedInput.getText().isEmpty(); + } + + @Override + protected void updatePosition() { + var grid = new GridWidget(); + grid.setSpacing(2); + grid.add(titleInput, 0, 0, 1, 3); + grid.add(speedInput, 0, 3, 1, 2); + grid.add(removeButton, 0, 5, 1, 1); + grid.refreshPositions(); + SimplePositioningWidget.setPos(grid, 0, 0, width, itemHeight, 0.5f, 0.5f); + } + + @Nullable + protected ObjectIntPair getMapping() { + if (isEmpty()) return null; + try { + return ObjectIntPair.of(titleInput.getText(), Integer.parseInt(speedInput.getText())); + } catch (NumberFormatException e) { + return null; + } + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresets.java b/src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresets.java new file mode 100644 index 00000000..b3af7c41 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresets.java @@ -0,0 +1,146 @@ +package de.hysky.skyblocker.skyblock.speedPreset; + +import com.google.common.io.Files; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.serialization.Codec; +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.utils.Utils; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.client.message.v1.ClientSendMessageEvents; +import net.minecraft.command.CommandSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.regex.Pattern; + +public class SpeedPresets { + + private static final Pattern COMMAND_PATTERN = Pattern.compile("^setmaxspeed\\s([a-zA-Z][a-zA-Z0-9_]*)$"); + + private static final Logger LOGGER = LoggerFactory.getLogger(SpeedPresets.class); + private static final Codec> MAP_CODEC = Codec.unboundedMap(Codec.STRING, Codec.INT); + private static final File PRESETS_FILE = new File(SkyblockerMod.CONFIG_DIR.toFile(), "speed_presets.json"); + + private static SpeedPresets instance; + + private final Object2IntMap presets; + + private SpeedPresets() { + this.presets = new Object2IntOpenHashMap<>(); + this.loadPresets(); + } + + public static SpeedPresets getInstance() { + return instance == null ? instance = new SpeedPresets() : instance; + } + + public static CommandNode getCommandNode() { + return ClientCommandManager.literal("setmaxspeed") + .requires(source -> Utils.isOnSkyblock()) + .then(ClientCommandManager.argument("preset", StringArgumentType.string()) + .suggests((ctx, builder) -> { + if (SkyblockerConfigManager.get().general.speedPresets.enableSpeedPresets) { + return CommandSource.suggestMatching(getInstance().presets.keySet(), builder); + } + return builder.buildFuture(); + })).build(); + } + + @Init + public static void init() { + ClientSendMessageEvents.MODIFY_COMMAND.register((command) -> { + var matcher = COMMAND_PATTERN.matcher(command); + if (matcher.matches() && SkyblockerConfigManager.get().general.speedPresets.enableSpeedPresets) { + var presets = getInstance(); + var preset = matcher.group(1); + if (presets.presets.containsKey(preset)) { + return String.format("setmaxspeed %d", presets.getPreset(preset)); + } + } + return command; + }); + } + + public void clear() { + this.presets.clear(); + } + + public boolean hasPreset(String name) { + return this.presets.containsKey(name); + } + + public int getPreset(String name) { + return this.presets.getOrDefault(name, 0); + } + + public void setPreset(String name, int value) { + this.presets.put(name, value); + savePresets(); + } + + public void forEach(BiConsumer consumer) { + this.presets.forEach(consumer); + } + + public boolean arePresetsEqual(Map presets) { + return this.presets.equals(presets); + } + + public int getPresetCount() { + return this.presets.size(); + } + + public void loadPresets() { + try (var reader = Files.newReader(PRESETS_FILE, StandardCharsets.UTF_8)) { + var element = JsonParser.parseReader(reader); + MAP_CODEC.parse(JsonOps.INSTANCE, element).resultOrPartial(LOGGER::error).ifPresent(this.presets::putAll); + } catch (FileNotFoundException e) { + LOGGER.warn("[Skyblocker Speed Presets] Couldn't find speed presets file, creating one automatically..."); + this.loadDefaults(); + this.savePresets(); + } catch (IOException e) { + LOGGER.error("[Skyblocker Speed Presets] Couldn't load speed presets", e); + } + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + public void savePresets() { + try { + if (!PRESETS_FILE.exists()) PRESETS_FILE.createNewFile(); + try (var writer = Files.newWriter(PRESETS_FILE, StandardCharsets.UTF_8)) { + var element = MAP_CODEC.encodeStart(JsonOps.INSTANCE, this.presets).resultOrPartial(LOGGER::error) + .orElse(new JsonObject()); + writer.write(SkyblockerMod.GSON.toJson(element) + "\n"); + } + } catch (IOException e) { + LOGGER.error("[Skyblocker Speed Presets] Couldn't create speed presets file", e); + } + } + + // According to: https://www.reddit.com/r/HypixelSkyblock/comments/14kkz07/speed_vs_farming_fortune/ + public void loadDefaults() { + this.presets.clear(); + this.presets.put("default", 100); + this.presets.put("crops", 93); + this.presets.put("cocoa", 155); + this.presets.put("mushroom", 233); + this.presets.put("cane", 327); + this.presets.put("squash", 327); + this.presets.put("cactus", 464); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresetsScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresetsScreen.java new file mode 100644 index 00000000..2f7d462b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresetsScreen.java @@ -0,0 +1,75 @@ +package de.hysky.skyblocker.skyblock.speedPreset; + +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ConfirmScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.*; +import net.minecraft.screen.ScreenTexts; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class SpeedPresetsScreen extends Screen { + + protected final Screen parent; + protected SpeedPresetListWidget list; + + public SpeedPresetsScreen(Screen parent) { + super(Text.translatable("skyblocker.config.general.speedPresets.config")); + this.parent = parent; + } + + @Override + protected void init() { + if (this.list == null) + this.list = new SpeedPresetListWidget(0, 0, 24); + this.list.setDimensions(this.width, this.height - 24 - 32); + this.list.updatePosition(); + this.addDrawableChild(this.list); + + var grid = new GridWidget(); + grid.setSpacing(4); + var doneButton = ButtonWidget.builder(ScreenTexts.DONE, + button -> { + this.list.save(); + assert this.client != null; + this.client.setScreen(parent); + }) + .width(Math.max(textRenderer.getWidth(ScreenTexts.DONE) + 8, 100)) + .build(); + grid.add(doneButton, 0, 0, 1, 2); + var plusButton = ButtonWidget.builder(Text.literal("+"), + button -> list.newEntry()) + .width(20) + .build(); + grid.add(plusButton, 0, 2, 1, 1); + grid.refreshPositions(); + SimplePositioningWidget.setPos(grid, 0, this.height - 24, this.width, 24, 0.5f, 0.5f); + grid.forEachChild(this::addDrawableChild); + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + super.render(context, mouseX, mouseY, delta); + assert this.client != null; + var renderer = this.client.textRenderer; + context.drawCenteredTextWithShadow(renderer, this.title, this.width / 2, + 8, 0xFFFFFF); + } + + @Override + public void close() { + assert this.client != null; + if (this.list.hasBeenChanged()) { + client.setScreen(new ConfirmScreen(confirmedAction -> { + if (confirmedAction) { + this.client.setScreen(parent); + } else { + this.client.setScreen(this); + } + }, Text.translatable("text.skyblocker.quit_config"), Text.translatable("text.skyblocker.quit_config_sure"), Text.translatable("text.skyblocker.quit_discard") + .formatted(Formatting.RED), ScreenTexts.CANCEL)); + return; + } + this.client.setScreen(parent); + } +} -- cgit