diff options
8 files changed, 232 insertions, 50 deletions
diff --git a/gradle.properties b/gradle.properties index f213ef1..09e0729 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,6 +6,6 @@ org.gradle.daemon=false name=${rootProject.name} group=com.anthonyhilyard.${name.toLowerCase()} author=anthonyhilyard -version=1.0.31 +version=1.0.33 mcVersion=1.18.1 -forgeVersion=39.0.0 +forgeVersion=39.0.9 diff --git a/src/main/java/com/anthonyhilyard/iceberg/events/GatherComponentsExtEvent.java b/src/main/java/com/anthonyhilyard/iceberg/events/GatherComponentsExtEvent.java new file mode 100644 index 0000000..753c7c0 --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/events/GatherComponentsExtEvent.java @@ -0,0 +1,21 @@ +package com.anthonyhilyard.iceberg.events; + +import java.util.List; + +import com.mojang.datafixers.util.Either; + +import net.minecraft.network.chat.FormattedText; +import net.minecraft.world.inventory.tooltip.TooltipComponent; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.client.event.RenderTooltipEvent.GatherComponents; + +public class GatherComponentsExtEvent extends GatherComponents +{ + private final int index; + public GatherComponentsExtEvent(ItemStack itemStack, int screenWidth, int screenHeight, List<Either<FormattedText, TooltipComponent>> tooltipElements, int maxWidth, int index) + { + super(itemStack, screenWidth, screenHeight, tooltipElements, maxWidth); + this.index = index; + } + public int getIndex() { return index; } +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/events/RenderTooltipExtEvent.java b/src/main/java/com/anthonyhilyard/iceberg/events/RenderTooltipExtEvent.java index 98f01a6..febe70f 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/events/RenderTooltipExtEvent.java +++ b/src/main/java/com/anthonyhilyard/iceberg/events/RenderTooltipExtEvent.java @@ -14,30 +14,45 @@ public class RenderTooltipExtEvent public static class Pre extends RenderTooltipEvent.Pre { private final boolean comparisonTooltip; + private final int index; - public Pre(ItemStack stack, PoseStack PoseStack, int x, int y, int screenWidth, int screenHeight, Font font, List<ClientTooltipComponent> components, boolean comparison) + public Pre(ItemStack stack, PoseStack PoseStack, int x, int y, int screenWidth, int screenHeight, Font font, List<ClientTooltipComponent> components, boolean comparison, int index) { super(stack, PoseStack, x, y, screenWidth, screenHeight, font, components); - comparisonTooltip = comparison; + this.comparisonTooltip = comparison; + this.index = index; + } + + public Pre(ItemStack stack, PoseStack PoseStack, int x, int y, int screenWidth, int screenHeight, Font font, List<ClientTooltipComponent> components, boolean comparison) + { + this(stack, PoseStack, x, y, screenWidth, screenHeight, font, components, comparison, 0); } public boolean isComparison() { return comparisonTooltip; } + public int getIndex() { return index; } } public static class Post extends RenderTooltipEvent { private final boolean comparisonTooltip; + private final int index; private final int width; private final int height; - public Post(ItemStack stack, PoseStack PoseStack, int x, int y, Font font, int width, int height, List<ClientTooltipComponent> components, boolean comparison) + public Post(ItemStack stack, PoseStack PoseStack, int x, int y, Font font, int width, int height, List<ClientTooltipComponent> components, boolean comparison, int index) { super(stack, PoseStack, x, y, font, components); this.width = width; this.height = height; + this.comparisonTooltip = comparison; + this.index = index; + } - comparisonTooltip = comparison; + public Post(ItemStack stack, PoseStack PoseStack, int x, int y, Font font, int width, int height, List<ClientTooltipComponent> components, boolean comparison) + { + this(stack, PoseStack, x, y, font, width, height, components, comparison, 0); } public boolean isComparison() { return comparisonTooltip; } + public int getIndex() { return index; } public int getWidth() { return width; } public int getHeight() { return height; } } @@ -45,18 +60,25 @@ public class RenderTooltipExtEvent public static class Color extends RenderTooltipEvent.Color { private final boolean comparisonTooltip; + private final int index; - public Color(ItemStack stack, PoseStack PoseStack, int x, int y, Font font, int background, int borderStart, int borderEnd, List<ClientTooltipComponent> components, boolean comparison) + public Color(ItemStack stack, PoseStack PoseStack, int x, int y, Font font, int background, int borderStart, int borderEnd, List<ClientTooltipComponent> components, boolean comparison, int index) { super(stack, PoseStack, x, y, font, background, borderStart, borderEnd, components); - comparisonTooltip = comparison; + this.comparisonTooltip = comparison; + this.index = index; + } + public Color(ItemStack stack, PoseStack PoseStack, int x, int y, Font font, int background, int borderStart, int borderEnd, List<ClientTooltipComponent> components, boolean comparison) + { + this(stack, PoseStack, x, y, font, background, borderStart, borderEnd, components, comparison, 0); } - public Color(ItemStack stack, PoseStack PoseStack, int x, int y, Font font, int backgroundStart, int backgroundEnd, int borderStart, int borderEnd, List<ClientTooltipComponent> components, boolean comparison) + public Color(ItemStack stack, PoseStack PoseStack, int x, int y, Font font, int backgroundStart, int backgroundEnd, int borderStart, int borderEnd, List<ClientTooltipComponent> components, boolean comparison, int index) { - this(stack, PoseStack, x, y, font, backgroundStart, borderStart, borderEnd, components, comparison); + this(stack, PoseStack, x, y, font, backgroundStart, borderStart, borderEnd, components, comparison, index); setBackgroundStart(backgroundStart); setBackgroundEnd(backgroundEnd); } public boolean isComparison() { return comparisonTooltip; } + public int getIndex() { return index; } } } diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/TextColorMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/TextColorMixin.java new file mode 100644 index 0000000..2bd40ba --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/mixin/TextColorMixin.java @@ -0,0 +1,36 @@ +package com.anthonyhilyard.iceberg.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.network.chat.TextColor; + +@Mixin(TextColor.class) +public class TextColorMixin +{ + /** + * Fix an issue in TextColor parsing that makes it so only alpha values up to 0x7F are supported. + */ + @Inject(method = "parseColor", at = @At("HEAD"), cancellable = true) + private static boolean parseColor(String colorString, CallbackInfoReturnable<TextColor> info) + { + if (!colorString.startsWith("#")) + { + return false; + } + + try + { + int i = Integer.parseUnsignedInt(colorString.substring(1), 16); + info.setReturnValue(TextColor.fromRgb(i)); + return true; + } + catch (NumberFormatException numberformatexception) + { + info.setReturnValue(null); + return true; + } + } +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/ItemColor.java b/src/main/java/com/anthonyhilyard/iceberg/util/ItemColor.java index 3714681..6b614c5 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/util/ItemColor.java +++ b/src/main/java/com/anthonyhilyard/iceberg/util/ItemColor.java @@ -40,7 +40,7 @@ public class ItemColor try { ChatFormatting format = ChatFormatting.getByCode(rawTitle.charAt(i + 1)); - if (format.isColor()) + if (format != null && format.isColor()) { return TextColor.fromLegacyFormat(format); } diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java b/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java index 6f450f1..5099644 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java +++ b/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java @@ -2,6 +2,7 @@ package com.anthonyhilyard.iceberg.util; import net.minecraft.world.item.ItemStack; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.function.BiPredicate; @@ -12,6 +13,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NumericTag; import net.minecraft.nbt.Tag; +import net.minecraft.nbt.ListTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextColor; import net.minecraft.resources.ResourceLocation; @@ -72,6 +74,25 @@ public class Selectors }); }}; + public static record SelectorDocumentation(String name, String description, List<String> examples) + { + public SelectorDocumentation(String name, String description, String... examples) { this(name, description, Arrays.asList(examples)); } + } + + public static List<SelectorDocumentation> selectorDocumentation() + { + return Arrays.asList( + new SelectorDocumentation("Item name", "Use item name for vanilla items or include mod name for modded items.", "minecraft:stick", "iron_ore"), + new SelectorDocumentation("Tag", "$ followed by tag name.", "$forge:stone", "$planks"), + new SelectorDocumentation("Mod name", "@ followed by mod identifier.", "@spoiledeggs"), + new SelectorDocumentation("Rarity", "! followed by item's rarity. This is ONLY vanilla rarities.", "!uncommon", "!rare", "!epic"), + new SelectorDocumentation("Item name color", "# followed by color hex code, the hex code must match exactly.", "#23F632"), + new SelectorDocumentation("Display name", "% followed by any text. Will match any item with this text in its tooltip display name.", "%Netherite", "%[Uncommon]"), + new SelectorDocumentation("Tooltip text", "Will match any item with this text anywhere in the tooltip text (besides the name).", "^Legendary"), + new SelectorDocumentation("NBT tag", "& followed by tag name and optional comparator (=, >, <, or !=) and value, in the format <tag><comparator><value> or just <tag>.", "&Damage=0", "&Tier>1", "&map!=128", "&Enchantments") + ); + } + /** * Returns true if this selector is syntactically valid. * @param value The selector. @@ -212,24 +233,8 @@ public class Selectors } } - // Look for a tag matching the given name. - Tag matchedTag = getSubtag(item.getTag(), tagName); - if (matchedTag != null) - { - // A tag value of null means that we are just looking for the presence of this tag. - if (tagValue == null) - { - return true; - } - // Otherwise, we will use the provided comparator. - else - { - if (valueChecker != null) - { - return valueChecker.test(matchedTag, tagValue); - } - } - } + // Look for a tag matching the given name and value. + return findMatchingSubtag(item.getTag(), tagName, tagValue, valueChecker); } return false; @@ -238,31 +243,59 @@ public class Selectors /** * Retrieves the first inner tag with the given key, or null if there is no match. */ - private static Tag getSubtag(CompoundTag tag, String key) + private static boolean findMatchingSubtag(Tag tag, String key, String value, BiPredicate<Tag, String> valueChecker) { if (tag == null) { - return null; + return false; } - if (tag.contains(key)) + if (tag.getId() == Tag.TAG_COMPOUND) { - return tag.get(key); + CompoundTag compoundTag = (CompoundTag)tag; + + if (compoundTag.contains(key)) + { + // Just checking presence. + if (value == null && valueChecker == null) + { + return true; + } + // Otherwise, we will use the provided comparator. + else + { + return valueChecker.test(compoundTag.get(key), value); + } + } + else + { + for (String innerKey : compoundTag.getAllKeys()) + { + if (compoundTag.getTagType(innerKey) == Tag.TAG_LIST || compoundTag.getTagType(innerKey) == Tag.TAG_COMPOUND) + { + if (findMatchingSubtag(compoundTag.get(innerKey), key, value, valueChecker)) + { + return true; + } + } + } + return false; + } } - else + else if (tag.getId() == Tag.TAG_LIST) { - for (String innerKey : tag.getAllKeys()) + ListTag listTag = (ListTag)tag; + for (Tag innerTag : listTag) { - if (tag.getTagType(innerKey) == Tag.TAG_COMPOUND) + if (innerTag.getId() == Tag.TAG_LIST || innerTag.getId() == Tag.TAG_COMPOUND) { - Tag innerTag = getSubtag(tag.getCompound(innerKey), key); - if (innerTag != null) + if (findMatchingSubtag(innerTag, key, value, valueChecker)) { - return innerTag; + return true; } } } - return null; } + return false; } }
\ No newline at end of file diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java b/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java index 1673815..467604b 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java +++ b/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java @@ -10,6 +10,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import com.anthonyhilyard.iceberg.events.GatherComponentsExtEvent; import com.anthonyhilyard.iceberg.events.RenderTooltipExtEvent; import com.mojang.blaze3d.vertex.PoseStack; @@ -23,6 +24,8 @@ import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.locale.Language; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.FormattedText; +import net.minecraft.network.chat.Style; +import net.minecraft.util.FormattedCharSequence; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.datafixers.util.Either; @@ -93,12 +96,13 @@ public class Tooltips Rect2i rect, int screenWidth, int screenHeight, int backgroundColor, int borderColorStart, int borderColorEnd, boolean comparison, boolean constrain) { - renderItemTooltip(stack, poseStack, info, rect, screenWidth, screenHeight, backgroundColor, backgroundColor, borderColorStart, borderColorEnd, comparison, constrain); + renderItemTooltip(stack, poseStack, info, rect, screenWidth, screenHeight, backgroundColor, backgroundColor, borderColorStart, borderColorEnd, comparison, constrain, false, 0); } public static void renderItemTooltip(@Nonnull final ItemStack stack, PoseStack poseStack, TooltipInfo info, - Rect2i rect, int screenWidth, int screenHeight, - int backgroundColorStart, int backgroundColorEnd, int borderColorStart, int borderColorEnd, boolean comparison, boolean constrain) + Rect2i rect, int screenWidth, int screenHeight, + int backgroundColorStart, int backgroundColorEnd, int borderColorStart, int borderColorEnd, + boolean comparison, boolean constrain, boolean centeredTitle, int index) { if (info.getComponents().isEmpty()) { @@ -111,10 +115,16 @@ public class Tooltips itemRenderer = Minecraft.getInstance().getItemRenderer(); } + // Center the title now if needed. + if (centeredTitle) + { + info = new TooltipInfo(centerTitle(info.getComponents(), info.getFont(), rect.getWidth()), info.getFont()); + } + int rectX = rect.getX() + 4; int rectY = rect.getY() + 4; - RenderTooltipExtEvent.Pre preEvent = new RenderTooltipExtEvent.Pre(stack, poseStack, rectX, rectY, screenWidth, screenHeight, info.getFont(), info.getComponents(), comparison); + RenderTooltipExtEvent.Pre preEvent = new RenderTooltipExtEvent.Pre(stack, poseStack, rectX, rectY, screenWidth, screenHeight, info.getFont(), info.getComponents(), comparison, index); if (MinecraftForge.EVENT_BUS.post(preEvent)) { return; @@ -133,7 +143,7 @@ public class Tooltips itemRenderer.blitOffset = zLevel; Matrix4f mat = poseStack.last().pose(); - RenderTooltipExtEvent.Color colorEvent = new RenderTooltipExtEvent.Color(stack, poseStack, rectX, rectY, info.getFont(), backgroundColorStart, backgroundColorEnd, borderColorStart, borderColorEnd, info.getComponents(), comparison); + RenderTooltipExtEvent.Color colorEvent = new RenderTooltipExtEvent.Color(stack, poseStack, rectX, rectY, info.getFont(), backgroundColorStart, backgroundColorEnd, borderColorStart, borderColorEnd, info.getComponents(), comparison, index); MinecraftForge.EVENT_BUS.post(colorEvent); backgroundColorStart = colorEvent.getBackgroundStart(); @@ -176,12 +186,18 @@ public class Tooltips itemRenderer.blitOffset = f; - RenderTooltipExtEvent.Post postEvent = new RenderTooltipExtEvent.Post(stack, poseStack, rectX, rectY, info.getFont(), rect.getWidth(), rect.getHeight(), info.getComponents(), comparison); + RenderTooltipExtEvent.Post postEvent = new RenderTooltipExtEvent.Post(stack, poseStack, rectX, rectY, info.getFont(), rect.getWidth(), rect.getHeight(), info.getComponents(), comparison, index); MinecraftForge.EVENT_BUS.post(postEvent); } public static List<ClientTooltipComponent> gatherTooltipComponents(ItemStack stack, List<? extends FormattedText> textElements, Optional<TooltipComponent> itemComponent, - int mouseX, int screenWidth, int screenHeight, @Nullable Font forcedFont, Font fallbackFont, int maxWidth) + int mouseX, int screenWidth, int screenHeight, @Nullable Font forcedFont, Font fallbackFont, int maxWidth) + { + return gatherTooltipComponents(stack, textElements, itemComponent, mouseX, screenWidth, screenHeight, forcedFont, fallbackFont, maxWidth, 0); + } + + public static List<ClientTooltipComponent> gatherTooltipComponents(ItemStack stack, List<? extends FormattedText> textElements, Optional<TooltipComponent> itemComponent, + int mouseX, int screenWidth, int screenHeight, @Nullable Font forcedFont, Font fallbackFont, int maxWidth, int index) { Font font = ForgeHooksClient.getTooltipFont(forcedFont, stack, fallbackFont); List<Either<FormattedText, TooltipComponent>> elements = textElements.stream() @@ -190,7 +206,7 @@ public class Tooltips itemComponent.ifPresent(c -> elements.add(1, Either.right(c))); - var event = new RenderTooltipEvent.GatherComponents(stack, screenWidth, screenHeight, elements, maxWidth); + var event = new GatherComponentsExtEvent(stack, screenWidth, screenHeight, elements, maxWidth, index); if (MinecraftForge.EVENT_BUS.post(event)) { return List.of(); @@ -244,6 +260,12 @@ public class Tooltips public static Rect2i calculateRect(final ItemStack stack, PoseStack poseStack, List<ClientTooltipComponent> components, int mouseX, int mouseY,int screenWidth, int screenHeight, int maxTextWidth, Font font) { + return calculateRect(stack, poseStack, components, mouseX, mouseY, screenWidth, screenHeight, maxTextWidth, font, 0, false); + } + + public static Rect2i calculateRect(final ItemStack stack, PoseStack poseStack, List<ClientTooltipComponent> components, + int mouseX, int mouseY,int screenWidth, int screenHeight, int maxTextWidth, Font font, int minWidth, boolean centeredTitle) + { Rect2i rect = new Rect2i(0, 0, 0, 0); if (components == null || components.isEmpty() || stack == null) { @@ -263,9 +285,14 @@ public class Tooltips screenHeight = event.getScreenHeight(); font = event.getFont(); - int tooltipTextWidth = 0; + int tooltipTextWidth = minWidth; int tooltipHeight = components.size() == 1 ? -2 : 0; + if (centeredTitle) + { + components = centerTitle(components, font, minWidth); + } + for (ClientTooltipComponent component : components) { int componentWidth = component.getWidth(event.getFont()); @@ -292,4 +319,46 @@ public class Tooltips rect = new Rect2i(tooltipX - 2, tooltipY - 4, tooltipTextWidth, tooltipHeight); return rect; } + + public static List<ClientTooltipComponent> centerTitle(List<ClientTooltipComponent> components, Font font, int minWidth) + { + // Calculate tooltip width first. + int tooltipWidth = minWidth; + + for (ClientTooltipComponent clienttooltipcomponent : components) + { + if (clienttooltipcomponent == null) + { + return components; + } + int componentWidth = clienttooltipcomponent.getWidth(font); + if (componentWidth > tooltipWidth) + { + tooltipWidth = componentWidth; + } + } + + // TODO: If the title is multiple lines, we need to extend this for each one. + + List<FormattedText> recomposedLines = StringRecomposer.recompose(List.of(components.get(0))); + if (recomposedLines.isEmpty()) + { + return components; + } + + List<ClientTooltipComponent> result = new ArrayList<>(components); + + FormattedCharSequence title = Language.getInstance().getVisualOrder(recomposedLines.get(0)); + FormattedCharSequence space = FormattedCharSequence.forward(" ", Style.EMPTY); + while (result.get(0).getWidth(font) < tooltipWidth) + { + title = FormattedCharSequence.fromList(List.of(space, title, space)); + if (title == null) + { + break; + } + result.set(0, ClientTooltipComponent.create(title)); + } + return result; + } } diff --git a/src/main/resources/iceberg.mixins.json b/src/main/resources/iceberg.mixins.json index 93984a4..6ee1aca 100644 --- a/src/main/resources/iceberg.mixins.json +++ b/src/main/resources/iceberg.mixins.json @@ -9,7 +9,8 @@ ], "client": [ "ScreenMixin", - "ClientPacketListenerMixin" + "ClientPacketListenerMixin", + "TextColorMixin" ], "injectors": { "defaultRequire": 1 |