diff options
author | Grayray75 <69988482+Grayray75@users.noreply.github.com> | 2023-10-08 16:39:15 +0200 |
---|---|---|
committer | Grayray75 <69988482+Grayray75@users.noreply.github.com> | 2023-10-08 16:40:47 +0200 |
commit | e65f26f096e998025fe4c46602adf4638710a977 (patch) | |
tree | ff1ea8544a923ca41a99e6ee93ba6e8c28a3c295 /src/main/java | |
parent | 19c8450f00b1f18725deb8014b642817bb607682 (diff) | |
parent | 0c9381191663a74a9ec3cff9aae1c8b523215f0e (diff) | |
download | Skyblocker-e65f26f096e998025fe4c46602adf4638710a977.tar.gz Skyblocker-e65f26f096e998025fe4c46602adf4638710a977.tar.bz2 Skyblocker-e65f26f096e998025fe4c46602adf4638710a977.zip |
Merge branch 'master' into item-cooldown
Diffstat (limited to 'src/main/java')
16 files changed, 415 insertions, 101 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java index beff7a49..6f4276e9 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java +++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java @@ -102,6 +102,8 @@ public class SkyblockerMod implements ClientModInitializer { TicTacToe.init(); 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 7b801d89..8e014124 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java @@ -1,19 +1,18 @@ 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; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import me.xmrvizzy.skyblocker.skyblock.item.CustomArmorTrims; import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; 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; @@ -209,6 +208,9 @@ public class SkyblockerConfig { @SerialEntry public List<Integer> lockedSlots = new ArrayList<>(); + + @SerialEntry + public ObjectOpenHashSet<String> protectedItems = new ObjectOpenHashSet<>(); @SerialEntry public Object2ObjectOpenHashMap<String, Text> customItemNames = new Object2ObjectOpenHashMap<>(); @@ -465,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 { @@ -585,54 +593,17 @@ 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 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; - } + public Formatting incompleteColor = Formatting.BLUE; - @Override - public String toString() { - return StringUtils.capitalize(formatting.getName().replaceAll("_", " ")); - } } public static class LividColor { 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 1a483ef5..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,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Item Cooldown .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemCooldown")) @@ -216,7 +213,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Shortcuts .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts")) @@ -251,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")) @@ -278,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")) @@ -291,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")) @@ -348,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")) @@ -361,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")) @@ -376,7 +388,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Hitboxes .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.hitbox")) @@ -396,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")) @@ -443,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/mixin/ClientPlayerEntityMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java index ee1fc5b8..8b2ec417 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java @@ -4,6 +4,7 @@ import com.mojang.authlib.GameProfile; import dev.cbyrne.betterinject.annotations.Inject; import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock; +import me.xmrvizzy.skyblocker.skyblock.item.ItemProtection; import me.xmrvizzy.skyblocker.skyblock.rift.HealingMelonIndicator; import me.xmrvizzy.skyblocker.utils.Utils; import net.minecraft.client.network.AbstractClientPlayerEntity; @@ -21,7 +22,10 @@ public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity @Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true) public void skyblocker$dropSelectedItem(CallbackInfoReturnable<Boolean> cir) { - if (Utils.isOnSkyblock()) HotbarSlotLock.handleDropSelectedItem(this.getInventory().selectedSlot, cir); + if (Utils.isOnSkyblock()) { + if (ItemProtection.isItemProtected(this.getInventory().getMainHandStack())) cir.setReturnValue(false); + HotbarSlotLock.handleDropSelectedItem(this.getInventory().selectedSlot, cir); + } } @Inject(method = "updateHealth", at = @At("RETURN")) diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java index 17497ded..7e94d660 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java @@ -8,20 +8,27 @@ import me.xmrvizzy.skyblocker.skyblock.experiment.SuperpairsSolver; 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; import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.item.TooltipContext; import net.minecraft.inventory.SimpleInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.SlotActionType; import net.minecraft.text.Text; import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -36,10 +43,20 @@ import java.util.Map; import java.util.regex.Matcher; @Mixin(HandledScreen.class) -public abstract class HandledScreenMixin extends Screen { +public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen { + /** + * This is the slot id returned for when a click is outside of the screen's bounds + */ + @Unique + private static final int OUT_OF_BOUNDS_SLOT = -999; + @Shadow @Nullable protected Slot focusedSlot; + + @Shadow + @Final + protected T handler; protected HandledScreenMixin(Text title) { super(title); @@ -120,4 +137,57 @@ public abstract class HandledScreenMixin extends Screen { } } } + + /** + * The naming of this method in yarn is half true, its mostly to handle slot/item interactions (which are mouse or keyboard clicks) + * For example, using the drop key bind while hovering over an item will invoke this method to drop the players item + */ + @Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true) + private void skyblocker$onSlotInteract(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) { + if (Utils.isOnSkyblock()) { + // When you try and drop the item by picking it up then clicking outside of the screen + if (slotId == OUT_OF_BOUNDS_SLOT) { + ItemStack cursorStack = this.handler.getCursorStack(); + + if (ItemProtection.isItemProtected(cursorStack)) ci.cancel(); + } + + if (slot != null) { + // When you click your drop key while hovering over an item + if (actionType == SlotActionType.THROW) { + ItemStack stack = slot.getStack(); + + if (ItemProtection.isItemProtected(stack)) ci.cancel(); + } + + //Prevent salvaging + if (this.getTitle().getString().equals("Salvage Items")) { + ItemStack stack = slot.getStack(); + + if (ItemProtection.isItemProtected(stack)) ci.cancel(); + } + + //Prevent selling to NPC shops + if (this.client != null && this.handler instanceof GenericContainerScreenHandler genericContainerScreenHandler && genericContainerScreenHandler.getRows() == 6) { + ItemStack sellItem = this.handler.slots.get(49).getStack(); + + if (sellItem.getName().getString().equals("Sell Item") || skyblocker$doesLoreContain(sellItem, this.client, "buyback")) { + ItemStack stack = slot.getStack(); + + if (ItemProtection.isItemProtected(stack)) ci.cancel(); + } + } + } + } + } + + //TODO make this a util method somewhere else, eventually + 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 7df38bde..3376907b 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java @@ -8,12 +8,14 @@ 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; @@ -42,9 +44,10 @@ public abstract class InGameHudMixin { 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); } } 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/ItemProtection.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemProtection.java new file mode 100644 index 00000000..db671787 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemProtection.java @@ -0,0 +1,75 @@ +package me.xmrvizzy.skyblocker.skyblock.item; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; +import me.xmrvizzy.skyblocker.utils.Utils; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.text.Text; + +public class ItemProtection { + + public static void init() { + ClientCommandRegistrationCallback.EVENT.register(ItemProtection::registerCommand); + } + + public static boolean isItemProtected(ItemStack stack) { + if (stack == null || stack.isEmpty()) return false; + + NbtCompound nbt = stack.getNbt(); + + if (nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : ""; + + return SkyblockerConfigManager.get().general.protectedItems.contains(itemUuid); + } + + return false; + } + + private static void registerCommand(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { + dispatcher.register(ClientCommandManager.literal("skyblocker") + .then(ClientCommandManager.literal("protectItem") + .executes(context -> protectMyItem(context.getSource())))); + } + + private static int protectMyItem(FabricClientCommandSource source) { + ItemStack heldItem = source.getPlayer().getMainHandStack(); + NbtCompound nbt = (heldItem != null) ? heldItem.getNbt() : null; + + if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; + + if (itemUuid != null) { + ObjectOpenHashSet<String> protectedItems = SkyblockerConfigManager.get().general.protectedItems; + + if (!protectedItems.contains(itemUuid)) { + protectedItems.add(itemUuid); + SkyblockerConfigManager.save(); + + source.sendFeedback(Text.translatable("skyblocker.itemProtection.added", heldItem.getName())); + } else { + protectedItems.remove(itemUuid); + SkyblockerConfigManager.save(); + + source.sendFeedback(Text.translatable("skyblocker.itemProtection.removed", heldItem.getName())); + } + } else { + source.sendFeedback(Text.translatable("skyblocker.itemProtection.noItemUuid")); + } + } else { + source.sendFeedback(Text.translatable("skyblocker.itemProtection.unableToProtect")); + } + + return Command.SINGLE_SUCCESS; + } +} 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); |