feat: implemented MonologueRenderer
This commit is contained in:
parent
e8c588b2b6
commit
1b231463f3
@ -25,10 +25,14 @@ public class ServerBootstrap {
|
|||||||
"==============================================",
|
"==============================================",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Getter private static ServerBootstrap instance;
|
||||||
|
|
||||||
private final MinecraftServer server;
|
private final MinecraftServer server;
|
||||||
@Getter private final ResourcePackManager resourcePackManager;
|
@Getter private final ResourcePackManager resourcePackManager;
|
||||||
|
|
||||||
public ServerBootstrap() {
|
public ServerBootstrap() {
|
||||||
|
instance = this;
|
||||||
|
|
||||||
for (var line: LOGO) log.info(line);
|
for (var line: LOGO) log.info(line);
|
||||||
|
|
||||||
server = MinecraftServer.init();
|
server = MinecraftServer.init();
|
||||||
|
|||||||
@ -52,7 +52,7 @@ public class DefaultGlyphResourcePack implements GlyphResourcePack {
|
|||||||
@Override
|
@Override
|
||||||
public <T extends ResourceProducer> @NotNull T get(@NotNull ResourceIdentifier<@NotNull T> id) throws IllegalArgumentException {
|
public <T extends ResourceProducer> @NotNull T get(@NotNull ResourceIdentifier<@NotNull T> id) throws IllegalArgumentException {
|
||||||
if (!compiled.containsKey(id.key())) {
|
if (!compiled.containsKey(id.key())) {
|
||||||
throw new IllegalArgumentException("Producer with that identifier is not compiled");
|
throw new IllegalArgumentException("Producer with that identifier is not compiled. Provided key: " + id.key());
|
||||||
}
|
}
|
||||||
ResourceProducer producer = compiled.get(id.key());
|
ResourceProducer producer = compiled.get(id.key());
|
||||||
if (!id.getType().isAssignableFrom(producer.getClass())) {
|
if (!id.getType().isAssignableFrom(producer.getClass())) {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import ru.dragonestia.msb3.api.glyph.compile.GlyphCompiler;
|
|||||||
import ru.dragonestia.msb3.api.glyph.font.GlyphFont;
|
import ru.dragonestia.msb3.api.glyph.font.GlyphFont;
|
||||||
import ru.dragonestia.msb3.api.glyph.pack.GlyphResourcePack;
|
import ru.dragonestia.msb3.api.glyph.pack.GlyphResourcePack;
|
||||||
import ru.dragonestia.msb3.api.item.BlankSlotItem;
|
import ru.dragonestia.msb3.api.item.BlankSlotItem;
|
||||||
|
import ru.dragonestia.msb3.api.talk.MonologueRenderer;
|
||||||
import ru.dragonestia.msb3.api.title.BlackScreen;
|
import ru.dragonestia.msb3.api.title.BlackScreen;
|
||||||
import team.unnamed.creative.ResourcePack;
|
import team.unnamed.creative.ResourcePack;
|
||||||
import team.unnamed.creative.atlas.Atlas;
|
import team.unnamed.creative.atlas.Atlas;
|
||||||
@ -37,9 +38,11 @@ public class ResourcePackManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initDefaultGlyphs() {
|
private void initDefaultGlyphs() {
|
||||||
|
glyphResourcePack.withMojangSpaces();
|
||||||
glyphResourcePack.with(BlankSlotItem.compile());
|
glyphResourcePack.with(BlankSlotItem.compile());
|
||||||
glyphResourcePack.with(GlyphCompiler.instance().compile(BlackScreen.GLYPH));
|
glyphResourcePack.with(GlyphCompiler.instance().compile(BlackScreen.GLYPH));
|
||||||
glyphResourcePack.with(GlyphFont.compile());
|
glyphResourcePack.with(GlyphFont.compile());
|
||||||
|
MonologueRenderer.compile(glyphResourcePack);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void compile() {
|
public void compile() {
|
||||||
|
|||||||
@ -0,0 +1,128 @@
|
|||||||
|
package ru.dragonestia.msb3.api.talk;
|
||||||
|
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.kyori.adventure.text.format.Style;
|
||||||
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import ru.dragonestia.msb3.api.ServerBootstrap;
|
||||||
|
import ru.dragonestia.msb3.api.glyph.glyph.GlyphComponentBuilder;
|
||||||
|
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
|
||||||
|
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
|
||||||
|
import ru.dragonestia.msb3.api.glyph.pack.GlyphResourcePack;
|
||||||
|
import ru.dragonestia.msb3.api.glyph.pack.ResourceIdentifier;
|
||||||
|
import ru.dragonestia.msb3.api.glyph.pack.StringIdentifier;
|
||||||
|
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
||||||
|
import ru.dragonestia.msb3.api.util.StringUtil;
|
||||||
|
import team.unnamed.creative.base.Writable;
|
||||||
|
import team.unnamed.creative.texture.Texture;
|
||||||
|
|
||||||
|
public final class MonologueRenderer {
|
||||||
|
|
||||||
|
private static final Key MONOLOG_FONT_KEY = Key.key("msb3", "monolog");
|
||||||
|
private static final Writable DEFAULT_COMPANION_AVATAR_WRITABLE = ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/default_avatar.png");
|
||||||
|
private static final Writable COMPANION_AVATAR_FRAME_WRITABLE = ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/avatar_frame.png");
|
||||||
|
private static final Writable SPEECH_INDICATOR_WRITABLE = ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/speech_indicator.png");
|
||||||
|
private static final Component INDENT_COMPONENT = Component.text(" ".repeat(14));
|
||||||
|
private static final ImageGlyph AVATAR_FRAME;
|
||||||
|
private static final ImageGlyph DEFAULT_AVATAR;
|
||||||
|
private static final ImageGlyph SPEECH_INDICATOR;
|
||||||
|
private static final ResourceIdentifier<@NotNull ImageGlyph> AVATAR_FRAME_RESOURCE_IDENTIFIER;
|
||||||
|
private static final ResourceIdentifier<@NotNull ImageGlyph> SPEECH_INDICATOR_RESOURCE_IDENTIFIER;
|
||||||
|
private static final ResourceIdentifier<@NotNull ImageGlyph> DEFAULT_AVATAR_RESOURCE_IDENTIFIER;
|
||||||
|
|
||||||
|
static {
|
||||||
|
AVATAR_FRAME = ImageGlyph.of(MONOLOG_FONT_KEY,
|
||||||
|
Texture.texture().key(Key.key("msb3", "monolog/avatar_frame.png")).data(COMPANION_AVATAR_FRAME_WRITABLE).build(),
|
||||||
|
new TextureProperties(50, 6));
|
||||||
|
|
||||||
|
DEFAULT_AVATAR = ImageGlyph.of(MONOLOG_FONT_KEY,
|
||||||
|
Texture.texture().key(Key.key("msb3", "monolog/default_avatar.png")).data(DEFAULT_COMPANION_AVATAR_WRITABLE).build(),
|
||||||
|
new TextureProperties(42, 2));
|
||||||
|
|
||||||
|
SPEECH_INDICATOR = ImageGlyph.of(MONOLOG_FONT_KEY,
|
||||||
|
Texture.texture().key(Key.key("msb3", "monolog/speech_indicator.png")).data(SPEECH_INDICATOR_WRITABLE).build(),
|
||||||
|
new TextureProperties(8, 6));
|
||||||
|
|
||||||
|
AVATAR_FRAME_RESOURCE_IDENTIFIER = StringIdentifier.image("monolog_avatar_frame");
|
||||||
|
SPEECH_INDICATOR_RESOURCE_IDENTIFIER = StringIdentifier.image("monolog_speech_indicator");
|
||||||
|
DEFAULT_AVATAR_RESOURCE_IDENTIFIER = StringIdentifier.image("monolog_default_avatar");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Player player;
|
||||||
|
private final String title;
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
private MonologueRenderer(Player player, String title, String message) {
|
||||||
|
this.player = player;
|
||||||
|
this.title = title;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
player.sendMessage(toComponent(ServerBootstrap.getInstance().getResourcePackManager().getGlyphResourcePack()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MonologueRenderer create(Player player, String title, String message) {
|
||||||
|
return new MonologueRenderer(player, title, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component toComponent(@NotNull GlyphResourcePack resourcePack) {
|
||||||
|
Component text = Component.text(message);
|
||||||
|
Component[] parts = StringUtil.splitIntoParts(text, 180, string -> string.length() * 4);
|
||||||
|
if (parts != null && parts.length != 0) {
|
||||||
|
int avatarLineStart = Math.max(parts.length - 4, 0) / 2;
|
||||||
|
if (parts.length > 3) ++avatarLineStart;
|
||||||
|
|
||||||
|
var avatarComponent = GlyphComponentBuilder.universal(resourcePack.spaces())
|
||||||
|
.append(resourcePack.get(AVATAR_FRAME_RESOURCE_IDENTIFIER))
|
||||||
|
.append(4, resourcePack.get(DEFAULT_AVATAR_RESOURCE_IDENTIFIER))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
var titleComponent = Component.empty()
|
||||||
|
.append(resourcePack.get(SPEECH_INDICATOR_RESOURCE_IDENTIFIER).toAdventure())
|
||||||
|
.append(Component.space()).append(Component.text(title, NamedTextColor.WHITE, TextDecoration.BOLD));
|
||||||
|
|
||||||
|
var monolog = Component.text();
|
||||||
|
|
||||||
|
int lineIdx;
|
||||||
|
for(lineIdx = 0; lineIdx < parts.length + 2; ++lineIdx) {
|
||||||
|
if (lineIdx == avatarLineStart) {
|
||||||
|
monolog.append(avatarComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
monolog.append(INDENT_COMPONENT);
|
||||||
|
if (lineIdx == 0) {
|
||||||
|
monolog.append(Component.newline());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineIdx == 1) {
|
||||||
|
monolog.append(titleComponent).append(Component.newline());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineIdx > 1) {
|
||||||
|
monolog.append(parts[lineIdx - 2].applyFallbackStyle(Style.style(NamedTextColor.GRAY)))
|
||||||
|
.append(Component.newline());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.length < 4) {
|
||||||
|
for(lineIdx = parts.length + 2; lineIdx < 5; ++lineIdx) {
|
||||||
|
monolog.append(Component.newline());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return monolog.asComponent();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Cannot fit speech text");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compile(GlyphResourcePack resourcePack) {
|
||||||
|
resourcePack.with(DEFAULT_AVATAR_RESOURCE_IDENTIFIER, DEFAULT_AVATAR)
|
||||||
|
.with(SPEECH_INDICATOR_RESOURCE_IDENTIFIER, SPEECH_INDICATOR)
|
||||||
|
.with(AVATAR_FRAME_RESOURCE_IDENTIFIER, AVATAR_FRAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
271
api/src/main/java/ru/dragonestia/msb3/api/util/StringUtil.java
Normal file
271
api/src/main/java/ru/dragonestia/msb3/api/util/StringUtil.java
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
package ru.dragonestia.msb3.api.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.flattener.ComponentFlattener;
|
||||||
|
import net.kyori.adventure.text.flattener.FlattenerListener;
|
||||||
|
import net.kyori.adventure.text.format.Style;
|
||||||
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
|
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public final class StringUtil {
|
||||||
|
|
||||||
|
public static String[] splitIntoParts(String stringToSplit, int maxPartWidth, Function<String, Integer> stringWidthCalculateFunction) {
|
||||||
|
var partsMap = new TreeMap<Integer, String>();
|
||||||
|
int beginIndex = 0;
|
||||||
|
int endIndex = 0;
|
||||||
|
|
||||||
|
String lastLineWithTail;
|
||||||
|
for(int currentIndex = calcMinIndex(stringToSplit.indexOf(10), stringToSplit.indexOf(32)); currentIndex < stringToSplit.length() && currentIndex != -1; currentIndex = calcMinIndex(stringToSplit.indexOf(32, currentIndex + 1), stringToSplit.indexOf(10, currentIndex + 1))) {
|
||||||
|
lastLineWithTail = stringToSplit.substring(beginIndex, currentIndex);
|
||||||
|
if (!lastLineWithTail.contains("\n") && stringWidthCalculateFunction.apply(lastLineWithTail) <= maxPartWidth) {
|
||||||
|
endIndex = currentIndex + 1;
|
||||||
|
partsMap.put(beginIndex, lastLineWithTail);
|
||||||
|
} else {
|
||||||
|
if (beginIndex == endIndex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginIndex = endIndex;
|
||||||
|
--currentIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String tail = stringToSplit.substring(endIndex);
|
||||||
|
String var10000 = partsMap.containsKey(beginIndex) ? partsMap.get(beginIndex) + " " : "";
|
||||||
|
lastLineWithTail = var10000 + tail;
|
||||||
|
if (stringWidthCalculateFunction.apply(lastLineWithTail) <= maxPartWidth) {
|
||||||
|
partsMap.put(beginIndex, lastLineWithTail);
|
||||||
|
} else {
|
||||||
|
if (stringWidthCalculateFunction.apply(tail) > maxPartWidth) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
partsMap.put(endIndex, tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
return partsMap.values().toArray(String[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Component[] splitIntoParts(Component componentToSplit, int maxPartWidth, Function<String, Integer> stringWidthCalculateFunction) {
|
||||||
|
var flattenerListener = new StringSplitterFlattenerListener(maxPartWidth, stringWidthCalculateFunction);
|
||||||
|
ComponentFlattener.basic().flatten(componentToSplit, flattenerListener);
|
||||||
|
var components = flattenerListener.retrieveResult();
|
||||||
|
return components.isEmpty()? null : components.toArray(Component[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Component cutEnding(Component component, String endingSuffix) {
|
||||||
|
CutEndingFlattenerListener flattenerListener = new CutEndingFlattenerListener(endingSuffix);
|
||||||
|
ComponentFlattener.basic().flatten(component, flattenerListener);
|
||||||
|
return flattenerListener.retrieveResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int calcMinIndex(int... indexes) {
|
||||||
|
int min = Integer.MAX_VALUE;
|
||||||
|
boolean containNotMinusOne = false;
|
||||||
|
|
||||||
|
for (int index : indexes) {
|
||||||
|
if (index != -1) {
|
||||||
|
containNotMinusOne = true;
|
||||||
|
if (index < min) {
|
||||||
|
min = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return containNotMinusOne ? min : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringUtil() {
|
||||||
|
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StringSplitterFlattenerListener implements FlattenerListener {
|
||||||
|
|
||||||
|
private final Deque<TextColor> colors = new LinkedList<>();
|
||||||
|
private final List<Component> lines = new LinkedList<>();
|
||||||
|
private final int maxPartWidth;
|
||||||
|
private final Function<String, Integer> stringWidthCalculateFunction;
|
||||||
|
private Component currentComponent = Component.text("");
|
||||||
|
private boolean retrieve = false;
|
||||||
|
private boolean broken = false;
|
||||||
|
|
||||||
|
public StringSplitterFlattenerListener(int maxPartWidth, Function<String, Integer> stringWidthCalculateFunction) {
|
||||||
|
this.maxPartWidth = maxPartWidth;
|
||||||
|
this.stringWidthCalculateFunction = stringWidthCalculateFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushStyle(Style style) {
|
||||||
|
var color = style.color();
|
||||||
|
if (color != null) colors.add(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void component(@NotNull String text) {
|
||||||
|
String currentColoredPart = PlainTextComponentSerializer.plainText().serialize(currentComponent);
|
||||||
|
String resultText = currentColoredPart + text;
|
||||||
|
String[] targetLines = StringUtil.splitIntoParts(resultText, maxPartWidth, stringWidthCalculateFunction);
|
||||||
|
if (targetLines == null) broken = true;
|
||||||
|
else {
|
||||||
|
TextColor currentColor = colors.peekLast();
|
||||||
|
if (!resultText.isEmpty()) {
|
||||||
|
int startIndex = 1;
|
||||||
|
String firstLine = null;
|
||||||
|
|
||||||
|
if (targetLines[0].length() > currentColoredPart.length()) {
|
||||||
|
firstLine = targetLines[0].substring(currentColoredPart.length());
|
||||||
|
startIndex = 0;
|
||||||
|
} else {
|
||||||
|
lines.add(currentComponent);
|
||||||
|
currentComponent = Component.text("");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = startIndex; i < targetLines.length - 1; ++i) {
|
||||||
|
String targetLine = targetLines[i];
|
||||||
|
if (i == 0) {
|
||||||
|
targetLine = firstLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetLineComponent = currentColor == null ? Component.text(targetLine) : Component.text(targetLine, currentColor);
|
||||||
|
lines.add(i == 0 ? currentComponent.append(targetLineComponent) : targetLineComponent.applyFallbackStyle(Style.empty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetLines.length == 1) {
|
||||||
|
if (startIndex != 1) {
|
||||||
|
currentComponent = currentComponent.append(currentColor == null ? Component.text(firstLine).applyFallbackStyle(Style.empty()) : Component.text(firstLine, currentColor));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentComponent = Component.text("")
|
||||||
|
.append(currentColor == null ? Component.text(targetLines[targetLines.length - 1]) : Component.text(targetLines[targetLines.length - 1], currentColor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void popStyle(Style style) {
|
||||||
|
var color = style.color();
|
||||||
|
if (color != null) colors.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Component> retrieveResult() {
|
||||||
|
if (retrieve) {
|
||||||
|
throw new IllegalStateException("Result had been already retrieved");
|
||||||
|
} else {
|
||||||
|
retrieve = true;
|
||||||
|
if (broken) return List.of();
|
||||||
|
else {
|
||||||
|
if (!PlainTextComponentSerializer.plainText().serialize(currentComponent).isEmpty()) {
|
||||||
|
lines.add(currentComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.replaceAll(Component::compact);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CutEndingFlattenerListener implements FlattenerListener {
|
||||||
|
|
||||||
|
private final Deque<TextColor> colors = new LinkedList<>();
|
||||||
|
private final List<TextColorPair> pairs = new ArrayList<>();
|
||||||
|
private String endingSuffix;
|
||||||
|
private boolean retrieve = false;
|
||||||
|
|
||||||
|
public CutEndingFlattenerListener(String endingSuffix) {
|
||||||
|
this.endingSuffix = endingSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushStyle(@NotNull Style style) {
|
||||||
|
TextColor color = style.color();
|
||||||
|
if (color != null) colors.add(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void component(@NotNull String text) {
|
||||||
|
pairs.add(new TextColorPair(text, colors.peekLast()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void popStyle(@NotNull Style style) {
|
||||||
|
TextColor color = style.color();
|
||||||
|
if (color != null) colors.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Component retrieveResult() {
|
||||||
|
if (retrieve) {
|
||||||
|
throw new IllegalStateException("Result had been already retrieved");
|
||||||
|
} else {
|
||||||
|
retrieve = true;
|
||||||
|
int removed = 0;
|
||||||
|
|
||||||
|
for (int i = pairs.size() - 1; i >= 0; --i) {
|
||||||
|
TextColorPair pair = pairs.get(i);
|
||||||
|
if (pair.getColor() != null) {
|
||||||
|
pairs.remove(i);
|
||||||
|
removed += pair.getText().length();
|
||||||
|
} else {
|
||||||
|
int suffixLength = endingSuffix.length();
|
||||||
|
String var10001;
|
||||||
|
if (removed >= suffixLength) {
|
||||||
|
var10001 = pair.getText().replaceFirst(" $", "");
|
||||||
|
pair.setText(var10001 + endingSuffix);
|
||||||
|
endingSuffix = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int partLength = pair.getText().length();
|
||||||
|
if (partLength >= suffixLength) {
|
||||||
|
var10001 = pair.getText().substring(0, partLength - suffixLength).replaceFirst(" $", "");
|
||||||
|
pair.setText(var10001 + endingSuffix);
|
||||||
|
endingSuffix = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pair.setText(endingSuffix.substring(endingSuffix.length() - partLength));
|
||||||
|
endingSuffix = endingSuffix.substring(0, endingSuffix.length() - partLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!endingSuffix.isEmpty()) {
|
||||||
|
pairs.clear();
|
||||||
|
pairs.add(new TextColorPair(endingSuffix.trim(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
Component result = null;
|
||||||
|
|
||||||
|
for (TextColorPair pair: pairs) {
|
||||||
|
Component part = Component.text(pair.getText(), pair.getColor());
|
||||||
|
if (result == null) {
|
||||||
|
result = part;
|
||||||
|
} else {
|
||||||
|
result = result.append(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private static final class TextColorPair {
|
||||||
|
|
||||||
|
private String text;
|
||||||
|
private TextColor color;
|
||||||
|
|
||||||
|
public TextColorPair(String text, TextColor color) {
|
||||||
|
this.text = text;
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
api/src/main/resources/glyphs/monologue/avatar_frame.png
Normal file
BIN
api/src/main/resources/glyphs/monologue/avatar_frame.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 217 B |
BIN
api/src/main/resources/glyphs/monologue/default_avatar.png
Normal file
BIN
api/src/main/resources/glyphs/monologue/default_avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 921 B |
BIN
api/src/main/resources/glyphs/monologue/speech_indicator.png
Normal file
BIN
api/src/main/resources/glyphs/monologue/speech_indicator.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 171 B |
Loading…
x
Reference in New Issue
Block a user