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 b041a9fe74..944e9d2ec7 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.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 static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .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 912dd76958..bb66625a5d 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 b96d5a5f83..83605ca95b 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 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 c833d06c5d..d092de6e2e 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 @@ 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.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 @@ protected HandledScreenMixin(Text title) { 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 04342f3793..14769a7618 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 @@ 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 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 0000000000..bfe22a6c97 --- /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 0000000000..b3af7c413c --- /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 0000000000..2f7d462be7 --- /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); + } +} diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index f1575d0280..8181ae1c63 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -332,6 +332,12 @@ "skyblocker.config.general.searchOverlay.maxPet": "Max Pet Level", "skyblocker.config.general.searchOverlay.maxPet.@Tooltip": "Only show pets that are max level", + "skyblocker.config.general.speedPresets": "Speed Presets", + "skyblocker.config.general.speedPresets.config": "Speed Presets Config...", + "skyblocker.config.general.speedPresets.config.title": "Title", + "skyblocker.config.general.speedPresets.config.speed": "Speed", + "skyblocker.config.general.speedPresets.enableSpeedPresets": "Enable Speed Presets", + "skyblocker.config.general.shortcuts": "Shortcuts", "skyblocker.config.general.shortcuts.config": "Shortcuts Config...", "skyblocker.config.general.shortcuts.enableCommandArgShortcuts": "Enable Command Argument Shortcuts",