Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Speed Presets for configurable speed settings #1111

Merged
merged 18 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2ac58b9
Add Speed Presets for configurable speed settings
Dec 28, 2024
9682f4a
Reverted the proposed changes to the protected item texture
Dec 29, 2024
919f9b8
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
50774d4
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
4dd9fb6
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
1aa8859
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
d36930b
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
617084c
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
947c21b
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
5c4d6ee
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
6a4b474
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
ed4cfdc
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
ab8f7ce
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
1c78583
Update src/main/java/de/hysky/skyblocker/skyblock/speedPreset/SpeedPr…
Manchick0 Dec 29, 2024
43d08ea
Refactor modification checks for SpeedPreset entries.
Dec 29, 2024
010b328
Clean up pointless changes
Dec 29, 2024
ea4efb8
Refactor SpeedPresets to use Object2IntMap.
Dec 29, 2024
5332083
Refactor SpeedPresets
kevinthegreat1 Dec 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.<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"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public class GeneralConfig {
@SerialEntry
public boolean acceptReparty = true;

@SerialEntry
public SpeedPresets speedPresets = new SpeedPresets();

@SerialEntry
public Shortcuts shortcuts = new Shortcuts();

Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
11 changes: 3 additions & 8 deletions src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -63,9 +61,6 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> 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<String> FILLER_ITEMS = Set.of(
" ", // Empty menu item
Expand Down Expand Up @@ -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
Expand Down
59 changes: 43 additions & 16 deletions src/main/java/de/hysky/skyblocker/mixins/SignEditScreenMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic has some problems, where an entry changed and then changed back to the original would still be marked as changed. In general, you need a validation method that checks if all entries are the same. I'd suggest looking at ShortcutsConfigListWidget#hasChanges.

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;
}
}
}
}
Loading
Loading