aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java18
-rw-r--r--src/main/java/de/hysky/skyblocker/config/configs/GeneralConfig.java9
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/CommandTreeS2CPacketMixin.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java11
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/SignEditScreenMixin.java59
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresetListWidget.java180
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresets.java146
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPresetsScreen.java75
8 files changed, 476 insertions, 24 deletions
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.<Boolean>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<String, CustomArmorAnimatedDyes.AnimatedDye> 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<? extends CommandSource> modifyCommandSuggestions(CommandNode<CommandSource> 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;
@@ -64,9 +62,6 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
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<String> FILLER_ITEMS = Set.of(
" ", // Empty menu item
"Locked Page",
@@ -335,10 +330,10 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> 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<SpeedPresetListWidget.AbstractEntry> {
+
+ 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<AbstractEntry> {
+
+ 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<? extends Selectable> selectableChildren() {
+ return List.of();
+ }
+
+ @Override
+ public List<? extends Element> 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<? extends Selectable> selectableChildren() {
+ return List.of(titleInput, speedInput, removeButton);
+ }
+
+ @Override
+ public List<? extends Element> 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<String> 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<String, Integer>> 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<String> presets;
+
+ private SpeedPresets() {
+ this.presets = new Object2IntOpenHashMap<>();
+ this.loadPresets();
+ }
+
+ public static SpeedPresets getInstance() {
+ return instance == null ? instance = new SpeedPresets() : instance;
+ }
+
+ public static CommandNode<FabricClientCommandSource> 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<String, Integer> consumer) {
+ this.presets.forEach(consumer);
+ }
+
+ public boolean arePresetsEqual(Map<String, Integer> 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);
+ }
+}