|
|
@@ -1,254 +0,0 @@
|
|
|
-package me.lethunderhawk.bazaarflux.util.itemdesign;
|
|
|
-
|
|
|
-import net.kyori.adventure.text.Component;
|
|
|
-import net.kyori.adventure.text.ComponentBuilder;
|
|
|
-import net.kyori.adventure.text.TextComponent;
|
|
|
-import net.kyori.adventure.text.format.NamedTextColor;
|
|
|
-import net.kyori.adventure.text.format.TextDecoration;
|
|
|
-
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.EnumSet;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
-
|
|
|
-public final class LoreDesigner {
|
|
|
-
|
|
|
- private LoreDesigner() {}
|
|
|
- public static Component createSingle(String text) {
|
|
|
- if (text == null || text.isBlank()) {
|
|
|
- return Component.empty();
|
|
|
- }
|
|
|
-
|
|
|
- StyleState style = new StyleState();
|
|
|
- ComponentBuilder<TextComponent, TextComponent.Builder> builder = Component.text();
|
|
|
-
|
|
|
- int i = 0;
|
|
|
- while (i < text.length()) {
|
|
|
- if (text.charAt(i) == '<') {
|
|
|
- int end = text.indexOf('>', i);
|
|
|
- if (end != -1) {
|
|
|
- String token = text.substring(i + 1, end).toLowerCase();
|
|
|
- applyToken(style, token);
|
|
|
- i = end + 1;
|
|
|
- continue;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- char c = text.charAt(i);
|
|
|
- builder.append(Component.text(String.valueOf(c), style.toStyle()));
|
|
|
- i++;
|
|
|
- }
|
|
|
-
|
|
|
- return builder.build();
|
|
|
- }
|
|
|
- public static List<Component> createLore(String text, String widthReference) {
|
|
|
- int maxLength = widthReference.length();
|
|
|
- if (text == null || text.isBlank() || maxLength <= 0) {
|
|
|
- return List.of();
|
|
|
- }
|
|
|
-
|
|
|
- List<Component> lore = new ArrayList<>();
|
|
|
-
|
|
|
- StyleState style = new StyleState();
|
|
|
- ComponentBuilder<TextComponent, TextComponent.Builder> lineBuilder = Component.text();
|
|
|
- int currentLength = 0;
|
|
|
-
|
|
|
- String[] lines = text.split("<br>", -1);
|
|
|
-
|
|
|
- for (int i = 0; i < lines.length; i++) {
|
|
|
-
|
|
|
- String[] wrapParts = lines[i].split(" ");
|
|
|
-
|
|
|
- for (int p = 0; p < wrapParts.length; p++) {
|
|
|
-
|
|
|
- ParsedWord parsed = parseWord(wrapParts[p], style);
|
|
|
- appendWord(
|
|
|
- lore,
|
|
|
- parsed,
|
|
|
- maxLength,
|
|
|
- style,
|
|
|
- Holder.of(lineBuilder, currentLength)
|
|
|
- );
|
|
|
-
|
|
|
- currentLength = Holder.length;
|
|
|
- lineBuilder = Holder.builder;
|
|
|
-
|
|
|
- // add space between parts
|
|
|
- if (p < wrapParts.length - 1) {
|
|
|
- lineBuilder.append(Component.space());
|
|
|
- currentLength++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- currentLength = Holder.length;
|
|
|
- lineBuilder = Holder.builder;
|
|
|
-
|
|
|
- // force line break if <br> occurred
|
|
|
- if (i < lines.length - 1) {
|
|
|
- lore.add(lineBuilder.build());
|
|
|
- lineBuilder = Component.text().style(style.toStyle());
|
|
|
- currentLength = 0;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (currentLength > 0) {
|
|
|
- lore.add(lineBuilder.build());
|
|
|
- }
|
|
|
-
|
|
|
- return lore;
|
|
|
- }
|
|
|
-
|
|
|
- // -------------------- Append Logic --------------------
|
|
|
-
|
|
|
- private static void appendWord(
|
|
|
- List<Component> lore,
|
|
|
- ParsedWord parsed,
|
|
|
- int maxLength,
|
|
|
- StyleState style,
|
|
|
- Holder holder
|
|
|
- ) {
|
|
|
- int wordLength = parsed.visibleLength();
|
|
|
-
|
|
|
- if (holder.length > 0 && holder.length + 1 + wordLength > maxLength) {
|
|
|
- lore.add(holder.builder.build());
|
|
|
- holder.builder = Component.text().style(style.toStyle());
|
|
|
- holder.length = 0;
|
|
|
- }
|
|
|
-
|
|
|
- holder.builder.append(parsed.component());
|
|
|
- holder.length += wordLength;
|
|
|
- }
|
|
|
-
|
|
|
- // -------------------- Parsing --------------------
|
|
|
-
|
|
|
- private static ParsedWord parseWord(String raw, StyleState style) {
|
|
|
- ComponentBuilder<TextComponent, TextComponent.Builder> builder = Component.text();
|
|
|
- int visibleLength = 0;
|
|
|
-
|
|
|
- int i = 0;
|
|
|
- while (i < raw.length()) {
|
|
|
- if (raw.charAt(i) == '<') {
|
|
|
- int end = raw.indexOf('>', i);
|
|
|
- if (end != -1) {
|
|
|
- String token = raw.substring(i + 1, end).toLowerCase();
|
|
|
- applyToken(style, token);
|
|
|
- i = end + 1;
|
|
|
- continue;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- char c = raw.charAt(i);
|
|
|
- builder.append(Component.text(String.valueOf(c), style.toStyle()));
|
|
|
- visibleLength++;
|
|
|
- i++;
|
|
|
- }
|
|
|
-
|
|
|
- return new ParsedWord(builder.build(), visibleLength);
|
|
|
- }
|
|
|
-
|
|
|
- private static void applyToken(StyleState style, String token) {
|
|
|
-
|
|
|
- if (token.startsWith("/")) {
|
|
|
- String closing = token.substring(1);
|
|
|
-
|
|
|
- switch (closing) {
|
|
|
- case "bold" -> style.undecorate(TextDecoration.BOLD);
|
|
|
- case "italic" -> style.undecorate(TextDecoration.ITALIC);
|
|
|
- case "underlined" -> style.undecorate(TextDecoration.UNDERLINED);
|
|
|
- case "strikethrough" -> style.undecorate(TextDecoration.STRIKETHROUGH);
|
|
|
- case "obfuscated" -> style.undecorate(TextDecoration.OBFUSCATED);
|
|
|
- default -> {
|
|
|
- // closing color tag like </red>
|
|
|
- if (NAMED_COLORS.containsKey(closing)) {
|
|
|
- style.color(style.defaultColor);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- switch (token) {
|
|
|
- case "reset" -> style.reset();
|
|
|
-
|
|
|
- case "bold" -> style.decorate(TextDecoration.BOLD);
|
|
|
- case "italic" -> style.decorate(TextDecoration.ITALIC);
|
|
|
- case "underlined" -> style.decorate(TextDecoration.UNDERLINED);
|
|
|
- case "strikethrough" -> style.decorate(TextDecoration.STRIKETHROUGH);
|
|
|
- case "obfuscated" -> style.decorate(TextDecoration.OBFUSCATED);
|
|
|
-
|
|
|
- default -> {
|
|
|
- NamedTextColor color = NAMED_COLORS.get(token);
|
|
|
- if (color != null) {
|
|
|
- style.color(color);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // -------------------- Helpers --------------------
|
|
|
-
|
|
|
- private static final Map<String, NamedTextColor> NAMED_COLORS = Map.ofEntries(
|
|
|
- Map.entry("black", NamedTextColor.BLACK),
|
|
|
- Map.entry("dark_blue", NamedTextColor.DARK_BLUE),
|
|
|
- Map.entry("dark_green", NamedTextColor.DARK_GREEN),
|
|
|
- Map.entry("dark_aqua", NamedTextColor.DARK_AQUA),
|
|
|
- Map.entry("dark_red", NamedTextColor.DARK_RED),
|
|
|
- Map.entry("dark_purple", NamedTextColor.DARK_PURPLE),
|
|
|
- Map.entry("gold", NamedTextColor.GOLD),
|
|
|
- Map.entry("gray", NamedTextColor.GRAY),
|
|
|
- Map.entry("dark_gray", NamedTextColor.DARK_GRAY),
|
|
|
- Map.entry("blue", NamedTextColor.BLUE),
|
|
|
- Map.entry("green", NamedTextColor.GREEN),
|
|
|
- Map.entry("aqua", NamedTextColor.AQUA),
|
|
|
- Map.entry("red", NamedTextColor.RED),
|
|
|
- Map.entry("light_purple", NamedTextColor.LIGHT_PURPLE),
|
|
|
- Map.entry("yellow", NamedTextColor.YELLOW),
|
|
|
- Map.entry("white", NamedTextColor.WHITE)
|
|
|
- );
|
|
|
-
|
|
|
- private record ParsedWord(Component component, int visibleLength) {}
|
|
|
-
|
|
|
- private static final class StyleState {
|
|
|
- private final NamedTextColor defaultColor = NamedTextColor.GRAY;
|
|
|
-
|
|
|
- private NamedTextColor color = defaultColor;
|
|
|
- private final EnumSet<TextDecoration> decorations = EnumSet.noneOf(TextDecoration.class);
|
|
|
-
|
|
|
- void color(NamedTextColor color) {
|
|
|
- this.color = color;
|
|
|
- }
|
|
|
- void undecorate(TextDecoration decoration) {
|
|
|
- decorations.remove(decoration);
|
|
|
- }
|
|
|
- void decorate(TextDecoration decoration) {
|
|
|
- decorations.add(decoration);
|
|
|
- }
|
|
|
-
|
|
|
- void reset() {
|
|
|
- color = defaultColor;
|
|
|
- decorations.clear();
|
|
|
- }
|
|
|
-
|
|
|
- net.kyori.adventure.text.format.Style toStyle() {
|
|
|
- net.kyori.adventure.text.format.Style.Builder style =
|
|
|
- net.kyori.adventure.text.format.Style.style();
|
|
|
- if (color != null) style.color(color);
|
|
|
- for (TextDecoration d : decorations) style.decorate(d);
|
|
|
- return style.build();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Mutable holder to avoid excessive object creation
|
|
|
- */
|
|
|
- private static final class Holder {
|
|
|
- static ComponentBuilder builder;
|
|
|
- static int length;
|
|
|
-
|
|
|
- static Holder of(ComponentBuilder b, int l) {
|
|
|
- builder = b;
|
|
|
- length = l;
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|