From 190194450f91fe4efe02319cd97174d36e8c2ad6 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Mon, 2 Dec 2024 03:49:31 +0700 Subject: [PATCH 01/49] feat: base of implementation of dialogue renderer --- .../msb3/api/glyph/font/GlyphFont.java | 15 +- .../LanguageGlyphCollection.java | 3 + .../LanguageGlyphCollectionImpl.java | 18 +- .../msb3/api/glyph/util/KyoriUtil.java | 57 +++ .../api/resource/ResourcePackManager.java | 16 +- .../talk/dialogue/DialogGlyphPositions.java | 82 ++++ .../api/talk/dialogue/DialogueRenderer.java | 440 ++++++++++++++++++ .../dialogue/answer_active_text_field.png | Bin 0 -> 445 bytes .../dialogue/answer_button_active_1.png | Bin 0 -> 281 bytes .../dialogue/answer_button_active_2.png | Bin 0 -> 305 bytes .../dialogue/answer_button_active_3.png | Bin 0 -> 284 bytes .../dialogue/answer_button_active_4.png | Bin 0 -> 301 bytes .../dialogue/answer_not_active_text_field.png | Bin 0 -> 442 bytes .../glyphs/dialogue/avatar_frame.png | Bin 0 -> 648 bytes .../glyphs/dialogue/background_1.png | Bin 0 -> 4893 bytes .../glyphs/dialogue/background_2.png | Bin 0 -> 3909 bytes .../glyphs/dialogue/background_3.png | Bin 0 -> 6208 bytes .../glyphs/dialogue/background_4.png | Bin 0 -> 5957 bytes .../glyphs/dialogue/default_avatar.png | Bin 0 -> 2761 bytes .../glyphs/dialogue/phrase_substrate_1.png | Bin 0 -> 739 bytes .../glyphs/dialogue/phrase_substrate_2.png | Bin 0 -> 796 bytes .../dialogue/scroll_phrase_down_button.png | Bin 0 -> 246 bytes .../dialogue/scroll_phrase_up_button.png | Bin 0 -> 234 bytes 23 files changed, 626 insertions(+), 5 deletions(-) create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/glyph/util/KyoriUtil.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogGlyphPositions.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java create mode 100644 api/src/main/resources/glyphs/dialogue/answer_active_text_field.png create mode 100644 api/src/main/resources/glyphs/dialogue/answer_button_active_1.png create mode 100644 api/src/main/resources/glyphs/dialogue/answer_button_active_2.png create mode 100644 api/src/main/resources/glyphs/dialogue/answer_button_active_3.png create mode 100644 api/src/main/resources/glyphs/dialogue/answer_button_active_4.png create mode 100644 api/src/main/resources/glyphs/dialogue/answer_not_active_text_field.png create mode 100644 api/src/main/resources/glyphs/dialogue/avatar_frame.png create mode 100644 api/src/main/resources/glyphs/dialogue/background_1.png create mode 100644 api/src/main/resources/glyphs/dialogue/background_2.png create mode 100644 api/src/main/resources/glyphs/dialogue/background_3.png create mode 100644 api/src/main/resources/glyphs/dialogue/background_4.png create mode 100644 api/src/main/resources/glyphs/dialogue/default_avatar.png create mode 100644 api/src/main/resources/glyphs/dialogue/phrase_substrate_1.png create mode 100644 api/src/main/resources/glyphs/dialogue/phrase_substrate_2.png create mode 100644 api/src/main/resources/glyphs/dialogue/scroll_phrase_down_button.png create mode 100644 api/src/main/resources/glyphs/dialogue/scroll_phrase_up_button.png diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/font/GlyphFont.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/font/GlyphFont.java index 2f1640c..6657ebd 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/glyph/font/GlyphFont.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/font/GlyphFont.java @@ -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(" АБВГДЕЖЗИК", "ЛМНОПРСТУФХЦЧШЩЪ", diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollection.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollection.java index 3208d92..02ece03 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollection.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollection.java @@ -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); } diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollectionImpl.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollectionImpl.java index 36eba45..4cf78ef 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollectionImpl.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollectionImpl.java @@ -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(); + List textAndColors = KyoriUtil.toColoredParts(component); + + for (var parts: textAndColors) { + result.addAll(glyphCollection.translate(parts.text(), parts.color())); + } + + return Collections.unmodifiableList(result); + } } diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/util/KyoriUtil.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/util/KyoriUtil.java new file mode 100644 index 0000000..0bea98b --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/util/KyoriUtil.java @@ -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 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 colors = new LinkedList<>(); + private final List 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 result() { + return result; + } + + public record ColoredComponentTextPart(String text, TextColor color) {} + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/resource/ResourcePackManager.java b/api/src/main/java/ru/dragonestia/msb3/api/resource/ResourcePackManager.java index 85fcfdf..adac065 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/resource/ResourcePackManager.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/resource/ResourcePackManager.java @@ -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(); + 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(); diff --git a/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogGlyphPositions.java b/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogGlyphPositions.java new file mode 100644 index 0000000..a387abc --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogGlyphPositions.java @@ -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)); + } + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java b/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java new file mode 100644 index 0000000..819340f --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java @@ -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 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(); + 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 toList(TextComponent text, TextureProperties properties) { + return GLYPH_FONT.translate(properties.height(), properties.ascent(), text); + } + } +} diff --git a/api/src/main/resources/glyphs/dialogue/answer_active_text_field.png b/api/src/main/resources/glyphs/dialogue/answer_active_text_field.png new file mode 100644 index 0000000000000000000000000000000000000000..2f3bd34eb176d3daaf7bc0a34c094720577428f9 GIT binary patch literal 445 zcmeAS@N?(olHy`uVBq!ia0vp^`+!)7gAGXDzQuGENHG=%xjQkeJ16rJ$S(JEaSW-L z^Y)Id7jvM<(T~OtTfa2jSnBz3-JSOC0!Fi@ZUOFK4M7=J&-C24$C*?bBkmqN(;oZm zp!NUwVw)R2h4;^@_ypDNz5kwN#)|pzDxQHu6!~H-2ombo0kf zf2Fy0Y3Yv-pL$?x_dp>wKF)KJpZMo9)0ukOd-=V2>~su&Y&i1ZuuwB5=cA3?-Bs^Y`=7ytbJ)b|6VDTU2>YA^}@vQW6%_rAdBK?AUCo ztbgjie3bho8Jp_Qj7wdeL$cXcB|6NCKEbe>d16h&_PrjuA3l6_p8_;4Lbk!%aIa%t b-Jg1fgTa=ESVYZ09%t}$^>bP0l+XkK06u5+ literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/answer_button_active_2.png b/api/src/main/resources/glyphs/dialogue/answer_button_active_2.png new file mode 100644 index 0000000000000000000000000000000000000000..183942890cbf1ed5b92de80c43bd6b5348ad9d87 GIT binary patch literal 305 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<FVdQ&MBb@04;ZW Al>h($ literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/answer_button_active_3.png b/api/src/main/resources/glyphs/dialogue/answer_button_active_3.png new file mode 100644 index 0000000000000000000000000000000000000000..065c30aecba16155900363c655d1cc3f04ac17e9 GIT binary patch literal 284 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<gTe~DWM4fxO8p) literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/answer_button_active_4.png b/api/src/main/resources/glyphs/dialogue/answer_button_active_4.png new file mode 100644 index 0000000000000000000000000000000000000000..aee8169712ac4093ef3ee8bf80699c1dade716a3 GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<knn30@BfRkoXiq@Hf2w_?i8k4t9vx8q{M^XEz#zahE3qLtfVtS6p6IMI<5 z)#UizVNR&*&W5O$0&Hxx97$^zNJ=R5vS!G77JqnimVJsC@4;(4TTUK5ao~V~!dcyv uhOM?9`2}xIiO%`P$Ho4LY3I+6{~3SYa;m#87%vL+GJ~h9pUXO@geCybCVAZe literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/answer_not_active_text_field.png b/api/src/main/resources/glyphs/dialogue/answer_not_active_text_field.png new file mode 100644 index 0000000000000000000000000000000000000000..2e3329634b7adf404b60e6e5f3869904cbb5a3b0 GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^`+!)7gAGXDzQuGENHG=%xjQkeJ16rJ$S(18aSW-L z^Y)Id7jvM<(T~QHS|Ygg4mbrIS-X?n{DGgyR)e_@*mPWu@Ov!m$mTG1UGwh16V=pD z59a+}ztb|MOY`0CO5wC+mGzZTTm~P%|JL=qwYo;mL3HsCgZkQam2wCC-pI>%B|SPU z(#!8{VyE-9*M6SQ$}3+?O+J>ZiEl3mh`Dnut~2D7UY3N}RL zmrU)vzqR`ym*V8jPcN9)$+#>{TetB3t3NgSGD`&~oV;1I@#@Q4M;|B{Stj#ktJ_Vu zdaTo5%{oQRZjFere1XxdyZ1aMojdQRJ)diXwNFjNqK}E4$J$k;p`*7Vg}&C&KH_YG(dB{r=m|9jz{X1hgT0TE(+zInxd&^v74LPC9w}-1p7yp3?VL zJFBIiO?*51%8Dy%Out0Wo%~pt@uq0>+#iw)f{d>pIMO~f=@GLZ&_1c!nzO2&fsE`x chx}si@sSgnf0JnzFftfCUHx3vIVCg!0Po+*v;Y7A literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/avatar_frame.png b/api/src/main/resources/glyphs/dialogue/avatar_frame.png new file mode 100644 index 0000000000000000000000000000000000000000..4ba48057e087a17c455e76dee0f48d9ddad4d3cf GIT binary patch literal 648 zcmeAS@N?(olHy`uVBq!ia0vp^B_Pbf1|&mfiKzoA#^NA%Cx&(BWL`2bFgba;IEGZr zd3#6So4HV?;p6+1i)Jx`hEY7;e%#6O%$jNV74n5h`CU-Y(`d+@L z8@6xz_q=+}1-4zg>m@3y%rtr&6NA<-ym!}Zx-eIAfJE~l36Wz94A@!?K{7lf3$1hv z|E=9>Gtv2Ku@jnhNgLLuUw)`4DQzn+&x-E(bn=#RhP|zwo6C_$hiXD!2X@*#g;>7T zc2UgZ-8=VmamY9PSr2v3$7L%TC&ljVcsSQq9ppmi<#*;k-g6~-U5VkWIZ&tn*tqoJ zr0RLkr(f?Va4IxXH@SJY|7MtIb|B*%n9q2>eETPu|6YFOdP$f+eO?_qw1RuxviZaW zKzwV)f_E|LPqO_m!^e~N*4)=Abrn~?6oLFcIa`1G_n)EPtU?69@$vbNgz4AWS*drw zh}PO1i(M|vW&H8K^WTck(RYs@<^%@#nFmv5+nfzN`>f?q4YR@ymzX<}M}R4dfx*+& K&t;ucLK6TuP#Wm~ literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/background_1.png b/api/src/main/resources/glyphs/dialogue/background_1.png new file mode 100644 index 0000000000000000000000000000000000000000..d8d8e1fa9741a44fea943717c3ab4c5caddfd62e GIT binary patch literal 4893 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 P00000NkvXXu0mjfH)mbE literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/background_2.png b/api/src/main/resources/glyphs/dialogue/background_2.png new file mode 100644 index 0000000000000000000000000000000000000000..1f07f84d5075cc99d6443e1f3f6eabb07858d5c8 GIT binary patch literal 3909 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 P00000NkvXXu0mjfyx&GK literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/phrase_substrate_1.png b/api/src/main/resources/glyphs/dialogue/phrase_substrate_1.png new file mode 100644 index 0000000000000000000000000000000000000000..2c0af6f43eae5e70a41b5d2b5148472f09f5ac7c GIT binary patch literal 739 zcmeAS@N?(olHy`uVBq!ia0vp^n}E24gAGV3T4x*tQk(@Ik;M!Q+`=Ht$S`Y;1Oo%p zR8JSjkcv5P@7m^F36N+_^q*3uBP9hisqCm-1WWWuHV?;>Mk{`1+Hf~yEqeWNYYMN& zxAKa6&z|(g_!Pdocy`B$m(REB?`L0N!Czg^@F0G{iH-*wr=Iusudd~Luztgd4u^?? zjY`T)u5K({JscvEf@{Qn+Vstt{xSG>*RAg-R-8(j_}wmY&i%(?KqI=5D(==% zhEhe_PQ=fj-+tO8w$!3u`MGM%wiDAcXPM6ZtaWd%vi!>>h5L@~E`Opt*W}#xA8Ss$ zbq)$U^-ebORMyMBGs<^lXS(mO-qhn!o+3F>EYj`CBhk{`XSREv{TW#M@p#!BoU7FioT%Pi$+~&@|MY(&X zgns(+@^Slg*Si1j-ZF$7Q99kR)psZ~9aVZ?yUAkzaa|*&h}aZO?WMqg2l+i`;{82! zrgM&eTy;2gmW4jZXZ?@YyqpvM@ty2X5B1*9x^~e|zAaol2R$S+C#JeY!2+<=10H~1 zqagvE)n&?WQU2XNu=e0j638Pgg&ebxsLQ09(3B@c;k- literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/phrase_substrate_2.png b/api/src/main/resources/glyphs/dialogue/phrase_substrate_2.png new file mode 100644 index 0000000000000000000000000000000000000000..cf8a2b3fca149e51123b8276a449c9430d4d3e7a GIT binary patch literal 796 zcmeAS@N?(olHy`uVBq!ia0vp^n}E24gAGV3T4x*tQk(@Ik;M!Q+`=Ht$S`Y;1Oo%p zIZqeIkcv5P?;83^2a2!;99y*2e8QTG+6laIjC-xMtY*2GsQ3mg?ViHA#E83KmHios z=mm_H$%E?den|ggB z7x&(ol{-r`Jy|(;T5ry=GsoPP=}6z~nd!Uzes2Hkv(H}rEO=o*1~dG$Bv%WKTsUFZEy^(lTOQ{+DFby|6fr6T`M z<=LsvSDYx`-;?P#*KCudVf(J%w`c#GzW4I7)1M}NzWVEF<<(iWw@M=8s^nh1%g^sC zp05)*Z;HS9r#tH2)0RI=D{*fN(z`p+ck`{BI}@Mox+xp!ce2;os1#_h=H`tPSDtn= z3r+(%|9XM)_V&wdr;C>6069CYi-3k-B% zFc{B%_wU=YA2k*+Yt8lVD`}n<{j|Dw&%GSf5VrmK2uBESnRvD%Q+c-FWj8bBX92*- z`fa~|u37A~>2vD;{km(vK5Dc7Y`xRo=bi7?ZMQvFyD6p8t8VMW=XQ~sY)s7;14E?f zV_(j!tDu0pmVp#-*KWcC4#-3exa)`S|5>0V4Ng_{1`=x*>_2&SJ21I3c)I$ztaD0e F0su5|YB>M^ literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/scroll_phrase_down_button.png b/api/src/main/resources/glyphs/dialogue/scroll_phrase_down_button.png new file mode 100644 index 0000000000000000000000000000000000000000..ad906a1803a49faa659eeaff7321ad288cf56d49 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^MnKHP!3HG1iSZT!DaPU;cPEB*=VV?2IU77(978O6 zw_dg7I^-bH^3Xg%o^|_0C3k^?%@Nhi?V1?M%TuC8PA@BUnN z@8^x>vsfIBrvJJjz5CUima@}I-w!@Lo}%~l^G%Z-`yFVQfwwE zIB+!nH}GQVP`r@9_E*pOdUoyR^;vw!C-X2~2$bFR^p_?7o}&>82N;;%N@%l4I6e?f u?qqQ0uu0YtbTC#+o#@VgYq`%g@e6m|GK zBXLRV!V~iv7=@i{+g_eFGq9=oJ5jAH=x#s`P%x^hZ57+2&n!F_P8o+7`@~K8zkf;x zL-Upu3XWU- Date: Mon, 2 Dec 2024 18:07:23 +0700 Subject: [PATCH 02/49] feat: implemented DialogueRenderer --- .../api/talk/dialogue/DialogueRenderer.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java b/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java index 819340f..75970da 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java @@ -277,7 +277,7 @@ public class DialogueRenderer { // Answers - var answers = List.of("Hello world!", "I am a teapot", "I love pizza", "msb3 is top!"); + var answers = List.of("Hello world!", "I am a teapot", "I love pizza\nMamma mia\nPeperoni\nPapa carlo\nZaebumba\nPidoraso ebanino", "msb3 is top!"); for (int i = 0; i < 4; i++) { if (true) { //TODO: checking answer exists builder.append(i % 2 == 0 @@ -305,25 +305,23 @@ public class DialogueRenderer { var lines = breakIntoLines( answers.get(i), i < 2 - ? new TextureProperties(positions.answerText().fontHeight(), - positions.answerText().topFirstLineAscent()) - : new TextureProperties( - positions.answerText().fontHeight(), - positions.answerText().bottomFirstLineAscent()), + ? 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++) { - + 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())); + + builder.append( + i % 2 == 0 + ? positions.answerText().leftLineX() + : positions.answerText().rightLineX(), + line.toGlyphList(line.textureProperties(), 0, endWithDots, positions.answerText().textColor()) + ); } } else { builder.append(i % 2 == 0 @@ -360,7 +358,8 @@ public class DialogueRenderer { 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)))); + var properties = new TextureProperties(firstLineProperties.height(), firstLineProperties.ascent() - lineIdx * (fontHeight + 1)); + textLines.add(new TextLine(text, properties)); } return textLines; } @@ -377,7 +376,8 @@ public class DialogueRenderer { 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, + line.textureProperties(), + shift, textLines.size() - lineIdx > 0 && lineIdx - shift == positions.phraseText().maxLines() - 1, positions.phraseText().textColor() )); @@ -418,12 +418,12 @@ public class DialogueRenderer { private record TextLine(Component text, TextureProperties textureProperties) { - public List<@NotNull AppendableGlyph> toGlyphList(int lineShift, boolean cutEnding, TextColor color) { + public List<@NotNull AppendableGlyph> toGlyphList(TextureProperties parentProperties, 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) + parentProperties.height(), + parentProperties.ascent() + lineShift * (textureProperties().height() + 1) ); Component text = this.text; From aa3e3a32baa4a895907e6bbe75fa9c3b9336fa72 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sun, 8 Dec 2024 01:30:15 +0700 Subject: [PATCH 03/49] feat: updated dialog textures compiling --- .../api/talk/dialogue/DialogueRenderer.java | 108 ++++++++++++------ .../dragonestia/msb3/api/util/ImageUtil.java | 27 +++++ .../msb3/api/util/ResourceFromJar.java | 29 +++-- .../resources/glyphs/dialogue/background.png | Bin 0 -> 19794 bytes .../glyphs/dialogue/background_1.png | Bin 4893 -> 0 bytes .../glyphs/dialogue/background_2.png | Bin 3909 -> 0 bytes .../glyphs/dialogue/background_3.png | Bin 6208 -> 0 bytes .../glyphs/dialogue/background_4.png | Bin 5957 -> 0 bytes .../glyphs/dialogue/phrase_substrate.png | Bin 0 -> 970 bytes .../glyphs/dialogue/phrase_substrate_1.png | Bin 739 -> 0 bytes .../glyphs/dialogue/phrase_substrate_2.png | Bin 796 -> 0 bytes 11 files changed, 121 insertions(+), 43 deletions(-) create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/util/ImageUtil.java create mode 100644 api/src/main/resources/glyphs/dialogue/background.png delete mode 100644 api/src/main/resources/glyphs/dialogue/background_1.png delete mode 100644 api/src/main/resources/glyphs/dialogue/background_2.png delete mode 100644 api/src/main/resources/glyphs/dialogue/background_3.png delete mode 100644 api/src/main/resources/glyphs/dialogue/background_4.png create mode 100644 api/src/main/resources/glyphs/dialogue/phrase_substrate.png delete mode 100644 api/src/main/resources/glyphs/dialogue/phrase_substrate_1.png delete mode 100644 api/src/main/resources/glyphs/dialogue/phrase_substrate_2.png diff --git a/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java b/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java index 75970da..749adec 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueRenderer.java @@ -19,11 +19,15 @@ 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.ImageUtil; 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 javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.stream.IntStream; @@ -42,13 +46,7 @@ public class DialogueRenderer { 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"); @@ -127,34 +125,16 @@ public class DialogueRenderer { 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_BACKGROUND_1; + private static final ImageGlyph GLYPH_BACKGROUND_2; + private static final ImageGlyph GLYPH_BACKGROUND_3; + private static final ImageGlyph GLYPH_BACKGROUND_4; 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_PHRASE_SUBSTRATE_1; + private static final ImageGlyph GLYPH_PHRASE_SUBSTRATE_2; private static final ImageGlyph GLYPH_SCROLL_PHRASE_DOWN_BUTTON = createGlyph("dialog/scroll_phrase_down_button.png", SCROLL_PHRASE_DOWN_BUTTON, DialogGlyphPositions.DEFAULT.scrollPhraseButton().height(), @@ -168,6 +148,72 @@ public class DialogueRenderer { private static final StringIdentifier<@NotNull LanguageGlyphCollection> ID_FONT = StringIdentifier.of("dialog_font", LanguageGlyphCollection.class); static { + BufferedImage backgroundImage; + Writable b1; + Writable b2; + Writable b3; + Writable b4; + + try (var steam = ResourceFromJar.streamOf("glyphs/dialogue/background.png")) { + backgroundImage = ImageIO.read(steam); + var w = backgroundImage.getWidth(); + var h = backgroundImage.getHeight(); + + b1 = ImageUtil.imageToWritable(backgroundImage.getSubimage(0, 0, w / 2, h / 2)); + b2 = ImageUtil.imageToWritable(backgroundImage.getSubimage(w / 2, 0, w / 2, h / 2)); + b3 = ImageUtil.imageToWritable(backgroundImage.getSubimage(0, h / 2, w / 2, h / 2)); + b4 = ImageUtil.imageToWritable(backgroundImage.getSubimage(w / 2, h / 2, w / 2, h / 2)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + GLYPH_BACKGROUND_1 = createGlyph("dialog/background_1.png", + b1, + DialogGlyphPositions.DEFAULT.guiBackground().height() / 2, + DialogGlyphPositions.DEFAULT.guiBackground().topPartsY()); + + GLYPH_BACKGROUND_2 = createGlyph("dialog/background_2.png", + b2, + DialogGlyphPositions.DEFAULT.guiBackground().height() / 2, + DialogGlyphPositions.DEFAULT.guiBackground().topPartsY()); + + GLYPH_BACKGROUND_3 = createGlyph("dialog/background_3.png", + b3, + DialogGlyphPositions.DEFAULT.guiBackground().height() / 2, + DialogGlyphPositions.DEFAULT.guiBackground().bottomPartsY()); + + GLYPH_BACKGROUND_4 = createGlyph("dialog/background_4.png", + b4, + DialogGlyphPositions.DEFAULT.guiBackground().height() / 2, + DialogGlyphPositions.DEFAULT.guiBackground().bottomPartsY()); + + + BufferedImage substrateImage; + Writable s1; + Writable s2; + + try (var stream = ResourceFromJar.streamOf("glyphs/dialogue/phrase_substrate.png")) { + substrateImage = ImageIO.read(stream); + var w = substrateImage.getWidth(); + var h = substrateImage.getHeight(); + + s1 = ImageUtil.imageToWritable(substrateImage.getSubimage(0, 0, w / 2, h)); + s2 = ImageUtil.imageToWritable(substrateImage.getSubimage(w / 2, 0, w / 2, h)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + GLYPH_PHRASE_SUBSTRATE_1 = createGlyph("dialog/phrase_substrate_1.png", + s1, + DialogGlyphPositions.DEFAULT.phraseSubstrate().height(), + DialogGlyphPositions.DEFAULT.phraseSubstrate().y()); + + GLYPH_PHRASE_SUBSTRATE_2 = createGlyph("dialog/phrase_substrate_2.png", + s2, + DialogGlyphPositions.DEFAULT.phraseSubstrate().height(), + DialogGlyphPositions.DEFAULT.phraseSubstrate().y()); + + 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; @@ -222,8 +268,6 @@ public class DialogueRenderer { private DialogueRenderer(Player player) { this.player = player; - - } public static DialogueRenderer create(Player player) { diff --git a/api/src/main/java/ru/dragonestia/msb3/api/util/ImageUtil.java b/api/src/main/java/ru/dragonestia/msb3/api/util/ImageUtil.java new file mode 100644 index 0000000..73a2492 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/util/ImageUtil.java @@ -0,0 +1,27 @@ +package ru.dragonestia.msb3.api.util; + +import lombok.experimental.UtilityClass; +import team.unnamed.creative.base.Writable; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; + +@UtilityClass +public class ImageUtil { + + public Writable imageToWritable(BufferedImage image) { + try { + return imageToWritable0(image); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + private Writable imageToWritable0(BufferedImage image) throws Exception { + try (var stream = new ByteArrayOutputStream()) { + ImageIO.write(image, "png", stream); + return Writable.bytes(stream.toByteArray()); + } + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/util/ResourceFromJar.java b/api/src/main/java/ru/dragonestia/msb3/api/util/ResourceFromJar.java index ef9c363..fe8974b 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/util/ResourceFromJar.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/util/ResourceFromJar.java @@ -4,6 +4,7 @@ import ru.dragonestia.msb3.api.ServerBootstrap; import team.unnamed.creative.base.Writable; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.net.URLConnection; @@ -14,20 +15,26 @@ public interface ResourceFromJar { } static Writable of(ClassLoader classLoader, String fileName) { - return Writable.inputStream(() -> { - try { - URL url = classLoader.getResource(fileName); + return Writable.inputStream(() -> streamOf(classLoader, fileName)); + } - if (url == null) { - return null; - } + static InputStream streamOf(String fileName) { + return streamOf(ServerBootstrap.CLASS_LOADER, fileName); + } - URLConnection connection = url.openConnection(); - connection.setUseCaches(false); - return connection.getInputStream(); - } catch (IOException ex) { + static InputStream streamOf(ClassLoader classLoader, String fileName) { + try { + URL url = classLoader.getResource(fileName); + + if (url == null) { return null; } - }); + + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + return connection.getInputStream(); + } catch (IOException ex) { + return null; + } } } diff --git a/api/src/main/resources/glyphs/dialogue/background.png b/api/src/main/resources/glyphs/dialogue/background.png new file mode 100644 index 0000000000000000000000000000000000000000..df41d25ac6c19b09f97e8dacd116f6776914c5cc GIT binary patch literal 19794 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 literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/background_1.png b/api/src/main/resources/glyphs/dialogue/background_1.png deleted file mode 100644 index d8d8e1fa9741a44fea943717c3ab4c5caddfd62e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4893 zcmV+&6XNWNP)>?3riS57-NwIa>>)=oNL}EhkdUca>e$K7Rh2ayH!8HklgAIiDZ4U*!@%bKY#wyAOD+zAb2Q#O6iC2 z|MuHo*8Mg?5G=}1A^bt`xcr<_2!9YfDo?oK?|=L28s8%bf))6A?Jp1n!Fq%O2!aP@ zvkKtpa1^(3p9Mj1&s`2+R{Pc&<2j-Qh#cBDlO0D%_<6D$-%xQ z!P?SyeRNLe=af>q3t#g#`bzhs&>R+qvP+>(1HQd}ODUyvI)7#V_h8A4eJN?U23i;t z%**xiekZ`vG&JpFPSB~sz-jK$h#tV(>$gvWo`v5FOxWdZqnI%vC?z(7`QZ##DS6HH z_ga=e!zzF*>{$s=;rFoqG}|vVkhL)m$1mw{{BnD50cRla+M|(*n!*d%7PL0S<6LWS zrvT8>bHeZ84M^6%)TmkD^WXLIp01bo+xuE2kl!!S0jO{^w=viTUSnsiFI5u#QoIW zr`xjSw_1OsRD|S#Hr>Nn5iKkT#V`)7;0z5_ujR+0wP|tyNEUjp?7#O9ZxwtBHzm`e z;ai21n=+DwpC(X48PsE`HnkLx^|t%k3BZ&ZRs(u?0k?}+2!9sxQk;*U)r15Xljrxi zO#?r#`HiJn0Tg_0Wn?50b!xmlX`bGjn(^gLQ2|J^06mJpqFs^N*uEwqNa4O)O|nk& zBzi%Mf#(n5*J<_WMnM(P8h!V4IF>^I?|J|_Sgq4vRFF$TO%+9c->V3?|0PiPxq1f= zP#KKQVQh`rYb<7&{V^v2x&Pl@zf~&)Df3T1lw73+Yi9e9}I=v91L2(lIB#ZAR(bM%-4S^9U`l=79O`#1+gYA3aCbL^;SDtE##5{gqFQjBqn)R`1`ztOR7SpN}N`t zmilUBD9L$%Qu}m;06hsn{KEJ???>Ht>Sm$a%qfA{y#h{=ap1zI7Pum!8l^z-h^;k2 z*8Z2!63V^cqO*(^maZ8F&;mB@TD0v~yCoPGbJHT_G`#rko zhs&j)%y8H4RsbH0gGw1|8mpEDJmwaGyIKE3q~JwjO%ch4aFs7Nzb4~rhl zwL7*a=2=Fyfu>xfK3@H{;9RmoDq={fY{6+>V9i+pt1{?Wub|~RtauN|lIx3oJvvEi zweDPhNy3lEru5@k7x`84YCVg7v{?^SI8wi?RtijAA}w)||3)pUu_AT6^|%tP2}E>5 zmn?hE@AE6S3X;nw z3IPseEwPo!?bcfwozF#IwL%=uBDE+$F3*E;IktI9az@`0R?=6xoeLYd^I2yFs7K7X z_^529@n3TNGcJ{YBb0zNEl3%eP@(h+r&P9VaW_lnmy`&)zVtfON=a_9^nJJ~CCS|^ z6@5w9DkV2HrKhzcC#)qe0?&_ajmJn{>xjC5T91G|NA*vQiVT&4j>g#i+1akK{a#u7KzSo4uH7WCYbyIP|}Qj!N~tpd*bT9cRP;#;Uqz*_d_ z`iYK1tY49mpj=)=YFoM=#Us(^k_24Z3qau?jpl~-#?d3Q_fmY#4R|hO;FOc+_p~Zf z>3ex1wUMQNxoSHtv1b)ji&C}5`Vy;D0=B}rPhQh48aVjS6|^P=*TfQ%sbHM!v=n4`9jKfF(VFR#$#hjPJ=(-ipPeSd&q|o6F+o?eyB( z^(~6i(ymm|xRnCuq99KAtrnVV9IX^`sXBnn76iEkFX6W+bJN>86lR$nTJ;{MO$ zu4Y=({Qb;(3eaL8uLp>xG{>s3l=|oDw}97goTblt;M&4N>C>cMK7UpOv?z;1DqF6d z7Ny4x2?1(^AFcZuF?|V|4BIpGH+oO=Ng4LpsZ9WuIGvwOm8{a5hT1mcJcxXL`kC8v z0q8vfwJ@Nxx@i{SPl+#-*(0NVo3wyt3wS3zQ%au}387HpzW81{FNam3yB7jUUVt<& z>Rr|40S?x!3|cGH;!76Cli9QWrA0oy?NM4x)Ovm`C%~PTQ#jp=fwbfwMY8OrtS5cd z-k0D68t3z+YOuYbx#c55p4samBn4$nzFE#sUvYcyd982?$9*|4wf@%m$dYijC<#uO zrP}va7i`PWfi0~S@L)u4!3!z(!3k<>!VRxSUzdja?rskTIi0`W#rY+--=YywXtj zWQ8@w!M(R!=MgC_{ON^j|JmIC5|-rUmS;Pf6~MRu{41sO-3xd+9B}UvOQ5ZAPTnI#6{*-=r1PHbCRyUAZj-?uMD;+jF zw6C`$2v$not9Hzc+<5@M`~4qG2WE?(JpqXp%Q`YC6?~?(2&lC0Ui*%EuQ-de#ZdFk z1Niqp|6w|?x7=6r24-%sJuxEe*=yff63AQp?RN@5yVEcIEWb*wzXm7#Veyv*`=Dq3 zpZ41$^y8j>pD71`);|BQt1f%K)8+bpw+-55vuJ2Q2|K8%(?%)w?S@6 zLIumf)$xbI-VbUBkm2sb&pjiqm93YF?wwf|^922{9zZF%=lLAIu>9L1l=Z$~6@c{E zEqsh|3o&)HO~UoxCu|5%dhAw@_2<_2hkF?W>!5l7-cTzd-JvJKfgo58F$XY03E;9| zoO=#8GYFRDP63R*EjQkUCX#?bupVL^fVU#RxE~265CqH9{b(==6m+~(4DK5Q!Sa|( zfvt-^BAFKi+o5_bfMvy)ML`5Xus)&!z>GXvbm=o`X=w<45X?u>1K?bLI*ljY2D-FQ z5CrQZDu9-ocGlieCJ2Icxtj&hgxhl0m>>wYLsb$migtpajalWUX%=AC9}@(#Guw@C zo1P6AE`JcTfwO08D}%1bg9kw%Q}Y0vwG=@pfFKwIE`7(vZ(P;v(uyDyKoAUqbMd(+ zWm`TQMJRwEaD-a>jFdCe1brzb5DFj&oROX++;fgmgaQZxCwgyiv&IZTD1abvqPIqd zTW~izK^O`k2%PY~#XUYXdqh|;?2bS|@Q7HgwDg2+WOObdJb)lriG25M?GD_d^8ld$ zg1`YyQ1OXc(v!D1XdWPP06{PrmTWyfXM=mqy7vPx} z2n7%X9a!#sReGkTB`m1wNZdnV-=KoGWcW+q zvE0+Ndksq{fI(m<)vgQ+w$IbySoNS?E;-2I83u#`7(}U)q?<(q+XQm~)cR*dPy&;M zc%HY{mz2`yH3v1GB{#l80XU&qcde5w%SMCw;XFJl^tGV?7e3JQ)d)Y{fP@kXz=4wG z=E8xR#Rz_L?FGkm!?;YxvTeAHg*OQQxKpw;N*z4sz6aa;L z`f>h$ErHMyJJxANX+Q2p6H?3=H48F5+5VAm$>-DgIi>VtY=;6Mcn@xFUbh$SzVaGU z&Z2ZaO(}%O@Eysr&dSI2a;qgs>3bhPqyi|#O59+Vkc1GIf3^+~E{P)*8A&l=J_qN~ zlq@?fI+47Joc#1IQ{4i&eU@#K*H2E^$R&H`El4)>$b%rLr$ zH65sZH;aJP+)$#F5V~M*50Uq;E9snoQrNQn|cTJaiwpxl> zA(sBPf?h)lRxe{lsH>cjyhLgLQM61qa>gj72P*{PEWYF>TZ((|#*UVlU%E34Jyuc@ zOA1hr&~gPTTZS7ilr`diynlez%e*U88ub09fX1(-2Qf+=iw%hjJh*k=k`kcXIhU-* zLzoS%owuM6PUq)agdNC)L@J=m~P+MGcjQ>5r?}On5%`>3Db6x~a|IDZer2lKN7B$iT^R~Cw zm%F+ATsgPrFl|)@pv}Wuta{Gy)Ai9Mq_~bt^p&9ddkzZA?W5_wDCk)EC7rt`glUD= zU!eEqIrT`-^>Mv?s#fHV&9AwAxE#@*EPrM(1;7bAx`MP5v4TzOI14z~(W=td{N9oT z93ezC=aa0HmQ#?9(Lx-r-&iWND)qVLoOu3xvyQwUhIw3eU$XppT|hzg)&nQM{i z#p!mm4z=gk1}WzPbMCYh|Ci1?+koJHhxGfT{VAhoK!43xaE zy@Ua*0?^{=);P_Y*we04s!7L~UW%17mQK{%``I(Yt>1}T-dbwQUZjdl4>+Yyf8*)= zd|M{V9kXZd6oBM{NwxDeaN53Ru?qLSQu5czCHIiqcTY&JZ^k>}nc>1e3nhbv^&0to z-v0LbayK0~n@2YaH3xt$dFwszr0gsAS*Zr!l963ACf^HMJ8K?Q%`_x>8m-}9O7eKg zg3ZP4TE9L@pQ{R>C-H_IS<1fRHc8RcEUkSMEk25#WF+0DO+!jvmbP}oI%9ydB99`} zHJE1sq}Z&a5NhVYwEbmK?N$GwNO8W)ANK=TC!qt>F{Y)A^TyBaxA2*(24BYcS#NtzDT++BU P00000NkvXXu0mjfH)mbE diff --git a/api/src/main/resources/glyphs/dialogue/background_2.png b/api/src/main/resources/glyphs/dialogue/background_2.png deleted file mode 100644 index 1f07f84d5075cc99d6443e1f3f6eabb07858d5c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3909 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 literal 0 HcmV?d00001 diff --git a/api/src/main/resources/glyphs/dialogue/phrase_substrate_1.png b/api/src/main/resources/glyphs/dialogue/phrase_substrate_1.png deleted file mode 100644 index 2c0af6f43eae5e70a41b5d2b5148472f09f5ac7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 739 zcmeAS@N?(olHy`uVBq!ia0vp^n}E24gAGV3T4x*tQk(@Ik;M!Q+`=Ht$S`Y;1Oo%p zR8JSjkcv5P@7m^F36N+_^q*3uBP9hisqCm-1WWWuHV?;>Mk{`1+Hf~yEqeWNYYMN& zxAKa6&z|(g_!Pdocy`B$m(REB?`L0N!Czg^@F0G{iH-*wr=Iusudd~Luztgd4u^?? zjY`T)u5K({JscvEf@{Qn+Vstt{xSG>*RAg-R-8(j_}wmY&i%(?KqI=5D(==% zhEhe_PQ=fj-+tO8w$!3u`MGM%wiDAcXPM6ZtaWd%vi!>>h5L@~E`Opt*W}#xA8Ss$ zbq)$U^-ebORMyMBGs<^lXS(mO-qhn!o+3F>EYj`CBhk{`XSREv{TW#M@p#!BoU7FioT%Pi$+~&@|MY(&X zgns(+@^Slg*Si1j-ZF$7Q99kR)psZ~9aVZ?yUAkzaa|*&h}aZO?WMqg2l+i`;{82! zrgM&eTy;2gmW4jZXZ?@YyqpvM@ty2X5B1*9x^~e|zAaol2R$S+C#JeY!2+<=10H~1 zqagvE)n&?WQU2XNu=e0j638Pgg&ebxsLQ09(3B@c;k- diff --git a/api/src/main/resources/glyphs/dialogue/phrase_substrate_2.png b/api/src/main/resources/glyphs/dialogue/phrase_substrate_2.png deleted file mode 100644 index cf8a2b3fca149e51123b8276a449c9430d4d3e7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 796 zcmeAS@N?(olHy`uVBq!ia0vp^n}E24gAGV3T4x*tQk(@Ik;M!Q+`=Ht$S`Y;1Oo%p zIZqeIkcv5P?;83^2a2!;99y*2e8QTG+6laIjC-xMtY*2GsQ3mg?ViHA#E83KmHios z=mm_H$%E?den|ggB z7x&(ol{-r`Jy|(;T5ry=GsoPP=}6z~nd!Uzes2Hkv(H}rEO=o*1~dG$Bv%WKTsUFZEy^(lTOQ{+DFby|6fr6T`M z<=LsvSDYx`-;?P#*KCudVf(J%w`c#GzW4I7)1M}NzWVEF<<(iWw@M=8s^nh1%g^sC zp05)*Z;HS9r#tH2)0RI=D{*fN(z`p+ck`{BI}@Mox+xp!ce2;os1#_h=H`tPSDtn= z3r+(%|9XM)_V&wdr;C>6069CYi-3k-B% zFc{B%_wU=YA2k*+Yt8lVD`}n<{j|Dw&%GSf5VrmK2uBESnRvD%Q+c-FWj8bBX92*- z`fa~|u37A~>2vD;{km(vK5Dc7Y`xRo=bi7?ZMQvFyD6p8t8VMW=XQ~sY)s7;14E?f zV_(j!tDu0pmVp#-*KWcC4#-3exa)`S|5>0V4Ng_{1`=x*>_2&SJ21I3c)I$ztaD0e F0su5|YB>M^ From bd29931a601767614ea2f2740e51666b45c33534 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Tue, 10 Dec 2024 00:24:31 +0700 Subject: [PATCH 04/49] feat: implemented dialogue themes --- .../msb3/api/resource/DialogueResources.java | 377 ++++++++++++++++++ .../msb3/api/resource/FontGlyphEntry.java | 7 + .../msb3/api/resource/GlyphEntry.java | 7 + .../msb3/api/resource/MonologueResources.java | 4 - .../api/resource/ResourcePackManager.java | 4 +- .../msb3/api/resource/dialog/Background.java | 5 + .../msb3/api/resource/dialog/Button.java | 15 + .../api/resource/dialog/ButtonNumber.java | 15 + .../msb3/api/resource/dialog/Substrate.java | 5 + .../msb3/api/resource/dialog/TextField.java | 15 + .../api/talk/dialogue/DialogueRenderer.java | 341 ++-------------- .../msb3/api/talk/dialogue/DialogueTheme.java | 190 +++++++++ 12 files changed, 683 insertions(+), 302 deletions(-) create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/resource/DialogueResources.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/resource/FontGlyphEntry.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/resource/GlyphEntry.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/resource/dialog/Background.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/resource/dialog/Button.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/resource/dialog/ButtonNumber.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/resource/dialog/Substrate.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/resource/dialog/TextField.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/talk/dialogue/DialogueTheme.java diff --git a/api/src/main/java/ru/dragonestia/msb3/api/resource/DialogueResources.java b/api/src/main/java/ru/dragonestia/msb3/api/resource/DialogueResources.java new file mode 100644 index 0000000..3033a7d --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/resource/DialogueResources.java @@ -0,0 +1,377 @@ +package ru.dragonestia.msb3.api.resource; + +import net.kyori.adventure.key.Key; +import ru.dragonestia.msb3.api.glyph.font.GlyphFont; +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.resource.dialog.Background; +import ru.dragonestia.msb3.api.resource.dialog.Button; +import ru.dragonestia.msb3.api.resource.dialog.Substrate; +import ru.dragonestia.msb3.api.resource.dialog.TextField; +import ru.dragonestia.msb3.api.talk.dialogue.DialogGlyphPositions; +import ru.dragonestia.msb3.api.util.ImageUtil; +import ru.dragonestia.msb3.api.util.ResourceFromJar; +import team.unnamed.creative.base.Writable; +import team.unnamed.creative.texture.Texture; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class DialogueResources { + + private static final Key DIALOGUE_FONT_KEY = Key.key("msb3", "dialog"); + public static final String DEFAULT = "default"; + + private final GlyphResourcePack glyphResourcePack; + private final Map avatars = new HashMap<>(); + private final Map avatarFrames = new HashMap<>(); + private final Map scrollUp = new HashMap<>(); + private final Map scrollDown = new HashMap<>(); + private final Map backgrounds = new HashMap<>(); + private final Map substrates = new HashMap<>(); + private final Map fonts = new HashMap<>(); + private final Map buttons = new HashMap<>(); + private final Map activeFields = new HashMap<>(); + private final Map notActiveFields = new HashMap<>(); + + public DialogueResources(GlyphResourcePack glyphResourcePack) { + this.glyphResourcePack = glyphResourcePack; + + registerAvatar(DEFAULT, ResourceFromJar.of("glyphs/dialogue/default_avatar.png")); + registerAvatarFrame(DEFAULT, ResourceFromJar.of("glyphs/dialogue/avatar_frame.png")); + registerScrollTextUp(DEFAULT, ResourceFromJar.of("glyphs/dialogue/scroll_phrase_up_button.png")); + registerScrollTextDown(DEFAULT, ResourceFromJar.of("glyphs/dialogue/scroll_phrase_down_button.png")); + registerBackground(DEFAULT, ResourceFromJar.of("glyphs/dialogue/background.png")); + registerSubstrate(DEFAULT, ResourceFromJar.of("glyphs/dialogue/phrase_substrate.png")); + registerActiveTextField(DEFAULT, ResourceFromJar.of("glyphs/dialogue/answer_active_text_field.png")); + registerNotActiveTextField(DEFAULT, ResourceFromJar.of("glyphs/dialogue/answer_not_active_text_field.png")); + registerButton(DEFAULT + 1, ResourceFromJar.of("glyphs/dialogue/answer_button_active_1.png")); + registerButton(DEFAULT + 2, ResourceFromJar.of("glyphs/dialogue/answer_button_active_2.png")); + registerButton(DEFAULT + 3, ResourceFromJar.of("glyphs/dialogue/answer_button_active_3.png")); + registerButton(DEFAULT + 4, ResourceFromJar.of("glyphs/dialogue/answer_button_active_4.png")); + registerFont(DEFAULT, ResourceFromJar.of("glyphs/defaults/minecraft_font.png")); + } + + public void compile(GlyphResourcePack resourcePack) {} + + public void registerAvatar(String identifier, Writable writable) { + registerAvatar(identifier, writable, DialogGlyphPositions.DEFAULT); + } + + public void registerAvatar(String identifier, Writable writable, DialogGlyphPositions positions) { + var glyph = createGlyph(identifier, writable, "avatar", positions.avatar().height(), positions.avatar().y()); + var glyphIdentifier = StringIdentifier.image("dialog_avatar__" + identifier); + avatars.put(identifier, new GlyphEntry(glyphIdentifier, glyph)); + glyphResourcePack.with(glyphIdentifier, glyph); + } + + public void registerAvatarFrame(String identifier, Writable writable) { + registerAvatarFrame(identifier, writable, DialogGlyphPositions.DEFAULT); + } + + public void registerAvatarFrame(String identifier, Writable writable, DialogGlyphPositions positions) { + var glyph = createGlyph(identifier, writable, "avatar_frame", positions.avatar().frameHeight(), positions.avatar().y() + positions.avatar().frameBorderSize()); + var glyphIdentifier = StringIdentifier.image("dialog_avatar_frame__" + identifier); + avatarFrames.put(identifier, new GlyphEntry(glyphIdentifier, glyph)); + glyphResourcePack.with(glyphIdentifier, glyph); + } + + public void registerScrollTextUp(String identifier, Writable writable) { + registerScrollTextUp(identifier, writable, DialogGlyphPositions.DEFAULT); + } + + public void registerScrollTextUp(String identifier, Writable writable, DialogGlyphPositions positions) { + var glyph = createGlyph(identifier, writable, "scroll_up", positions.scrollPhraseButton().height(), positions.scrollPhraseButton().buttonY()); + var glyphIdentifier = StringIdentifier.image("dialog_scroll_up__" + identifier); + scrollUp.put(identifier, new GlyphEntry(glyphIdentifier, glyph)); + glyphResourcePack.with(glyphIdentifier, glyph); + } + + public void registerScrollTextDown(String identifier, Writable writable) { + registerScrollTextDown(identifier, writable, DialogGlyphPositions.DEFAULT); + } + + public void registerScrollTextDown(String identifier, Writable writable, DialogGlyphPositions positions) { + var glyph = createGlyph(identifier, writable, "scroll_down", positions.scrollPhraseButton().height(), positions.scrollPhraseButton().buttonY()); + var glyphIdentifier = StringIdentifier.image("dialog_scroll_down__" + identifier); + scrollDown.put(identifier, new GlyphEntry(glyphIdentifier, glyph)); + glyphResourcePack.with(glyphIdentifier, glyph); + } + + public void registerBackground(String identifier, Writable writable) { + registerBackground(identifier, writable, DialogGlyphPositions.DEFAULT); + } + + public void registerBackground(String identifier, Writable writable, DialogGlyphPositions positions) { + BufferedImage image; + Writable part1; + Writable part2; + Writable part3; + Writable part4; + + try (var steam = new ByteArrayInputStream(writable.toByteArray())) { + image = ImageIO.read(steam); + var w = image.getWidth(); + var h = image.getHeight(); + + part1 = ImageUtil.imageToWritable(image.getSubimage(0, 0, w / 2, h / 2)); + part2 = ImageUtil.imageToWritable(image.getSubimage(w / 2, 0, w / 2, h / 2)); + part3 = ImageUtil.imageToWritable(image.getSubimage(0, h / 2, w / 2, h / 2)); + part4 = ImageUtil.imageToWritable(image.getSubimage(w / 2, h / 2, w / 2, h / 2)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + var glyph1 = new GlyphEntry( + StringIdentifier.image("dialog_background1__" + identifier), + createGlyph(identifier, part1, "background1", positions.guiBackground().height() / 2, positions.guiBackground().topPartsY()) + ); + var glyph2 = new GlyphEntry( + StringIdentifier.image("dialog_background2__" + identifier), + createGlyph(identifier, part2, "background2", positions.guiBackground().height() / 2, positions.guiBackground().topPartsY()) + ); + var glyph3 = new GlyphEntry( + StringIdentifier.image("dialog_background3__" + identifier), + createGlyph(identifier, part3, "background3", positions.guiBackground().height() / 2, positions.guiBackground().bottomPartsY()) + ); + var glyph4 = new GlyphEntry( + StringIdentifier.image("dialog_background4__" + identifier), + createGlyph(identifier, part4, "background4", positions.guiBackground().height() / 2, positions.guiBackground().bottomPartsY()) + ); + + backgrounds.put(identifier, new Background(glyph1.glyph(), glyph2.glyph(), glyph3.glyph(), glyph4.glyph())); + glyphResourcePack.with(glyph1.identifier(), glyph1.glyph()); + glyphResourcePack.with(glyph2.identifier(), glyph2.glyph()); + glyphResourcePack.with(glyph3.identifier(), glyph3.glyph()); + glyphResourcePack.with(glyph4.identifier(), glyph4.glyph()); + } + + public void registerSubstrate(String identifier, Writable writable) { + registerSubstrate(identifier, writable, DialogGlyphPositions.DEFAULT); + } + + public void registerSubstrate(String identifier, Writable writable, DialogGlyphPositions positions) { + BufferedImage image; + Writable part1; + Writable part2; + + try (var steam = new ByteArrayInputStream(writable.toByteArray())) { + image = ImageIO.read(steam); + var w = image.getWidth(); + var h = image.getHeight(); + + part1 = ImageUtil.imageToWritable(image.getSubimage(0, 0, w / 2, h)); + part2 = ImageUtil.imageToWritable(image.getSubimage(w / 2, 0, w / 2, h)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + var glyph1 = new GlyphEntry( + StringIdentifier.image("dialog_substrate1__" + identifier), + createGlyph(identifier, part1, "substrate1", positions.phraseSubstrate().height(), positions.phraseSubstrate().y()) + ); + var glyph2 = new GlyphEntry( + StringIdentifier.image("dialog_substrate2__" + identifier), + createGlyph(identifier, part2, "substrate2", positions.phraseSubstrate().height(), positions.phraseSubstrate().y()) + ); + + substrates.put(identifier, new Substrate(glyph1.glyph(), glyph2.glyph())); + glyphResourcePack.with(glyph1.identifier(), glyph1.glyph()); + glyphResourcePack.with(glyph2.identifier(), glyph2.glyph()); + } + + public void registerFont(String identifier, Writable writable) { + registerFont(identifier, writable, DialogGlyphPositions.DEFAULT); + } + + public void registerFont(String identifier, Writable writable, DialogGlyphPositions positions) { + 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(); + + var glyphIdentifier = StringIdentifier.of("dialog_font", LanguageGlyphCollection.class); + var font = GlyphFont.minecraftFontGlyphCollection(DIALOGUE_FONT_KEY, Key.key("dialog/font.png"), writable, propertiesList); + + fonts.put(identifier, new FontGlyphEntry(glyphIdentifier, font)); + glyphResourcePack.with(glyphIdentifier, font); + } + + public void registerButton(String identifier, Writable writable) { + registerButton(identifier, writable, DialogGlyphPositions.DEFAULT); + } + + public void registerButton(String identifier, Writable writable, DialogGlyphPositions positions) { + var glyph1 = createGlyph(identifier, + writable, + "button1", + positions.answerButton().height(), + positions.answerButton().topButtonY()); + var glyph2 = createGlyph(identifier, + writable, + "button2", + positions.answerButton().height(), + positions.answerButton().topButtonY()); + var glyph3 = createGlyph(identifier, + writable, + "button3", + positions.answerButton().height(), + positions.answerButton().bottomButtonY()); + var glyph4 = createGlyph(identifier, + writable, + "button4", + positions.answerButton().height(), + positions.answerButton().bottomButtonY()); + + buttons.put(identifier, new Button(glyph1, glyph2, glyph3, glyph4)); + glyphResourcePack + .with(StringIdentifier.image("dialog_button1__" + identifier), glyph1) + .with(StringIdentifier.image("dialog_button2__" + identifier), glyph2) + .with(StringIdentifier.image("dialog_button3__" + identifier), glyph3) + .with(StringIdentifier.image("dialog_button4__" + identifier), glyph4); + } + + public void registerActiveTextField(String identifier, Writable writable) { + registerActiveTextField(identifier, writable, DialogGlyphPositions.DEFAULT); + } + + public void registerActiveTextField(String identifier, Writable writable, DialogGlyphPositions positions) { + var glyph1 = createGlyph(identifier, + writable, + "active_field1", + positions.answerField().height(), + positions.answerField().topFieldY()); + var glyph2 = createGlyph(identifier, + writable, + "active_field2", + positions.answerField().height(), + positions.answerField().topFieldY()); + var glyph3 = createGlyph(identifier, + writable, + "active_field3", + positions.answerField().height(), + positions.answerField().bottomFieldY()); + var glyph4 = createGlyph(identifier, + writable, + "active_field4", + positions.answerField().height(), + positions.answerField().bottomFieldY()); + + activeFields.put(identifier, new TextField(glyph1, glyph2, glyph3, glyph4)); + glyphResourcePack + .with(StringIdentifier.image("active_field1__" + identifier), glyph1) + .with(StringIdentifier.image("active_field2__" + identifier), glyph2) + .with(StringIdentifier.image("active_field3__" + identifier), glyph3) + .with(StringIdentifier.image("active_field4__" + identifier), glyph4); + } + + public void registerNotActiveTextField(String identifier, Writable writable) { + registerNotActiveTextField(identifier, writable, DialogGlyphPositions.DEFAULT); + } + + public void registerNotActiveTextField(String identifier, Writable writable, DialogGlyphPositions positions) { + var glyph1 = createGlyph(identifier, + writable, + "not_active_field1", + positions.answerField().height(), + positions.answerField().topFieldY()); + var glyph2 = createGlyph(identifier, + writable, + "not_active_field2", + positions.answerField().height(), + positions.answerField().topFieldY()); + var glyph3 = createGlyph(identifier, + writable, + "not_active_field3", + positions.answerField().height(), + positions.answerField().bottomFieldY()); + var glyph4 = createGlyph(identifier, + writable, + "not_active_field4", + positions.answerField().height(), + positions.answerField().bottomFieldY()); + + notActiveFields.put(identifier, new TextField(glyph1, glyph2, glyph3, glyph4)); + glyphResourcePack + .with(StringIdentifier.image("not_active_field1__" + identifier), glyph1) + .with(StringIdentifier.image("not_active_field2__" + identifier), glyph2) + .with(StringIdentifier.image("not_active_field3__" + identifier), glyph3) + .with(StringIdentifier.image("not_active_field4__" + identifier), glyph4); + } + + public Optional getAvatar(String identifier) { + return getGlyphFromMap(identifier, avatars); + } + + public Optional getAvatarFrame(String identifier) { + return getGlyphFromMap(identifier, avatarFrames); + } + + public Optional getScrollUp(String identifier) { + return getGlyphFromMap(identifier, scrollUp); + } + + public Optional getScrollDown(String identifier) { + return getGlyphFromMap(identifier, scrollDown); + } + + public Optional getBackground(String identifier) { + return Optional.ofNullable(backgrounds.get(identifier)); + } + + public Optional getSubstrate(String identifier) { + return Optional.ofNullable(substrates.get(identifier)); + } + + public Optional getFont(String identifier) { + return Optional.ofNullable(fonts.get(identifier)).map(FontGlyphEntry::glyph); + } + + public Optional