diff options
Diffstat (limited to 'src/main/java/me')
23 files changed, 507 insertions, 111 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java index 07a5be76..6f4276e9 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java +++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java @@ -4,6 +4,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; import me.xmrvizzy.skyblocker.skyblock.*; +import me.xmrvizzy.skyblocker.skyblock.item.ItemCooldowns; import me.xmrvizzy.skyblocker.skyblock.dungeon.*; import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud; @@ -78,6 +79,7 @@ public class SkyblockerMod implements ClientModInitializer { Relics.init(); BackpackPreview.init(); QuickNav.init(); + ItemCooldowns.init(); DwarvenHud.init(); ChatMessageListener.init(); Shortcuts.init(); @@ -101,6 +103,7 @@ public class SkyblockerMod implements ClientModInitializer { QuiverWarning.init(); SpecialEffects.init(); ItemProtection.init(); + ItemRarityBackgrounds.init(); containerSolverManager.init(); statusBarTracker.init(); Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20); diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java b/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java index 28ed704b..552ed091 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java @@ -3,9 +3,17 @@ package me.xmrvizzy.skyblocker.config; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.BooleanControllerBuilder; import dev.isxander.yacl3.api.controller.EnumControllerBuilder; -import me.xmrvizzy.skyblocker.config.controllers.EnumDropdownControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.apache.commons.lang3.StringUtils; + +import java.util.function.Function; public class ConfigUtils { + public static final Function<Formatting, String> FORMATTING_TO_STRING = formatting -> StringUtils.capitalize(formatting.getName().replaceAll("_", " ")); + public static final ValueFormatter<Float> FLOAT_TWO_FORMATTER = value -> Text.literal(String.format("%,.2f", value).replaceAll("[\u00a0\u202F]", " ")); + public static BooleanControllerBuilder createBooleanController(Option<Boolean> opt) { return BooleanControllerBuilder.create(opt).yesNoFormatter().coloured(true); } @@ -14,8 +22,4 @@ public class ConfigUtils { public static <E extends Enum<E>> EnumControllerBuilder<E> createEnumCyclingListController(Option<E> opt) { return EnumControllerBuilder.create(opt).enumClass((Class<E>) opt.binding().defaultValue().getClass()); } - - public static <E extends Enum<E>> EnumDropdownControllerBuilder<E> createEnumDropdownController(Option<E> opt) { - return EnumDropdownControllerBuilder.create(opt); - } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java index e40c3f21..8e014124 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java @@ -1,10 +1,5 @@ package me.xmrvizzy.skyblocker.config; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - import dev.isxander.yacl3.config.v2.api.SerialEntry; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -15,6 +10,9 @@ import net.minecraft.client.resource.language.I18n; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import java.util.ArrayList; +import java.util.List; + public class SkyblockerConfig { @SerialEntry public int version = 1; @@ -179,6 +177,9 @@ public class SkyblockerConfig { public FairySouls fairySouls = new FairySouls(); @SerialEntry + public ItemCooldown itemCooldown = new ItemCooldown(); + + @SerialEntry public Shortcuts shortcuts = new Shortcuts(); @SerialEntry @@ -315,6 +316,11 @@ public class SkyblockerConfig { public boolean highlightOnlyNearbySouls = false; } + public static class ItemCooldown { + @SerialEntry + public boolean enableItemCooldowns = true; + } + public static class Shortcuts { @SerialEntry public boolean enableShortcuts = true; @@ -461,6 +467,12 @@ public class SkyblockerConfig { public static class ItemInfoDisplay { @SerialEntry public boolean attributeShardInfo = true; + + @SerialEntry + public boolean itemRarityBackgrounds = false; + + @SerialEntry + public float itemRarityBackgroundsOpacity = 1f; } public static class SpecialEffects { @@ -581,55 +593,18 @@ public class SkyblockerConfig { public int neutralThreshold = 1000; @SerialEntry - public FormattingOption neutralColor = FormattingOption.DARK_GRAY; + public Formatting neutralColor = Formatting.DARK_GRAY; @SerialEntry - public FormattingOption profitColor = FormattingOption.DARK_GREEN; + public Formatting profitColor = Formatting.DARK_GREEN; @SerialEntry - public FormattingOption lossColor = FormattingOption.RED; + public Formatting lossColor = Formatting.RED; @SerialEntry - public FormattingOption incompleteColor = FormattingOption.BLUE; + public Formatting incompleteColor = Formatting.BLUE; } - - public enum FormattingOption { - BLACK(Formatting.BLACK), - DARK_BLUE(Formatting.DARK_BLUE), - DARK_GREEN(Formatting.DARK_GREEN), - DARK_AQUA(Formatting.DARK_AQUA), - DARK_RED(Formatting.DARK_RED), - DARK_PURPLE(Formatting.DARK_PURPLE), - GOLD(Formatting.GOLD), - GRAY(Formatting.GRAY), - DARK_GRAY(Formatting.DARK_GRAY), - BLUE(Formatting.BLUE), - GREEN(Formatting.GREEN), - AQUA(Formatting.AQUA), - RED(Formatting.RED), - LIGHT_PURPLE(Formatting.LIGHT_PURPLE), - YELLOW(Formatting.YELLOW), - WHITE(Formatting.WHITE), - OBFUSCATED(Formatting.OBFUSCATED), - BOLD(Formatting.BOLD), - STRIKETHROUGH(Formatting.STRIKETHROUGH), - UNDERLINE(Formatting.UNDERLINE), - ITALIC(Formatting.ITALIC), - RESET(Formatting.RESET); - - public final Formatting formatting; - - - FormattingOption(Formatting formatting) { - this.formatting = formatting; - } - - @Override - public String toString() { - return StringUtils.capitalize(formatting.getName().replaceAll("_", " ")); - } - } public static class LividColor { @SerialEntry diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java index 16439cb5..2d641ac1 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java @@ -10,11 +10,12 @@ import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; import dev.isxander.yacl3.api.controller.StringControllerBuilder; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig.FormattingOption; import me.xmrvizzy.skyblocker.config.ConfigUtils; +import me.xmrvizzy.skyblocker.config.controllers.EnumDropdownControllerBuilder; import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMapConfigScreen; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; public class DungeonsCategory { @@ -150,34 +151,34 @@ public class DungeonsCategory { newValue -> config.locations.dungeons.dungeonChestProfit.neutralThreshold = newValue) .controller(IntegerFieldControllerBuilder::create) .build()) - .option(Option.<FormattingOption>createBuilder() + .option(Option.<Formatting>createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralColor")) .binding(defaults.locations.dungeons.dungeonChestProfit.neutralColor, () -> config.locations.dungeons.dungeonChestProfit.neutralColor, newValue -> config.locations.dungeons.dungeonChestProfit.neutralColor = newValue) - .controller(ConfigUtils::createEnumDropdownController) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) .build()) - .option(Option.<FormattingOption>createBuilder() + .option(Option.<Formatting>createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.profitColor")) .binding(defaults.locations.dungeons.dungeonChestProfit.profitColor, () -> config.locations.dungeons.dungeonChestProfit.profitColor, newValue -> config.locations.dungeons.dungeonChestProfit.profitColor = newValue) - .controller(ConfigUtils::createEnumDropdownController) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) .build()) - .option(Option.<FormattingOption>createBuilder() + .option(Option.<Formatting>createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.lossColor")) .binding(defaults.locations.dungeons.dungeonChestProfit.lossColor, () -> config.locations.dungeons.dungeonChestProfit.lossColor, newValue -> config.locations.dungeons.dungeonChestProfit.lossColor = newValue) - .controller(ConfigUtils::createEnumDropdownController) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) .build()) - .option(Option.<FormattingOption>createBuilder() + .option(Option.<Formatting>createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.incompleteColor")) .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.incompleteColor.@Tooltip"))) .binding(defaults.locations.dungeons.dungeonChestProfit.incompleteColor, () -> config.locations.dungeons.dungeonChestProfit.incompleteColor, newValue -> config.locations.dungeons.dungeonChestProfit.incompleteColor = newValue) - .controller(ConfigUtils::createEnumDropdownController) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) .build()) .build()) diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java index c5e2d34c..318d579c 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java @@ -1,15 +1,12 @@ package me.xmrvizzy.skyblocker.config.categories; -import dev.isxander.yacl3.api.ButtonOption; -import dev.isxander.yacl3.api.ConfigCategory; -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.OptionDescription; -import dev.isxander.yacl3.api.OptionGroup; +import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder; import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.config.ConfigUtils; +import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.skyblock.shortcut.ShortcutsConfigScreen; import me.xmrvizzy.skyblocker.utils.render.title.TitleContainerConfigScreen; import net.minecraft.client.MinecraftClient; @@ -17,10 +14,10 @@ import net.minecraft.text.Text; public class GeneralCategory { - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { return ConfigCategory.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.category.general")) - + //Ungrouped Options .option(Option.<Boolean>createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.acceptReparty")) @@ -57,7 +54,7 @@ public class GeneralCategory { newValue -> config.general.hideStatusEffectOverlay = newValue) .controller(ConfigUtils::createBooleanController) .build()) - + //Tab Hud .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud")) @@ -94,7 +91,7 @@ public class GeneralCategory { .controller(ConfigUtils::createEnumCyclingListController) .build()) .build()) - + //Fancy Bars .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars")) @@ -135,7 +132,7 @@ public class GeneralCategory { .controller(ConfigUtils::createEnumCyclingListController) .build()) .build()) - + //Experiments Solver .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.experiments")) @@ -162,7 +159,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Fishing Helper .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.fishing")) @@ -175,7 +172,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Fairy Souls Helper .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls")) @@ -203,7 +200,20 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + + //Item Cooldown + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemCooldown")) + .collapsed(true) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemCooldown.enableItemCooldowns")) + .binding(defaults.general.itemCooldown.enableItemCooldowns, + () -> config.general.itemCooldown.enableItemCooldowns, + newValue -> config.general.itemCooldown.enableItemCooldowns = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + //Shortcuts .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts")) @@ -238,7 +248,7 @@ public class GeneralCategory { .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new ShortcutsConfigScreen(screen))) .build()) .build()) - + //Quiver Warning .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.quiverWarning")) @@ -265,7 +275,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Item List .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemList")) @@ -278,7 +288,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Item Tooltip .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip")) @@ -335,7 +345,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Item Info Display .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay")) @@ -348,8 +358,23 @@ public class GeneralCategory { newValue -> config.general.itemInfoDisplay.attributeShardInfo = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds.@Tooltip"))) + .binding(defaults.general.itemInfoDisplay.itemRarityBackgrounds, + () -> config.general.itemInfoDisplay.itemRarityBackgrounds, + newValue -> config.general.itemInfoDisplay.itemRarityBackgrounds = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Float>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgroundsOpacity")) + .binding(defaults.general.itemInfoDisplay.itemRarityBackgroundsOpacity, + () -> config.general.itemInfoDisplay.itemRarityBackgroundsOpacity, + newValue -> config.general.itemInfoDisplay.itemRarityBackgroundsOpacity = newValue) + .controller(opt -> FloatSliderControllerBuilder.create(opt).range(0f, 1f).step(0.05f).formatValue(ConfigUtils.FLOAT_TWO_FORMATTER)) + .build()) .build()) - + //Special Effects .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.specialEffects")) @@ -363,7 +388,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Hitboxes .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.hitbox")) @@ -383,7 +408,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Title Container .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer")) @@ -430,7 +455,7 @@ public class GeneralCategory { .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new TitleContainerConfigScreen(screen))) .build()) .build()) - + //Teleport Overlays .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay")) diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java index cf40c7d5..6db0028c 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java @@ -8,16 +8,23 @@ import dev.isxander.yacl3.gui.controllers.dropdown.AbstractDropdownController; import org.jetbrains.annotations.NotNull; import java.util.Arrays; +import java.util.function.Function; import java.util.stream.Stream; public class EnumDropdownController<E extends Enum<E>> extends AbstractDropdownController<E> { - protected EnumDropdownController(Option<E> option) { + /** + * The function used to convert enum constants to strings used for display, suggestion, and validation. Defaults to {@link Enum#toString}. + */ + protected final Function<E, String> toString; + + protected EnumDropdownController(Option<E> option, Function<E, String> toString) { super(option); + this.toString = toString; } @Override public String getString() { - return option().pendingValue().toString(); + return toString.apply(option().pendingValue()); } @Override @@ -26,15 +33,15 @@ public class EnumDropdownController<E extends Enum<E>> extends AbstractDropdownC } /** - * Searches through enum constants for one whose {@link Enum#toString()} result equals {@code value} + * Searches through enum constants for one whose {@link #toString} result equals {@code value} * * @return The enum constant associated with the {@code value} or the pending value if none are found - * @implNote The return value of {@link Enum#toString()} on each enum constant should be unique in order to ensure accuracy + * @implNote The return value of {@link #toString} on each enum constant should be unique in order to ensure accuracy */ private E getEnumFromString(String value) { value = value.toLowerCase(); for (E constant : option().pendingValue().getDeclaringClass().getEnumConstants()) { - if (constant.toString().toLowerCase().equals(value)) return constant; + if (toString.apply(constant).toLowerCase().equals(value)) return constant; } return option().pendingValue(); @@ -44,7 +51,7 @@ public class EnumDropdownController<E extends Enum<E>> extends AbstractDropdownC public boolean isValueValid(String value) { value = value.toLowerCase(); for (E constant : option().pendingValue().getDeclaringClass().getEnumConstants()) { - if (constant.toString().equals(value)) return true; + if (toString.apply(constant).equals(value)) return true; } return false; @@ -59,16 +66,16 @@ public class EnumDropdownController<E extends Enum<E>> extends AbstractDropdownC } /** - * Filters and sorts through enum constants for those whose {@link Enum#toString()} result equals {@code value} + * Filters and sorts through enum constants for those whose {@link #toString} result equals {@code value} * * @return a sorted stream containing enum constants associated with the {@code value} - * @implNote The return value of {@link Enum#toString()} on each enum constant should be unique in order to ensure accuracy + * @implNote The return value of {@link #toString} on each enum constant should be unique in order to ensure accuracy */ @NotNull protected Stream<String> getValidEnumConstants(String value) { String valueLowerCase = value.toLowerCase(); return Arrays.stream(option().pendingValue().getDeclaringClass().getEnumConstants()) - .map(Enum::toString) + .map(this.toString) .filter(constant -> constant.toLowerCase().contains(valueLowerCase)) .sorted((s1, s2) -> { String s1LowerCase = s1.toLowerCase(); diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java index baadb8b3..a17f58d6 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java @@ -3,8 +3,25 @@ package me.xmrvizzy.skyblocker.config.controllers; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ControllerBuilder; +import java.util.function.Function; + public interface EnumDropdownControllerBuilder<E extends Enum<E>> extends ControllerBuilder<E> { + EnumDropdownControllerBuilder<E> toString(Function<E, String> toString); + static <E extends Enum<E>> EnumDropdownControllerBuilder<E> create(Option<E> option) { return new EnumDropdownControllerBuilderImpl<>(option); } + + /** + * Creates a factory for {@link EnumDropdownControllerBuilder}s with the given function for converting enum constants to strings. + * Use this if a custom toString function for an enum is needed. + * Use it like this: + * <pre>{@code Option.<MyEnum>createBuilder().controller(createEnumDropdownControllerBuilder.getFactory(MY_CUSTOM_ENUM_TO_STRING_FUNCTION))}</pre> + * @param toString The function used to convert enum constants to strings used for display, suggestion, and validation + * @return a factory for {@link EnumDropdownControllerBuilder}s + * @param <E> the enum type + */ + static <E extends Enum<E>> Function<Option<E>, ControllerBuilder<E>> getFactory(Function<E, String> toString) { + return opt -> EnumDropdownControllerBuilder.create(opt).toString(toString); + } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java index ea30d1da..27878c86 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java @@ -4,14 +4,24 @@ import dev.isxander.yacl3.api.Controller; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.impl.controller.AbstractControllerBuilderImpl; +import java.util.function.Function; + public class EnumDropdownControllerBuilderImpl<E extends Enum<E>> extends AbstractControllerBuilderImpl<E> implements EnumDropdownControllerBuilder<E> { + private Function<E, String> toString = Enum::toString; + public EnumDropdownControllerBuilderImpl(Option<E> option) { super(option); } + @Override + public EnumDropdownControllerBuilder<E> toString(Function<E, String> toString) { + this.toString = toString; + return this; + } + @SuppressWarnings("UnstableApiUsage") @Override public Controller<E> build() { - return new EnumDropdownController<>(option); + return new EnumDropdownController<>(option, toString); } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java b/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java new file mode 100644 index 00000000..76298612 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java @@ -0,0 +1,23 @@ +package me.xmrvizzy.skyblocker.events; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.block.BlockState; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +// Fabric API currently doesn't have an event for this +public class ClientPlayerBlockBreakEvent { + public static final Event<AfterBlockBreak> AFTER = EventFactory.createArrayBacked(AfterBlockBreak.class, + (listeners) -> (world, player, pos, state) -> { + for (AfterBlockBreak listener : listeners) { + listener.afterBlockBreak(world, player, pos, state); + } + }); + + @FunctionalInterface + public interface AfterBlockBreak { + void afterBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state); + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/SkyblockEvents.java b/src/main/java/me/xmrvizzy/skyblocker/events/SkyblockEvents.java index 2dd83ffa..477d68b0 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/SkyblockEvents.java +++ b/src/main/java/me/xmrvizzy/skyblocker/events/SkyblockEvents.java @@ -1,4 +1,4 @@ -package me.xmrvizzy.skyblocker.utils; +package me.xmrvizzy.skyblocker.events; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java new file mode 100644 index 00000000..3963f9d3 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java @@ -0,0 +1,27 @@ +package me.xmrvizzy.skyblocker.mixin; + +import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; +import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ClientPlayerInteractionManager.class) +public class ClientPlayerInteractionManagerMixin { + @Shadow + @Final + private MinecraftClient client; + + @Inject(method = "breakBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;onBroken(Lnet/minecraft/world/WorldAccess;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V"), locals = LocalCapture.CAPTURE_FAILHARD) + private void skyblocker$onBlockBroken(BlockPos pos, CallbackInfoReturnable<Boolean> cir, World world, BlockState blockState) { + ClientPlayerBlockBreakEvent.AFTER.invoker().afterBlockBreak(world, this.client.player, pos, blockState); + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java index 2cb83e87..257a5127 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java @@ -1,12 +1,13 @@ package me.xmrvizzy.skyblocker.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.ref.LocalRef; - import dev.cbyrne.betterinject.annotations.Arg; import dev.cbyrne.betterinject.annotations.Inject; import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; import me.xmrvizzy.skyblocker.skyblock.item.AttributeShards; +import me.xmrvizzy.skyblocker.skyblock.item.ItemCooldowns; import me.xmrvizzy.skyblocker.utils.ItemUtils; import me.xmrvizzy.skyblocker.utils.Utils; import net.minecraft.client.font.TextRenderer; @@ -15,7 +16,6 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.util.Formatting; - import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -64,4 +64,10 @@ public abstract class DrawContextMixin { } } } -}
\ No newline at end of file + + @ModifyExpressionValue(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/ItemCooldownManager;getCooldownProgress(Lnet/minecraft/item/Item;F)F")) + private float skyblocker$modifyItemCooldown(float cooldownProgress, @Local ItemStack stack) { + return Utils.isOnSkyblock() && ItemCooldowns.isOnCooldown(stack) ? ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent() : cooldownProgress; + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java index 9ba2107b..7e94d660 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java @@ -9,6 +9,7 @@ import me.xmrvizzy.skyblocker.skyblock.experiment.UltrasequencerSolver; import me.xmrvizzy.skyblocker.skyblock.item.BackpackPreview; import me.xmrvizzy.skyblocker.skyblock.item.CompactorDeletorPreview; import me.xmrvizzy.skyblocker.skyblock.item.ItemProtection; +import me.xmrvizzy.skyblocker.skyblock.item.ItemRarityBackgrounds; import me.xmrvizzy.skyblocker.skyblock.item.WikiLookup; import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; import me.xmrvizzy.skyblocker.utils.Utils; @@ -184,4 +185,9 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen private static boolean skyblocker$doesLoreContain(ItemStack stack, MinecraftClient client, String searchString) { return stack.getTooltip(client.player, TooltipContext.BASIC).stream().map(Text::getString).anyMatch(line -> line.contains(searchString)); } + + @Inject(method = "drawSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawItem(Lnet/minecraft/item/ItemStack;III)V")) + private void skyblocker$drawItemRarityBackground(DrawContext context, Slot slot, CallbackInfo ci) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds) ItemRarityBackgrounds.tryDraw(slot.getStack(), context, slot.x, slot.y); + } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java index c73ca2aa..3376907b 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java @@ -1,17 +1,24 @@ package me.xmrvizzy.skyblocker.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; import me.xmrvizzy.skyblocker.SkyblockerMod; import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; import me.xmrvizzy.skyblocker.skyblock.FancyStatusBars; import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock; +import me.xmrvizzy.skyblocker.skyblock.item.ItemCooldowns; import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap; +import me.xmrvizzy.skyblocker.skyblock.item.ItemRarityBackgrounds; import me.xmrvizzy.skyblocker.utils.Utils; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -32,10 +39,15 @@ public abstract class InGameHudMixin { @Shadow private int scaledWidth; + @Shadow + @Final + private MinecraftClient client; + @Inject(method = "renderHotbar", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;renderHotbarItem(Lnet/minecraft/client/gui/DrawContext;IIFLnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/ItemStack;I)V", ordinal = 0)) - public void skyblocker$renderHotbarItemLock(float tickDelta, DrawContext context, CallbackInfo ci, @Local(ordinal = 4, name = "m") int index, @Local(ordinal = 5, name = "n") int x, @Local(ordinal = 6, name = "o") int y) { - if (Utils.isOnSkyblock() && HotbarSlotLock.isLocked(index)) { - context.drawTexture(SLOT_LOCK, x, y, 0, 0, 16, 16); + public void skyblocker$renderHotbarItemLockOrRarityBg(float tickDelta, DrawContext context, CallbackInfo ci, @Local(ordinal = 4, name = "m") int index, @Local(ordinal = 5, name = "n") int x, @Local(ordinal = 6, name = "o") int y, @Local PlayerEntity player) { + if (Utils.isOnSkyblock()) { + if (SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds) ItemRarityBackgrounds.tryDraw(player.getInventory().main.get(index), context, x, y); + if (HotbarSlotLock.isLocked(index)) context.drawTexture(SLOT_LOCK, x, y, 0, 0, 16, 16); } } @@ -61,9 +73,21 @@ public abstract class InGameHudMixin { if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.bars.enableBars && !Utils.isInTheRift()) ci.cancel(); } - + @Inject(method = "renderStatusEffectOverlay", at = @At("HEAD"), cancellable = true) private void skyblocker$dontRenderStatusEffects(CallbackInfo ci) { if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.hideStatusEffectOverlay) ci.cancel(); } -}
\ No newline at end of file + + @ModifyExpressionValue(method = "renderCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getAttackCooldownProgress(F)F")) + private float skyblocker$modifyAttackIndicatorCooldown(float cooldownProgress) { + if (Utils.isOnSkyblock() && client.player != null) { + ItemStack stack = client.player.getMainHandStack(); + if (ItemCooldowns.isOnCooldown(stack)) { + return ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent(); + } + } + + return cooldownProgress; + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java index 91be5248..ea54ff32 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java @@ -160,7 +160,7 @@ public class DungeonChestProfit { private static Text getProfitText(int profit, boolean hasIncompleteData) { SkyblockerConfig.DungeonChestProfit config = SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit; - return getProfitText(profit, hasIncompleteData, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting); + return getProfitText(profit, hasIncompleteData, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor); } static Text getProfitText(int profit, boolean hasIncompleteData, int neutralThreshold, Formatting neutralColor, Formatting profitColor, Formatting lossColor, Formatting incompleteColor) { diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java index dba066a5..6eb0623c 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java @@ -8,7 +8,7 @@ import dev.isxander.yacl3.config.v2.api.SerialEntry; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.SkyblockEvents; +import me.xmrvizzy.skyblocker.events.SkyblockEvents; import me.xmrvizzy.skyblocker.utils.Utils; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java new file mode 100644 index 00000000..dbe0c16e --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java @@ -0,0 +1,115 @@ +package me.xmrvizzy.skyblocker.skyblock.item; + +import com.google.common.collect.ImmutableList; +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; +import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; +import me.xmrvizzy.skyblocker.utils.ItemUtils; +import net.fabricmc.fabric.api.event.player.UseItemCallback; +import net.minecraft.block.BlockState; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.util.Hand; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import java.util.HashMap; +import java.util.Map; + +public class ItemCooldowns { + private static final String JUNGLE_AXE_ID = "JUNGLE_AXE"; + private static final String TREECAPITATOR_ID = "TREECAPITATOR_AXE"; + private static final String GRAPPLING_HOOK_ID = "GRAPPLING_HOOK"; + private static final ImmutableList<String> BAT_ARMOR_IDS = ImmutableList.of("BAT_PERSON_HELMET", "BAT_PERSON_CHESTPLATE", "BAT_PERSON_LEGGINGS", "BAT_PERSON_BOOTS"); + + private static final Map<String, CooldownEntry> ITEM_COOLDOWNS = new HashMap<>(); + + public static void init() { + ClientPlayerBlockBreakEvent.AFTER.register(ItemCooldowns::afterBlockBreak); + UseItemCallback.EVENT.register(ItemCooldowns::onItemInteract); + } + + public static void afterBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state) { + if (!SkyblockerConfigManager.get().general.itemCooldown.enableItemCooldowns) return; + + String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); + if (usedItemId == null) return; + + if (state.isIn(BlockTags.LOGS)) { + if (usedItemId.equals(JUNGLE_AXE_ID)) { + if (!isOnCooldown(JUNGLE_AXE_ID)) { + ITEM_COOLDOWNS.put(JUNGLE_AXE_ID, new CooldownEntry(2000)); + } + } else if (usedItemId.equals(TREECAPITATOR_ID)) { + if (!isOnCooldown(TREECAPITATOR_ID)) { + ITEM_COOLDOWNS.put(TREECAPITATOR_ID, new CooldownEntry(2000)); + } + } + } + } + + private static TypedActionResult<ItemStack> onItemInteract(PlayerEntity player, World world, Hand hand) { + if (!SkyblockerConfigManager.get().general.itemCooldown.enableItemCooldowns) return TypedActionResult.pass(ItemStack.EMPTY); + + String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); + if (usedItemId != null && usedItemId.equals(GRAPPLING_HOOK_ID) && player.fishHook != null) { + if (!isOnCooldown(GRAPPLING_HOOK_ID) && !isWearingBatArmor(player)) { + ITEM_COOLDOWNS.put(GRAPPLING_HOOK_ID, new CooldownEntry(2000)); + } + } + + return TypedActionResult.pass(ItemStack.EMPTY); + } + + public static boolean isOnCooldown(ItemStack itemStack) { + return isOnCooldown(ItemUtils.getItemId(itemStack)); + } + + private static boolean isOnCooldown(String itemId) { + if (ITEM_COOLDOWNS.containsKey(itemId)) { + CooldownEntry cooldownEntry = ITEM_COOLDOWNS.get(itemId); + if (cooldownEntry.isOnCooldown()) { + return true; + } else { + ITEM_COOLDOWNS.remove(itemId); + return false; + } + } + + return false; + } + + public static CooldownEntry getItemCooldownEntry(ItemStack itemStack) { + return ITEM_COOLDOWNS.get(ItemUtils.getItemId(itemStack)); + } + + private static boolean isWearingBatArmor(PlayerEntity player) { + for (ItemStack stack : player.getArmorItems()) { + String itemId = ItemUtils.getItemId(stack); + if (!BAT_ARMOR_IDS.contains(itemId)) { + return false; + } + } + return true; + } + + public record CooldownEntry(int cooldown, long startTime) { + public CooldownEntry(int cooldown) { + this(cooldown, System.currentTimeMillis()); + } + + public boolean isOnCooldown() { + return (this.startTime + this.cooldown) > System.currentTimeMillis(); + } + + public long getRemainingCooldown() { + long time = (this.startTime + this.cooldown) - System.currentTimeMillis(); + return Math.max(time, 0); + } + + public float getRemainingCooldownPercent() { + return this.isOnCooldown() ? (float) this.getRemainingCooldown() / cooldown : 0.0f; + } + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java new file mode 100644 index 00000000..837c209a --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java @@ -0,0 +1,109 @@ +package me.xmrvizzy.skyblocker.skyblock.item; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import com.google.common.collect.ImmutableMap; +import com.mojang.blaze3d.systems.RenderSystem; + +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; +import me.xmrvizzy.skyblocker.SkyblockerMod; +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; +import me.xmrvizzy.skyblocker.utils.Utils; +import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.item.TooltipContext; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.texture.Sprite; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class ItemRarityBackgrounds { + private static final Identifier RARITY_BG_TEX = new Identifier(SkyblockerMod.NAMESPACE, "item_rarity_background"); + private static final Supplier<Sprite> SPRITE = () -> MinecraftClient.getInstance().getGuiAtlasManager().getSprite(RARITY_BG_TEX); + private static final ImmutableMap<String, SkyblockItemRarity> LORE_RARITIES = ImmutableMap.ofEntries( + Map.entry("ADMIN", SkyblockItemRarity.ADMIN), + Map.entry("SPECIAL", SkyblockItemRarity.SPECIAL), //Very special is the same color so this will cover it + Map.entry("DIVINE", SkyblockItemRarity.DIVINE), + Map.entry("MYTHIC", SkyblockItemRarity.MYTHIC), + Map.entry("LEGENDARY", SkyblockItemRarity.LEGENDARY), + Map.entry("LEGENJERRY", SkyblockItemRarity.LEGENDARY), + Map.entry("EPIC", SkyblockItemRarity.EPIC), + Map.entry("RARE", SkyblockItemRarity.RARE), + Map.entry("UNCOMMON", SkyblockItemRarity.UNCOMMON), + Map.entry("COMMON", SkyblockItemRarity.COMMON) + ); + private static final Int2ReferenceOpenHashMap<SkyblockItemRarity> CACHE = new Int2ReferenceOpenHashMap<>(); + + public static void init() { + //Clear the cache every 5 minutes, ints are very compact! + Scheduler.INSTANCE.scheduleCyclic(CACHE::clear, 4800); + + //Clear cache after a screen where items can be upgraded in rarity closes + ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + String title = screen.getTitle().getString(); + + if (Utils.isOnSkyblock() && (title.equals("The Hex") || title.equals("Craft Item") || title.equals("Anvil") || title.equals("Reforge Anvil"))) { + ScreenEvents.remove(screen).register(screen1 -> CACHE.clear()); + } + }); + } + + public static void tryDraw(ItemStack stack, DrawContext context, int x, int y) { + MinecraftClient client = MinecraftClient.getInstance(); + + if (client.player != null) { + SkyblockItemRarity itemRarity = getItemRarity(stack, client.player); + + if (itemRarity != null) draw(context, x, y, itemRarity); + } + } + + private static SkyblockItemRarity getItemRarity(ItemStack stack, ClientPlayerEntity player) { + if (stack == null || stack.isEmpty()) return null; + + int hashCode = 0; + NbtCompound nbt = stack.getNbt(); + + if (nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.getString("uuid"); + + //If the item has an uuid, then use the hash code of the uuid otherwise use the identity hash code of the stack + hashCode = itemUuid.isEmpty() ? System.identityHashCode(stack) : itemUuid.hashCode(); + } + + if (CACHE.containsKey(hashCode)) return CACHE.get(hashCode); + + List<Text> tooltip = stack.getTooltip(player, TooltipContext.BASIC); + String[] stringifiedTooltip = tooltip.stream().map(Text::getString).toArray(String[]::new); + + for (String rarityString : LORE_RARITIES.keySet()) { + if (Arrays.stream(stringifiedTooltip).anyMatch(line -> line.contains(rarityString))) { + SkyblockItemRarity rarity = LORE_RARITIES.get(rarityString); + + CACHE.put(hashCode, rarity); + return rarity; + } + } + + CACHE.put(hashCode, null); + return null; + } + + private static void draw(DrawContext context, int x, int y, SkyblockItemRarity rarity) { + //Enable blending to handle HUD translucency + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + + context.drawSprite(x, y, 0, 16, 16, SPRITE.get(), rarity.r, rarity.g, rarity.b, SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgroundsOpacity); + + RenderSystem.disableBlend(); + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java new file mode 100644 index 00000000..f7ff1fb9 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java @@ -0,0 +1,29 @@ +package me.xmrvizzy.skyblocker.skyblock.item; + +import net.minecraft.util.Formatting; + +public enum SkyblockItemRarity { + ADMIN(Formatting.DARK_RED), + VERY_SPECIAL(Formatting.RED), + SPECIAL(Formatting.RED), + DIVINE(Formatting.AQUA), + MYTHIC(Formatting.LIGHT_PURPLE), + LEGENDARY(Formatting.GOLD), + EPIC(Formatting.DARK_PURPLE), + RARE(Formatting.BLUE), + UNCOMMON(Formatting.GREEN), + COMMON(Formatting.WHITE); + + public final float r; + public final float g; + public final float b; + + SkyblockItemRarity(Formatting formatting) { + @SuppressWarnings("DataFlowIssue") + int rgb = formatting.getColorValue(); + + this.r = ((rgb >> 16) & 0xFF) / 255f; + this.g = ((rgb >> 8) & 0xFF) / 255f; + this.b = (rgb & 0xFF) / 255f; + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java index 19f656e5..61a9aa20 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java @@ -15,7 +15,7 @@ import net.minecraft.text.Text; import net.minecraft.util.Identifier; public class ResultButtonWidget extends ClickableWidget { - private static final Identifier BACKGROUND_TEXTURE = new Identifier("textures/gui/recipe_book.png"); + private static final Identifier BACKGROUND_TEXTURE = new Identifier("recipe_book/slot_craftable"); protected ItemStack itemStack = null; @@ -38,7 +38,7 @@ public class ResultButtonWidget extends ClickableWidget { public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) { MinecraftClient client = MinecraftClient.getInstance(); // this.drawTexture(matrices, this.x, this.y, 29, 206, this.width, this.height); - context.drawTexture(BACKGROUND_TEXTURE, this.getX(), this.getY(), 29, 206, this.getWidth(), this.getHeight()); + context.drawGuiTexture(BACKGROUND_TEXTURE, this.getX(), this.getY(), this.getWidth(), this.getHeight()); // client.getItemRenderer().renderInGui(this.itemStack, this.x + 4, this.y + 4); context.drawItem(this.itemStack, this.getX() + 4, this.getY() + 4); // client.getItemRenderer().renderGuiItemOverlay(client.textRenderer, itemStack, this.x + 4, this.y + 4); diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java index 30c20524..b8192b45 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java @@ -92,6 +92,20 @@ public class ItemUtils { } } + public static String getItemId(ItemStack itemStack) { + if (itemStack == null) return null; + + NbtCompound nbt = itemStack.getNbt(); + if (nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + if (extraAttributes.contains("id")) { + return extraAttributes.getString("id"); + } + } + + return null; + } + public record Durability(int current, int max) { } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java index b0fb6edf..20edfca2 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java @@ -2,6 +2,7 @@ package me.xmrvizzy.skyblocker.utils; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import me.xmrvizzy.skyblocker.events.SkyblockEvents; import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip; import me.xmrvizzy.skyblocker.skyblock.rift.TheRift; import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler; diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java b/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java index 39d91bba..6196fec0 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java @@ -2,7 +2,7 @@ package me.xmrvizzy.skyblocker.utils.discord; import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.SkyblockEvents; +import me.xmrvizzy.skyblocker.events.SkyblockEvents; import me.xmrvizzy.skyblocker.utils.Utils; import meteordevelopment.discordipc.DiscordIPC; import meteordevelopment.discordipc.RichPresence; @@ -34,7 +34,7 @@ public class DiscordRPCManager { } /** - * Checks the {@link SkyblockerConfigManager.RichPresence#customMessage custom message}, updates {@link #cycleCount} if enabled, and updates rich presence. + * Checks the {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#customMessage custom message}, updates {@link #cycleCount} if enabled, and updates rich presence. */ public static void updateDataAndPresence() { // If the custom message is empty, discord will keep the last message, this is can serve as a default if the user doesn't want a custom message @@ -58,16 +58,16 @@ public class DiscordRPCManager { * <p> * When the {@link #updateTask previous update} does not exist or {@link CompletableFuture#isDone() has completed}: * <p> - * Connects to discord if {@link SkyblockerConfigManager.RichPresence#enableRichPresence rich presence is enabled}, + * Connects to discord if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled}, * the player {@link Utils#isOnSkyblock() is on Skyblock}, and {@link DiscordIPC#isConnected() discord is not already connected}. - * Updates the presence if {@link SkyblockerConfigManager.RichPresence#enableRichPresence rich presence is enabled} + * Updates the presence if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled} * and the player {@link Utils#isOnSkyblock() is on Skyblock}. - * Stops the connection if {@link SkyblockerConfigManager.RichPresence#enableRichPresence rich presence is disabled} + * Stops the connection if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled} * or the player {@link Utils#isOnSkyblock() is not on Skyblock} and {@link DiscordIPC#isConnected() discord is connected}. * Saves the update task in {@link #updateTask} * * @param initialization whether this is the first time the presence is being updates. If {@code true}, a message will be logged - * if {@link SkyblockerConfigManager.RichPresence#enableRichPresence rich presence is disabled}. + * if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled}. */ private static void initAndUpdatePresence(boolean initialization) { if (updateTask == null || updateTask.isDone()) { |