Skip to content

Commit

Permalink
Add Speed Presets for configurable speed settings (#1111)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* 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 <[email protected]>
  • Loading branch information
Manchick0 and kevinthegreat1 authored Dec 30, 2024
1 parent 145ba09 commit 19a1df6
Show file tree
Hide file tree
Showing 9 changed files with 482 additions and 24 deletions.
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() {
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

0 comments on commit 19a1df6

Please sign in to comment.