feat: base of implementation of dialogue renderer

This commit is contained in:
Andrey Terentev 2024-12-02 03:49:31 +07:00
parent dd4e1b17f1
commit 190194450f
23 changed files with 626 additions and 5 deletions

View File

@ -25,8 +25,10 @@ public class GlyphFont {
public static final LanguageGlyphCollection LANGUAGE_GLYPH = minecraftFontGlyphCollection(
MINECRAFT_FONT_KEY,
Key.key(MINECRAFT_FONT_KEY.asString().concat(".png")),
MINECRAFT_FONT_IMAGE_WRITABLE,
List.of(new TextureProperties(12,-6),
new TextureProperties(8,-24),
new TextureProperties(8,8),
new TextureProperties(8,-36)));
private GlyphFont() {}
@ -35,9 +37,18 @@ public class GlyphFont {
return GlyphCompiler.instance().compile(SPACES, LANGUAGE_GLYPH);
}
private static @NotNull LanguageGlyphCollection minecraftFontGlyphCollection(@NotNull Key fontKey, @NotNull Key textureKey, @NotNull List<@NotNull TextureProperties> propertiesList) {
public static @NotNull LanguageGlyphCollection minecraftFontGlyphCollection(@NotNull Key fontKey, @NotNull Key textureKey, @NotNull List<@NotNull TextureProperties> propertiesList) {
return minecraftFontGlyphCollection(fontKey, textureKey, MINECRAFT_FONT_IMAGE_WRITABLE, propertiesList);
}
public static @NotNull LanguageGlyphCollection minecraftFontGlyphCollection(
@NotNull Key fontKey,
@NotNull Key textureKey,
@NotNull Writable writable,
@NotNull List<@NotNull TextureProperties> propertiesList
) {
return LanguageGlyphCollection.of(fontKey,
Texture.texture(textureKey, MINECRAFT_FONT_IMAGE_WRITABLE),
Texture.texture(textureKey, writable),
propertiesList,
List.of(" АБВГДЕЖЗИК",
"ЛМНОПРСТУФХЦЧШЩЪ",

View File

@ -1,6 +1,7 @@
package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -32,6 +33,8 @@ public interface LanguageGlyphCollection extends ResourceProducer {
@NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull String text, @Nullable TextColor color) throws IllegalArgumentException;
@NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull Component text) throws IllegalArgumentException;
default @NotNull AppendableGlyph translate(int height, int ascent, @NotNull Character character) throws IllegalArgumentException {
return translate(height, ascent, character, null);
}

View File

@ -1,6 +1,7 @@
package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -9,6 +10,7 @@ import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph;
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException;
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
import ru.dragonestia.msb3.api.glyph.util.KyoriUtil;
import team.unnamed.creative.font.FontProvider;
import team.unnamed.creative.texture.Texture;
@ -85,8 +87,22 @@ public class LanguageGlyphCollectionImpl implements LanguageGlyphCollection {
public @NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull String text, @Nullable TextColor color) throws IllegalArgumentException {
TextureProperties properties = new TextureProperties(height, ascent);
if (!propertiesToMulticharacterMap.containsKey(properties)) {
throw new IllegalArgumentException();
throw new IllegalArgumentException(properties.toString());
}
return propertiesToMulticharacterMap.get(properties).translate(text, color);
}
public @NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull Component component) throws IllegalArgumentException {
var property = new TextureProperties(height, ascent);
var glyphCollection = Objects.requireNonNull(propertiesToMulticharacterMap.get(property), property.toString());
var result = new ArrayList<AppendableGlyph>();
List<? extends KyoriUtil.ColoredPartsFlattenerListener.ColoredComponentTextPart> textAndColors = KyoriUtil.toColoredParts(component);
for (var parts: textAndColors) {
result.addAll(glyphCollection.translate(parts.text(), parts.color()));
}
return Collections.unmodifiableList(result);
}
}

View File

@ -0,0 +1,57 @@
package ru.dragonestia.msb3.api.glyph.util;
import lombok.experimental.UtilityClass;
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.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
@UtilityClass
public class KyoriUtil {
public @NotNull List<? extends ColoredPartsFlattenerListener.ColoredComponentTextPart> toColoredParts(@NotNull Component component) {
var listener = new ColoredPartsFlattenerListener();
ComponentFlattener.basic().flatten(component, listener);
return listener.result();
}
public static class ColoredPartsFlattenerListener implements FlattenerListener {
private final Deque<TextColor> colors = new LinkedList<>();
private final List<ColoredComponentTextPart> result = new ArrayList<>();
public void pushStyle(@NotNull Style style) {
var color = style.color();
if (color != null) colors.add(color);
}
public void component(@NotNull String text) {
result.add(new ColoredComponentTextPart(text, current()));
}
public void popStyle(@NotNull Style style) {
var color = style.color();
if (color != null) colors.removeLast();
}
private TextColor current() {
var color = colors.peekLast();
return color != null ? color : NamedTextColor.WHITE;
}
public @NotNull List<? extends @NotNull ColoredComponentTextPart> result() {
return result;
}
public record ColoredComponentTextPart(String text, TextColor color) {}
}
}

View File

@ -7,17 +7,18 @@ import ru.dragonestia.msb3.api.glyph.compile.GlyphCompiler;
import ru.dragonestia.msb3.api.glyph.font.GlyphFont;
import ru.dragonestia.msb3.api.glyph.pack.GlyphResourcePack;
import ru.dragonestia.msb3.api.item.BlankSlotItem;
import ru.dragonestia.msb3.api.talk.dialogue.DialogueRenderer;
import ru.dragonestia.msb3.api.title.BlackScreen;
import team.unnamed.creative.ResourcePack;
import team.unnamed.creative.atlas.Atlas;
import team.unnamed.creative.atlas.AtlasSource;
import team.unnamed.creative.base.Writable;
import team.unnamed.creative.lang.Language;
import team.unnamed.creative.model.Model;
import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackWriter;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
@Getter
@Log4j2
@ -35,7 +36,9 @@ public class ResourcePackManager {
glyphResourcePack = GlyphResourcePack.create();
monologueResources = new MonologueResources(glyphResourcePack);
DialogueRenderer.compile(glyphResourcePack);
initDefaultGlyphs();
initLocales();
}
private void initDefaultGlyphs() {
@ -46,6 +49,15 @@ public class ResourcePackManager {
monologueResources.compile(glyphResourcePack);
}
private void initLocales() {
var locales = new HashMap<String, String>();
locales.put("container.inventory", " ");
for (var lang: List.of("en_us", "ru_ru")) {
resourcePack.language(Language.language(Key.key("minecraft", lang), locales));
}
}
public void compile() {
glyphResourcePack.writeAll(resourcePack);
generateAtlases();

View File

@ -0,0 +1,82 @@
package ru.dragonestia.msb3.api.talk.dialogue;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
public record DialogGlyphPositions(GuiBackground guiBackground,
PhraseText phraseText,
PhraseSubstrate phraseSubstrate,
Avatar avatar,
AnswerText answerText,
AnswerField answerField,
AnswerButton answerButton,
ScrollPhraseButton scrollPhraseButton) {
public static final DialogGlyphPositions DEFAULT = new DialogGlyphPositions(
new GuiBackground(35, -96, 262),
new PhraseText(11, -11, 340, 8, 8, "#000000"),
new PhraseSubstrate(-24, 19, 356, 116),
new Avatar(-144, 11, 100, 116),
new AnswerText(175, 4, 8, -145, 149, -122, -170, "#000000"),
new AnswerField(-117, -165, -153, 141, 44),
new AnswerButton(-124, -182, 41, 97, 20, "#a18262", "#ffd8a8"),
new ScrollPhraseButton(0, 126, -100, 10, "#a18262")
);
public record GuiBackground(int topPartsY, int bottomPartsY, int height) {}
public record PhraseText(int maxLines, int lineX, int lineWidth, int fontHeight, int firstLineAscent, String textColorHex) {
public TextColor textColor() {
return TextColor.fromHexString(textColorHex);
}
public TextureProperties firstLineProperties() {
return new TextureProperties(fontHeight, firstLineAscent);
}
}
public record PhraseSubstrate(int x, int y, int width, int height) {}
public record Avatar(int x, int y, int height, int frameHeight) {
public int frameBorderSize() {
return (frameHeight - height) / 2;
}
}
public record AnswerText(int lineWidth, int maxLines, int fontHeight, int leftLineX,
int rightLineX, int topFirstLineAscent, int bottomFirstLineAscent,
String textColorHex) {
public TextColor textColor() {
return TextColor.fromHexString(textColorHex);
}
}
public record AnswerField(int topFieldY, int bottomFieldY, int leftFieldX, int rightFieldX, int height) {}
public record AnswerButton(int topButtonY, int bottomButtonY, int leftButtonX,
int rightButtonX, int height,
String loreHeadingTextColorHex,
String loreFallbackTextColorHex) {
public Style loreHeadingTextStyle() {
return Style.style(TextColor.fromHexString(loreHeadingTextColorHex), TextDecoration.ITALIC.withState(false));
}
public Style loreFallbackTextStyle() {
return Style.style(TextColor.fromHexString(loreFallbackTextColorHex), TextDecoration.ITALIC.withState(false));
}
}
public record ScrollPhraseButton(int scrollUpButtonX, int scrollDownButtonX, int buttonY, int height, String textColorHex) {
public Style textStyle() {
return Style.style(TextColor.fromHexString(textColorHex),
TextDecoration.ITALIC.withState(false));
}
}
}

View File

@ -0,0 +1,440 @@
package ru.dragonestia.msb3.api.talk.dialogue;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.minestom.server.entity.Player;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.InventoryType;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.msb3.api.ServerBootstrap;
import ru.dragonestia.msb3.api.glyph.font.GlyphFont;
import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph;
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.glyph.image.multicharacter.LanguageGlyphCollection;
import ru.dragonestia.msb3.api.glyph.pack.GlyphResourcePack;
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;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class DialogueRenderer {
public static final int CHEST_GUI_WIDTH = 176;
private static final Key DIALOGUE_FONT_KEY = Key.key("msb3", "dialog");
private static final Writable ANSWER_ACTIVE_TEXT_FIELD = ResourceFromJar.of("glyphs/dialogue/answer_active_text_field.png");
private static final Writable ANSWER_BUTTON_ACTIVE_1 = ResourceFromJar.of("glyphs/dialogue/answer_button_active_1.png");
private static final Writable ANSWER_BUTTON_ACTIVE_2 = ResourceFromJar.of("glyphs/dialogue/answer_button_active_2.png");
private static final Writable ANSWER_BUTTON_ACTIVE_3 = ResourceFromJar.of("glyphs/dialogue/answer_button_active_3.png");
private static final Writable ANSWER_BUTTON_ACTIVE_4 = ResourceFromJar.of("glyphs/dialogue/answer_button_active_4.png");
private static final Writable ANSWER_NOT_ACTIVE_TEXT_FIELD = ResourceFromJar.of("glyphs/dialogue/answer_not_active_text_field.png");
private static final Writable AVATAR_FRAME = ResourceFromJar.of("glyphs/dialogue/avatar_frame.png");
private static final Writable BACKGROUND_1 = ResourceFromJar.of("glyphs/dialogue/background_1.png");
private static final Writable BACKGROUND_2 = ResourceFromJar.of("glyphs/dialogue/background_2.png");
private static final Writable BACKGROUND_3 = ResourceFromJar.of("glyphs/dialogue/background_3.png");
private static final Writable BACKGROUND_4 = ResourceFromJar.of("glyphs/dialogue/background_4.png");
private static final Writable DEFAULT_AVATAR = ResourceFromJar.of("glyphs/dialogue/default_avatar.png");
private static final Writable PHRASE_SUBSTRATE_1 = ResourceFromJar.of("glyphs/dialogue/phrase_substrate_1.png");
private static final Writable PHRASE_SUBSTRATE_2 = ResourceFromJar.of("glyphs/dialogue/phrase_substrate_2.png");
private static final Writable SCROLL_PHRASE_DOWN_BUTTON = ResourceFromJar.of("glyphs/dialogue/scroll_phrase_down_button.png");
private static final Writable SCROLL_PHRASE_UP_BUTTON = ResourceFromJar.of("glyphs/dialogue/scroll_phrase_up_button.png");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_ACTIVE_TEXT_FIELD_1 = StringIdentifier.image("dialog_answer_active_text_field_1");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_ACTIVE_TEXT_FIELD_2 = StringIdentifier.image("dialog_answer_active_text_field_2");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_ACTIVE_TEXT_FIELD_3 = StringIdentifier.image("dialog_answer_active_text_field_3");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_ACTIVE_TEXT_FIELD_4 = StringIdentifier.image("dialog_answer_active_text_field_4");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_BUTTON_ACTIVE_1 = StringIdentifier.image("dialog_answer_button_active_1");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_BUTTON_ACTIVE_2 = StringIdentifier.image("dialog_answer_button_active_2");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_BUTTON_ACTIVE_3 = StringIdentifier.image("dialog_answer_button_active_3");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_BUTTON_ACTIVE_4 = StringIdentifier.image("dialog_answer_button_active_4");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_1 = StringIdentifier.image("dialog_answer_not_active_text_field_1");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_2 = StringIdentifier.image("dialog_answer_not_active_text_field_2");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_3 = StringIdentifier.image("dialog_answer_not_active_text_field_3");
private static final StringIdentifier<@NotNull ImageGlyph> ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_4 = StringIdentifier.image("dialog_answer_not_active_text_field_4");
private static final StringIdentifier<@NotNull ImageGlyph> ID_AVATAR_FRAME = StringIdentifier.image("dialog_avatar_frame");
private static final StringIdentifier<@NotNull ImageGlyph> ID_BACKGROUND_1 = StringIdentifier.image("dialog_background_1");
private static final StringIdentifier<@NotNull ImageGlyph> ID_BACKGROUND_2 = StringIdentifier.image("dialog_background_2");
private static final StringIdentifier<@NotNull ImageGlyph> ID_BACKGROUND_3 = StringIdentifier.image("dialog_background_3");
private static final StringIdentifier<@NotNull ImageGlyph> ID_BACKGROUND_4 = StringIdentifier.image("dialog_background_4");
private static final StringIdentifier<@NotNull ImageGlyph> ID_DEFAULT_AVATAR = StringIdentifier.image("dialog_default_avatar");
private static final StringIdentifier<@NotNull ImageGlyph> ID_PHRASE_SUBSTRATE_1 = StringIdentifier.image("dialog_phrase_substrate_1");
private static final StringIdentifier<@NotNull ImageGlyph> ID_PHRASE_SUBSTRATE_2 = StringIdentifier.image("dialog_phrase_substrate_2");
private static final StringIdentifier<@NotNull ImageGlyph> ID_SCROLL_PHRASE_DOWN_BUTTON = StringIdentifier.image("dialog_scroll_phrase_down_button");
private static final StringIdentifier<@NotNull ImageGlyph> ID_SCROLL_PHRASE_UP_BUTTON = StringIdentifier.image("dialog_scroll_phrase_up_button");
private static final ImageGlyph GLYPH_ANSWER_ACTIVE_TEXT_FIELD_1 = createGlyph("dialog/answer_active_text_field.png",
ANSWER_ACTIVE_TEXT_FIELD,
DialogGlyphPositions.DEFAULT.answerField().height(),
DialogGlyphPositions.DEFAULT.answerField().topFieldY());
private static final ImageGlyph GLYPH_ANSWER_ACTIVE_TEXT_FIELD_2 = createGlyph("dialog/answer_active_text_field.png",
ANSWER_ACTIVE_TEXT_FIELD,
DialogGlyphPositions.DEFAULT.answerField().height(),
DialogGlyphPositions.DEFAULT.answerField().topFieldY());
private static final ImageGlyph GLYPH_ANSWER_ACTIVE_TEXT_FIELD_3 = createGlyph("dialog/answer_active_text_field.png",
ANSWER_ACTIVE_TEXT_FIELD,
DialogGlyphPositions.DEFAULT.answerField().height(),
DialogGlyphPositions.DEFAULT.answerField().bottomFieldY());
private static final ImageGlyph GLYPH_ANSWER_ACTIVE_TEXT_FIELD_4 = createGlyph("dialog/answer_active_text_field.png",
ANSWER_ACTIVE_TEXT_FIELD,
DialogGlyphPositions.DEFAULT.answerField().height(),
DialogGlyphPositions.DEFAULT.answerField().bottomFieldY());
private static final ImageGlyph GLYPH_ANSWER_BUTTON_ACTIVE_1 = createGlyph("dialog/answer_button_active_1.png",
ANSWER_BUTTON_ACTIVE_1,
DialogGlyphPositions.DEFAULT.answerButton().height(),
DialogGlyphPositions.DEFAULT.answerButton().topButtonY());
private static final ImageGlyph GLYPH_ANSWER_BUTTON_ACTIVE_2 = createGlyph("dialog/answer_button_active_2.png",
ANSWER_BUTTON_ACTIVE_2,
DialogGlyphPositions.DEFAULT.answerButton().height(),
DialogGlyphPositions.DEFAULT.answerButton().topButtonY());
private static final ImageGlyph GLYPH_ANSWER_BUTTON_ACTIVE_3 = createGlyph("dialog/answer_button_active_3.png",
ANSWER_BUTTON_ACTIVE_3,
DialogGlyphPositions.DEFAULT.answerButton().height(),
DialogGlyphPositions.DEFAULT.answerButton().bottomButtonY());
private static final ImageGlyph GLYPH_ANSWER_BUTTON_ACTIVE_4 = createGlyph("dialog/answer_button_active_4.png",
ANSWER_BUTTON_ACTIVE_4,
DialogGlyphPositions.DEFAULT.answerButton().height(),
DialogGlyphPositions.DEFAULT.answerButton().bottomButtonY());
private static final ImageGlyph GLYPH_ANSWER_NOT_ACTIVE_TEXT_FIELD_1 = createGlyph("dialog/answer_not_active_text_field.png",
ANSWER_NOT_ACTIVE_TEXT_FIELD,
DialogGlyphPositions.DEFAULT.answerField().height(),
DialogGlyphPositions.DEFAULT.answerField().topFieldY());
private static final ImageGlyph GLYPH_ANSWER_NOT_ACTIVE_TEXT_FIELD_2 = createGlyph("dialog/answer_not_active_text_field.png",
ANSWER_NOT_ACTIVE_TEXT_FIELD,
DialogGlyphPositions.DEFAULT.answerField().height(),
DialogGlyphPositions.DEFAULT.answerField().topFieldY());
private static final ImageGlyph GLYPH_ANSWER_NOT_ACTIVE_TEXT_FIELD_3 = createGlyph("dialog/answer_not_active_text_field.png",
ANSWER_NOT_ACTIVE_TEXT_FIELD,
DialogGlyphPositions.DEFAULT.answerField().height(),
DialogGlyphPositions.DEFAULT.answerField().bottomFieldY());
private static final ImageGlyph GLYPH_ANSWER_NOT_ACTIVE_TEXT_FIELD_4 = createGlyph("dialog/answer_not_active_text_field.png",
ANSWER_NOT_ACTIVE_TEXT_FIELD,
DialogGlyphPositions.DEFAULT.answerField().height(),
DialogGlyphPositions.DEFAULT.answerField().bottomFieldY());
private static final ImageGlyph GLYPH_AVATAR_FRAME = createGlyph("dialog/avatar_frame.png",
AVATAR_FRAME,
DialogGlyphPositions.DEFAULT.avatar().frameHeight(),
DialogGlyphPositions.DEFAULT.avatar().y() + DialogGlyphPositions.DEFAULT.avatar().frameBorderSize());
private static final ImageGlyph GLYPH_BACKGROUND_1 = createGlyph("dialog/background_1.png",
BACKGROUND_1,
DialogGlyphPositions.DEFAULT.guiBackground().height() / 2,
DialogGlyphPositions.DEFAULT.guiBackground().topPartsY());
private static final ImageGlyph GLYPH_BACKGROUND_2 = createGlyph("dialog/background_2.png",
BACKGROUND_2,
DialogGlyphPositions.DEFAULT.guiBackground().height() / 2,
DialogGlyphPositions.DEFAULT.guiBackground().topPartsY());
private static final ImageGlyph GLYPH_BACKGROUND_3 = createGlyph("dialog/background_3.png",
BACKGROUND_3,
DialogGlyphPositions.DEFAULT.guiBackground().height() / 2,
DialogGlyphPositions.DEFAULT.guiBackground().bottomPartsY());
private static final ImageGlyph GLYPH_BACKGROUND_4 = createGlyph("dialog/background_4.png",
BACKGROUND_4,
DialogGlyphPositions.DEFAULT.guiBackground().height() / 2,
DialogGlyphPositions.DEFAULT.guiBackground().bottomPartsY());
private static final ImageGlyph GLYPH_DEFAULT_AVATAR = createGlyph("dialog/default_avatar.png",
DEFAULT_AVATAR,
DialogGlyphPositions.DEFAULT.avatar().height(),
DialogGlyphPositions.DEFAULT.avatar().y());
private static final ImageGlyph GLYPH_PHRASE_SUBSTRATE_1 = createGlyph("dialog/phrase_substrate_1.png",
PHRASE_SUBSTRATE_1,
DialogGlyphPositions.DEFAULT.phraseSubstrate().height(),
DialogGlyphPositions.DEFAULT.phraseSubstrate().y());
private static final ImageGlyph GLYPH_PHRASE_SUBSTRATE_2 = createGlyph("dialog/phrase_substrate_2.png",
PHRASE_SUBSTRATE_2,
DialogGlyphPositions.DEFAULT.phraseSubstrate().height(),
DialogGlyphPositions.DEFAULT.phraseSubstrate().y());
private static final ImageGlyph GLYPH_SCROLL_PHRASE_DOWN_BUTTON = createGlyph("dialog/scroll_phrase_down_button.png",
SCROLL_PHRASE_DOWN_BUTTON,
DialogGlyphPositions.DEFAULT.scrollPhraseButton().height(),
DialogGlyphPositions.DEFAULT.scrollPhraseButton().buttonY());
private static final ImageGlyph GLYPH_SCROLL_PHRASE_UP_BUTTON = createGlyph("dialog/scroll_phrase_up_button.png",
SCROLL_PHRASE_UP_BUTTON,
DialogGlyphPositions.DEFAULT.scrollPhraseButton().height(),
DialogGlyphPositions.DEFAULT.scrollPhraseButton().buttonY());
private static final LanguageGlyphCollection GLYPH_FONT;
private static final StringIdentifier<@NotNull LanguageGlyphCollection> ID_FONT = StringIdentifier.of("dialog_font", LanguageGlyphCollection.class);
static {
var positions = DialogGlyphPositions.DEFAULT;
var propertiesList = Stream.concat(Stream.concat(IntStream.range(0, positions.phraseText().maxLines()).map((idx) -> {
return idx * (positions.phraseText().fontHeight() + 1) + positions.phraseText().firstLineAscent() * -1;
}).boxed().map((ascent) -> {
return new TextureProperties(positions.phraseText().fontHeight(), ascent * -1);
}), IntStream.range(0, positions.answerText().maxLines()).map((idx) -> {
return idx * (positions.answerText().fontHeight() + 1) + positions.answerText().topFirstLineAscent() * -1;
}).boxed().map((ascent) -> {
return new TextureProperties(positions.answerText().fontHeight(), ascent * -1);
})), IntStream.range(0, positions.answerText().maxLines()).map((idx) -> {
return idx * (positions.answerText().fontHeight() + 1) + positions.answerText().bottomFirstLineAscent() * -1;
}).boxed().map((ascent) -> {
return new TextureProperties(positions.answerText().fontHeight(), ascent * -1);
})).toList();
GLYPH_FONT = GlyphFont.minecraftFontGlyphCollection(DIALOGUE_FONT_KEY, Key.key("dialog/font.png"), propertiesList);
}
private final Player player;
private final String text = """
Абсолютно точно.
Я знаю точнo - невозможное возможно
Сойти с ума, влюбиться так неосторoжно
Найти тебя, не отпускать ни днём, ни ночью
Всё невозможное - возможно, знаю точно!
А где тебя искать, прошу ты мне ответь
В какие города мне за тобой лететь
Я готов на край Земли, я всё должен объяснить
Пойми, что без тебя я не умею жить
Я знаю точно - невозможное возможно
Сойти с ума, влюбиться так неосторожно
Найти тебя, не отпускать ни днём, ни ночью
Всё невозможное - возможно, знаю точно!
На-на-на-на (на-на-на-на), а-а, а-а
На-на-на-на (на-на-на-на), а-а, а-а
Всё готов делить, с тобой я пополам
Ты только мне поверь, я сделал выбор сам
Дай же мне последний шанс, я всё должен объяснить
Пойми, что без тебя я не умею жить
Я знаю точно - невозможное возможно
Сойти с ума, влюбиться так неосторожно
Найти тебя, не отпускать ни днём, ни ночью
Всё невозможное - возможно, знаю точно!
На-на-на-на (на-на-на-на), а-а, а-а
На-на-на-на (на-на-на-на), а-а, а-а
Я знаю точно - невозможное возможно
Сойти с ума, влюбиться так неосторожно
Найти тебя, не отпускать ни днём, ни ночью
Всё невозможное - возможно, знаю точно!
На-на-на-на (на-на-на-на), а-а, а-а
На-на-на-на (на-на-на-на), а-а, а-а""";
private DialogueRenderer(Player player) {
this.player = player;
}
public static DialogueRenderer create(Player player) {
return new DialogueRenderer(player);
}
public void show() {
var inv = new Inventory(InventoryType.CHEST_6_ROW, render(ServerBootstrap.getInstance().getResourcePackManager().getGlyphResourcePack()));
player.openInventory(inv);
}
private Component render(GlyphResourcePack resourcePack) {
var positions = DialogGlyphPositions.DEFAULT;
var avatar = resourcePack.get(ID_DEFAULT_AVATAR);
var avatarFrame = resourcePack.get(ID_AVATAR_FRAME);
var guiBackground1 = resourcePack.get(ID_BACKGROUND_1);
var guiBackground2 = resourcePack.get(ID_BACKGROUND_2);
var guiBackground3 = resourcePack.get(ID_BACKGROUND_3);
var guiBackground4 = resourcePack.get(ID_BACKGROUND_4);
var phraseSubstrate1 = resourcePack.get(ID_PHRASE_SUBSTRATE_1);
var phraseSubstrate2 = resourcePack.get(ID_PHRASE_SUBSTRATE_2);
var builder = GlyphComponentBuilder.gui(resourcePack.spaces());
// GUI background
builder.append(CHEST_GUI_WIDTH / 2 + 2 - guiBackground1.width(), guiBackground1);
builder.append(CHEST_GUI_WIDTH / 2 + 1, guiBackground2);
builder.append(CHEST_GUI_WIDTH / 2 + 2 - guiBackground3.width(), guiBackground3);
builder.append(CHEST_GUI_WIDTH / 2 + 1, guiBackground4);
// Phrase substrate
builder.append(positions.phraseSubstrate().x(), phraseSubstrate1);
builder.append(positions.phraseSubstrate().x() + positions.phraseSubstrate().width() / 2, phraseSubstrate2);
// Frame and avatar
builder.append(positions.avatar().x() - 8, avatarFrame);
builder.append(positions.avatar().x(), avatar);
// Phrase text
renderText(builder, 0);
// Scroll phrase buttons (optional)
builder.append(
positions.scrollPhraseButton().scrollUpButtonX(),
resourcePack.get(ID_SCROLL_PHRASE_UP_BUTTON));
builder.append(
positions.scrollPhraseButton().scrollDownButtonX(),
resourcePack.get(ID_SCROLL_PHRASE_DOWN_BUTTON));
// Answers
var answers = List.of("Hello world!", "I am a teapot", "I love pizza", "msb3 is top!");
for (int i = 0; i < 4; i++) {
if (true) { //TODO: checking answer exists
builder.append(i % 2 == 0
? positions.answerField().leftFieldX()
: positions.answerField().rightFieldX(),
switch (i) {
case 0 -> resourcePack.get(ID_ANSWER_ACTIVE_TEXT_FIELD_1);
case 1 -> resourcePack.get(ID_ANSWER_ACTIVE_TEXT_FIELD_2);
case 2 -> resourcePack.get(ID_ANSWER_ACTIVE_TEXT_FIELD_3);
case 3 -> resourcePack.get(ID_ANSWER_ACTIVE_TEXT_FIELD_4);
default -> throw new IllegalStateException("Unexpected value: " + i);
});
builder.append(i % 2 == 0
? positions.answerButton().leftButtonX()
: positions.answerButton().rightButtonX(),
switch (i) {
case 0 -> resourcePack.get(ID_ANSWER_BUTTON_ACTIVE_1);
case 1 -> resourcePack.get(ID_ANSWER_BUTTON_ACTIVE_2);
case 2 -> resourcePack.get(ID_ANSWER_BUTTON_ACTIVE_3);
case 3 -> resourcePack.get(ID_ANSWER_BUTTON_ACTIVE_4);
default -> throw new IllegalStateException("Unexpected value: " + i);
});
var lines = breakIntoLines(
answers.get(i),
i < 2
? new TextureProperties(positions.answerText().fontHeight(),
positions.answerText().topFirstLineAscent())
: new TextureProperties(
positions.answerText().fontHeight(),
positions.answerText().bottomFirstLineAscent()),
positions.answerText().fontHeight(),
positions.answerText().lineWidth());
for (int lineIdx = 0;
lineIdx < Math.min(lines.size(), positions.answerText().maxLines());
lineIdx++) {
boolean endWithDots = lineIdx + 1 == positions.answerText().maxLines()
&& lineIdx + 1 != lines.size();
var line = lines.get(lineIdx);
builder.append(i % 2 == 0
? positions.answerText().leftLineX()
: positions.answerText().rightLineX(), line.toGlyphList(0, endWithDots, positions.answerText().textColor()));
}
} else {
builder.append(i % 2 == 0
? positions.answerField().leftFieldX()
: positions.answerField().rightFieldX(),
switch (i) {
case 0 -> resourcePack.get(ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_1);
case 1 -> resourcePack.get(ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_2);
case 2 -> resourcePack.get(ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_3);
case 3 -> resourcePack.get(ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_4);
default -> throw new IllegalStateException("Unexpected value: " + i);
});
}
}
return builder.build();
}
private List<TextLine> breakIntoLines(String input, TextureProperties firstLineProperties, int fontHeight, int maxWidth) throws IllegalStateException {
var resolvedText = MiniMessage.miniMessage().deserialize(input);
var lines = StringUtil.splitIntoParts(resolvedText, maxWidth, line -> {
int width = 0;
for (var glyph: GLYPH_FONT.translate(firstLineProperties.height(), firstLineProperties.ascent(), line)) {
width += glyph.width();
}
return width;
});
if (lines == null) {
throw new IllegalStateException("Cannot fit text into dialog");
}
var textLines = new ArrayList<TextLine>();
for (int lineIdx = 0; lineIdx < lines.length; lineIdx++) {
var text = lines[lineIdx];
textLines.add(new TextLine(text, new TextureProperties(firstLineProperties.height(), firstLineProperties.ascent() - lineIdx * (fontHeight + 1))));
}
return textLines;
}
private void renderText(GlyphComponentBuilder builder, int shift) {
var positions = DialogGlyphPositions.DEFAULT;
var textLines = breakIntoLines(
text,
positions.phraseText().firstLineProperties(),
positions.phraseText().fontHeight(),
positions.phraseText().lineWidth());
for (int lineIdx = shift; (lineIdx - shift) < Math.min(textLines.size() - shift, positions.phraseText().maxLines()); lineIdx++) {
var line = textLines.get(lineIdx);
builder.append(positions.phraseText().lineX(), line.toGlyphList(
-lineIdx + shift,
textLines.size() - lineIdx > 0 && lineIdx - shift == positions.phraseText().maxLines() - 1,
positions.phraseText().textColor()
));
}
}
public static void compile(GlyphResourcePack resourcePack) {
resourcePack.with(ID_ANSWER_ACTIVE_TEXT_FIELD_1, GLYPH_ANSWER_ACTIVE_TEXT_FIELD_1)
.with(ID_ANSWER_ACTIVE_TEXT_FIELD_2, GLYPH_ANSWER_ACTIVE_TEXT_FIELD_2)
.with(ID_ANSWER_ACTIVE_TEXT_FIELD_3, GLYPH_ANSWER_ACTIVE_TEXT_FIELD_3)
.with(ID_ANSWER_ACTIVE_TEXT_FIELD_4, GLYPH_ANSWER_ACTIVE_TEXT_FIELD_4)
.with(ID_ANSWER_BUTTON_ACTIVE_1, GLYPH_ANSWER_BUTTON_ACTIVE_1)
.with(ID_ANSWER_BUTTON_ACTIVE_2, GLYPH_ANSWER_BUTTON_ACTIVE_2)
.with(ID_ANSWER_BUTTON_ACTIVE_3, GLYPH_ANSWER_BUTTON_ACTIVE_3)
.with(ID_ANSWER_BUTTON_ACTIVE_4, GLYPH_ANSWER_BUTTON_ACTIVE_4)
.with(ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_1, GLYPH_ANSWER_NOT_ACTIVE_TEXT_FIELD_1)
.with(ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_2, GLYPH_ANSWER_NOT_ACTIVE_TEXT_FIELD_2)
.with(ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_3, GLYPH_ANSWER_NOT_ACTIVE_TEXT_FIELD_3)
.with(ID_ANSWER_NOT_ACTIVE_TEXT_FIELD_4, GLYPH_ANSWER_NOT_ACTIVE_TEXT_FIELD_4)
.with(ID_AVATAR_FRAME, GLYPH_AVATAR_FRAME)
.with(ID_BACKGROUND_1, GLYPH_BACKGROUND_1)
.with(ID_BACKGROUND_2, GLYPH_BACKGROUND_2)
.with(ID_BACKGROUND_3, GLYPH_BACKGROUND_3)
.with(ID_BACKGROUND_4, GLYPH_BACKGROUND_4)
.with(ID_DEFAULT_AVATAR, GLYPH_DEFAULT_AVATAR)
.with(ID_PHRASE_SUBSTRATE_1, GLYPH_PHRASE_SUBSTRATE_1)
.with(ID_PHRASE_SUBSTRATE_2, GLYPH_PHRASE_SUBSTRATE_2)
.with(ID_SCROLL_PHRASE_DOWN_BUTTON, GLYPH_SCROLL_PHRASE_DOWN_BUTTON)
.with(ID_SCROLL_PHRASE_UP_BUTTON, GLYPH_SCROLL_PHRASE_UP_BUTTON)
.with(ID_FONT, GLYPH_FONT);
}
private static ImageGlyph createGlyph(String texturePath, Writable writable, int height, int ascent) {
return ImageGlyph.of(DIALOGUE_FONT_KEY,
Texture.texture().key(Key.key("msb3", texturePath)).data(writable).build(),
new TextureProperties(height, ascent));
}
private record TextLine(Component text, TextureProperties textureProperties) {
public List<@NotNull AppendableGlyph> toGlyphList(int lineShift, boolean cutEnding, TextColor color) {
var positions = DialogGlyphPositions.DEFAULT;
var properties = new TextureProperties(
positions.phraseText().firstLineProperties().height(),
positions.phraseText().firstLineProperties().ascent() + lineShift * (textureProperties().height() + 1)
);
Component text = this.text;
if (cutEnding) text = StringUtil.cutEnding(text, " <...>");
text = text.applyFallbackStyle(Style.style(color));
return toList((TextComponent) text, properties);
}
private List<AppendableGlyph> toList(TextComponent text, TextureProperties properties) {
return GLYPH_FONT.translate(properties.height(), properties.ascent(), text);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B