From d825b167c6896e9f5638e22d027b6c28f4a70904 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Mon, 4 May 2026 21:42:02 +0200 Subject: [PATCH 01/19] New research category and funky formatting --- .../resources/assets/thavma/lang/en_us.json | 11 ++++++++ .../impl/client/gui/book/TextPageRenderer.kt | 17 +++++++++--- .../T7DatapackBuiltinEntriesProvider.kt | 10 +++++++ .../init/data/providers/T7LanguageProvider.kt | 27 +++++++++++++++++++ .../registries/deferred/ResearchCategories.kt | 1 + .../registries/deferred/ResearchEntries.kt | 4 +++ 6 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/generated/resources/assets/thavma/lang/en_us.json b/src/generated/resources/assets/thavma/lang/en_us.json index 964113ad..00d319a0 100644 --- a/src/generated/resources/assets/thavma/lang/en_us.json +++ b/src/generated/resources/assets/thavma/lang/en_us.json @@ -179,8 +179,19 @@ "research.thavma.scroll_given": "You have received a research scroll", "research.thavma.toast": "Research Complete!", "research/category.thavma.alchemy": "Alchemy", + "research/category.thavma.story": "???", "research/category.thavma.thavma": "Thavma", "research/entry.thavma.alchemy.alchemy": "Alchemy", + "research/entry.thavma.story.story_test": "A Courtesy Call", + "research/entry.thavma.story.story_test.page0.paragraph0": "Lorem ipsum %s sit amet,", + "research/entry.thavma.story.story_test.page0.paragraph1": "this story a great meaning haveth.", + "research/entry.thavma.story.story_test.page0.title": "A Courtesy Call", + "research/entry.thavma.story.story_test.page1.paragraph0": "Lorem ipsum dolor sit amet,", + "research/entry.thavma.story.story_test.page1.paragraph1": "this story a great meaning haveth.", + "research/entry.thavma.story.story_test.page1.title": "A Courtesy Call", + "research/entry.thavma.story.story_test.page2.paragraph0": "Lorem ipsum dolor sit amet,", + "research/entry.thavma.story.story_test.page2.paragraph1": "this story a great meaning haveth.", + "research/entry.thavma.story.story_test.page2.title": "A Courtesy Call", "research/entry.thavma.thavma.alchemy": "Alchemy", "research/entry.thavma.thavma.arcane_lens": "The Arcane Lens", "research/entry.thavma.thavma.arcane_lens.page0.paragraph0": "The part of the book I can read describes an arcane tool that \"allows the user to see\", whatever that might mean. I have a feeling that crafting it could assist my work in unsealing the other pages.", diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/TextPageRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/TextPageRenderer.kt index 249277e8..bb609674 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/TextPageRenderer.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/TextPageRenderer.kt @@ -7,12 +7,18 @@ import me.alegian.thavma.impl.client.util.drawString import me.alegian.thavma.impl.client.util.translateXY import me.alegian.thavma.impl.client.util.usePose import me.alegian.thavma.impl.common.book.TextPage +import net.minecraft.ChatFormatting import net.minecraft.client.Minecraft import net.minecraft.network.chat.Component +import net.minecraft.network.chat.Style +import net.minecraft.util.FormattedCharSequence object TextPageRenderer : PageRenderer { private val SEPARATOR = Texture("gui/book/separator", 128, 16, 128, 16) + val dolor = Component.literal("dolor") + .withStyle(ChatFormatting.DARK_RED, ChatFormatting.UNDERLINE) + override fun initPage(screen: EntryScreen, page: TextPage) { val font = Minecraft.getInstance().font val LINE_HEIGHT = font.lineHeight + 2 @@ -31,11 +37,16 @@ object TextPageRenderer : PageRenderer { relativeRenderable { guiGraphics, _, _, _ -> guiGraphics.usePose { for (paragraph in page.paragraphs) { - for (line in font.split(paragraph, size.x.toInt())) { - guiGraphics.drawString(Minecraft.getInstance().font, line) + //for (line in font.split(paragraph, size.x.toInt())) { + for (line in font.splitter.splitLines(paragraph, size.x.toInt(), Style.EMPTY)) { + if ("ipsum" in line.string) { + guiGraphics.drawString(Minecraft.getInstance().font, Component.translatable("research/entry.thavma.story.story_test.page0.paragraph0", dolor)) + //.withStyle(ChatFormatting.RED, ChatFormatting.BOLD)) + } + else guiGraphics.drawString(Minecraft.getInstance().font, Component.literal(line.string)) translateXY(0, LINE_HEIGHT) } - translateXY(0, LINE_HEIGHT) + translateXY(0, LINE_HEIGHT*2/3) } } } diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index f219bfa1..1d0994ce 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -36,6 +36,7 @@ import net.minecraft.resources.ResourceKey import net.minecraft.tags.ItemTags import net.minecraft.world.entity.EquipmentSlotGroup import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items import net.minecraft.world.item.Rarity import net.minecraft.world.item.enchantment.ConditionalEffect import net.minecraft.world.item.enchantment.Enchantment @@ -114,6 +115,7 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .add(T7DatapackRegistries.RESEARCH_CATEGORY) { ctx -> ctx.registerCategory(ResearchCategories.THAVMA, T7Items.BOOK.get().defaultInstance, 0f) ctx.registerCategory(ResearchCategories.ALCHEMY, T7Blocks.CRUCIBLE.get().asItem().defaultInstance, 1f) + ctx.registerCategory(ResearchCategories.STORY, Items.WRITABLE_BOOK.defaultInstance, 2f) } .add(T7DatapackRegistries.RESEARCH_ENTRY) { ctx -> ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) @@ -125,6 +127,14 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .defaultKnown() .build(ctx) + ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, -3), false, Items.TURTLE_HELMET.defaultInstance) + .research() + .addPage(simpleTextPage(2, true)) + .addPage(simpleTextPage(2, true)) + .addPage(simpleTextPage(2, true)) + .defaultKnown() + .build(ctx) + ResearchEntryBuilder(ResearchEntries.Thavma.TREES, Vector2i(0, -3), false, T7Blocks.GREATWOOD_LOG.get().asItem().defaultInstance) .research(lockedAspect(2, 0, Aspects.HERBA), lockedAspect(2, 4, Aspects.HERBA)) .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index f19bd5fe..a7c7961a 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -100,9 +100,12 @@ import me.alegian.thavma.impl.init.registries.deferred.WandPlatingMaterials.ORIC import me.alegian.thavma.impl.init.registries.deferred.WandPlatingMaterials.THAVMITE import me.alegian.thavma.impl.integration.RecipeViewerAliases import me.alegian.thavma.impl.integration.RecipeViewerDescriptions +import net.minecraft.ChatFormatting import net.minecraft.Util import net.minecraft.core.registries.Registries import net.minecraft.data.PackOutput +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.MutableComponent import net.minecraft.resources.ResourceKey import net.minecraft.world.entity.ai.attributes.Attribute import net.minecraft.world.item.crafting.RecipeType @@ -271,6 +274,30 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( addCategory(ResearchCategories.ALCHEMY, "Alchemy") addEntry(ResearchEntries.Alchemy.ALCHEMY, "Alchemy") + addCategory(ResearchCategories.STORY, "???") + addEntry(ResearchEntries.Story.TEST, "A Courtesy Call") + + addTextPage( + ResearchEntries.Story.TEST, 0, + "A Courtesy Call", + "Lorem ipsum %s sit amet,", + "this story a great meaning haveth." + ) + + addTextPage( + ResearchEntries.Story.TEST, 1, + "A Courtesy Call", + "Lorem ipsum dolor sit amet,", + "this story a great meaning haveth." + ) + + addTextPage( + ResearchEntries.Story.TEST, 2, + "A Courtesy Call", + "Lorem ipsum dolor sit amet,", + "this story a great meaning haveth." + ) + addTextPage( ResearchEntries.Thavma.THAVMA, 0, "Thavma", diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchCategories.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchCategories.kt index 43c8a571..390ef405 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchCategories.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchCategories.kt @@ -7,4 +7,5 @@ import net.minecraft.resources.ResourceKey object ResearchCategories { val THAVMA = ResourceKey.create(T7DatapackRegistries.RESEARCH_CATEGORY, rl("thavma")) val ALCHEMY = ResourceKey.create(T7DatapackRegistries.RESEARCH_CATEGORY, rl("alchemy")) + val STORY = ResourceKey.create(T7DatapackRegistries.RESEARCH_CATEGORY, rl("story")) } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt index a236d3f1..fc7d78da 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt @@ -25,6 +25,10 @@ object ResearchEntries { object Alchemy { val ALCHEMY = register("alchemy", ResearchCategories.ALCHEMY) } + + object Story { + val TEST = register("story_test", ResearchCategories.STORY) + } } // prepend category id to entry id to avoid duplicates From 13d3bd5c00baff076bb45073f6fa47256359afd0 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Tue, 5 May 2026 19:19:40 +0200 Subject: [PATCH 02/19] Page turning in the Thavmonomicon --- .../impl/client/gui/book/EntryScreen.kt | 39 ++++++++++++- .../impl/client/gui/book/PageTurningWidget.kt | 57 +++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/main/java/me/alegian/thavma/impl/client/gui/book/PageTurningWidget.kt diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt index 72fbf7c7..33fe56a6 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt @@ -1,20 +1,27 @@ package me.alegian.thavma.impl.client.gui.book import me.alegian.thavma.impl.client.gui.layout.* +import me.alegian.thavma.impl.client.gui.research_table.ButtonWidget import me.alegian.thavma.impl.client.texture.Texture import me.alegian.thavma.impl.common.book.Page import me.alegian.thavma.impl.common.research.ResearchEntry +import me.alegian.thavma.impl.init.registries.deferred.ResearchEntries +import me.alegian.thavma.impl.rl import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.screens.Screen import net.minecraft.core.Holder import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceKey +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.level.Level +import kotlin.math.ceil class EntryScreen(private val entry: Holder) : Screen(Component.literal("Book Entry")) { companion object { private val BG = Texture("gui/book/background", 510, 282, 512, 512) } - private val currentPage = 0 + private var currentPage = 0 override fun init() { super.init() @@ -33,17 +40,47 @@ class EntryScreen(private val entry: Holder) : Screen(Component.l paddingBottom = 42 gap = 48 }) { + //for (doublepage in 0..ceil(entry.value().pages.size/2.0).toInt()){ Row({ size = grow() }) { initPage(entry.value().pages.getOrNull(currentPage)) + Box({ + width = fixed(PageTurningWidget.LEFT_TEXTURE.width) + height = fixed(PageTurningWidget.LEFT_TEXTURE.height) + }) { + afterLayout { + if (currentPage != 0) { + addRenderableWidget(PageTurningWidget(position, false) { + // reinitiate the screen for this research entry when clicked + // with an updated page + currentPage -= 2 + init() + })} + } + } } Row({ size = grow() }) { initPage(entry.value().pages.getOrNull(currentPage + 1)) + Box({ + width = fixed(PageTurningWidget.RIGHT_TEXTURE.width) + height = fixed(PageTurningWidget.RIGHT_TEXTURE.height) + }) { + afterLayout { + if ((entry.value().pages.getOrNull(currentPage + 2) ?: 1) is Page) { + addRenderableWidget(PageTurningWidget(position, true) { + // reinitiate the screen for this research entry when clicked + // with an updated page + currentPage += 2 + init() + })} + } + } } + // } } } } diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/PageTurningWidget.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageTurningWidget.kt new file mode 100644 index 00000000..6c111175 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageTurningWidget.kt @@ -0,0 +1,57 @@ +package me.alegian.thavma.impl.client.gui.book + +import com.mojang.blaze3d.systems.RenderSystem +import me.alegian.thavma.impl.client.gui.research_table.ButtonWidget +import me.alegian.thavma.impl.client.gui.research_table.ButtonWidget.Companion.LEFT_TEXTURE +import me.alegian.thavma.impl.client.gui.research_table.ButtonWidget.Companion.leftTranslationId +import me.alegian.thavma.impl.client.gui.research_table.ButtonWidget.Companion.rightTranslationId +import me.alegian.thavma.impl.client.texture.Texture +import me.alegian.thavma.impl.client.util.blit +import me.alegian.thavma.impl.client.util.translateXY +import me.alegian.thavma.impl.client.util.usePose +import me.alegian.thavma.impl.common.block.ResearchTableBlock +import net.minecraft.client.gui.GuiGraphics +import net.minecraft.client.gui.components.AbstractWidget +import net.minecraft.client.gui.components.Tooltip +import net.minecraft.client.gui.narration.NarrationElementOutput +import net.minecraft.network.chat.Component +import net.minecraft.world.phys.Vec2 + +class PageTurningWidget(position: Vec2, private val invert: Boolean, private val handleClick: () -> Unit) : + AbstractWidget(position.x.toInt(), position.y.toInt(), LEFT_TEXTURE.width, LEFT_TEXTURE.height, Component.translatable(if (invert) rightTranslationId else leftTranslationId)) { + + init { + tooltip = Tooltip.create(message) + } + + + override fun renderWidget( + guiGraphics: GuiGraphics, + mouseX: Int, + mouseY: Int, + partialTick: Float + ) { + RenderSystem.disableCull() + guiGraphics.usePose { + translateXY(x, y) + val texture = if (invert) ButtonWidget.Companion.RIGHT_TEXTURE else ButtonWidget.Companion.LEFT_TEXTURE + guiGraphics.blit(texture) + } + RenderSystem.enableCull() + } + + override fun onClick(mouseX: Double, mouseY: Double, button: Int) { + handleClick() + } + + companion object { + val LEFT_TEXTURE = Texture("gui/research_table/left_button", 35, 12, 35, 12) + val RIGHT_TEXTURE = Texture("gui/research_table/right_button", 35, 12, 35, 12) + private val namespace = ".buttonWidget" + val leftTranslationId = ResearchTableBlock.CONTAINER_TITLE + namespace + ".left" + val rightTranslationId = ResearchTableBlock.CONTAINER_TITLE + namespace + ".right" + } + + override fun updateWidgetNarration(narrationElementOutput: NarrationElementOutput) { + } +} \ No newline at end of file From cd5aa513f0bcb93946215a60c6b73eb9b0de7ac6 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Tue, 5 May 2026 19:40:17 +0200 Subject: [PATCH 03/19] Fixed the underlining and persistent widget bugs --- .../resources/assets/thavma/lang/en_us.json | 13 +++++++------ .../thavma/impl/client/gui/book/EntryScreen.kt | 12 ++++++++---- .../init/data/providers/T7LanguageProvider.kt | 15 ++++++++------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/generated/resources/assets/thavma/lang/en_us.json b/src/generated/resources/assets/thavma/lang/en_us.json index 00d319a0..590d0fb9 100644 --- a/src/generated/resources/assets/thavma/lang/en_us.json +++ b/src/generated/resources/assets/thavma/lang/en_us.json @@ -183,15 +183,16 @@ "research/category.thavma.thavma": "Thavma", "research/entry.thavma.alchemy.alchemy": "Alchemy", "research/entry.thavma.story.story_test": "A Courtesy Call", - "research/entry.thavma.story.story_test.page0.paragraph0": "Lorem ipsum %s sit amet,", + "research/entry.thavma.story.story_test.page0.paragraph0": "Lorem ipsum %s 1 sit amet,", "research/entry.thavma.story.story_test.page0.paragraph1": "this story a great meaning haveth.", - "research/entry.thavma.story.story_test.page0.title": "A Courtesy Call", - "research/entry.thavma.story.story_test.page1.paragraph0": "Lorem ipsum dolor sit amet,", + "research/entry.thavma.story.story_test.page0.title": "A Courtesy Call 1", + "research/entry.thavma.story.story_test.page1.paragraph0": "Lorem dolor 2 sit amet,", "research/entry.thavma.story.story_test.page1.paragraph1": "this story a great meaning haveth.", - "research/entry.thavma.story.story_test.page1.title": "A Courtesy Call", - "research/entry.thavma.story.story_test.page2.paragraph0": "Lorem ipsum dolor sit amet,", + "research/entry.thavma.story.story_test.page1.paragraph2": "", + "research/entry.thavma.story.story_test.page1.title": "A Courtesy Call 2", + "research/entry.thavma.story.story_test.page2.paragraph0": "Lorem lotrumatum dolor 3 sit amet,", "research/entry.thavma.story.story_test.page2.paragraph1": "this story a great meaning haveth.", - "research/entry.thavma.story.story_test.page2.title": "A Courtesy Call", + "research/entry.thavma.story.story_test.page2.title": "A Courtesy Call 3", "research/entry.thavma.thavma.alchemy": "Alchemy", "research/entry.thavma.thavma.arcane_lens": "The Arcane Lens", "research/entry.thavma.thavma.arcane_lens.page0.paragraph0": "The part of the book I can read describes an arcane tool that \"allows the user to see\", whatever that might mean. I have a feeling that crafting it could assist my work in unsealing the other pages.", diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt index 33fe56a6..fdc97a7e 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt @@ -40,21 +40,23 @@ class EntryScreen(private val entry: Holder) : Screen(Component.l paddingBottom = 42 gap = 48 }) { - //for (doublepage in 0..ceil(entry.value().pages.size/2.0).toInt()){ Row({ size = grow() }) { initPage(entry.value().pages.getOrNull(currentPage)) + if (currentPage != 0) { Box({ width = fixed(PageTurningWidget.LEFT_TEXTURE.width) height = fixed(PageTurningWidget.LEFT_TEXTURE.height) }) { afterLayout { - if (currentPage != 0) { + addRenderableWidget(PageTurningWidget(position, false) { // reinitiate the screen for this research entry when clicked // with an updated page + // clearWidgets() is essential, also clears underline formatting! currentPage -= 2 + clearWidgets() init() })} } @@ -65,22 +67,24 @@ class EntryScreen(private val entry: Holder) : Screen(Component.l size = grow() }) { initPage(entry.value().pages.getOrNull(currentPage + 1)) + if ((entry.value().pages.getOrNull(currentPage + 2) ?: 1) is Page) { Box({ width = fixed(PageTurningWidget.RIGHT_TEXTURE.width) height = fixed(PageTurningWidget.RIGHT_TEXTURE.height) }) { afterLayout { - if ((entry.value().pages.getOrNull(currentPage + 2) ?: 1) is Page) { + addRenderableWidget(PageTurningWidget(position, true) { // reinitiate the screen for this research entry when clicked // with an updated page + // clearWidgets() is essential, also clears underline formatting! currentPage += 2 + clearWidgets() init() })} } } } - // } } } } diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index a7c7961a..c22e97d3 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -279,22 +279,23 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( addTextPage( ResearchEntries.Story.TEST, 0, - "A Courtesy Call", - "Lorem ipsum %s sit amet,", + "A Courtesy Call 1", + "Lorem ipsum %s 1 sit amet,", "this story a great meaning haveth." ) addTextPage( ResearchEntries.Story.TEST, 1, - "A Courtesy Call", - "Lorem ipsum dolor sit amet,", - "this story a great meaning haveth." + "A Courtesy Call 2", + "Lorem dolor 2 sit amet,", + "this story a great meaning haveth.", + "" ) addTextPage( ResearchEntries.Story.TEST, 2, - "A Courtesy Call", - "Lorem ipsum dolor sit amet,", + "A Courtesy Call 3", + "Lorem lotrumatum dolor 3 sit amet,", "this story a great meaning haveth." ) From 6d5060b5a59c9ac73784e5277691f3a9433c1658 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Tue, 5 May 2026 20:53:24 +0200 Subject: [PATCH 04/19] Gentle PageTurning touches --- .../alegian/thavma/impl/client/gui/book/PageTurningWidget.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/PageTurningWidget.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageTurningWidget.kt index 6c111175..0128d77c 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/PageTurningWidget.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageTurningWidget.kt @@ -41,6 +41,10 @@ class PageTurningWidget(position: Vec2, private val invert: Boolean, private val } override fun onClick(mouseX: Double, mouseY: Double, button: Int) { + //handleClick() + } + + override fun onRelease(mouseX: Double, mouseY: Double) { handleClick() } From 7caad42bc9bf70efe13fd871dc389abc69a7192f Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Wed, 6 May 2026 22:24:28 +0200 Subject: [PATCH 05/19] Additionally pushed changed jsons --- .../thavma/research/category/story.json | 10 +++ .../research/entry/story/story_test.json | 66 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/generated/resources/data/thavma/thavma/research/category/story.json create mode 100644 src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json diff --git a/src/generated/resources/data/thavma/thavma/research/category/story.json b/src/generated/resources/data/thavma/thavma/research/category/story.json new file mode 100644 index 00000000..26df1d1e --- /dev/null +++ b/src/generated/resources/data/thavma/thavma/research/category/story.json @@ -0,0 +1,10 @@ +{ + "icon": { + "count": 1, + "id": "minecraft:writable_book" + }, + "index": 2.0, + "title": { + "translate": "research/category.thavma.story" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json b/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json new file mode 100644 index 00000000..17b97fbc --- /dev/null +++ b/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json @@ -0,0 +1,66 @@ +{ + "category": "thavma:story", + "children": [], + "defaultKnown": true, + "defaultResearchState": [], + "icon": { + "count": 1, + "id": "minecraft:turtle_helmet" + }, + "pages": [ + { + "type": "thavma:text", + "paragraphs": [ + { + "translate": "research/entry.thavma.story.story_test.page0.paragraph0" + }, + { + "translate": "research/entry.thavma.story.story_test.page0.paragraph1" + } + ], + "title": { + "bold": true, + "translate": "research/entry.thavma.story.story_test.page0.title" + } + }, + { + "type": "thavma:text", + "paragraphs": [ + { + "translate": "research/entry.thavma.story.story_test.page1.paragraph0" + }, + { + "translate": "research/entry.thavma.story.story_test.page1.paragraph1" + } + ], + "title": { + "bold": true, + "translate": "research/entry.thavma.story.story_test.page1.title" + } + }, + { + "type": "thavma:text", + "paragraphs": [ + { + "translate": "research/entry.thavma.story.story_test.page2.paragraph0" + }, + { + "translate": "research/entry.thavma.story.story_test.page2.paragraph1" + } + ], + "title": { + "bold": true, + "translate": "research/entry.thavma.story.story_test.page2.title" + } + } + ], + "position": [ + 0, + -3 + ], + "preferX": false, + "title": { + "color": "yellow", + "translate": "research/entry.thavma.story.story_test" + } +} \ No newline at end of file From a91200ee1150b6e4522556790c3e2a47cb819fd3 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Wed, 6 May 2026 22:29:32 +0200 Subject: [PATCH 06/19] Very WIP dynamic research entry rendering --- .../thavma/impl/common/book/DynamicPage.kt | 22 + .../thavma/impl/common/book/PageFeature.kt | 22 + .../impl/common/book/PageFeatureType.kt | 12 + .../impl/common/book/ParagraphFeature.kt | 44 ++ .../thavma/impl/common/book/TitleFeature.kt | 36 ++ .../research/OriginalCodeHolderCuzImLazy.kt | 389 ++++++++++++++++++ .../impl/common/research/ResearchEntry.kt | 21 +- .../T7DatapackBuiltinEntriesProvider.kt | 65 +-- .../init/data/providers/T7LanguageProvider.kt | 8 + .../impl/init/registries/T7Registries.kt | 5 + .../registries/deferred/PageFeatureTypes.kt | 17 + .../init/registries/deferred/PageTypes.kt | 3 + 12 files changed, 610 insertions(+), 34 deletions(-) create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt create mode 100644 src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt diff --git a/src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt b/src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt new file mode 100644 index 00000000..fecf0139 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt @@ -0,0 +1,22 @@ +package me.alegian.thavma.impl.common.book + +import com.mojang.serialization.codecs.RecordCodecBuilder +import me.alegian.thavma.impl.init.registries.deferred.PageTypes +import net.minecraft.network.chat.ComponentSerialization +import java.util.Optional + +class DynamicPage(val pageFeatures: List): Page { + // this might be a problem but for a future Tobias + override val type: PageType<*> + get() = PageTypes.TEXT.get() + + +// companion object { +// val CODEC = RecordCodecBuilder.mapCodec { builder -> +// builder.group( +// ComponentSerialization.CODEC.fieldOf("text").forGetter(ParagraphFeature::text) +// ).apply(builder, ::ParagraphFeature) +// } +// +// } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt new file mode 100644 index 00000000..c3563481 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt @@ -0,0 +1,22 @@ +package me.alegian.thavma.impl.common.book + +import me.alegian.thavma.impl.init.registries.T7Registries + +interface PageFeature { + val coversWholePage: Boolean + val mustStartPage: Boolean + val mustOccupySetPage: Boolean + val preferredPageIndex: Int + get() = 1 + val renderedHeight: Int + val pageWidth: Int + get() = 256 + + val type: PageFeatureType<*> + + companion object{ + val CODEC = T7Registries.PAGE_FEATURE_TYPE.byNameCodec().dispatch({ pageFeature -> pageFeature.type }, { type -> type.codec }) + + fun translationId(baseId: String, featureIndex: Int) = "$baseId.pageFeature$featureIndex" + } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt b/src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt new file mode 100644 index 00000000..df95f3c4 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt @@ -0,0 +1,12 @@ +package me.alegian.thavma.impl.common.book + +import com.mojang.serialization.MapCodec +import net.minecraft.resources.ResourceLocation + +class PageFeatureType(name: ResourceLocation, val codec: MapCodec) { + val stringName = name.toString() + + override fun toString(): String { + return stringName + } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt new file mode 100644 index 00000000..fcce37fe --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt @@ -0,0 +1,44 @@ +package me.alegian.thavma.impl.common.book + +import com.mojang.serialization.codecs.RecordCodecBuilder +import me.alegian.thavma.impl.common.book.PageFeature +import me.alegian.thavma.impl.init.data.worldgen.tree.trunk.SilverwoodTrunkPlacer +import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Font +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.network.chat.Style +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer.trunkPlacerParts +import java.util.Optional + +class ParagraphFeature(val text: Component): PageFeature { + override val type: PageFeatureType<*> + get() = PageFeatureTypes.PARAGRAPH.get() + + override val coversWholePage = false + override val mustStartPage = false + override val mustOccupySetPage = false + + val font: Font = Minecraft.getInstance().font + // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor + val LINE_HEIGHT = font.lineHeight + 2 + val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) + + override val renderedHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 2 / 3 + + companion object { + val CODEC = RecordCodecBuilder.mapCodec { builder -> + builder.group( + ComponentSerialization.CODEC.fieldOf("text").forGetter(ParagraphFeature::text) + ).apply(builder, ::ParagraphFeature) + } + + fun translationId(baseId: String, featureIndex: Int) = "$baseId.paragraphFeature$featureIndex" + } + + //fun titleTranslationId(baseId: String, pageIndex: Int) = Page.translationId(baseId, pageIndex) + ".title" + //fun paragraphFeatureTranslationId(baseId: String, index: Int) = Page.translationId(baseId, pageIndex) + ".paragraph$index" + +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt new file mode 100644 index 00000000..c5a628a8 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt @@ -0,0 +1,36 @@ +package me.alegian.thavma.impl.common.book + +import com.mojang.serialization.codecs.RecordCodecBuilder +import me.alegian.thavma.impl.common.book.PageFeature +import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Font +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.network.chat.Style + +class TitleFeature(val text: Component, ): PageFeature { + override val type: PageFeatureType<*> + get() = PageFeatureTypes.TITLE.get() + + override val coversWholePage = false + override val mustStartPage = false + override val mustOccupySetPage = false + + val font: Font = Minecraft.getInstance().font + // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor + val LINE_HEIGHT = font.lineHeight + 2 + val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) + + override val renderedHeight = LINE_HEIGHT * lines.size + 16 + + companion object { + val CODEC = RecordCodecBuilder.mapCodec { builder -> + builder.group( + ComponentSerialization.CODEC.fieldOf("text").forGetter(TitleFeature::text) + ).apply(builder, ::TitleFeature) + } + + fun translationId(baseId: String, featureIndex: Int) = "$baseId.titleFeature$featureIndex" + } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt b/src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt new file mode 100644 index 00000000..70297783 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt @@ -0,0 +1,389 @@ +package me.alegian.thavma.impl.common.research + +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder +import me.alegian.thavma.impl.Thavma +import me.alegian.thavma.impl.common.aspect.Aspect +import me.alegian.thavma.impl.common.book.CraftingPage +import me.alegian.thavma.impl.common.book.Page +import me.alegian.thavma.impl.common.book.TextPage +import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION +import me.alegian.thavma.impl.common.util.Indices +import me.alegian.thavma.impl.common.util.T7ExtraCodecs +import me.alegian.thavma.impl.common.util.registry +import me.alegian.thavma.impl.init.data.Recipes +import me.alegian.thavma.impl.init.data.worldgen.Node +import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedOre +import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedStoneOre +import me.alegian.thavma.impl.init.data.worldgen.spawn.AngryZombieSpawn +import me.alegian.thavma.impl.init.data.worldgen.tree.GreatwoodTree +import me.alegian.thavma.impl.init.data.worldgen.tree.SilverwoodTree +import me.alegian.thavma.impl.init.registries.T7DatapackRegistries +import me.alegian.thavma.impl.init.registries.T7Tags +import me.alegian.thavma.impl.init.registries.deferred.Aspects +import me.alegian.thavma.impl.init.registries.deferred.ResearchCategories +import me.alegian.thavma.impl.init.registries.deferred.ResearchEntries +import me.alegian.thavma.impl.init.registries.deferred.T7Blocks +import me.alegian.thavma.impl.init.registries.deferred.T7Items +import me.alegian.thavma.impl.init.registries.deferred.WandCoreMaterials +import me.alegian.thavma.impl.init.registries.deferred.WandPlatingMaterials +import me.alegian.thavma.impl.init.registries.deferred.util.DeferredAspect +import net.minecraft.ChatFormatting +import net.minecraft.Util +import net.minecraft.advancements.critereon.DamageSourcePredicate +import net.minecraft.advancements.critereon.TagPredicate +import net.minecraft.core.Holder +import net.minecraft.core.HolderLookup +import net.minecraft.core.HolderSet +import net.minecraft.core.RegistrySetBuilder +import net.minecraft.core.component.DataComponentMap +import net.minecraft.core.registries.Registries +import net.minecraft.data.PackOutput +import net.minecraft.data.worldgen.BootstrapContext +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.resources.RegistryFileCodec +import net.minecraft.resources.ResourceKey +import net.minecraft.tags.ItemTags +import net.minecraft.world.entity.EquipmentSlotGroup +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraft.world.item.Rarity +import net.minecraft.world.item.enchantment.ConditionalEffect +import net.minecraft.world.item.enchantment.Enchantment +import net.minecraft.world.item.enchantment.Enchantment.Cost +import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition +import net.minecraft.world.item.enchantment.EnchantmentEffectComponents +import net.minecraft.world.item.enchantment.LevelBasedValue +import net.minecraft.world.item.enchantment.effects.AddValue +import net.minecraft.world.level.Level +import net.minecraft.world.level.storage.loot.predicates.DamageSourceCondition +import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider +import net.neoforged.neoforge.registries.NeoForgeRegistries +import org.joml.Vector2i +import java.util.Optional +import java.util.concurrent.CompletableFuture + +object OriginalCodeHolderCuzImLazy { + + +// package me.alegian.thavma.impl.common.research +// +// import com.mojang.serialization.Codec +// import com.mojang.serialization.codecs.RecordCodecBuilder +// import me.alegian.thavma.impl.Thavma +// import me.alegian.thavma.impl.common.book.Page +// import me.alegian.thavma.impl.common.util.T7ExtraCodecs +// import me.alegian.thavma.impl.common.util.registry +// import me.alegian.thavma.impl.init.registries.T7DatapackRegistries +// import net.minecraft.Util +// import net.minecraft.core.Holder +// import net.minecraft.network.chat.Component +// import net.minecraft.network.chat.ComponentSerialization +// import net.minecraft.resources.RegistryFileCodec +// import net.minecraft.resources.ResourceKey +// import net.minecraft.world.item.ItemStack +// import net.minecraft.world.level.Level +// import org.joml.Vector2i +// +// private val parentsMap = mutableMapOf>>() +// +// class ResearchEntry( +// val category: Holder, +// val position: Vector2i, +// val preferX: Boolean, +// val children: List>, +// val pages: List, +// val icon: ItemStack, +// val title: Component, +// val defaultResearchState: List, +// val defaultKnown: Boolean +// ) { +// fun parents(level: Level) = +// parentsMap.computeIfAbsent(this) { _ -> +// val registry = level.registry(T7DatapackRegistries.RESEARCH_ENTRY) +// registry.holders().filter { e -> e.value().children.map{it.value()}.contains(this) }.toList() +// } +// +// companion object { +// val CODEC: Codec = RecordCodecBuilder.create { +// it.group( +// RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_CATEGORY, ResearchCategory.CODEC).fieldOf("category").forGetter(ResearchEntry::category), +// T7ExtraCodecs.VECTOR2I.fieldOf("position").forGetter(ResearchEntry::position), +// Codec.BOOL.fieldOf("preferX").forGetter(ResearchEntry::preferX), +// RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_ENTRY, CODEC).listOf().fieldOf("children").forGetter(ResearchEntry::children), +// Page.CODEC.listOf().fieldOf("pages").forGetter(ResearchEntry::pages), +// ItemStack.STRICT_CODEC.fieldOf("icon").forGetter(ResearchEntry::icon), +// ComponentSerialization.CODEC.fieldOf("title").forGetter(ResearchEntry::title), +// SocketState.CODEC.listOf().fieldOf("defaultResearchState").forGetter(ResearchEntry::defaultResearchState), +// Codec.BOOL.fieldOf("defaultKnown").forGetter(ResearchEntry::defaultKnown), +// ).apply(it, ::ResearchEntry) +// } +// +// fun translationId(entryKey: ResourceKey) = Util.makeDescriptionId(T7DatapackRegistries.RESEARCH_ENTRY.location().path, entryKey.location()) +// +// val TOAST_TRANSLATION = "research." + Thavma.MODID + ".toast" +// val SCROLL_GIVEN_TRANSLATION = "research." + Thavma.MODID + ".scroll_given" +// val PARENTS_UNKNOWN_TRANSLATION = "research." + Thavma.MODID + ".parents_unknown" +// } +// } + + + +// package me.alegian.thavma.impl.init.data.providers +// +// import me.alegian.thavma.impl.Thavma +// import me.alegian.thavma.impl.common.aspect.Aspect +// import me.alegian.thavma.impl.common.book.CraftingPage +// import me.alegian.thavma.impl.common.book.Page +// import me.alegian.thavma.impl.common.book.TextPage +// import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION +// import me.alegian.thavma.impl.common.research.ResearchCategory +// import me.alegian.thavma.impl.common.research.ResearchEntry +// import me.alegian.thavma.impl.common.research.SocketState +// import me.alegian.thavma.impl.common.util.Indices +// import me.alegian.thavma.impl.init.data.Recipes +// import me.alegian.thavma.impl.init.data.worldgen.Node +// import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedOre +// import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedStoneOre +// import me.alegian.thavma.impl.init.data.worldgen.spawn.AngryZombieSpawn +// import me.alegian.thavma.impl.init.data.worldgen.tree.GreatwoodTree +// import me.alegian.thavma.impl.init.data.worldgen.tree.SilverwoodTree +// import me.alegian.thavma.impl.init.registries.T7DatapackRegistries +// import me.alegian.thavma.impl.init.registries.T7Tags +// import me.alegian.thavma.impl.init.registries.deferred.* +// import me.alegian.thavma.impl.init.registries.deferred.util.DeferredAspect +// import net.minecraft.ChatFormatting +// import net.minecraft.advancements.critereon.DamageSourcePredicate +// import net.minecraft.advancements.critereon.TagPredicate +// import net.minecraft.core.HolderLookup +// import net.minecraft.core.HolderSet +// import net.minecraft.core.RegistrySetBuilder +// import net.minecraft.core.component.DataComponentMap +// import net.minecraft.core.registries.Registries +// import net.minecraft.data.PackOutput +// import net.minecraft.data.worldgen.BootstrapContext +// import net.minecraft.network.chat.Component +// import net.minecraft.resources.ResourceKey +// import net.minecraft.tags.ItemTags +// import net.minecraft.world.entity.EquipmentSlotGroup +// import net.minecraft.world.item.ItemStack +// import net.minecraft.world.item.Items +// import net.minecraft.world.item.Rarity +// import net.minecraft.world.item.enchantment.ConditionalEffect +// import net.minecraft.world.item.enchantment.Enchantment +// import net.minecraft.world.item.enchantment.Enchantment.Cost +// import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition +// import net.minecraft.world.item.enchantment.EnchantmentEffectComponents +// import net.minecraft.world.item.enchantment.LevelBasedValue +// import net.minecraft.world.item.enchantment.effects.AddValue +// import net.minecraft.world.level.storage.loot.predicates.DamageSourceCondition +// import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider +// import net.neoforged.neoforge.registries.NeoForgeRegistries +// import org.joml.Vector2i +// import java.util.* +// import java.util.concurrent.CompletableFuture +// +// class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: CompletableFuture) : DatapackBuiltinEntriesProvider(output, registries, builder, setOf(Thavma.MODID)) { +// companion object { +// private val builder: RegistrySetBuilder = RegistrySetBuilder() +// .add(Registries.CONFIGURED_FEATURE) { ctx -> +// GreatwoodTree.registerConfigured(ctx) +// SilverwoodTree.registerConfigured(ctx) +// InfusedOre.registerConfigured(ctx) +// InfusedStoneOre.registerConfigured(ctx) +// Node.registerConfigured(ctx) +// } +// .add(Registries.PLACED_FEATURE) { ctx -> +// GreatwoodTree.registerPlaced(ctx) +// SilverwoodTree.registerPlaced(ctx) +// InfusedOre.registerPlaced(ctx) +// InfusedStoneOre.registerPlaced(ctx) +// Node.registerPlaced(ctx) +// } +// .add(NeoForgeRegistries.Keys.BIOME_MODIFIERS) { ctx -> +// GreatwoodTree.registerBiomeModifier(ctx) +// SilverwoodTree.registerBiomeModifier(ctx) +// InfusedStoneOre.registerBiomeModifier(ctx) +// AngryZombieSpawn.registerBiomeModifier(ctx) +// Node.registerBiomeModifier(ctx) +// } +// .add(Registries.ENCHANTMENT) { bootstrap -> +// val itemRegistry = bootstrap.lookup(Registries.ITEM) +// bootstrap.register( +// ResourceKey.create( +// Registries.ENCHANTMENT, +// LOCATION +// ), +// Enchantment( +// Component.literal("Shriek Resistance"), +// EnchantmentDefinition( +// itemRegistry.getOrThrow(ItemTags.ARMOR_ENCHANTABLE), +// Optional.empty(), +// 5, +// 4, +// Cost(10, 8), +// Cost(18, 8), +// 2, +// listOf(EquipmentSlotGroup.ARMOR) +// ), +// HolderSet.empty(), +// DataComponentMap.builder() +// .set( +// EnchantmentEffectComponents.DAMAGE_PROTECTION, listOf( +// ConditionalEffect( +// AddValue(LevelBasedValue.perLevel(2.0f)), +// Optional.of( +// DamageSourceCondition.hasDamageSource( +// DamageSourcePredicate.Builder.damageType().tag(TagPredicate.`is`(T7Tags.DamageTypes.SONIC)) +// ).build() +// ) +// ) +// ) +// ).build() +// ) +// ) +// } +// .add(T7DatapackRegistries.RESEARCH_CATEGORY) { ctx -> +// ctx.registerCategory(ResearchCategories.THAVMA, T7Items.BOOK.get().defaultInstance, 0f) +// ctx.registerCategory(ResearchCategories.ALCHEMY, T7Blocks.CRUCIBLE.get().asItem().defaultInstance, 1f) +// ctx.registerCategory(ResearchCategories.STORY, Items.WRITABLE_BOOK.defaultInstance, 2f) +// } +// .add(T7DatapackRegistries.RESEARCH_ENTRY) { ctx -> +// ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) +// .addPage(simpleTextPage(3, true)) +// .addPage(simpleTextPage(1, false)) +// .addChild(ResearchEntries.Thavma.TREES) +// .addChild(ResearchEntries.Thavma.ORES) +// .defaultKnown() +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, -3), false, Items.TURTLE_HELMET.defaultInstance) +// .research() +// .addPage(simpleTextPage(2, true)) +// .addPage(simpleTextPage(2, true)) +// .addPage(simpleTextPage(2, true)) +// .defaultKnown() +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.TREES, Vector2i(0, -3), false, T7Blocks.GREATWOOD_LOG.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.HERBA), lockedAspect(2, 4, Aspects.HERBA)) +// .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.ORES, Vector2i(2, -4), false, T7Items.SHARDS[Aspects.AQUA]!!.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.TERRA), lockedAspect(2, 4, Aspects.AETHER)) +// .addChild(ResearchEntries.Thavma.ARCANE_LENS) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) +// .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) +// .addPage(simpleTextPage(3, true)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) +// .addPage { _, _ -> CraftingPage(Recipes.CHEST) } +// .addChild(ResearchEntries.Thavma.WANDS) +// .addChild(ResearchEntries.Thavma.TECHNOLOGY) +// .addChild(ResearchEntries.Thavma.ALCHEMY) +// .addChild(ResearchEntries.Thavma.INFUSION) +// .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_PROFICIENCY, Vector2i(-1, -1), false, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.ALCHEMY, Vector2i(-2, 2), true, T7Blocks.CRUCIBLE.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AQUA), lockedAspect(2, 4, Aspects.ALKIMIA)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.WANDS, Vector2i(-2, 4), true, T7Items.wandOrThrow(WandPlatingMaterials.THAVMITE.get(), WandCoreMaterials.SILVERWOOD.get()).defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.INSTRUMENTUM)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.INFUSION, Vector2i(2, 2), true, T7Blocks.MATRIX.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.TERRA), lockedAspect(2, 4, Aspects.AETHER)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.TECHNOLOGY, Vector2i(2, 4), true, T7Items.GOGGLES.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.INSTRUMENTUM), lockedAspect(2, 4, Aspects.CIVILIS)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Alchemy.ALCHEMY, Vector2i(0, 0), false, T7Blocks.CRUCIBLE.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AQUA), lockedAspect(2, 4, Aspects.ALKIMIA)) +// .defaultKnown() +// .build(ctx) +// } +// } +// } +// +// private class ResearchEntryBuilder( +// private val key: ResourceKey, +// private val pos: Vector2i, +// private val preferX: Boolean, +// private val icon: ItemStack +// ) { +// private val children = mutableListOf>() +// private val pages = mutableListOf() +// private val socketStates = mutableListOf() +// private var defaultKnown = false +// +// fun addChild(entryKey: ResourceKey): ResearchEntryBuilder { +// children.add(entryKey) +// return this +// } +// +// fun addPage(makePage: (ResourceKey, Int) -> Page): ResearchEntryBuilder { +// pages.add(makePage(key, pages.size)) +// return this +// } +// +// fun research(vararg states: SocketState): ResearchEntryBuilder { +// socketStates.addAll(states) +// return this +// } +// +// fun defaultKnown(): ResearchEntryBuilder { +// defaultKnown = true +// return this +// } +// +// fun build(ctx: BootstrapContext) = ResearchEntries.CATEGORIES[key]?.let { cat -> +// val categoryRegistry = ctx.lookup(T7DatapackRegistries.RESEARCH_CATEGORY) +// val entryRegistry = ctx.lookup(T7DatapackRegistries.RESEARCH_ENTRY) +// val categoryHolder = categoryRegistry.getOrThrow(cat) +// val childrenHolders = children.map { entryRegistry.getOrThrow(it) } +// ctx.register(key, ResearchEntry(categoryHolder, pos, preferX, childrenHolders, pages, icon, Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), socketStates, defaultKnown)) +// } +// } +// +// private fun BootstrapContext.registerCategory(key: ResourceKey, icon: ItemStack, sortIndex: Float) { +// register(key, ResearchCategory(Component.translatable(ResearchCategory.translationId(key)), sortIndex, icon)) +// } +// +// private fun simpleTextPage(paragraphCount: Int, hasTitle: Boolean): (ResourceKey, Int) -> Page { +// return { entryKey, pageIndex -> +// val baseId = ResearchEntry.translationId(entryKey) +// TextPage( +// if (hasTitle) simpleTitle(pageIndex, baseId) else null, +// simpleParagraphs(paragraphCount, pageIndex, baseId) +// ) +// } +// } +// +// private fun simpleTitle(pageIndex: Int, baseId: String) = +// Component.translatable(TextPage.titleTranslationId(baseId, pageIndex)).withStyle(ChatFormatting.BOLD) +// +// private fun simpleParagraphs(count: Int, pageIndex: Int, baseId: String) = List(count) { Component.translatable(TextPage.paragraphTranslationId(baseId, pageIndex, it)) } +// +// private fun lockedAspect(row: Int, col: Int, a: DeferredAspect) = SocketState(Indices(row, col), a.get(), false, true) +// private fun broken(row: Int, col: Int) = SocketState(Indices(row, col), null, true, true) + + + + +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt index e38fff18..a799e4c0 100644 --- a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt +++ b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt @@ -4,6 +4,7 @@ import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.common.book.Page +import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.util.T7ExtraCodecs import me.alegian.thavma.impl.common.util.registry import me.alegian.thavma.impl.init.registries.T7DatapackRegistries @@ -20,15 +21,15 @@ import org.joml.Vector2i private val parentsMap = mutableMapOf>>() class ResearchEntry( - val category: Holder, - val position: Vector2i, - val preferX: Boolean, - val children: List>, - val pages: List, - val icon: ItemStack, - val title: Component, - val defaultResearchState: List, - val defaultKnown: Boolean + val category: Holder, + val position: Vector2i, + val preferX: Boolean, + val children: List>, + val pageFeatures: List, + val icon: ItemStack, + val title: Component, + val defaultResearchState: List, + val defaultKnown: Boolean ) { fun parents(level: Level) = parentsMap.computeIfAbsent(this) { _ -> @@ -43,7 +44,7 @@ class ResearchEntry( T7ExtraCodecs.VECTOR2I.fieldOf("position").forGetter(ResearchEntry::position), Codec.BOOL.fieldOf("preferX").forGetter(ResearchEntry::preferX), RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_ENTRY, CODEC).listOf().fieldOf("children").forGetter(ResearchEntry::children), - Page.CODEC.listOf().fieldOf("pages").forGetter(ResearchEntry::pages), + PageFeature.CODEC.listOf().fieldOf("pageFeatures").forGetter(ResearchEntry::pageFeatures), ItemStack.STRICT_CODEC.fieldOf("icon").forGetter(ResearchEntry::icon), ComponentSerialization.CODEC.fieldOf("title").forGetter(ResearchEntry::title), SocketState.CODEC.listOf().fieldOf("defaultResearchState").forGetter(ResearchEntry::defaultResearchState), diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index 0d56c8da..f3c2c912 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -3,7 +3,9 @@ package me.alegian.thavma.impl.init.data.providers import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.common.aspect.Aspect import me.alegian.thavma.impl.common.book.CraftingPage +import me.alegian.thavma.impl.common.book.DynamicPage import me.alegian.thavma.impl.common.book.Page +import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.book.TextPage import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION import me.alegian.thavma.impl.common.research.ResearchCategory @@ -118,14 +120,14 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab ctx.registerCategory(ResearchCategories.STORY, Items.WRITABLE_BOOK.defaultInstance, 2f) } .add(T7DatapackRegistries.RESEARCH_ENTRY) { ctx -> - ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) - .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) - .addPage(simpleTextPage(3, true)) - .addPage(simpleTextPage(1, false)) - .addChild(ResearchEntries.Thavma.TREES) - .addChild(ResearchEntries.Thavma.ORES) - .defaultKnown() - .build(ctx) +// ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) +// .addPage(simpleTextPage(3, true)) +// .addPage(simpleTextPage(1, false)) +// .addChild(ResearchEntries.Thavma.TREES) +// .addChild(ResearchEntries.Thavma.ORES) +// .defaultKnown() +// .build(ctx) ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, -3), false, Items.TURTLE_HELMET.defaultInstance) .research() @@ -145,21 +147,21 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .addChild(ResearchEntries.Thavma.ARCANE_LENS) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) - .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) - .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) - .addPage(simpleTextPage(3, true)) - .build(ctx) +// ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) +// .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) +// .addPage(simpleTextPage(3, true)) +// .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) - .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) - .addPage { _, _ -> CraftingPage(Recipes.CHEST) } - .addChild(ResearchEntries.Thavma.WANDS) - .addChild(ResearchEntries.Thavma.TECHNOLOGY) - .addChild(ResearchEntries.Thavma.ALCHEMY) - .addChild(ResearchEntries.Thavma.INFUSION) - .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) - .build(ctx) +// ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) +// .addPage { _, _ -> CraftingPage(Recipes.CHEST) } +// .addChild(ResearchEntries.Thavma.WANDS) +// .addChild(ResearchEntries.Thavma.TECHNOLOGY) +// .addChild(ResearchEntries.Thavma.ALCHEMY) +// .addChild(ResearchEntries.Thavma.INFUSION) +// .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) +// .build(ctx) ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_PROFICIENCY, Vector2i(-1, -1), false, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) @@ -196,7 +198,7 @@ private class ResearchEntryBuilder( private val icon: ItemStack ) { private val children = mutableListOf>() - private val pages = mutableListOf() + private val pageFeatures = mutableListOf() private val socketStates = mutableListOf() private var defaultKnown = false @@ -225,7 +227,7 @@ private class ResearchEntryBuilder( val entryRegistry = ctx.lookup(T7DatapackRegistries.RESEARCH_ENTRY) val categoryHolder = categoryRegistry.getOrThrow(cat) val childrenHolders = children.map { entryRegistry.getOrThrow(it) } - ctx.register(key, ResearchEntry(categoryHolder, pos, preferX, childrenHolders, pages, icon, Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), socketStates, defaultKnown)) + ctx.register(key, ResearchEntry(categoryHolder, pos, preferX, childrenHolders, pageFeatures, icon, Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), socketStates, defaultKnown)) } } @@ -243,6 +245,21 @@ private fun simpleTextPage(paragraphCount: Int, hasTitle: Boolean): (ResourceKey } } +private fun simpleDynamicPages(vararg features: PageFeature): (ResourceKey) -> List { + + // tady prostě mám přístup k těm jednotlivejm features, z nich poskládám seznam + // dynamických stránek a ty renderuju pomocí zvláštního DynamicPageRenderer (TODO) + // zahrnout nejlépe všechnu tu logiku co mám v těch Features + + return { entryKey -> + val baseId = ResearchEntry.translationId(entryKey) + DynamicPage( + features.toList() + ) + + } +} + private fun simpleTitle(pageIndex: Int, baseId: String) = Component.translatable(TextPage.titleTranslationId(baseId, pageIndex)).withStyle(ChatFormatting.BOLD) diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index 760ad0ce..01f4ad8b 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -401,4 +401,12 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( for (parIndex in paragraphs.indices) add(TextPage.paragraphTranslationId(baseId, pageIndex, parIndex), paragraphs[parIndex].trimIndent().replace("\n", " ")) } + + private fun addParagraphFeature(entryKey: ResourceKey, featureIndex: Int, text: String?){ + + } + + private fun addTitleFeature(entryKey: ResourceKey, featureIndex: Int, text: String?){ + + } } diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt b/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt index 74aac755..467bff0a 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt @@ -1,6 +1,7 @@ package me.alegian.thavma.impl.init.registries import me.alegian.thavma.impl.common.aspect.Aspect +import me.alegian.thavma.impl.common.book.PageFeatureType import me.alegian.thavma.impl.common.book.PageType import me.alegian.thavma.impl.common.wand.WandCoreMaterial import me.alegian.thavma.impl.common.wand.WandPlatingMaterial @@ -25,4 +26,8 @@ object T7Registries { val PAGE_TYPE = RegistryBuilder(ResourceKey.createRegistryKey>(rl("page_type"))) .maxId(Int.MAX_VALUE) .create() + + val PAGE_FEATURE_TYPE = RegistryBuilder(ResourceKey.createRegistryKey>(rl("page_feature"))) + .maxId(Int.MAX_VALUE) + .create() } diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt new file mode 100644 index 00000000..a072a50f --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt @@ -0,0 +1,17 @@ +package me.alegian.thavma.impl.init.registries.deferred + +import me.alegian.thavma.impl.Thavma +import me.alegian.thavma.impl.common.book.PageFeatureType +import me.alegian.thavma.impl.common.book.ParagraphFeature +import me.alegian.thavma.impl.common.book.TitleFeature +import me.alegian.thavma.impl.init.registries.T7Registries +import me.alegian.thavma.impl.rl +import net.neoforged.neoforge.registries.DeferredRegister + +object PageFeatureTypes { + val REGISTRAR = DeferredRegister.create(T7Registries.PAGE_FEATURE_TYPE.key(), Thavma.MODID) + + val PARAGRAPH = REGISTRAR.register("paragraph") { -> PageFeatureType(rl("paragraph"), ParagraphFeature.CODEC)} + val TITLE = REGISTRAR.register("title") { -> PageFeatureType(rl("paragraph"), TitleFeature.CODEC)} + +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt index bec6449b..e0f0e06c 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt @@ -2,6 +2,8 @@ package me.alegian.thavma.impl.init.registries.deferred import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.common.book.CraftingPage +import me.alegian.thavma.impl.common.book.DynamicPage +import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.book.PageType import me.alegian.thavma.impl.common.book.TextPage import me.alegian.thavma.impl.init.registries.T7Registries @@ -13,4 +15,5 @@ object PageTypes { val TEXT = REGISTRAR.register("text") { -> PageType(rl("text"), TextPage.CODEC) } val CRAFTING = REGISTRAR.register("crafting") { -> PageType(rl("crafting"), CraftingPage.CODEC) } + //val DYNAMIC = REGISTRAR.register("dynamic") { -> PageType(rl("dynamic"), DynamicPage.CODEC) } } \ No newline at end of file From 63a321949eea7e957d7dfcb4c19316729866aa76 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Thu, 7 May 2026 14:16:48 +0200 Subject: [PATCH 07/19] Revert "Very WIP dynamic research entry rendering" This reverts commit a91200ee1150b6e4522556790c3e2a47cb819fd3. --- .../thavma/impl/common/book/DynamicPage.kt | 22 - .../thavma/impl/common/book/PageFeature.kt | 22 - .../impl/common/book/PageFeatureType.kt | 12 - .../impl/common/book/ParagraphFeature.kt | 44 -- .../thavma/impl/common/book/TitleFeature.kt | 36 -- .../research/OriginalCodeHolderCuzImLazy.kt | 389 ------------------ .../impl/common/research/ResearchEntry.kt | 21 +- .../T7DatapackBuiltinEntriesProvider.kt | 65 ++- .../init/data/providers/T7LanguageProvider.kt | 8 - .../impl/init/registries/T7Registries.kt | 5 - .../registries/deferred/PageFeatureTypes.kt | 17 - .../init/registries/deferred/PageTypes.kt | 3 - 12 files changed, 34 insertions(+), 610 deletions(-) delete mode 100644 src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt delete mode 100644 src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt delete mode 100644 src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt delete mode 100644 src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt delete mode 100644 src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt delete mode 100644 src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt delete mode 100644 src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt diff --git a/src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt b/src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt deleted file mode 100644 index fecf0139..00000000 --- a/src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt +++ /dev/null @@ -1,22 +0,0 @@ -package me.alegian.thavma.impl.common.book - -import com.mojang.serialization.codecs.RecordCodecBuilder -import me.alegian.thavma.impl.init.registries.deferred.PageTypes -import net.minecraft.network.chat.ComponentSerialization -import java.util.Optional - -class DynamicPage(val pageFeatures: List): Page { - // this might be a problem but for a future Tobias - override val type: PageType<*> - get() = PageTypes.TEXT.get() - - -// companion object { -// val CODEC = RecordCodecBuilder.mapCodec { builder -> -// builder.group( -// ComponentSerialization.CODEC.fieldOf("text").forGetter(ParagraphFeature::text) -// ).apply(builder, ::ParagraphFeature) -// } -// -// } -} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt deleted file mode 100644 index c3563481..00000000 --- a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt +++ /dev/null @@ -1,22 +0,0 @@ -package me.alegian.thavma.impl.common.book - -import me.alegian.thavma.impl.init.registries.T7Registries - -interface PageFeature { - val coversWholePage: Boolean - val mustStartPage: Boolean - val mustOccupySetPage: Boolean - val preferredPageIndex: Int - get() = 1 - val renderedHeight: Int - val pageWidth: Int - get() = 256 - - val type: PageFeatureType<*> - - companion object{ - val CODEC = T7Registries.PAGE_FEATURE_TYPE.byNameCodec().dispatch({ pageFeature -> pageFeature.type }, { type -> type.codec }) - - fun translationId(baseId: String, featureIndex: Int) = "$baseId.pageFeature$featureIndex" - } -} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt b/src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt deleted file mode 100644 index df95f3c4..00000000 --- a/src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt +++ /dev/null @@ -1,12 +0,0 @@ -package me.alegian.thavma.impl.common.book - -import com.mojang.serialization.MapCodec -import net.minecraft.resources.ResourceLocation - -class PageFeatureType(name: ResourceLocation, val codec: MapCodec) { - val stringName = name.toString() - - override fun toString(): String { - return stringName - } -} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt deleted file mode 100644 index fcce37fe..00000000 --- a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt +++ /dev/null @@ -1,44 +0,0 @@ -package me.alegian.thavma.impl.common.book - -import com.mojang.serialization.codecs.RecordCodecBuilder -import me.alegian.thavma.impl.common.book.PageFeature -import me.alegian.thavma.impl.init.data.worldgen.tree.trunk.SilverwoodTrunkPlacer -import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Font -import net.minecraft.network.chat.Component -import net.minecraft.network.chat.ComponentSerialization -import net.minecraft.network.chat.Style -import net.minecraft.resources.ResourceLocation -import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer.trunkPlacerParts -import java.util.Optional - -class ParagraphFeature(val text: Component): PageFeature { - override val type: PageFeatureType<*> - get() = PageFeatureTypes.PARAGRAPH.get() - - override val coversWholePage = false - override val mustStartPage = false - override val mustOccupySetPage = false - - val font: Font = Minecraft.getInstance().font - // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor - val LINE_HEIGHT = font.lineHeight + 2 - val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) - - override val renderedHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 2 / 3 - - companion object { - val CODEC = RecordCodecBuilder.mapCodec { builder -> - builder.group( - ComponentSerialization.CODEC.fieldOf("text").forGetter(ParagraphFeature::text) - ).apply(builder, ::ParagraphFeature) - } - - fun translationId(baseId: String, featureIndex: Int) = "$baseId.paragraphFeature$featureIndex" - } - - //fun titleTranslationId(baseId: String, pageIndex: Int) = Page.translationId(baseId, pageIndex) + ".title" - //fun paragraphFeatureTranslationId(baseId: String, index: Int) = Page.translationId(baseId, pageIndex) + ".paragraph$index" - -} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt deleted file mode 100644 index c5a628a8..00000000 --- a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt +++ /dev/null @@ -1,36 +0,0 @@ -package me.alegian.thavma.impl.common.book - -import com.mojang.serialization.codecs.RecordCodecBuilder -import me.alegian.thavma.impl.common.book.PageFeature -import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Font -import net.minecraft.network.chat.Component -import net.minecraft.network.chat.ComponentSerialization -import net.minecraft.network.chat.Style - -class TitleFeature(val text: Component, ): PageFeature { - override val type: PageFeatureType<*> - get() = PageFeatureTypes.TITLE.get() - - override val coversWholePage = false - override val mustStartPage = false - override val mustOccupySetPage = false - - val font: Font = Minecraft.getInstance().font - // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor - val LINE_HEIGHT = font.lineHeight + 2 - val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) - - override val renderedHeight = LINE_HEIGHT * lines.size + 16 - - companion object { - val CODEC = RecordCodecBuilder.mapCodec { builder -> - builder.group( - ComponentSerialization.CODEC.fieldOf("text").forGetter(TitleFeature::text) - ).apply(builder, ::TitleFeature) - } - - fun translationId(baseId: String, featureIndex: Int) = "$baseId.titleFeature$featureIndex" - } -} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt b/src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt deleted file mode 100644 index 70297783..00000000 --- a/src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt +++ /dev/null @@ -1,389 +0,0 @@ -package me.alegian.thavma.impl.common.research - -import com.mojang.serialization.Codec -import com.mojang.serialization.codecs.RecordCodecBuilder -import me.alegian.thavma.impl.Thavma -import me.alegian.thavma.impl.common.aspect.Aspect -import me.alegian.thavma.impl.common.book.CraftingPage -import me.alegian.thavma.impl.common.book.Page -import me.alegian.thavma.impl.common.book.TextPage -import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION -import me.alegian.thavma.impl.common.util.Indices -import me.alegian.thavma.impl.common.util.T7ExtraCodecs -import me.alegian.thavma.impl.common.util.registry -import me.alegian.thavma.impl.init.data.Recipes -import me.alegian.thavma.impl.init.data.worldgen.Node -import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedOre -import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedStoneOre -import me.alegian.thavma.impl.init.data.worldgen.spawn.AngryZombieSpawn -import me.alegian.thavma.impl.init.data.worldgen.tree.GreatwoodTree -import me.alegian.thavma.impl.init.data.worldgen.tree.SilverwoodTree -import me.alegian.thavma.impl.init.registries.T7DatapackRegistries -import me.alegian.thavma.impl.init.registries.T7Tags -import me.alegian.thavma.impl.init.registries.deferred.Aspects -import me.alegian.thavma.impl.init.registries.deferred.ResearchCategories -import me.alegian.thavma.impl.init.registries.deferred.ResearchEntries -import me.alegian.thavma.impl.init.registries.deferred.T7Blocks -import me.alegian.thavma.impl.init.registries.deferred.T7Items -import me.alegian.thavma.impl.init.registries.deferred.WandCoreMaterials -import me.alegian.thavma.impl.init.registries.deferred.WandPlatingMaterials -import me.alegian.thavma.impl.init.registries.deferred.util.DeferredAspect -import net.minecraft.ChatFormatting -import net.minecraft.Util -import net.minecraft.advancements.critereon.DamageSourcePredicate -import net.minecraft.advancements.critereon.TagPredicate -import net.minecraft.core.Holder -import net.minecraft.core.HolderLookup -import net.minecraft.core.HolderSet -import net.minecraft.core.RegistrySetBuilder -import net.minecraft.core.component.DataComponentMap -import net.minecraft.core.registries.Registries -import net.minecraft.data.PackOutput -import net.minecraft.data.worldgen.BootstrapContext -import net.minecraft.network.chat.Component -import net.minecraft.network.chat.ComponentSerialization -import net.minecraft.resources.RegistryFileCodec -import net.minecraft.resources.ResourceKey -import net.minecraft.tags.ItemTags -import net.minecraft.world.entity.EquipmentSlotGroup -import net.minecraft.world.item.ItemStack -import net.minecraft.world.item.Items -import net.minecraft.world.item.Rarity -import net.minecraft.world.item.enchantment.ConditionalEffect -import net.minecraft.world.item.enchantment.Enchantment -import net.minecraft.world.item.enchantment.Enchantment.Cost -import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition -import net.minecraft.world.item.enchantment.EnchantmentEffectComponents -import net.minecraft.world.item.enchantment.LevelBasedValue -import net.minecraft.world.item.enchantment.effects.AddValue -import net.minecraft.world.level.Level -import net.minecraft.world.level.storage.loot.predicates.DamageSourceCondition -import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider -import net.neoforged.neoforge.registries.NeoForgeRegistries -import org.joml.Vector2i -import java.util.Optional -import java.util.concurrent.CompletableFuture - -object OriginalCodeHolderCuzImLazy { - - -// package me.alegian.thavma.impl.common.research -// -// import com.mojang.serialization.Codec -// import com.mojang.serialization.codecs.RecordCodecBuilder -// import me.alegian.thavma.impl.Thavma -// import me.alegian.thavma.impl.common.book.Page -// import me.alegian.thavma.impl.common.util.T7ExtraCodecs -// import me.alegian.thavma.impl.common.util.registry -// import me.alegian.thavma.impl.init.registries.T7DatapackRegistries -// import net.minecraft.Util -// import net.minecraft.core.Holder -// import net.minecraft.network.chat.Component -// import net.minecraft.network.chat.ComponentSerialization -// import net.minecraft.resources.RegistryFileCodec -// import net.minecraft.resources.ResourceKey -// import net.minecraft.world.item.ItemStack -// import net.minecraft.world.level.Level -// import org.joml.Vector2i -// -// private val parentsMap = mutableMapOf>>() -// -// class ResearchEntry( -// val category: Holder, -// val position: Vector2i, -// val preferX: Boolean, -// val children: List>, -// val pages: List, -// val icon: ItemStack, -// val title: Component, -// val defaultResearchState: List, -// val defaultKnown: Boolean -// ) { -// fun parents(level: Level) = -// parentsMap.computeIfAbsent(this) { _ -> -// val registry = level.registry(T7DatapackRegistries.RESEARCH_ENTRY) -// registry.holders().filter { e -> e.value().children.map{it.value()}.contains(this) }.toList() -// } -// -// companion object { -// val CODEC: Codec = RecordCodecBuilder.create { -// it.group( -// RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_CATEGORY, ResearchCategory.CODEC).fieldOf("category").forGetter(ResearchEntry::category), -// T7ExtraCodecs.VECTOR2I.fieldOf("position").forGetter(ResearchEntry::position), -// Codec.BOOL.fieldOf("preferX").forGetter(ResearchEntry::preferX), -// RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_ENTRY, CODEC).listOf().fieldOf("children").forGetter(ResearchEntry::children), -// Page.CODEC.listOf().fieldOf("pages").forGetter(ResearchEntry::pages), -// ItemStack.STRICT_CODEC.fieldOf("icon").forGetter(ResearchEntry::icon), -// ComponentSerialization.CODEC.fieldOf("title").forGetter(ResearchEntry::title), -// SocketState.CODEC.listOf().fieldOf("defaultResearchState").forGetter(ResearchEntry::defaultResearchState), -// Codec.BOOL.fieldOf("defaultKnown").forGetter(ResearchEntry::defaultKnown), -// ).apply(it, ::ResearchEntry) -// } -// -// fun translationId(entryKey: ResourceKey) = Util.makeDescriptionId(T7DatapackRegistries.RESEARCH_ENTRY.location().path, entryKey.location()) -// -// val TOAST_TRANSLATION = "research." + Thavma.MODID + ".toast" -// val SCROLL_GIVEN_TRANSLATION = "research." + Thavma.MODID + ".scroll_given" -// val PARENTS_UNKNOWN_TRANSLATION = "research." + Thavma.MODID + ".parents_unknown" -// } -// } - - - -// package me.alegian.thavma.impl.init.data.providers -// -// import me.alegian.thavma.impl.Thavma -// import me.alegian.thavma.impl.common.aspect.Aspect -// import me.alegian.thavma.impl.common.book.CraftingPage -// import me.alegian.thavma.impl.common.book.Page -// import me.alegian.thavma.impl.common.book.TextPage -// import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION -// import me.alegian.thavma.impl.common.research.ResearchCategory -// import me.alegian.thavma.impl.common.research.ResearchEntry -// import me.alegian.thavma.impl.common.research.SocketState -// import me.alegian.thavma.impl.common.util.Indices -// import me.alegian.thavma.impl.init.data.Recipes -// import me.alegian.thavma.impl.init.data.worldgen.Node -// import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedOre -// import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedStoneOre -// import me.alegian.thavma.impl.init.data.worldgen.spawn.AngryZombieSpawn -// import me.alegian.thavma.impl.init.data.worldgen.tree.GreatwoodTree -// import me.alegian.thavma.impl.init.data.worldgen.tree.SilverwoodTree -// import me.alegian.thavma.impl.init.registries.T7DatapackRegistries -// import me.alegian.thavma.impl.init.registries.T7Tags -// import me.alegian.thavma.impl.init.registries.deferred.* -// import me.alegian.thavma.impl.init.registries.deferred.util.DeferredAspect -// import net.minecraft.ChatFormatting -// import net.minecraft.advancements.critereon.DamageSourcePredicate -// import net.minecraft.advancements.critereon.TagPredicate -// import net.minecraft.core.HolderLookup -// import net.minecraft.core.HolderSet -// import net.minecraft.core.RegistrySetBuilder -// import net.minecraft.core.component.DataComponentMap -// import net.minecraft.core.registries.Registries -// import net.minecraft.data.PackOutput -// import net.minecraft.data.worldgen.BootstrapContext -// import net.minecraft.network.chat.Component -// import net.minecraft.resources.ResourceKey -// import net.minecraft.tags.ItemTags -// import net.minecraft.world.entity.EquipmentSlotGroup -// import net.minecraft.world.item.ItemStack -// import net.minecraft.world.item.Items -// import net.minecraft.world.item.Rarity -// import net.minecraft.world.item.enchantment.ConditionalEffect -// import net.minecraft.world.item.enchantment.Enchantment -// import net.minecraft.world.item.enchantment.Enchantment.Cost -// import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition -// import net.minecraft.world.item.enchantment.EnchantmentEffectComponents -// import net.minecraft.world.item.enchantment.LevelBasedValue -// import net.minecraft.world.item.enchantment.effects.AddValue -// import net.minecraft.world.level.storage.loot.predicates.DamageSourceCondition -// import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider -// import net.neoforged.neoforge.registries.NeoForgeRegistries -// import org.joml.Vector2i -// import java.util.* -// import java.util.concurrent.CompletableFuture -// -// class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: CompletableFuture) : DatapackBuiltinEntriesProvider(output, registries, builder, setOf(Thavma.MODID)) { -// companion object { -// private val builder: RegistrySetBuilder = RegistrySetBuilder() -// .add(Registries.CONFIGURED_FEATURE) { ctx -> -// GreatwoodTree.registerConfigured(ctx) -// SilverwoodTree.registerConfigured(ctx) -// InfusedOre.registerConfigured(ctx) -// InfusedStoneOre.registerConfigured(ctx) -// Node.registerConfigured(ctx) -// } -// .add(Registries.PLACED_FEATURE) { ctx -> -// GreatwoodTree.registerPlaced(ctx) -// SilverwoodTree.registerPlaced(ctx) -// InfusedOre.registerPlaced(ctx) -// InfusedStoneOre.registerPlaced(ctx) -// Node.registerPlaced(ctx) -// } -// .add(NeoForgeRegistries.Keys.BIOME_MODIFIERS) { ctx -> -// GreatwoodTree.registerBiomeModifier(ctx) -// SilverwoodTree.registerBiomeModifier(ctx) -// InfusedStoneOre.registerBiomeModifier(ctx) -// AngryZombieSpawn.registerBiomeModifier(ctx) -// Node.registerBiomeModifier(ctx) -// } -// .add(Registries.ENCHANTMENT) { bootstrap -> -// val itemRegistry = bootstrap.lookup(Registries.ITEM) -// bootstrap.register( -// ResourceKey.create( -// Registries.ENCHANTMENT, -// LOCATION -// ), -// Enchantment( -// Component.literal("Shriek Resistance"), -// EnchantmentDefinition( -// itemRegistry.getOrThrow(ItemTags.ARMOR_ENCHANTABLE), -// Optional.empty(), -// 5, -// 4, -// Cost(10, 8), -// Cost(18, 8), -// 2, -// listOf(EquipmentSlotGroup.ARMOR) -// ), -// HolderSet.empty(), -// DataComponentMap.builder() -// .set( -// EnchantmentEffectComponents.DAMAGE_PROTECTION, listOf( -// ConditionalEffect( -// AddValue(LevelBasedValue.perLevel(2.0f)), -// Optional.of( -// DamageSourceCondition.hasDamageSource( -// DamageSourcePredicate.Builder.damageType().tag(TagPredicate.`is`(T7Tags.DamageTypes.SONIC)) -// ).build() -// ) -// ) -// ) -// ).build() -// ) -// ) -// } -// .add(T7DatapackRegistries.RESEARCH_CATEGORY) { ctx -> -// ctx.registerCategory(ResearchCategories.THAVMA, T7Items.BOOK.get().defaultInstance, 0f) -// ctx.registerCategory(ResearchCategories.ALCHEMY, T7Blocks.CRUCIBLE.get().asItem().defaultInstance, 1f) -// ctx.registerCategory(ResearchCategories.STORY, Items.WRITABLE_BOOK.defaultInstance, 2f) -// } -// .add(T7DatapackRegistries.RESEARCH_ENTRY) { ctx -> -// ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) -// .addPage(simpleTextPage(3, true)) -// .addPage(simpleTextPage(1, false)) -// .addChild(ResearchEntries.Thavma.TREES) -// .addChild(ResearchEntries.Thavma.ORES) -// .defaultKnown() -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, -3), false, Items.TURTLE_HELMET.defaultInstance) -// .research() -// .addPage(simpleTextPage(2, true)) -// .addPage(simpleTextPage(2, true)) -// .addPage(simpleTextPage(2, true)) -// .defaultKnown() -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Thavma.TREES, Vector2i(0, -3), false, T7Blocks.GREATWOOD_LOG.get().asItem().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.HERBA), lockedAspect(2, 4, Aspects.HERBA)) -// .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Thavma.ORES, Vector2i(2, -4), false, T7Items.SHARDS[Aspects.AQUA]!!.get().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.TERRA), lockedAspect(2, 4, Aspects.AETHER)) -// .addChild(ResearchEntries.Thavma.ARCANE_LENS) -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) -// .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) -// .addPage(simpleTextPage(3, true)) -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) -// .addPage { _, _ -> CraftingPage(Recipes.CHEST) } -// .addChild(ResearchEntries.Thavma.WANDS) -// .addChild(ResearchEntries.Thavma.TECHNOLOGY) -// .addChild(ResearchEntries.Thavma.ALCHEMY) -// .addChild(ResearchEntries.Thavma.INFUSION) -// .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_PROFICIENCY, Vector2i(-1, -1), false, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Thavma.ALCHEMY, Vector2i(-2, 2), true, T7Blocks.CRUCIBLE.get().asItem().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.AQUA), lockedAspect(2, 4, Aspects.ALKIMIA)) -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Thavma.WANDS, Vector2i(-2, 4), true, T7Items.wandOrThrow(WandPlatingMaterials.THAVMITE.get(), WandCoreMaterials.SILVERWOOD.get()).defaultInstance) -// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.INSTRUMENTUM)) -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Thavma.INFUSION, Vector2i(2, 2), true, T7Blocks.MATRIX.get().asItem().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.TERRA), lockedAspect(2, 4, Aspects.AETHER)) -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Thavma.TECHNOLOGY, Vector2i(2, 4), true, T7Items.GOGGLES.get().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.INSTRUMENTUM), lockedAspect(2, 4, Aspects.CIVILIS)) -// .build(ctx) -// -// ResearchEntryBuilder(ResearchEntries.Alchemy.ALCHEMY, Vector2i(0, 0), false, T7Blocks.CRUCIBLE.get().asItem().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.AQUA), lockedAspect(2, 4, Aspects.ALKIMIA)) -// .defaultKnown() -// .build(ctx) -// } -// } -// } -// -// private class ResearchEntryBuilder( -// private val key: ResourceKey, -// private val pos: Vector2i, -// private val preferX: Boolean, -// private val icon: ItemStack -// ) { -// private val children = mutableListOf>() -// private val pages = mutableListOf() -// private val socketStates = mutableListOf() -// private var defaultKnown = false -// -// fun addChild(entryKey: ResourceKey): ResearchEntryBuilder { -// children.add(entryKey) -// return this -// } -// -// fun addPage(makePage: (ResourceKey, Int) -> Page): ResearchEntryBuilder { -// pages.add(makePage(key, pages.size)) -// return this -// } -// -// fun research(vararg states: SocketState): ResearchEntryBuilder { -// socketStates.addAll(states) -// return this -// } -// -// fun defaultKnown(): ResearchEntryBuilder { -// defaultKnown = true -// return this -// } -// -// fun build(ctx: BootstrapContext) = ResearchEntries.CATEGORIES[key]?.let { cat -> -// val categoryRegistry = ctx.lookup(T7DatapackRegistries.RESEARCH_CATEGORY) -// val entryRegistry = ctx.lookup(T7DatapackRegistries.RESEARCH_ENTRY) -// val categoryHolder = categoryRegistry.getOrThrow(cat) -// val childrenHolders = children.map { entryRegistry.getOrThrow(it) } -// ctx.register(key, ResearchEntry(categoryHolder, pos, preferX, childrenHolders, pages, icon, Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), socketStates, defaultKnown)) -// } -// } -// -// private fun BootstrapContext.registerCategory(key: ResourceKey, icon: ItemStack, sortIndex: Float) { -// register(key, ResearchCategory(Component.translatable(ResearchCategory.translationId(key)), sortIndex, icon)) -// } -// -// private fun simpleTextPage(paragraphCount: Int, hasTitle: Boolean): (ResourceKey, Int) -> Page { -// return { entryKey, pageIndex -> -// val baseId = ResearchEntry.translationId(entryKey) -// TextPage( -// if (hasTitle) simpleTitle(pageIndex, baseId) else null, -// simpleParagraphs(paragraphCount, pageIndex, baseId) -// ) -// } -// } -// -// private fun simpleTitle(pageIndex: Int, baseId: String) = -// Component.translatable(TextPage.titleTranslationId(baseId, pageIndex)).withStyle(ChatFormatting.BOLD) -// -// private fun simpleParagraphs(count: Int, pageIndex: Int, baseId: String) = List(count) { Component.translatable(TextPage.paragraphTranslationId(baseId, pageIndex, it)) } -// -// private fun lockedAspect(row: Int, col: Int, a: DeferredAspect) = SocketState(Indices(row, col), a.get(), false, true) -// private fun broken(row: Int, col: Int) = SocketState(Indices(row, col), null, true, true) - - - - -} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt index a799e4c0..e38fff18 100644 --- a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt +++ b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt @@ -4,7 +4,6 @@ import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.common.book.Page -import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.util.T7ExtraCodecs import me.alegian.thavma.impl.common.util.registry import me.alegian.thavma.impl.init.registries.T7DatapackRegistries @@ -21,15 +20,15 @@ import org.joml.Vector2i private val parentsMap = mutableMapOf>>() class ResearchEntry( - val category: Holder, - val position: Vector2i, - val preferX: Boolean, - val children: List>, - val pageFeatures: List, - val icon: ItemStack, - val title: Component, - val defaultResearchState: List, - val defaultKnown: Boolean + val category: Holder, + val position: Vector2i, + val preferX: Boolean, + val children: List>, + val pages: List, + val icon: ItemStack, + val title: Component, + val defaultResearchState: List, + val defaultKnown: Boolean ) { fun parents(level: Level) = parentsMap.computeIfAbsent(this) { _ -> @@ -44,7 +43,7 @@ class ResearchEntry( T7ExtraCodecs.VECTOR2I.fieldOf("position").forGetter(ResearchEntry::position), Codec.BOOL.fieldOf("preferX").forGetter(ResearchEntry::preferX), RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_ENTRY, CODEC).listOf().fieldOf("children").forGetter(ResearchEntry::children), - PageFeature.CODEC.listOf().fieldOf("pageFeatures").forGetter(ResearchEntry::pageFeatures), + Page.CODEC.listOf().fieldOf("pages").forGetter(ResearchEntry::pages), ItemStack.STRICT_CODEC.fieldOf("icon").forGetter(ResearchEntry::icon), ComponentSerialization.CODEC.fieldOf("title").forGetter(ResearchEntry::title), SocketState.CODEC.listOf().fieldOf("defaultResearchState").forGetter(ResearchEntry::defaultResearchState), diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index f3c2c912..0d56c8da 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -3,9 +3,7 @@ package me.alegian.thavma.impl.init.data.providers import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.common.aspect.Aspect import me.alegian.thavma.impl.common.book.CraftingPage -import me.alegian.thavma.impl.common.book.DynamicPage import me.alegian.thavma.impl.common.book.Page -import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.book.TextPage import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION import me.alegian.thavma.impl.common.research.ResearchCategory @@ -120,14 +118,14 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab ctx.registerCategory(ResearchCategories.STORY, Items.WRITABLE_BOOK.defaultInstance, 2f) } .add(T7DatapackRegistries.RESEARCH_ENTRY) { ctx -> -// ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) -// .addPage(simpleTextPage(3, true)) -// .addPage(simpleTextPage(1, false)) -// .addChild(ResearchEntries.Thavma.TREES) -// .addChild(ResearchEntries.Thavma.ORES) -// .defaultKnown() -// .build(ctx) + ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) + .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) + .addPage(simpleTextPage(3, true)) + .addPage(simpleTextPage(1, false)) + .addChild(ResearchEntries.Thavma.TREES) + .addChild(ResearchEntries.Thavma.ORES) + .defaultKnown() + .build(ctx) ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, -3), false, Items.TURTLE_HELMET.defaultInstance) .research() @@ -147,21 +145,21 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .addChild(ResearchEntries.Thavma.ARCANE_LENS) .build(ctx) -// ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) -// .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) -// .addPage(simpleTextPage(3, true)) -// .build(ctx) + ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) + .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) + .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) + .addPage(simpleTextPage(3, true)) + .build(ctx) -// ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) -// .addPage { _, _ -> CraftingPage(Recipes.CHEST) } -// .addChild(ResearchEntries.Thavma.WANDS) -// .addChild(ResearchEntries.Thavma.TECHNOLOGY) -// .addChild(ResearchEntries.Thavma.ALCHEMY) -// .addChild(ResearchEntries.Thavma.INFUSION) -// .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) -// .build(ctx) + ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) + .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) + .addPage { _, _ -> CraftingPage(Recipes.CHEST) } + .addChild(ResearchEntries.Thavma.WANDS) + .addChild(ResearchEntries.Thavma.TECHNOLOGY) + .addChild(ResearchEntries.Thavma.ALCHEMY) + .addChild(ResearchEntries.Thavma.INFUSION) + .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) + .build(ctx) ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_PROFICIENCY, Vector2i(-1, -1), false, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) @@ -198,7 +196,7 @@ private class ResearchEntryBuilder( private val icon: ItemStack ) { private val children = mutableListOf>() - private val pageFeatures = mutableListOf() + private val pages = mutableListOf() private val socketStates = mutableListOf() private var defaultKnown = false @@ -227,7 +225,7 @@ private class ResearchEntryBuilder( val entryRegistry = ctx.lookup(T7DatapackRegistries.RESEARCH_ENTRY) val categoryHolder = categoryRegistry.getOrThrow(cat) val childrenHolders = children.map { entryRegistry.getOrThrow(it) } - ctx.register(key, ResearchEntry(categoryHolder, pos, preferX, childrenHolders, pageFeatures, icon, Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), socketStates, defaultKnown)) + ctx.register(key, ResearchEntry(categoryHolder, pos, preferX, childrenHolders, pages, icon, Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), socketStates, defaultKnown)) } } @@ -245,21 +243,6 @@ private fun simpleTextPage(paragraphCount: Int, hasTitle: Boolean): (ResourceKey } } -private fun simpleDynamicPages(vararg features: PageFeature): (ResourceKey) -> List { - - // tady prostě mám přístup k těm jednotlivejm features, z nich poskládám seznam - // dynamických stránek a ty renderuju pomocí zvláštního DynamicPageRenderer (TODO) - // zahrnout nejlépe všechnu tu logiku co mám v těch Features - - return { entryKey -> - val baseId = ResearchEntry.translationId(entryKey) - DynamicPage( - features.toList() - ) - - } -} - private fun simpleTitle(pageIndex: Int, baseId: String) = Component.translatable(TextPage.titleTranslationId(baseId, pageIndex)).withStyle(ChatFormatting.BOLD) diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index 01f4ad8b..760ad0ce 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -401,12 +401,4 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( for (parIndex in paragraphs.indices) add(TextPage.paragraphTranslationId(baseId, pageIndex, parIndex), paragraphs[parIndex].trimIndent().replace("\n", " ")) } - - private fun addParagraphFeature(entryKey: ResourceKey, featureIndex: Int, text: String?){ - - } - - private fun addTitleFeature(entryKey: ResourceKey, featureIndex: Int, text: String?){ - - } } diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt b/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt index 467bff0a..74aac755 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt @@ -1,7 +1,6 @@ package me.alegian.thavma.impl.init.registries import me.alegian.thavma.impl.common.aspect.Aspect -import me.alegian.thavma.impl.common.book.PageFeatureType import me.alegian.thavma.impl.common.book.PageType import me.alegian.thavma.impl.common.wand.WandCoreMaterial import me.alegian.thavma.impl.common.wand.WandPlatingMaterial @@ -26,8 +25,4 @@ object T7Registries { val PAGE_TYPE = RegistryBuilder(ResourceKey.createRegistryKey>(rl("page_type"))) .maxId(Int.MAX_VALUE) .create() - - val PAGE_FEATURE_TYPE = RegistryBuilder(ResourceKey.createRegistryKey>(rl("page_feature"))) - .maxId(Int.MAX_VALUE) - .create() } diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt deleted file mode 100644 index a072a50f..00000000 --- a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt +++ /dev/null @@ -1,17 +0,0 @@ -package me.alegian.thavma.impl.init.registries.deferred - -import me.alegian.thavma.impl.Thavma -import me.alegian.thavma.impl.common.book.PageFeatureType -import me.alegian.thavma.impl.common.book.ParagraphFeature -import me.alegian.thavma.impl.common.book.TitleFeature -import me.alegian.thavma.impl.init.registries.T7Registries -import me.alegian.thavma.impl.rl -import net.neoforged.neoforge.registries.DeferredRegister - -object PageFeatureTypes { - val REGISTRAR = DeferredRegister.create(T7Registries.PAGE_FEATURE_TYPE.key(), Thavma.MODID) - - val PARAGRAPH = REGISTRAR.register("paragraph") { -> PageFeatureType(rl("paragraph"), ParagraphFeature.CODEC)} - val TITLE = REGISTRAR.register("title") { -> PageFeatureType(rl("paragraph"), TitleFeature.CODEC)} - -} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt index e0f0e06c..bec6449b 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt @@ -2,8 +2,6 @@ package me.alegian.thavma.impl.init.registries.deferred import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.common.book.CraftingPage -import me.alegian.thavma.impl.common.book.DynamicPage -import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.book.PageType import me.alegian.thavma.impl.common.book.TextPage import me.alegian.thavma.impl.init.registries.T7Registries @@ -15,5 +13,4 @@ object PageTypes { val TEXT = REGISTRAR.register("text") { -> PageType(rl("text"), TextPage.CODEC) } val CRAFTING = REGISTRAR.register("crafting") { -> PageType(rl("crafting"), CraftingPage.CODEC) } - //val DYNAMIC = REGISTRAR.register("dynamic") { -> PageType(rl("dynamic"), DynamicPage.CODEC) } } \ No newline at end of file From 55021508eeda97e0b3b9373815cd059eebe067aa Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Thu, 7 May 2026 14:19:42 +0200 Subject: [PATCH 08/19] Reapply "Very WIP dynamic research entry rendering" This reverts commit 63a321949eea7e957d7dfcb4c19316729866aa76. --- .../thavma/impl/common/book/DynamicPage.kt | 22 + .../thavma/impl/common/book/PageFeature.kt | 22 + .../impl/common/book/PageFeatureType.kt | 12 + .../impl/common/book/ParagraphFeature.kt | 44 ++ .../thavma/impl/common/book/TitleFeature.kt | 36 ++ .../research/OriginalCodeHolderCuzImLazy.kt | 389 ++++++++++++++++++ .../impl/common/research/ResearchEntry.kt | 21 +- .../T7DatapackBuiltinEntriesProvider.kt | 65 +-- .../init/data/providers/T7LanguageProvider.kt | 8 + .../impl/init/registries/T7Registries.kt | 5 + .../registries/deferred/PageFeatureTypes.kt | 17 + .../init/registries/deferred/PageTypes.kt | 3 + 12 files changed, 610 insertions(+), 34 deletions(-) create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt create mode 100644 src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt diff --git a/src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt b/src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt new file mode 100644 index 00000000..fecf0139 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/DynamicPage.kt @@ -0,0 +1,22 @@ +package me.alegian.thavma.impl.common.book + +import com.mojang.serialization.codecs.RecordCodecBuilder +import me.alegian.thavma.impl.init.registries.deferred.PageTypes +import net.minecraft.network.chat.ComponentSerialization +import java.util.Optional + +class DynamicPage(val pageFeatures: List): Page { + // this might be a problem but for a future Tobias + override val type: PageType<*> + get() = PageTypes.TEXT.get() + + +// companion object { +// val CODEC = RecordCodecBuilder.mapCodec { builder -> +// builder.group( +// ComponentSerialization.CODEC.fieldOf("text").forGetter(ParagraphFeature::text) +// ).apply(builder, ::ParagraphFeature) +// } +// +// } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt new file mode 100644 index 00000000..c3563481 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt @@ -0,0 +1,22 @@ +package me.alegian.thavma.impl.common.book + +import me.alegian.thavma.impl.init.registries.T7Registries + +interface PageFeature { + val coversWholePage: Boolean + val mustStartPage: Boolean + val mustOccupySetPage: Boolean + val preferredPageIndex: Int + get() = 1 + val renderedHeight: Int + val pageWidth: Int + get() = 256 + + val type: PageFeatureType<*> + + companion object{ + val CODEC = T7Registries.PAGE_FEATURE_TYPE.byNameCodec().dispatch({ pageFeature -> pageFeature.type }, { type -> type.codec }) + + fun translationId(baseId: String, featureIndex: Int) = "$baseId.pageFeature$featureIndex" + } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt b/src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt new file mode 100644 index 00000000..df95f3c4 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/PageFeatureType.kt @@ -0,0 +1,12 @@ +package me.alegian.thavma.impl.common.book + +import com.mojang.serialization.MapCodec +import net.minecraft.resources.ResourceLocation + +class PageFeatureType(name: ResourceLocation, val codec: MapCodec) { + val stringName = name.toString() + + override fun toString(): String { + return stringName + } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt new file mode 100644 index 00000000..fcce37fe --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt @@ -0,0 +1,44 @@ +package me.alegian.thavma.impl.common.book + +import com.mojang.serialization.codecs.RecordCodecBuilder +import me.alegian.thavma.impl.common.book.PageFeature +import me.alegian.thavma.impl.init.data.worldgen.tree.trunk.SilverwoodTrunkPlacer +import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Font +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.network.chat.Style +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer.trunkPlacerParts +import java.util.Optional + +class ParagraphFeature(val text: Component): PageFeature { + override val type: PageFeatureType<*> + get() = PageFeatureTypes.PARAGRAPH.get() + + override val coversWholePage = false + override val mustStartPage = false + override val mustOccupySetPage = false + + val font: Font = Minecraft.getInstance().font + // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor + val LINE_HEIGHT = font.lineHeight + 2 + val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) + + override val renderedHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 2 / 3 + + companion object { + val CODEC = RecordCodecBuilder.mapCodec { builder -> + builder.group( + ComponentSerialization.CODEC.fieldOf("text").forGetter(ParagraphFeature::text) + ).apply(builder, ::ParagraphFeature) + } + + fun translationId(baseId: String, featureIndex: Int) = "$baseId.paragraphFeature$featureIndex" + } + + //fun titleTranslationId(baseId: String, pageIndex: Int) = Page.translationId(baseId, pageIndex) + ".title" + //fun paragraphFeatureTranslationId(baseId: String, index: Int) = Page.translationId(baseId, pageIndex) + ".paragraph$index" + +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt new file mode 100644 index 00000000..c5a628a8 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt @@ -0,0 +1,36 @@ +package me.alegian.thavma.impl.common.book + +import com.mojang.serialization.codecs.RecordCodecBuilder +import me.alegian.thavma.impl.common.book.PageFeature +import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Font +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.network.chat.Style + +class TitleFeature(val text: Component, ): PageFeature { + override val type: PageFeatureType<*> + get() = PageFeatureTypes.TITLE.get() + + override val coversWholePage = false + override val mustStartPage = false + override val mustOccupySetPage = false + + val font: Font = Minecraft.getInstance().font + // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor + val LINE_HEIGHT = font.lineHeight + 2 + val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) + + override val renderedHeight = LINE_HEIGHT * lines.size + 16 + + companion object { + val CODEC = RecordCodecBuilder.mapCodec { builder -> + builder.group( + ComponentSerialization.CODEC.fieldOf("text").forGetter(TitleFeature::text) + ).apply(builder, ::TitleFeature) + } + + fun translationId(baseId: String, featureIndex: Int) = "$baseId.titleFeature$featureIndex" + } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt b/src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt new file mode 100644 index 00000000..70297783 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/research/OriginalCodeHolderCuzImLazy.kt @@ -0,0 +1,389 @@ +package me.alegian.thavma.impl.common.research + +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder +import me.alegian.thavma.impl.Thavma +import me.alegian.thavma.impl.common.aspect.Aspect +import me.alegian.thavma.impl.common.book.CraftingPage +import me.alegian.thavma.impl.common.book.Page +import me.alegian.thavma.impl.common.book.TextPage +import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION +import me.alegian.thavma.impl.common.util.Indices +import me.alegian.thavma.impl.common.util.T7ExtraCodecs +import me.alegian.thavma.impl.common.util.registry +import me.alegian.thavma.impl.init.data.Recipes +import me.alegian.thavma.impl.init.data.worldgen.Node +import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedOre +import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedStoneOre +import me.alegian.thavma.impl.init.data.worldgen.spawn.AngryZombieSpawn +import me.alegian.thavma.impl.init.data.worldgen.tree.GreatwoodTree +import me.alegian.thavma.impl.init.data.worldgen.tree.SilverwoodTree +import me.alegian.thavma.impl.init.registries.T7DatapackRegistries +import me.alegian.thavma.impl.init.registries.T7Tags +import me.alegian.thavma.impl.init.registries.deferred.Aspects +import me.alegian.thavma.impl.init.registries.deferred.ResearchCategories +import me.alegian.thavma.impl.init.registries.deferred.ResearchEntries +import me.alegian.thavma.impl.init.registries.deferred.T7Blocks +import me.alegian.thavma.impl.init.registries.deferred.T7Items +import me.alegian.thavma.impl.init.registries.deferred.WandCoreMaterials +import me.alegian.thavma.impl.init.registries.deferred.WandPlatingMaterials +import me.alegian.thavma.impl.init.registries.deferred.util.DeferredAspect +import net.minecraft.ChatFormatting +import net.minecraft.Util +import net.minecraft.advancements.critereon.DamageSourcePredicate +import net.minecraft.advancements.critereon.TagPredicate +import net.minecraft.core.Holder +import net.minecraft.core.HolderLookup +import net.minecraft.core.HolderSet +import net.minecraft.core.RegistrySetBuilder +import net.minecraft.core.component.DataComponentMap +import net.minecraft.core.registries.Registries +import net.minecraft.data.PackOutput +import net.minecraft.data.worldgen.BootstrapContext +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.resources.RegistryFileCodec +import net.minecraft.resources.ResourceKey +import net.minecraft.tags.ItemTags +import net.minecraft.world.entity.EquipmentSlotGroup +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraft.world.item.Rarity +import net.minecraft.world.item.enchantment.ConditionalEffect +import net.minecraft.world.item.enchantment.Enchantment +import net.minecraft.world.item.enchantment.Enchantment.Cost +import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition +import net.minecraft.world.item.enchantment.EnchantmentEffectComponents +import net.minecraft.world.item.enchantment.LevelBasedValue +import net.minecraft.world.item.enchantment.effects.AddValue +import net.minecraft.world.level.Level +import net.minecraft.world.level.storage.loot.predicates.DamageSourceCondition +import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider +import net.neoforged.neoforge.registries.NeoForgeRegistries +import org.joml.Vector2i +import java.util.Optional +import java.util.concurrent.CompletableFuture + +object OriginalCodeHolderCuzImLazy { + + +// package me.alegian.thavma.impl.common.research +// +// import com.mojang.serialization.Codec +// import com.mojang.serialization.codecs.RecordCodecBuilder +// import me.alegian.thavma.impl.Thavma +// import me.alegian.thavma.impl.common.book.Page +// import me.alegian.thavma.impl.common.util.T7ExtraCodecs +// import me.alegian.thavma.impl.common.util.registry +// import me.alegian.thavma.impl.init.registries.T7DatapackRegistries +// import net.minecraft.Util +// import net.minecraft.core.Holder +// import net.minecraft.network.chat.Component +// import net.minecraft.network.chat.ComponentSerialization +// import net.minecraft.resources.RegistryFileCodec +// import net.minecraft.resources.ResourceKey +// import net.minecraft.world.item.ItemStack +// import net.minecraft.world.level.Level +// import org.joml.Vector2i +// +// private val parentsMap = mutableMapOf>>() +// +// class ResearchEntry( +// val category: Holder, +// val position: Vector2i, +// val preferX: Boolean, +// val children: List>, +// val pages: List, +// val icon: ItemStack, +// val title: Component, +// val defaultResearchState: List, +// val defaultKnown: Boolean +// ) { +// fun parents(level: Level) = +// parentsMap.computeIfAbsent(this) { _ -> +// val registry = level.registry(T7DatapackRegistries.RESEARCH_ENTRY) +// registry.holders().filter { e -> e.value().children.map{it.value()}.contains(this) }.toList() +// } +// +// companion object { +// val CODEC: Codec = RecordCodecBuilder.create { +// it.group( +// RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_CATEGORY, ResearchCategory.CODEC).fieldOf("category").forGetter(ResearchEntry::category), +// T7ExtraCodecs.VECTOR2I.fieldOf("position").forGetter(ResearchEntry::position), +// Codec.BOOL.fieldOf("preferX").forGetter(ResearchEntry::preferX), +// RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_ENTRY, CODEC).listOf().fieldOf("children").forGetter(ResearchEntry::children), +// Page.CODEC.listOf().fieldOf("pages").forGetter(ResearchEntry::pages), +// ItemStack.STRICT_CODEC.fieldOf("icon").forGetter(ResearchEntry::icon), +// ComponentSerialization.CODEC.fieldOf("title").forGetter(ResearchEntry::title), +// SocketState.CODEC.listOf().fieldOf("defaultResearchState").forGetter(ResearchEntry::defaultResearchState), +// Codec.BOOL.fieldOf("defaultKnown").forGetter(ResearchEntry::defaultKnown), +// ).apply(it, ::ResearchEntry) +// } +// +// fun translationId(entryKey: ResourceKey) = Util.makeDescriptionId(T7DatapackRegistries.RESEARCH_ENTRY.location().path, entryKey.location()) +// +// val TOAST_TRANSLATION = "research." + Thavma.MODID + ".toast" +// val SCROLL_GIVEN_TRANSLATION = "research." + Thavma.MODID + ".scroll_given" +// val PARENTS_UNKNOWN_TRANSLATION = "research." + Thavma.MODID + ".parents_unknown" +// } +// } + + + +// package me.alegian.thavma.impl.init.data.providers +// +// import me.alegian.thavma.impl.Thavma +// import me.alegian.thavma.impl.common.aspect.Aspect +// import me.alegian.thavma.impl.common.book.CraftingPage +// import me.alegian.thavma.impl.common.book.Page +// import me.alegian.thavma.impl.common.book.TextPage +// import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION +// import me.alegian.thavma.impl.common.research.ResearchCategory +// import me.alegian.thavma.impl.common.research.ResearchEntry +// import me.alegian.thavma.impl.common.research.SocketState +// import me.alegian.thavma.impl.common.util.Indices +// import me.alegian.thavma.impl.init.data.Recipes +// import me.alegian.thavma.impl.init.data.worldgen.Node +// import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedOre +// import me.alegian.thavma.impl.init.data.worldgen.ore.InfusedStoneOre +// import me.alegian.thavma.impl.init.data.worldgen.spawn.AngryZombieSpawn +// import me.alegian.thavma.impl.init.data.worldgen.tree.GreatwoodTree +// import me.alegian.thavma.impl.init.data.worldgen.tree.SilverwoodTree +// import me.alegian.thavma.impl.init.registries.T7DatapackRegistries +// import me.alegian.thavma.impl.init.registries.T7Tags +// import me.alegian.thavma.impl.init.registries.deferred.* +// import me.alegian.thavma.impl.init.registries.deferred.util.DeferredAspect +// import net.minecraft.ChatFormatting +// import net.minecraft.advancements.critereon.DamageSourcePredicate +// import net.minecraft.advancements.critereon.TagPredicate +// import net.minecraft.core.HolderLookup +// import net.minecraft.core.HolderSet +// import net.minecraft.core.RegistrySetBuilder +// import net.minecraft.core.component.DataComponentMap +// import net.minecraft.core.registries.Registries +// import net.minecraft.data.PackOutput +// import net.minecraft.data.worldgen.BootstrapContext +// import net.minecraft.network.chat.Component +// import net.minecraft.resources.ResourceKey +// import net.minecraft.tags.ItemTags +// import net.minecraft.world.entity.EquipmentSlotGroup +// import net.minecraft.world.item.ItemStack +// import net.minecraft.world.item.Items +// import net.minecraft.world.item.Rarity +// import net.minecraft.world.item.enchantment.ConditionalEffect +// import net.minecraft.world.item.enchantment.Enchantment +// import net.minecraft.world.item.enchantment.Enchantment.Cost +// import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition +// import net.minecraft.world.item.enchantment.EnchantmentEffectComponents +// import net.minecraft.world.item.enchantment.LevelBasedValue +// import net.minecraft.world.item.enchantment.effects.AddValue +// import net.minecraft.world.level.storage.loot.predicates.DamageSourceCondition +// import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider +// import net.neoforged.neoforge.registries.NeoForgeRegistries +// import org.joml.Vector2i +// import java.util.* +// import java.util.concurrent.CompletableFuture +// +// class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: CompletableFuture) : DatapackBuiltinEntriesProvider(output, registries, builder, setOf(Thavma.MODID)) { +// companion object { +// private val builder: RegistrySetBuilder = RegistrySetBuilder() +// .add(Registries.CONFIGURED_FEATURE) { ctx -> +// GreatwoodTree.registerConfigured(ctx) +// SilverwoodTree.registerConfigured(ctx) +// InfusedOre.registerConfigured(ctx) +// InfusedStoneOre.registerConfigured(ctx) +// Node.registerConfigured(ctx) +// } +// .add(Registries.PLACED_FEATURE) { ctx -> +// GreatwoodTree.registerPlaced(ctx) +// SilverwoodTree.registerPlaced(ctx) +// InfusedOre.registerPlaced(ctx) +// InfusedStoneOre.registerPlaced(ctx) +// Node.registerPlaced(ctx) +// } +// .add(NeoForgeRegistries.Keys.BIOME_MODIFIERS) { ctx -> +// GreatwoodTree.registerBiomeModifier(ctx) +// SilverwoodTree.registerBiomeModifier(ctx) +// InfusedStoneOre.registerBiomeModifier(ctx) +// AngryZombieSpawn.registerBiomeModifier(ctx) +// Node.registerBiomeModifier(ctx) +// } +// .add(Registries.ENCHANTMENT) { bootstrap -> +// val itemRegistry = bootstrap.lookup(Registries.ITEM) +// bootstrap.register( +// ResourceKey.create( +// Registries.ENCHANTMENT, +// LOCATION +// ), +// Enchantment( +// Component.literal("Shriek Resistance"), +// EnchantmentDefinition( +// itemRegistry.getOrThrow(ItemTags.ARMOR_ENCHANTABLE), +// Optional.empty(), +// 5, +// 4, +// Cost(10, 8), +// Cost(18, 8), +// 2, +// listOf(EquipmentSlotGroup.ARMOR) +// ), +// HolderSet.empty(), +// DataComponentMap.builder() +// .set( +// EnchantmentEffectComponents.DAMAGE_PROTECTION, listOf( +// ConditionalEffect( +// AddValue(LevelBasedValue.perLevel(2.0f)), +// Optional.of( +// DamageSourceCondition.hasDamageSource( +// DamageSourcePredicate.Builder.damageType().tag(TagPredicate.`is`(T7Tags.DamageTypes.SONIC)) +// ).build() +// ) +// ) +// ) +// ).build() +// ) +// ) +// } +// .add(T7DatapackRegistries.RESEARCH_CATEGORY) { ctx -> +// ctx.registerCategory(ResearchCategories.THAVMA, T7Items.BOOK.get().defaultInstance, 0f) +// ctx.registerCategory(ResearchCategories.ALCHEMY, T7Blocks.CRUCIBLE.get().asItem().defaultInstance, 1f) +// ctx.registerCategory(ResearchCategories.STORY, Items.WRITABLE_BOOK.defaultInstance, 2f) +// } +// .add(T7DatapackRegistries.RESEARCH_ENTRY) { ctx -> +// ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) +// .addPage(simpleTextPage(3, true)) +// .addPage(simpleTextPage(1, false)) +// .addChild(ResearchEntries.Thavma.TREES) +// .addChild(ResearchEntries.Thavma.ORES) +// .defaultKnown() +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, -3), false, Items.TURTLE_HELMET.defaultInstance) +// .research() +// .addPage(simpleTextPage(2, true)) +// .addPage(simpleTextPage(2, true)) +// .addPage(simpleTextPage(2, true)) +// .defaultKnown() +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.TREES, Vector2i(0, -3), false, T7Blocks.GREATWOOD_LOG.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.HERBA), lockedAspect(2, 4, Aspects.HERBA)) +// .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.ORES, Vector2i(2, -4), false, T7Items.SHARDS[Aspects.AQUA]!!.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.TERRA), lockedAspect(2, 4, Aspects.AETHER)) +// .addChild(ResearchEntries.Thavma.ARCANE_LENS) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) +// .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) +// .addPage(simpleTextPage(3, true)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) +// .addPage { _, _ -> CraftingPage(Recipes.CHEST) } +// .addChild(ResearchEntries.Thavma.WANDS) +// .addChild(ResearchEntries.Thavma.TECHNOLOGY) +// .addChild(ResearchEntries.Thavma.ALCHEMY) +// .addChild(ResearchEntries.Thavma.INFUSION) +// .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_PROFICIENCY, Vector2i(-1, -1), false, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.ALCHEMY, Vector2i(-2, 2), true, T7Blocks.CRUCIBLE.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AQUA), lockedAspect(2, 4, Aspects.ALKIMIA)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.WANDS, Vector2i(-2, 4), true, T7Items.wandOrThrow(WandPlatingMaterials.THAVMITE.get(), WandCoreMaterials.SILVERWOOD.get()).defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.INSTRUMENTUM)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.INFUSION, Vector2i(2, 2), true, T7Blocks.MATRIX.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.TERRA), lockedAspect(2, 4, Aspects.AETHER)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Thavma.TECHNOLOGY, Vector2i(2, 4), true, T7Items.GOGGLES.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.INSTRUMENTUM), lockedAspect(2, 4, Aspects.CIVILIS)) +// .build(ctx) +// +// ResearchEntryBuilder(ResearchEntries.Alchemy.ALCHEMY, Vector2i(0, 0), false, T7Blocks.CRUCIBLE.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AQUA), lockedAspect(2, 4, Aspects.ALKIMIA)) +// .defaultKnown() +// .build(ctx) +// } +// } +// } +// +// private class ResearchEntryBuilder( +// private val key: ResourceKey, +// private val pos: Vector2i, +// private val preferX: Boolean, +// private val icon: ItemStack +// ) { +// private val children = mutableListOf>() +// private val pages = mutableListOf() +// private val socketStates = mutableListOf() +// private var defaultKnown = false +// +// fun addChild(entryKey: ResourceKey): ResearchEntryBuilder { +// children.add(entryKey) +// return this +// } +// +// fun addPage(makePage: (ResourceKey, Int) -> Page): ResearchEntryBuilder { +// pages.add(makePage(key, pages.size)) +// return this +// } +// +// fun research(vararg states: SocketState): ResearchEntryBuilder { +// socketStates.addAll(states) +// return this +// } +// +// fun defaultKnown(): ResearchEntryBuilder { +// defaultKnown = true +// return this +// } +// +// fun build(ctx: BootstrapContext) = ResearchEntries.CATEGORIES[key]?.let { cat -> +// val categoryRegistry = ctx.lookup(T7DatapackRegistries.RESEARCH_CATEGORY) +// val entryRegistry = ctx.lookup(T7DatapackRegistries.RESEARCH_ENTRY) +// val categoryHolder = categoryRegistry.getOrThrow(cat) +// val childrenHolders = children.map { entryRegistry.getOrThrow(it) } +// ctx.register(key, ResearchEntry(categoryHolder, pos, preferX, childrenHolders, pages, icon, Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), socketStates, defaultKnown)) +// } +// } +// +// private fun BootstrapContext.registerCategory(key: ResourceKey, icon: ItemStack, sortIndex: Float) { +// register(key, ResearchCategory(Component.translatable(ResearchCategory.translationId(key)), sortIndex, icon)) +// } +// +// private fun simpleTextPage(paragraphCount: Int, hasTitle: Boolean): (ResourceKey, Int) -> Page { +// return { entryKey, pageIndex -> +// val baseId = ResearchEntry.translationId(entryKey) +// TextPage( +// if (hasTitle) simpleTitle(pageIndex, baseId) else null, +// simpleParagraphs(paragraphCount, pageIndex, baseId) +// ) +// } +// } +// +// private fun simpleTitle(pageIndex: Int, baseId: String) = +// Component.translatable(TextPage.titleTranslationId(baseId, pageIndex)).withStyle(ChatFormatting.BOLD) +// +// private fun simpleParagraphs(count: Int, pageIndex: Int, baseId: String) = List(count) { Component.translatable(TextPage.paragraphTranslationId(baseId, pageIndex, it)) } +// +// private fun lockedAspect(row: Int, col: Int, a: DeferredAspect) = SocketState(Indices(row, col), a.get(), false, true) +// private fun broken(row: Int, col: Int) = SocketState(Indices(row, col), null, true, true) + + + + +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt index e38fff18..a799e4c0 100644 --- a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt +++ b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt @@ -4,6 +4,7 @@ import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.common.book.Page +import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.util.T7ExtraCodecs import me.alegian.thavma.impl.common.util.registry import me.alegian.thavma.impl.init.registries.T7DatapackRegistries @@ -20,15 +21,15 @@ import org.joml.Vector2i private val parentsMap = mutableMapOf>>() class ResearchEntry( - val category: Holder, - val position: Vector2i, - val preferX: Boolean, - val children: List>, - val pages: List, - val icon: ItemStack, - val title: Component, - val defaultResearchState: List, - val defaultKnown: Boolean + val category: Holder, + val position: Vector2i, + val preferX: Boolean, + val children: List>, + val pageFeatures: List, + val icon: ItemStack, + val title: Component, + val defaultResearchState: List, + val defaultKnown: Boolean ) { fun parents(level: Level) = parentsMap.computeIfAbsent(this) { _ -> @@ -43,7 +44,7 @@ class ResearchEntry( T7ExtraCodecs.VECTOR2I.fieldOf("position").forGetter(ResearchEntry::position), Codec.BOOL.fieldOf("preferX").forGetter(ResearchEntry::preferX), RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_ENTRY, CODEC).listOf().fieldOf("children").forGetter(ResearchEntry::children), - Page.CODEC.listOf().fieldOf("pages").forGetter(ResearchEntry::pages), + PageFeature.CODEC.listOf().fieldOf("pageFeatures").forGetter(ResearchEntry::pageFeatures), ItemStack.STRICT_CODEC.fieldOf("icon").forGetter(ResearchEntry::icon), ComponentSerialization.CODEC.fieldOf("title").forGetter(ResearchEntry::title), SocketState.CODEC.listOf().fieldOf("defaultResearchState").forGetter(ResearchEntry::defaultResearchState), diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index 0d56c8da..f3c2c912 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -3,7 +3,9 @@ package me.alegian.thavma.impl.init.data.providers import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.common.aspect.Aspect import me.alegian.thavma.impl.common.book.CraftingPage +import me.alegian.thavma.impl.common.book.DynamicPage import me.alegian.thavma.impl.common.book.Page +import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.book.TextPage import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION import me.alegian.thavma.impl.common.research.ResearchCategory @@ -118,14 +120,14 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab ctx.registerCategory(ResearchCategories.STORY, Items.WRITABLE_BOOK.defaultInstance, 2f) } .add(T7DatapackRegistries.RESEARCH_ENTRY) { ctx -> - ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) - .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) - .addPage(simpleTextPage(3, true)) - .addPage(simpleTextPage(1, false)) - .addChild(ResearchEntries.Thavma.TREES) - .addChild(ResearchEntries.Thavma.ORES) - .defaultKnown() - .build(ctx) +// ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) +// .addPage(simpleTextPage(3, true)) +// .addPage(simpleTextPage(1, false)) +// .addChild(ResearchEntries.Thavma.TREES) +// .addChild(ResearchEntries.Thavma.ORES) +// .defaultKnown() +// .build(ctx) ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, -3), false, Items.TURTLE_HELMET.defaultInstance) .research() @@ -145,21 +147,21 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .addChild(ResearchEntries.Thavma.ARCANE_LENS) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) - .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) - .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) - .addPage(simpleTextPage(3, true)) - .build(ctx) +// ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) +// .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) +// .addPage(simpleTextPage(3, true)) +// .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) - .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) - .addPage { _, _ -> CraftingPage(Recipes.CHEST) } - .addChild(ResearchEntries.Thavma.WANDS) - .addChild(ResearchEntries.Thavma.TECHNOLOGY) - .addChild(ResearchEntries.Thavma.ALCHEMY) - .addChild(ResearchEntries.Thavma.INFUSION) - .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) - .build(ctx) +// ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) +// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) +// .addPage { _, _ -> CraftingPage(Recipes.CHEST) } +// .addChild(ResearchEntries.Thavma.WANDS) +// .addChild(ResearchEntries.Thavma.TECHNOLOGY) +// .addChild(ResearchEntries.Thavma.ALCHEMY) +// .addChild(ResearchEntries.Thavma.INFUSION) +// .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) +// .build(ctx) ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_PROFICIENCY, Vector2i(-1, -1), false, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) @@ -196,7 +198,7 @@ private class ResearchEntryBuilder( private val icon: ItemStack ) { private val children = mutableListOf>() - private val pages = mutableListOf() + private val pageFeatures = mutableListOf() private val socketStates = mutableListOf() private var defaultKnown = false @@ -225,7 +227,7 @@ private class ResearchEntryBuilder( val entryRegistry = ctx.lookup(T7DatapackRegistries.RESEARCH_ENTRY) val categoryHolder = categoryRegistry.getOrThrow(cat) val childrenHolders = children.map { entryRegistry.getOrThrow(it) } - ctx.register(key, ResearchEntry(categoryHolder, pos, preferX, childrenHolders, pages, icon, Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), socketStates, defaultKnown)) + ctx.register(key, ResearchEntry(categoryHolder, pos, preferX, childrenHolders, pageFeatures, icon, Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), socketStates, defaultKnown)) } } @@ -243,6 +245,21 @@ private fun simpleTextPage(paragraphCount: Int, hasTitle: Boolean): (ResourceKey } } +private fun simpleDynamicPages(vararg features: PageFeature): (ResourceKey) -> List { + + // tady prostě mám přístup k těm jednotlivejm features, z nich poskládám seznam + // dynamických stránek a ty renderuju pomocí zvláštního DynamicPageRenderer (TODO) + // zahrnout nejlépe všechnu tu logiku co mám v těch Features + + return { entryKey -> + val baseId = ResearchEntry.translationId(entryKey) + DynamicPage( + features.toList() + ) + + } +} + private fun simpleTitle(pageIndex: Int, baseId: String) = Component.translatable(TextPage.titleTranslationId(baseId, pageIndex)).withStyle(ChatFormatting.BOLD) diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index 760ad0ce..01f4ad8b 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -401,4 +401,12 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( for (parIndex in paragraphs.indices) add(TextPage.paragraphTranslationId(baseId, pageIndex, parIndex), paragraphs[parIndex].trimIndent().replace("\n", " ")) } + + private fun addParagraphFeature(entryKey: ResourceKey, featureIndex: Int, text: String?){ + + } + + private fun addTitleFeature(entryKey: ResourceKey, featureIndex: Int, text: String?){ + + } } diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt b/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt index 74aac755..467bff0a 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt @@ -1,6 +1,7 @@ package me.alegian.thavma.impl.init.registries import me.alegian.thavma.impl.common.aspect.Aspect +import me.alegian.thavma.impl.common.book.PageFeatureType import me.alegian.thavma.impl.common.book.PageType import me.alegian.thavma.impl.common.wand.WandCoreMaterial import me.alegian.thavma.impl.common.wand.WandPlatingMaterial @@ -25,4 +26,8 @@ object T7Registries { val PAGE_TYPE = RegistryBuilder(ResourceKey.createRegistryKey>(rl("page_type"))) .maxId(Int.MAX_VALUE) .create() + + val PAGE_FEATURE_TYPE = RegistryBuilder(ResourceKey.createRegistryKey>(rl("page_feature"))) + .maxId(Int.MAX_VALUE) + .create() } diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt new file mode 100644 index 00000000..a072a50f --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt @@ -0,0 +1,17 @@ +package me.alegian.thavma.impl.init.registries.deferred + +import me.alegian.thavma.impl.Thavma +import me.alegian.thavma.impl.common.book.PageFeatureType +import me.alegian.thavma.impl.common.book.ParagraphFeature +import me.alegian.thavma.impl.common.book.TitleFeature +import me.alegian.thavma.impl.init.registries.T7Registries +import me.alegian.thavma.impl.rl +import net.neoforged.neoforge.registries.DeferredRegister + +object PageFeatureTypes { + val REGISTRAR = DeferredRegister.create(T7Registries.PAGE_FEATURE_TYPE.key(), Thavma.MODID) + + val PARAGRAPH = REGISTRAR.register("paragraph") { -> PageFeatureType(rl("paragraph"), ParagraphFeature.CODEC)} + val TITLE = REGISTRAR.register("title") { -> PageFeatureType(rl("paragraph"), TitleFeature.CODEC)} + +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt index bec6449b..e0f0e06c 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageTypes.kt @@ -2,6 +2,8 @@ package me.alegian.thavma.impl.init.registries.deferred import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.common.book.CraftingPage +import me.alegian.thavma.impl.common.book.DynamicPage +import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.book.PageType import me.alegian.thavma.impl.common.book.TextPage import me.alegian.thavma.impl.init.registries.T7Registries @@ -13,4 +15,5 @@ object PageTypes { val TEXT = REGISTRAR.register("text") { -> PageType(rl("text"), TextPage.CODEC) } val CRAFTING = REGISTRAR.register("crafting") { -> PageType(rl("crafting"), CraftingPage.CODEC) } + //val DYNAMIC = REGISTRAR.register("dynamic") { -> PageType(rl("dynamic"), DynamicPage.CODEC) } } \ No newline at end of file From 7d82309b2f37489317048d4bfca4bc954a2e3e42 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Sat, 9 May 2026 22:33:21 +0200 Subject: [PATCH 09/19] Progress towards dynamic rendering mainly EntryScreen.kt --- .../RegisterPageFeatureRenderersEvent.kt | 12 + .../event/RegisterPageRenderersEvent.kt | 3 +- .../impl/client/event/T7ClientModEvents.kt | 6 + .../gui/book/DynamicFeaturesRenderer.kt | 18 ++ .../impl/client/gui/book/EntryScreen.kt | 235 +++++++++++++++--- .../client/gui/book/PageFeatureRenderer.kt | 7 + .../impl/client/gui/book/TextPageRenderer.kt | 12 +- .../thavma/impl/client/texture/Texture.kt | 31 ++- .../thavma/impl/common/book/FigureFeature.kt | 49 ++++ .../thavma/impl/common/book/PageFeature.kt | 2 +- .../impl/common/book/ParagraphFeature.kt | 43 ++-- .../impl/common/book/PlainTextFeature.kt | 32 +++ .../thavma/impl/common/book/RecipeFeature.kt | 33 +++ .../thavma/impl/common/book/TitleFeature.kt | 2 +- .../T7DatapackBuiltinEntriesProvider.kt | 2 + .../registries/deferred/PageFeatureTypes.kt | 11 +- 16 files changed, 424 insertions(+), 74 deletions(-) create mode 100644 src/main/java/me/alegian/thavma/impl/client/event/RegisterPageFeatureRenderersEvent.kt create mode 100644 src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt create mode 100644 src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/PlainTextFeature.kt create mode 100644 src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt diff --git a/src/main/java/me/alegian/thavma/impl/client/event/RegisterPageFeatureRenderersEvent.kt b/src/main/java/me/alegian/thavma/impl/client/event/RegisterPageFeatureRenderersEvent.kt new file mode 100644 index 00000000..41254976 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/client/event/RegisterPageFeatureRenderersEvent.kt @@ -0,0 +1,12 @@ +package me.alegian.thavma.impl.client.event + +import me.alegian.thavma.impl.client.gui.book.PageFeatureRenderer +import me.alegian.thavma.impl.common.book.PageFeature +import net.neoforged.bus.api.Event +import net.neoforged.fml.event.IModBusEvent + +class RegisterPageFeatureRenderersEvent: Event(), IModBusEvent { + fun register(pageFeatureRenderer: PageFeatureRenderer) { + //PAGE_FEATURE_RENDERERS[pageFeatureType] = pageFeatureRenderer + } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/client/event/RegisterPageRenderersEvent.kt b/src/main/java/me/alegian/thavma/impl/client/event/RegisterPageRenderersEvent.kt index 89591982..c3c90808 100644 --- a/src/main/java/me/alegian/thavma/impl/client/event/RegisterPageRenderersEvent.kt +++ b/src/main/java/me/alegian/thavma/impl/client/event/RegisterPageRenderersEvent.kt @@ -8,7 +8,8 @@ import net.neoforged.bus.api.Event import net.neoforged.fml.event.IModBusEvent class RegisterPageRenderersEvent : Event(), IModBusEvent { - fun register(pageType:PageType, pageRenderer: PageRenderer) { + fun register(pageType: PageType, pageRenderer: PageRenderer) { PAGE_RENDERERS[pageType] = pageRenderer + } } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/client/event/T7ClientModEvents.kt b/src/main/java/me/alegian/thavma/impl/client/event/T7ClientModEvents.kt index 35f385ad..6b391fa5 100644 --- a/src/main/java/me/alegian/thavma/impl/client/event/T7ClientModEvents.kt +++ b/src/main/java/me/alegian/thavma/impl/client/event/T7ClientModEvents.kt @@ -6,6 +6,7 @@ import me.alegian.thavma.impl.client.extension.BEWLRItemExtensionFactory import me.alegian.thavma.impl.client.extension.WandItemExtensions import me.alegian.thavma.impl.client.gui.WorkbenchScreen import me.alegian.thavma.impl.client.gui.book.CraftingPageRenderer +import me.alegian.thavma.impl.client.gui.book.DynamicFeaturesRenderer import me.alegian.thavma.impl.client.gui.book.TextPageRenderer import me.alegian.thavma.impl.client.gui.layer.ArcaneLensLayer import me.alegian.thavma.impl.client.gui.layer.WandLayer @@ -219,6 +220,10 @@ private fun registerPageRenderers(event: RegisterPageRenderersEvent) { event.register(PageTypes.CRAFTING.get(), CraftingPageRenderer) } +private fun registerPageFeatureRenderers(event: RegisterPageFeatureRenderersEvent){ + event.register(DynamicFeaturesRenderer) +} + private fun registerKeyMappings(event: RegisterKeyMappingsEvent) { event.register(T7KeyMappings.FOCI) } @@ -244,6 +249,7 @@ fun registerClientModEvents() { KFF_MOD_BUS.addListener(::registerClientTooltipComponentFactories) KFF_MOD_BUS.addListener(::registerScreens) KFF_MOD_BUS.addListener(::registerPageRenderers) + KFF_MOD_BUS.addListener(::registerPageFeatureRenderers) KFF_MOD_BUS.addListener(::registerKeyMappings) KFF_MOD_BUS.addListener(::registerRenderBuffers) } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt new file mode 100644 index 00000000..1cd2041b --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt @@ -0,0 +1,18 @@ +package me.alegian.thavma.impl.client.gui.book + +import me.alegian.thavma.impl.client.texture.Texture +import me.alegian.thavma.impl.common.book.PageFeature +import me.alegian.thavma.impl.common.book.ParagraphFeature +import net.minecraft.client.Minecraft + +object DynamicFeaturesRenderer : PageFeatureRenderer { + private val SEPARATOR = Texture("gui/book/separator", 128, 16, 128, 16) + + override fun initPageFeatures(screen: EntryScreen, features: List, currentPage: Int) { + val font = Minecraft.getInstance().font + val LINE_HEIGHT = font.lineHeight + 2 + + + } + +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt index fdc97a7e..7336f5ee 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt @@ -1,20 +1,17 @@ package me.alegian.thavma.impl.client.gui.book import me.alegian.thavma.impl.client.gui.layout.* -import me.alegian.thavma.impl.client.gui.research_table.ButtonWidget import me.alegian.thavma.impl.client.texture.Texture import me.alegian.thavma.impl.common.book.Page +import me.alegian.thavma.impl.common.book.PageFeature +import me.alegian.thavma.impl.common.book.ParagraphFeature +import me.alegian.thavma.impl.common.book.PlainTextFeature import me.alegian.thavma.impl.common.research.ResearchEntry -import me.alegian.thavma.impl.init.registries.deferred.ResearchEntries -import me.alegian.thavma.impl.rl import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.screens.Screen import net.minecraft.core.Holder import net.minecraft.network.chat.Component -import net.minecraft.resources.ResourceKey -import net.minecraft.resources.ResourceLocation -import net.minecraft.world.level.Level -import kotlin.math.ceil +import net.minecraft.network.chat.Style class EntryScreen(private val entry: Holder) : Screen(Component.literal("Book Entry")) { companion object { @@ -44,46 +41,48 @@ class EntryScreen(private val entry: Holder) : Screen(Component.l size = grow() }) { initPage(entry.value().pages.getOrNull(currentPage)) - if (currentPage != 0) { + if (currentPage != 0) { Box({ - width = fixed(PageTurningWidget.LEFT_TEXTURE.width) - height = fixed(PageTurningWidget.LEFT_TEXTURE.height) + width = fixed(PageTurningWidget.LEFT_TEXTURE.width) + height = fixed(PageTurningWidget.LEFT_TEXTURE.height) }) { - afterLayout { - - addRenderableWidget(PageTurningWidget(position, false) { - // reinitiate the screen for this research entry when clicked - // with an updated page - // clearWidgets() is essential, also clears underline formatting! - currentPage -= 2 - clearWidgets() - init() - })} - } + afterLayout { + + addRenderableWidget(PageTurningWidget(position, false) { + // reinitiate the screen for this research entry when clicked + // with an updated page + // clearWidgets() is essential, also clears underline formatting! + currentPage -= 2 + clearWidgets() + init() + }) + } } + } } Row({ size = grow() }) { initPage(entry.value().pages.getOrNull(currentPage + 1)) - if ((entry.value().pages.getOrNull(currentPage + 2) ?: 1) is Page) { + if (entry.value().pages.getOrNull(currentPage + 2) != null) { Box({ - width = fixed(PageTurningWidget.RIGHT_TEXTURE.width) - height = fixed(PageTurningWidget.RIGHT_TEXTURE.height) + width = fixed(PageTurningWidget.RIGHT_TEXTURE.width) + height = fixed(PageTurningWidget.RIGHT_TEXTURE.height) }) { - afterLayout { - - addRenderableWidget(PageTurningWidget(position, true) { - // reinitiate the screen for this research entry when clicked - // with an updated page - // clearWidgets() is essential, also clears underline formatting! - currentPage += 2 - clearWidgets() - init() - })} - } + afterLayout { + + addRenderableWidget(PageTurningWidget(position, true) { + // reinitiate the screen for this research entry when clicked + // with an updated page + // clearWidgets() is essential, also clears underline formatting! + currentPage += 2 + clearWidgets() + init() + }) + } } + } } } } @@ -101,5 +100,171 @@ class EntryScreen(private val entry: Holder) : Screen(Component.l renderer.initPage(this, page) } +// private fun initPageFeatures(features: List) { +// val renderer = DynamicFeaturesRenderer as PageFeatureRenderer +// renderer.initPageFeatures(this, features, currentPage) +// } + + val renderer = DynamicFeaturesRenderer + + private fun initPageFeatures(features: List) { + renderer.initPageFeatures(this, features, currentPage) + } + + + /** + * Return a list of plain text features based on a paragraph so that they fit on their + * respective pages (represented by list idices) + */ + private fun spliceParagraph( + paragraph: ParagraphFeature, maxPageHeight: Int, + currentHeight: Int + ): List { + + val result = mutableListOf() + // how many lines fit in the current page + val lineHeight = paragraph.LINE_HEIGHT + + // so that we get at least 2 lines at the end of the first page + val linesRemainingAtStart: Int = (maxPageHeight - currentHeight) / lineHeight + + // so that we get at least 2 lines at the start of the last page + // (making use of rounding down when dividing integers) + val numOfLinesCoveringFullPages: Int = + (paragraph.renderedHeight - linesRemainingAtStart * lineHeight) / maxPageHeight / lineHeight + val linesCroppingOutAtEnd: Int = + (paragraph.renderedHeight - linesRemainingAtStart * lineHeight - numOfLinesCoveringFullPages * lineHeight) / lineHeight + + val maxLinesPerPage = maxPageHeight / lineHeight + val numOfFullPagesCovered = numOfLinesCoveringFullPages / maxLinesPerPage + val lines = paragraph.font.splitter.splitLines(paragraph.text, paragraph.pageWidth, Style.EMPTY) + + when { + lines.size == 1 && currentHeight + lineHeight <= maxPageHeight -> result += PlainTextFeature( + Component.literal( + lines[0].string + ) + ) + + lines.size == 1 -> result += listOf( + PlainTextFeature(Component.literal("")), + PlainTextFeature(Component.literal(lines[0].string)) + ) + + linesRemainingAtStart >= 2 && linesCroppingOutAtEnd != 1 -> { + // separate into "underhang", middle and overhang + val start = lines.slice(0 until linesRemainingAtStart).joinToString(" ") { it.string } + result += PlainTextFeature(Component.literal(start)) + + for (i in 0 until numOfFullPagesCovered) { + val middle = + lines.slice(linesRemainingAtStart + i * maxLinesPerPage until linesRemainingAtStart + (i + 1) * maxLinesPerPage) + .joinToString(" ") { it.string } + result += PlainTextFeature(Component.literal(middle)) + } + + val end = + lines.slice(lines.size - 1 - linesCroppingOutAtEnd until lines.size - 1).joinToString(" ") { it.string } + result += PlainTextFeature(Component.literal(end)) + } + + else -> { + // add an empty paragraph to current page and continue on the next + result += PlainTextFeature(Component.literal("")) + for (i in 0 until numOfFullPagesCovered) { + val page = lines.slice(i * maxLinesPerPage until (i + 1) * maxLinesPerPage).joinToString(" ") { it.string } + result += PlainTextFeature(Component.literal(page)) + } + val finish = + lines.slice(numOfFullPagesCovered * maxLinesPerPage until lines.size).joinToString(" ") { it.string } + result += PlainTextFeature(Component.literal(finish)) + } + } + + return result + } + + /** + * Returns a list of lists of features where every index represents a page. + * Features in the same list belong together on one page. + */ + private fun pagifyFeatures(features: List): List> { + val maxHeight = this@EntryScreen.height + val partition = features.partition { !it.mustOccupySetPage } + val result = mutableListOf>() + val buffer = mutableListOf() + fun currentHeight() = buffer.sumOf { it.renderedHeight } + + for (feature in partition.first) { + + with(feature) { + when { + this !is ParagraphFeature && renderedHeight > maxHeight -> throw IllegalArgumentException("The size of the element ${this::class.simpleName} is too large at $renderedHeight while allowed $maxHeight.") + + coversWholePage -> { + if (buffer.isNotEmpty()) { + result += buffer + buffer.clear() + } + result += listOf(this) + } + + mustStartPage -> { + if (buffer.isNotEmpty()) { + result += buffer + buffer.clear() + } + if (this is ParagraphFeature) { + val processed = spliceParagraph(this, maxHeight, currentHeight()) + if (processed.size == 1) buffer += processed.first() + //only need to check this much since buffer is empty (starts page) + else { + processed.slice(0 until processed.size - 1).forEach { result += listOf(it) } + buffer += processed.last() + } + } else buffer += this + } + + this is ParagraphFeature -> { + val processed = spliceParagraph(this, maxHeight, currentHeight()) + if (processed.size == 1) buffer += processed.first() + // need to check one more value so as not to append null to result + else if (processed.size == 2) { + buffer += processed.first() + result += buffer + buffer.clear() + buffer += processed.last() + } else { + buffer += processed.first() + result += buffer + buffer.clear() + processed.slice(1 until processed.size - 1).forEach { result += listOf(it) } + buffer += processed.last() + } + } + + currentHeight() + renderedHeight <= maxHeight -> buffer += this + // the next check might be redundant: + buffer.isEmpty() -> throw IllegalArgumentException("The size of the element ${this::class.simpleName} is too large at $renderedHeight while allowed $maxHeight.") + else -> { + result += buffer + buffer.clear() + buffer += this + } + } + + } + + } + + if (buffer.isNotEmpty()) result += buffer + + // finally add features with pre-determined positions (cannot be paragraphs) + // these features have to be ordered correctly in the Research Entry builder + partition.second.groupBy { it.preferredPageIndex }.forEach { result.add(it.key, it.value) } + + return result + } + override fun isPauseScreen() = false } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt new file mode 100644 index 00000000..3d6b518a --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt @@ -0,0 +1,7 @@ +package me.alegian.thavma.impl.client.gui.book + +import me.alegian.thavma.impl.common.book.PageFeature + +interface PageFeatureRenderer{ + fun initPageFeatures(screen: EntryScreen, features: List, currentPage: Int) +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/TextPageRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/TextPageRenderer.kt index bb609674..43841f5f 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/TextPageRenderer.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/TextPageRenderer.kt @@ -16,9 +16,6 @@ import net.minecraft.util.FormattedCharSequence object TextPageRenderer : PageRenderer { private val SEPARATOR = Texture("gui/book/separator", 128, 16, 128, 16) - val dolor = Component.literal("dolor") - .withStyle(ChatFormatting.DARK_RED, ChatFormatting.UNDERLINE) - override fun initPage(screen: EntryScreen, page: TextPage) { val font = Minecraft.getInstance().font val LINE_HEIGHT = font.lineHeight + 2 @@ -37,16 +34,11 @@ object TextPageRenderer : PageRenderer { relativeRenderable { guiGraphics, _, _, _ -> guiGraphics.usePose { for (paragraph in page.paragraphs) { - //for (line in font.split(paragraph, size.x.toInt())) { for (line in font.splitter.splitLines(paragraph, size.x.toInt(), Style.EMPTY)) { - if ("ipsum" in line.string) { - guiGraphics.drawString(Minecraft.getInstance().font, Component.translatable("research/entry.thavma.story.story_test.page0.paragraph0", dolor)) - //.withStyle(ChatFormatting.RED, ChatFormatting.BOLD)) - } - else guiGraphics.drawString(Minecraft.getInstance().font, Component.literal(line.string)) + guiGraphics.drawString(Minecraft.getInstance().font, Component.literal(line.string)) translateXY(0, LINE_HEIGHT) } - translateXY(0, LINE_HEIGHT*2/3) + translateXY(0, LINE_HEIGHT * 2 / 3) } } } diff --git a/src/main/java/me/alegian/thavma/impl/client/texture/Texture.kt b/src/main/java/me/alegian/thavma/impl/client/texture/Texture.kt index dd6eb418..ab0b28bd 100644 --- a/src/main/java/me/alegian/thavma/impl/client/texture/Texture.kt +++ b/src/main/java/me/alegian/thavma/impl/client/texture/Texture.kt @@ -1,5 +1,7 @@ package me.alegian.thavma.impl.client.texture +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder import me.alegian.thavma.impl.rl import net.minecraft.resources.ResourceLocation import net.minecraft.world.phys.Vec2 @@ -8,11 +10,36 @@ import net.minecraft.world.phys.Vec2 * Width and height refer to the useful part of the texture, not its entirety. * In practice many textures (like inventories) have huge empty spaces we don't care about */ -class Texture(val location: ResourceLocation, val width: Int, val height: Int, val canvasWidth: Int, val canvasHeight: Int) { +data class Texture( + val location: ResourceLocation, + val width: Int, + val height: Int, + val canvasWidth: Int, + val canvasHeight: Int +) { constructor(path: String, width: Int, height: Int) : this(rl("textures/$path.png"), width, height, width, height) - constructor(path: String, width: Int, height: Int, canvasWidth: Int, canvasHeight: Int) : this(rl("textures/$path.png"), width, height, canvasWidth, canvasHeight) + constructor( + path: String, + width: Int, + height: Int, + canvasWidth: Int, + canvasHeight: Int + ) : this(rl("textures/$path.png"), width, height, canvasWidth, canvasHeight) val size: Vec2 get() = Vec2(width.toFloat(), height.toFloat()) + + companion object { + val CODEC: Codec = RecordCodecBuilder.create { instance -> + instance.group( + ResourceLocation.CODEC.fieldOf("location").forGetter(Texture::location), + Codec.INT.fieldOf("width").forGetter(Texture::width), + Codec.INT.fieldOf("height").forGetter(Texture::height), + Codec.INT.fieldOf("canvas_width").forGetter(Texture::canvasWidth), + Codec.INT.fieldOf("canvas_height").forGetter(Texture::canvasHeight) + ).apply(instance, ::Texture) + } + } } + diff --git a/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt new file mode 100644 index 00000000..7f600100 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt @@ -0,0 +1,49 @@ +package me.alegian.thavma.impl.common.book + +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder +import me.alegian.thavma.impl.client.texture.Texture +import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Font +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.ComponentSerialization +import net.minecraft.network.chat.Style +import java.util.Optional + +class FigureFeature(val image: Texture, val caption: Component?, override val mustStartPage: Boolean = false) : PageFeature { + override val type: PageFeatureType<*> + get() = PageFeatureTypes.FIGURE.get() + + override val coversWholePage = false + override val mustOccupySetPage = false + override val preferredPageIndex: Int + get() = 1 + + override val pageWidth: Int + get() = 256 + + + val font: Font = Minecraft.getInstance().font + + // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor + val LINE_HEIGHT = font.lineHeight + 2 + val lines = font.splitter.splitLines(caption, pageWidth - 25, Style.EMPTY) + override val renderedHeight = image.canvasHeight + LINE_HEIGHT * lines.size + LINE_HEIGHT * 4 / 3 + + + companion object { + val CODEC = RecordCodecBuilder.mapCodec { builder -> + builder.group( + Texture.CODEC.fieldOf("image").forGetter(FigureFeature::image), + ComponentSerialization.CODEC.optionalFieldOf("caption").forGetter { p -> Optional.ofNullable(p.caption) }, + Codec.BOOL.optionalFieldOf("starts_page", false).forGetter(FigureFeature::mustStartPage) + ).apply(builder) { img, cap, start -> + FigureFeature(img, cap.orElse(null), start) + } + } + + fun translationId(baseId: String, featureIndex: Int) = "$baseId.figure_feature$featureIndex" + } + +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt index c3563481..841afb40 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt @@ -10,7 +10,7 @@ interface PageFeature { get() = 1 val renderedHeight: Int val pageWidth: Int - get() = 256 + get() = 256 - 25 val type: PageFeatureType<*> diff --git a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt index fcce37fe..ddbb050c 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt @@ -1,5 +1,6 @@ package me.alegian.thavma.impl.common.book +import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.init.data.worldgen.tree.trunk.SilverwoodTrunkPlacer @@ -13,32 +14,34 @@ import net.minecraft.resources.ResourceLocation import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer.trunkPlacerParts import java.util.Optional -class ParagraphFeature(val text: Component): PageFeature { - override val type: PageFeatureType<*> - get() = PageFeatureTypes.PARAGRAPH.get() +class ParagraphFeature(val text: Component, override val mustStartPage: Boolean = false) : PageFeature { + override val type: PageFeatureType<*> + get() = PageFeatureTypes.PARAGRAPH.get() - override val coversWholePage = false - override val mustStartPage = false - override val mustOccupySetPage = false + override val coversWholePage = false + override val mustOccupySetPage = false + val font: Font = Minecraft.getInstance().font - val font: Font = Minecraft.getInstance().font - // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor - val LINE_HEIGHT = font.lineHeight + 2 - val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) - override val renderedHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 2 / 3 + // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor + val LINE_HEIGHT = font.lineHeight + 2 + val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) - companion object { - val CODEC = RecordCodecBuilder.mapCodec { builder -> - builder.group( - ComponentSerialization.CODEC.fieldOf("text").forGetter(ParagraphFeature::text) - ).apply(builder, ::ParagraphFeature) - } + override val renderedHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 2 / 3 - fun translationId(baseId: String, featureIndex: Int) = "$baseId.paragraphFeature$featureIndex" + companion object { + val CODEC = RecordCodecBuilder.mapCodec { builder -> + builder.group( + ComponentSerialization.CODEC.fieldOf("text").forGetter(ParagraphFeature::text), + //ResourceLocation.CODEC.optionalFieldOf("font", ResourceLocation.withDefaultNamespace("default")).forGetter(ParagraphFeature::font), + Codec.BOOL.optionalFieldOf("starts_page", false).forGetter(ParagraphFeature::mustStartPage) + ).apply(builder, ::ParagraphFeature) } - //fun titleTranslationId(baseId: String, pageIndex: Int) = Page.translationId(baseId, pageIndex) + ".title" - //fun paragraphFeatureTranslationId(baseId: String, index: Int) = Page.translationId(baseId, pageIndex) + ".paragraph$index" + fun translationId(baseId: String, featureIndex: Int) = "$baseId.paragraph_feature$featureIndex" + } + + //fun titleTranslationId(baseId: String, pageIndex: Int) = Page.translationId(baseId, pageIndex) + ".title" + //fun paragraphFeatureTranslationId(baseId: String, index: Int) = Page.translationId(baseId, pageIndex) + ".paragraph$index" } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PlainTextFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/PlainTextFeature.kt new file mode 100644 index 00000000..313e2b0f --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/PlainTextFeature.kt @@ -0,0 +1,32 @@ +package me.alegian.thavma.impl.common.book + +import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Font +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.Style + +/** + * Is only ever used in EntryScreen.kt to break ParagraphFeatures into segments + * so that they fit in their respective pages + */ + + +class PlainTextFeature(val text: Component, val font: Font = Minecraft.getInstance().font): PageFeature { + override val coversWholePage: Boolean + get() = false + override val mustStartPage: Boolean + get() = false + override val mustOccupySetPage: Boolean + get() = false + + override val type: PageFeatureType<*> + get() = PageFeatureTypes.PARAGRAPH.get() + + + // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor + val LINE_HEIGHT = font.lineHeight + 2 + val lines = font.splitter.splitLines(text, pageWidth - 25, Style.EMPTY) + + override val renderedHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 2 / 3 +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt new file mode 100644 index 00000000..a711d5aa --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt @@ -0,0 +1,33 @@ +package me.alegian.thavma.impl.common.book + +import com.mojang.serialization.codecs.RecordCodecBuilder +import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes +import net.minecraft.resources.ResourceLocation + +class RecipeFeature( + val recipeRL: ResourceLocation, +) : PageFeature { + override val type: PageFeatureType<*> + get() = PageFeatureTypes.RECIPE.get() + + override val coversWholePage: Boolean + get() = true + override val mustStartPage: Boolean + get() = true + override val mustOccupySetPage: Boolean + get() = true + + + override val renderedHeight: Int + get() = TODO("Not yet implemented") + + companion object { + val CODEC = RecordCodecBuilder.mapCodec { builder -> + builder.group( + ResourceLocation.CODEC.fieldOf("recipeRL").forGetter(RecipeFeature::recipeRL), + ).apply(builder, ::RecipeFeature) + } + + fun translationId(baseId: String, featureIndex: Int) = "$baseId.figure_feature$featureIndex" + } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt index c5a628a8..a23e701d 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt @@ -31,6 +31,6 @@ class TitleFeature(val text: Component, ): PageFeature { ).apply(builder, ::TitleFeature) } - fun translationId(baseId: String, featureIndex: Int) = "$baseId.titleFeature$featureIndex" + fun translationId(baseId: String, featureIndex: Int) = "$baseId.title_feature$featureIndex" } } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index f3c2c912..329c7850 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -250,6 +250,8 @@ private fun simpleDynamicPages(vararg features: PageFeature): (ResourceKey val baseId = ResearchEntry.translationId(entryKey) diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt index a072a50f..251de20a 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/PageFeatureTypes.kt @@ -1,17 +1,20 @@ package me.alegian.thavma.impl.init.registries.deferred import me.alegian.thavma.impl.Thavma +import me.alegian.thavma.impl.common.book.FigureFeature import me.alegian.thavma.impl.common.book.PageFeatureType import me.alegian.thavma.impl.common.book.ParagraphFeature +import me.alegian.thavma.impl.common.book.RecipeFeature import me.alegian.thavma.impl.common.book.TitleFeature import me.alegian.thavma.impl.init.registries.T7Registries import me.alegian.thavma.impl.rl import net.neoforged.neoforge.registries.DeferredRegister object PageFeatureTypes { - val REGISTRAR = DeferredRegister.create(T7Registries.PAGE_FEATURE_TYPE.key(), Thavma.MODID) - - val PARAGRAPH = REGISTRAR.register("paragraph") { -> PageFeatureType(rl("paragraph"), ParagraphFeature.CODEC)} - val TITLE = REGISTRAR.register("title") { -> PageFeatureType(rl("paragraph"), TitleFeature.CODEC)} + val REGISTRAR = DeferredRegister.create(T7Registries.PAGE_FEATURE_TYPE.key(), Thavma.MODID) + val PARAGRAPH = REGISTRAR.register("paragraph") { -> PageFeatureType(rl("paragraph"), ParagraphFeature.CODEC) } + val TITLE = REGISTRAR.register("title") { -> PageFeatureType(rl("title"), TitleFeature.CODEC) } + val FIGURE = REGISTRAR.register("figure") { -> PageFeatureType(rl("figure"), FigureFeature.CODEC) } + val RECIPE = REGISTRAR.register("recipe") { -> PageFeatureType(rl("figure"), RecipeFeature.CODEC) } } \ No newline at end of file From 557d77eed8f077e576f78756f9663b91423e28f1 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Sun, 10 May 2026 14:57:54 +0200 Subject: [PATCH 10/19] More progress towards dynamic, made DynamicRenderingHelper.kt --- .../client/gui/book/DynamicRenderingHelper.kt | 227 ++++++++++++++++++ .../impl/client/gui/book/EntryScreen.kt | 172 +------------ .../thavma/impl/common/book/FigureFeature.kt | 6 +- ...TextFeature.kt => FormattedTextFeature.kt} | 12 +- .../thavma/impl/common/book/PageFeature.kt | 2 +- .../impl/common/book/ParagraphFeature.kt | 2 +- .../thavma/impl/common/book/RecipeFeature.kt | 2 +- .../thavma/impl/common/book/TitleFeature.kt | 2 +- 8 files changed, 249 insertions(+), 176 deletions(-) create mode 100644 src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt rename src/main/java/me/alegian/thavma/impl/common/book/{PlainTextFeature.kt => FormattedTextFeature.kt} (64%) diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt new file mode 100644 index 00000000..8a5012f1 --- /dev/null +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt @@ -0,0 +1,227 @@ +package me.alegian.thavma.impl.client.gui.book + +import me.alegian.thavma.impl.common.book.FigureFeature +import me.alegian.thavma.impl.common.book.FormattedTextFeature +import me.alegian.thavma.impl.common.book.PageFeature +import me.alegian.thavma.impl.common.book.ParagraphFeature +import net.minecraft.network.chat.Style +import kotlin.collections.plusAssign + +/** + * Return a list of PageFeatures containing a list of lines - FormattedText + * or possibly a Figure with just the image and its processed caption - FormattedText, + * so that they all fit on their + * respective pages (represented by list indices) + */ +fun spliceParagraphOrFigure( + input: PageFeature, maxPageHeight: Int, + currentHeight: Int +): List { + val result = mutableListOf() + + if (input is ParagraphFeature) { + // how many lines fit in the current page + val lineHeight = input.LINE_HEIGHT + + // so that we get at least 2 lines at the end of the first page + val linesRemainingAtStart: Int = (maxPageHeight - currentHeight) / lineHeight + + // so that we get at least 2 lines at the start of the last page + // (making use of rounding down when dividing integers) + val numOfLinesCoveringFullPages: Int = + (input.renderedHeight - linesRemainingAtStart * lineHeight) / maxPageHeight / lineHeight + val linesCroppingOutAtEnd: Int = + (input.renderedHeight - linesRemainingAtStart * lineHeight - numOfLinesCoveringFullPages * lineHeight) / lineHeight + + val maxLinesPerPage = maxPageHeight / lineHeight + val numOfFullPagesCovered = numOfLinesCoveringFullPages / maxLinesPerPage + val lines = input.font.splitter.splitLines(input.text, input.pageWidth, Style.EMPTY) + + when { + lines.size == 1 && currentHeight + lineHeight <= maxPageHeight -> result += FormattedTextFeature(lines) + + lines.size == 1 -> result += listOf( + FormattedTextFeature(listOf()), + FormattedTextFeature(lines) + ) + + linesRemainingAtStart != 1 && linesCroppingOutAtEnd != 1 -> { + // separate into "underhang", middle and overhang + val start = lines.slice(0 until linesRemainingAtStart) + result += FormattedTextFeature(start) + + for (i in 0 until numOfFullPagesCovered) { + val middle = + lines.slice(linesRemainingAtStart + i * maxLinesPerPage until linesRemainingAtStart + (i + 1) * maxLinesPerPage) + result += FormattedTextFeature(middle) + } + + val end = + lines.slice(lines.size - 1 - linesCroppingOutAtEnd until lines.size - 1) + result += FormattedTextFeature(end) + } + + else -> { + // add an empty paragraph to current page and continue on the next + result += FormattedTextFeature(listOf()) + for (i in 0 until numOfFullPagesCovered) { + val page = lines.slice(i * maxLinesPerPage until (i + 1) * maxLinesPerPage) + result += FormattedTextFeature(page) + } + val finish = + lines.slice(numOfFullPagesCovered * maxLinesPerPage until lines.size) + result += FormattedTextFeature(finish) + } + } + return result + } + + if (input is FigureFeature) { + with(input) { + when { + caption == null && currentHeight + textureHeight <= maxPageHeight -> result += input + caption == null -> { + result += listOf() + result += this + } + + caption != null && currentHeight + textureHeight <= maxPageHeight -> { + result += FigureFeature(image, null) + result.addAll( + spliceParagraphOrFigure( + ParagraphFeature(caption), + maxPageHeight, + currentHeight + textureHeight + ) + ) + } + + caption != null -> { + result += FormattedTextFeature(listOf()) + result += FigureFeature(image, null) + result.addAll(spliceParagraphOrFigure(ParagraphFeature(caption), maxPageHeight, textureHeight)) + } + } + } + return result + } + throw IllegalArgumentException("The supplied input $input is not a Paragraph or Figure") +} + +/** + * Returns a list of lists of features where every index represents a page. + * Features in the same list belong together on one page. + */ +fun pagifyFeatures(features: List, maxHeight: Int): List> { + // maxHeight is height of background texture minus padding (32 top 42 bottom) + //val maxHeight = this@EntryScreen.height - 74 + val partition = features.partition { !it.mustOccupySetPage } + val pages = mutableListOf>() + val buffer = mutableListOf() + fun currentHeight() = buffer.sumOf { it.renderedHeight } + + // deal with elements without predetermined order (bulk of the logic) + for (feature in partition.first) { + with(feature) { + when { + (this !is ParagraphFeature && this !is FigureFeature) && renderedHeight > maxHeight -> throw IllegalArgumentException( + "The size of the element ${this::class.simpleName} is too large at $renderedHeight while allowed $maxHeight." + ) + + coversOneWholePage -> { + if (buffer.isNotEmpty()) { + pages += buffer + buffer.clear() + } + pages += listOf(this) + } + + mustStartPage -> { + if (buffer.isNotEmpty()) { + pages += buffer + buffer.clear() + } + if (this is ParagraphFeature || this is FigureFeature) { + val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight()) + if (processed.size == 1) buffer += processed.first() + // only need to check this much since buffer is empty (starts page) + // applies to both Paragraph and Figure + else { + processed.slice(0 until processed.size - 1).forEach { pages += listOf(it) } + buffer += processed.last() + } + } else buffer += this + } + + (this is ParagraphFeature || this is FigureFeature) -> { + val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight()) + if (processed.size < 2) buffer += processed.first() + else if (this is ParagraphFeature) { + buffer += processed.first() + pages += buffer + buffer.clear() + if (processed.size > 2) processed.slice(1 until processed.size - 1).forEach { pages += listOf(it) } + buffer += processed.last() + } else { + // we are dealing with figure features now + when { + // Size 1 is already taken care of above. The result is at least size 2. + // If the first thing in a processed Figure is a FormattedTextFeature, + // it's an empty one to signify end of page. Size 2 means + // there is only this emptiness + image. + processed.first() is FormattedTextFeature && processed.size == 2 -> { + pages += buffer + buffer.clear() + buffer += processed.last() + } + // Now there are either exactly 2 elements and the first one is an image + // that fits in the page and the second one is text, + // or there are more than 2 elements and they can be anything. + else -> { + var i = 0 + // address possible page break + if (processed.first() is FormattedTextFeature) { + pages += buffer + buffer.clear() + i++ + } + // always takes the image next + buffer += processed[i] + // the check whether the start of the caption fits in the page with the + // image is done by the Paragraph half of the function, so just add + // all that remains + buffer += processed[i + 1] + if (processed.size > 2 + i) { + pages += buffer + buffer.clear() + for (j in 2 + i until processed.size - 1) { + pages += listOf(processed[j]) + } + buffer += processed[processed.size - 1] + } + } + } + } + } + + currentHeight() + renderedHeight <= maxHeight -> buffer += this + // the next check might be redundant: + buffer.isEmpty() -> throw IllegalArgumentException("The size of the element ${this::class.simpleName} is too large at $renderedHeight while allowed $maxHeight.") + else -> { + pages += buffer + buffer.clear() + buffer += this + } + } + } + } + + // add anything left over in the buffer + if (buffer.isNotEmpty()) pages += buffer + + // finally add features with pre-determined positions (cannot be paragraphs) + // these features have to be ordered correctly in the Research Entry builder + partition.second.groupBy { it.preferredPageIndex }.forEach { pages.add(it.key, it.value) } + + return pages +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt index 7336f5ee..85d57ccd 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt @@ -4,14 +4,11 @@ import me.alegian.thavma.impl.client.gui.layout.* import me.alegian.thavma.impl.client.texture.Texture import me.alegian.thavma.impl.common.book.Page import me.alegian.thavma.impl.common.book.PageFeature -import me.alegian.thavma.impl.common.book.ParagraphFeature -import me.alegian.thavma.impl.common.book.PlainTextFeature import me.alegian.thavma.impl.common.research.ResearchEntry import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.screens.Screen import net.minecraft.core.Holder import net.minecraft.network.chat.Component -import net.minecraft.network.chat.Style class EntryScreen(private val entry: Holder) : Screen(Component.literal("Book Entry")) { companion object { @@ -20,6 +17,9 @@ class EntryScreen(private val entry: Holder) : Screen(Component.l private var currentPage = 0 + // maxHeight is height of background texture minus padding (32 top 42 bottom) + var pages = pagifyFeatures(entry.value().pageFeatures, this.height - 74) + override fun init() { super.init() @@ -40,7 +40,7 @@ class EntryScreen(private val entry: Holder) : Screen(Component.l Row({ size = grow() }) { - initPage(entry.value().pages.getOrNull(currentPage)) + initPageFeatures(pages.getOrNull(currentPage)) if (currentPage != 0) { Box({ width = fixed(PageTurningWidget.LEFT_TEXTURE.width) @@ -64,8 +64,8 @@ class EntryScreen(private val entry: Holder) : Screen(Component.l Row({ size = grow() }) { - initPage(entry.value().pages.getOrNull(currentPage + 1)) - if (entry.value().pages.getOrNull(currentPage + 2) != null) { + initPageFeatures(pages.getOrNull(currentPage + 1)) + if (pages.getOrNull(currentPage + 2) != null) { Box({ width = fixed(PageTurningWidget.RIGHT_TEXTURE.width) height = fixed(PageTurningWidget.RIGHT_TEXTURE.height) @@ -107,163 +107,9 @@ class EntryScreen(private val entry: Holder) : Screen(Component.l val renderer = DynamicFeaturesRenderer - private fun initPageFeatures(features: List) { - renderer.initPageFeatures(this, features, currentPage) - } - - - /** - * Return a list of plain text features based on a paragraph so that they fit on their - * respective pages (represented by list idices) - */ - private fun spliceParagraph( - paragraph: ParagraphFeature, maxPageHeight: Int, - currentHeight: Int - ): List { - - val result = mutableListOf() - // how many lines fit in the current page - val lineHeight = paragraph.LINE_HEIGHT - - // so that we get at least 2 lines at the end of the first page - val linesRemainingAtStart: Int = (maxPageHeight - currentHeight) / lineHeight - - // so that we get at least 2 lines at the start of the last page - // (making use of rounding down when dividing integers) - val numOfLinesCoveringFullPages: Int = - (paragraph.renderedHeight - linesRemainingAtStart * lineHeight) / maxPageHeight / lineHeight - val linesCroppingOutAtEnd: Int = - (paragraph.renderedHeight - linesRemainingAtStart * lineHeight - numOfLinesCoveringFullPages * lineHeight) / lineHeight - - val maxLinesPerPage = maxPageHeight / lineHeight - val numOfFullPagesCovered = numOfLinesCoveringFullPages / maxLinesPerPage - val lines = paragraph.font.splitter.splitLines(paragraph.text, paragraph.pageWidth, Style.EMPTY) - - when { - lines.size == 1 && currentHeight + lineHeight <= maxPageHeight -> result += PlainTextFeature( - Component.literal( - lines[0].string - ) - ) - - lines.size == 1 -> result += listOf( - PlainTextFeature(Component.literal("")), - PlainTextFeature(Component.literal(lines[0].string)) - ) - - linesRemainingAtStart >= 2 && linesCroppingOutAtEnd != 1 -> { - // separate into "underhang", middle and overhang - val start = lines.slice(0 until linesRemainingAtStart).joinToString(" ") { it.string } - result += PlainTextFeature(Component.literal(start)) - - for (i in 0 until numOfFullPagesCovered) { - val middle = - lines.slice(linesRemainingAtStart + i * maxLinesPerPage until linesRemainingAtStart + (i + 1) * maxLinesPerPage) - .joinToString(" ") { it.string } - result += PlainTextFeature(Component.literal(middle)) - } - - val end = - lines.slice(lines.size - 1 - linesCroppingOutAtEnd until lines.size - 1).joinToString(" ") { it.string } - result += PlainTextFeature(Component.literal(end)) - } - - else -> { - // add an empty paragraph to current page and continue on the next - result += PlainTextFeature(Component.literal("")) - for (i in 0 until numOfFullPagesCovered) { - val page = lines.slice(i * maxLinesPerPage until (i + 1) * maxLinesPerPage).joinToString(" ") { it.string } - result += PlainTextFeature(Component.literal(page)) - } - val finish = - lines.slice(numOfFullPagesCovered * maxLinesPerPage until lines.size).joinToString(" ") { it.string } - result += PlainTextFeature(Component.literal(finish)) - } - } - - return result - } - - /** - * Returns a list of lists of features where every index represents a page. - * Features in the same list belong together on one page. - */ - private fun pagifyFeatures(features: List): List> { - val maxHeight = this@EntryScreen.height - val partition = features.partition { !it.mustOccupySetPage } - val result = mutableListOf>() - val buffer = mutableListOf() - fun currentHeight() = buffer.sumOf { it.renderedHeight } - - for (feature in partition.first) { - - with(feature) { - when { - this !is ParagraphFeature && renderedHeight > maxHeight -> throw IllegalArgumentException("The size of the element ${this::class.simpleName} is too large at $renderedHeight while allowed $maxHeight.") - - coversWholePage -> { - if (buffer.isNotEmpty()) { - result += buffer - buffer.clear() - } - result += listOf(this) - } - - mustStartPage -> { - if (buffer.isNotEmpty()) { - result += buffer - buffer.clear() - } - if (this is ParagraphFeature) { - val processed = spliceParagraph(this, maxHeight, currentHeight()) - if (processed.size == 1) buffer += processed.first() - //only need to check this much since buffer is empty (starts page) - else { - processed.slice(0 until processed.size - 1).forEach { result += listOf(it) } - buffer += processed.last() - } - } else buffer += this - } - - this is ParagraphFeature -> { - val processed = spliceParagraph(this, maxHeight, currentHeight()) - if (processed.size == 1) buffer += processed.first() - // need to check one more value so as not to append null to result - else if (processed.size == 2) { - buffer += processed.first() - result += buffer - buffer.clear() - buffer += processed.last() - } else { - buffer += processed.first() - result += buffer - buffer.clear() - processed.slice(1 until processed.size - 1).forEach { result += listOf(it) } - buffer += processed.last() - } - } - - currentHeight() + renderedHeight <= maxHeight -> buffer += this - // the next check might be redundant: - buffer.isEmpty() -> throw IllegalArgumentException("The size of the element ${this::class.simpleName} is too large at $renderedHeight while allowed $maxHeight.") - else -> { - result += buffer - buffer.clear() - buffer += this - } - } - - } - - } - - if (buffer.isNotEmpty()) result += buffer - - // finally add features with pre-determined positions (cannot be paragraphs) - // these features have to be ordered correctly in the Research Entry builder - partition.second.groupBy { it.preferredPageIndex }.forEach { result.add(it.key, it.value) } - - return result + private fun initPageFeatures(features: List?) { + if (features != null) + renderer.initPageFeatures(this, features, currentPage) } override fun isPauseScreen() = false diff --git a/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt index 7f600100..16270e9a 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt @@ -15,7 +15,7 @@ class FigureFeature(val image: Texture, val caption: Component?, override val mu override val type: PageFeatureType<*> get() = PageFeatureTypes.FIGURE.get() - override val coversWholePage = false + override val coversOneWholePage = false override val mustOccupySetPage = false override val preferredPageIndex: Int get() = 1 @@ -29,8 +29,10 @@ class FigureFeature(val image: Texture, val caption: Component?, override val mu // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor val LINE_HEIGHT = font.lineHeight + 2 val lines = font.splitter.splitLines(caption, pageWidth - 25, Style.EMPTY) - override val renderedHeight = image.canvasHeight + LINE_HEIGHT * lines.size + LINE_HEIGHT * 4 / 3 + val textureHeight = image.height + val captionHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 4 / 3 + override val renderedHeight = textureHeight + captionHeight companion object { val CODEC = RecordCodecBuilder.mapCodec { builder -> diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PlainTextFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt similarity index 64% rename from src/main/java/me/alegian/thavma/impl/common/book/PlainTextFeature.kt rename to src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt index 313e2b0f..2a1f4e4a 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/PlainTextFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt @@ -3,17 +3,15 @@ package me.alegian.thavma.impl.common.book import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes import net.minecraft.client.Minecraft import net.minecraft.client.gui.Font -import net.minecraft.network.chat.Component -import net.minecraft.network.chat.Style +import net.minecraft.network.chat.FormattedText /** * Is only ever used in EntryScreen.kt to break ParagraphFeatures into segments * so that they fit in their respective pages */ - -class PlainTextFeature(val text: Component, val font: Font = Minecraft.getInstance().font): PageFeature { - override val coversWholePage: Boolean +class FormattedTextFeature(val text: List, val font: Font = Minecraft.getInstance().font) : PageFeature { + override val coversOneWholePage: Boolean get() = false override val mustStartPage: Boolean get() = false @@ -26,7 +24,7 @@ class PlainTextFeature(val text: Component, val font: Font = Minecraft.getInstan // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor val LINE_HEIGHT = font.lineHeight + 2 - val lines = font.splitter.splitLines(text, pageWidth - 25, Style.EMPTY) + //val lines = font.splitter.splitLines(text, pageWidth - 25, Style.EMPTY) - override val renderedHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 2 / 3 + override val renderedHeight = LINE_HEIGHT * text.size + LINE_HEIGHT * 2 / 3 } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt index 841afb40..aac3d5af 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt @@ -3,7 +3,7 @@ package me.alegian.thavma.impl.common.book import me.alegian.thavma.impl.init.registries.T7Registries interface PageFeature { - val coversWholePage: Boolean + val coversOneWholePage: Boolean val mustStartPage: Boolean val mustOccupySetPage: Boolean val preferredPageIndex: Int diff --git a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt index ddbb050c..a8315a11 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt @@ -18,7 +18,7 @@ class ParagraphFeature(val text: Component, override val mustStartPage: Boolean override val type: PageFeatureType<*> get() = PageFeatureTypes.PARAGRAPH.get() - override val coversWholePage = false + override val coversOneWholePage = false override val mustOccupySetPage = false val font: Font = Minecraft.getInstance().font diff --git a/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt index a711d5aa..ab76dce8 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt @@ -10,7 +10,7 @@ class RecipeFeature( override val type: PageFeatureType<*> get() = PageFeatureTypes.RECIPE.get() - override val coversWholePage: Boolean + override val coversOneWholePage: Boolean get() = true override val mustStartPage: Boolean get() = true diff --git a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt index a23e701d..44eca10e 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt @@ -13,7 +13,7 @@ class TitleFeature(val text: Component, ): PageFeature { override val type: PageFeatureType<*> get() = PageFeatureTypes.TITLE.get() - override val coversWholePage = false + override val coversOneWholePage = false override val mustStartPage = false override val mustOccupySetPage = false From 2f1f2332f4db079fb45d94adcc1961942aef4713 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Sun, 10 May 2026 21:13:04 +0200 Subject: [PATCH 11/19] Theory of dynamic rendering finished, time to test --- .../gui/book/DynamicFeaturesRenderer.kt | 99 ++++++++++- .../client/gui/book/DynamicRenderingHelper.kt | 62 +++---- .../impl/client/gui/book/EntryScreen.kt | 7 +- .../client/gui/book/PageFeatureRenderer.kt | 2 +- .../thavma/impl/common/book/FigureFeature.kt | 18 +- .../impl/common/book/FormattedTextFeature.kt | 4 +- .../impl/common/book/ParagraphFeature.kt | 13 +- .../thavma/impl/common/book/RecipeFeature.kt | 19 +- .../thavma/impl/common/book/TitleFeature.kt | 13 +- .../T7DatapackBuiltinEntriesProvider.kt | 164 ++++++++++++++---- .../init/data/providers/T7LanguageProvider.kt | 130 ++++++++++---- 11 files changed, 394 insertions(+), 137 deletions(-) diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt index 1cd2041b..a176518b 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt @@ -1,18 +1,109 @@ package me.alegian.thavma.impl.client.gui.book +import me.alegian.thavma.impl.client.gui.layout.* import me.alegian.thavma.impl.client.texture.Texture +import me.alegian.thavma.impl.client.util.drawCenteredString +import me.alegian.thavma.impl.client.util.drawString +import me.alegian.thavma.impl.client.util.translateXY +import me.alegian.thavma.impl.client.util.usePose +import me.alegian.thavma.impl.common.book.FigureFeature +import me.alegian.thavma.impl.common.book.FormattedTextFeature import me.alegian.thavma.impl.common.book.PageFeature -import me.alegian.thavma.impl.common.book.ParagraphFeature +import me.alegian.thavma.impl.common.book.TitleFeature import net.minecraft.client.Minecraft object DynamicFeaturesRenderer : PageFeatureRenderer { private val SEPARATOR = Texture("gui/book/separator", 128, 16, 128, 16) - override fun initPageFeatures(screen: EntryScreen, features: List, currentPage: Int) { + override fun initPageFeatures(screen: EntryScreen, features: List) { + + Column({ + size = grow() + gap = 4 + }) { + for (feature in features) { + when (feature) { + is TitleFeature -> { + Title(feature) + Separator() + } + + is FormattedTextFeature -> Row({ + size = grow() + }) { + relativeRenderable { guiGraphics, _, _, _ -> + guiGraphics.usePose { + for (line in feature.text) { + guiGraphics.drawString(feature.font, line) + translateXY(0, feature.font.lineHeight) + } + translateXY(0, feature.font.lineHeight * 2 / 3) + } + } + } + is FigureFeature -> Image(feature) + } + } + } + } + +// Column({ +// size = grow() +// gap = 4 +// }) { +// if (page.title != null) { +// Title(page.title) +// Separator() +// } +// Row({ +// size = grow() +// }) { +// relativeRenderable { guiGraphics, _, _, _ -> +// guiGraphics.usePose { +// for (paragraph in page.paragraphs) { +// for (line in font.splitter.splitLines(paragraph, size.x.toInt(), Style.EMPTY)) { +// guiGraphics.drawString(Minecraft.getInstance().font, Component.literal(line.string)) +// translateXY(0, LINE_HEIGHT) +// } +// translateXY(0, LINE_HEIGHT * 2 / 3) +// } +// } +// } +// } +// } +// } + + private fun Separator() { + Row({ + width = grow() + alignMain = Alignment.CENTER + }) { + TextureBox(SEPARATOR) {} + } + } + + private fun Title(title: TitleFeature) { val font = Minecraft.getInstance().font - val LINE_HEIGHT = font.lineHeight + 2 + Row({ + width = grow() + height = fixed(font.lineHeight) + }) { + relativeRenderable { guiGraphics, _, _, _ -> + guiGraphics.drawCenteredString(font, title.text, size.x / 2) + } + } + } + private fun Image(figure: FigureFeature) { + Row({ + width = grow() + height = fixed(figure.textureHeight) + }) { + TextureBox(figure.image) {} + } + } } -} \ No newline at end of file + + diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt index 8a5012f1..4c1d2eb9 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt @@ -4,12 +4,10 @@ import me.alegian.thavma.impl.common.book.FigureFeature import me.alegian.thavma.impl.common.book.FormattedTextFeature import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.book.ParagraphFeature -import net.minecraft.network.chat.Style -import kotlin.collections.plusAssign /** - * Return a list of PageFeatures containing a list of lines - FormattedText - * or possibly a Figure with just the image and its processed caption - FormattedText, + * Return a list of PageFeatures containing a list of lines - FormattedCharSequence + * or possibly a Figure with just the image and its processed caption - FormattedCharSequence, * so that they all fit on their * respective pages (represented by list indices) */ @@ -35,7 +33,7 @@ fun spliceParagraphOrFigure( val maxLinesPerPage = maxPageHeight / lineHeight val numOfFullPagesCovered = numOfLinesCoveringFullPages / maxLinesPerPage - val lines = input.font.splitter.splitLines(input.text, input.pageWidth, Style.EMPTY) + val lines = input.font.split(input.text, input.pageWidth) when { lines.size == 1 && currentHeight + lineHeight <= maxPageHeight -> result += FormattedTextFeature(lines) @@ -85,7 +83,7 @@ fun spliceParagraphOrFigure( result += this } - caption != null && currentHeight + textureHeight <= maxPageHeight -> { + currentHeight + textureHeight <= maxPageHeight -> { result += FigureFeature(image, null) result.addAll( spliceParagraphOrFigure( @@ -96,7 +94,7 @@ fun spliceParagraphOrFigure( ) } - caption != null -> { + else -> { result += FormattedTextFeature(listOf()) result += FigureFeature(image, null) result.addAll(spliceParagraphOrFigure(ParagraphFeature(caption), maxPageHeight, textureHeight)) @@ -119,6 +117,10 @@ fun pagifyFeatures(features: List, maxHeight: Int): List>() val buffer = mutableListOf() fun currentHeight() = buffer.sumOf { it.renderedHeight } + fun submitBufferAndClear() { + pages += buffer + buffer.clear() + } // deal with elements without predetermined order (bulk of the logic) for (feature in partition.first) { @@ -129,27 +131,32 @@ fun pagifyFeatures(features: List, maxHeight: Int): List { - if (buffer.isNotEmpty()) { - pages += buffer - buffer.clear() - } + if (buffer.isNotEmpty()) submitBufferAndClear() pages += listOf(this) } mustStartPage -> { - if (buffer.isNotEmpty()) { - pages += buffer - buffer.clear() - } + if (buffer.isNotEmpty()) submitBufferAndClear() if (this is ParagraphFeature || this is FigureFeature) { val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight()) - if (processed.size == 1) buffer += processed.first() + if (processed.size < 2) buffer += processed.first() // only need to check this much since buffer is empty (starts page) - // applies to both Paragraph and Figure - else { + else if (this is ParagraphFeature) { processed.slice(0 until processed.size - 1).forEach { pages += listOf(it) } buffer += processed.last() } + // we are dealing with figure features now + else { + buffer += processed.first() + buffer += processed[1] + if (processed.size > 2) { + submitBufferAndClear() + for (j in 2 until processed.size - 1) { + pages += listOf(processed[j]) + } + buffer += processed.last() + } + } } else buffer += this } @@ -158,8 +165,7 @@ fun pagifyFeatures(features: List, maxHeight: Int): List 2) processed.slice(1 until processed.size - 1).forEach { pages += listOf(it) } buffer += processed.last() } else { @@ -170,8 +176,7 @@ fun pagifyFeatures(features: List, maxHeight: Int): List { - pages += buffer - buffer.clear() + submitBufferAndClear() buffer += processed.last() } // Now there are either exactly 2 elements and the first one is an image @@ -181,8 +186,7 @@ fun pagifyFeatures(features: List, maxHeight: Int): List, maxHeight: Int): List 2 + i) { - pages += buffer - buffer.clear() + submitBufferAndClear() for (j in 2 + i until processed.size - 1) { pages += listOf(processed[j]) } - buffer += processed[processed.size - 1] + buffer += processed.last() } } } @@ -208,8 +211,7 @@ fun pagifyFeatures(features: List, maxHeight: Int): List throw IllegalArgumentException("The size of the element ${this::class.simpleName} is too large at $renderedHeight while allowed $maxHeight.") else -> { - pages += buffer - buffer.clear() + submitBufferAndClear() buffer += this } } @@ -217,7 +219,7 @@ fun pagifyFeatures(features: List, maxHeight: Int): List) : Screen(Component.l // renderer.initPageFeatures(this, features, currentPage) // } - val renderer = DynamicFeaturesRenderer private fun initPageFeatures(features: List?) { - if (features != null) - renderer.initPageFeatures(this, features, currentPage) + if (features != null) { + val renderer = DynamicFeaturesRenderer + renderer.initPageFeatures(this, features) + } } override fun isPauseScreen() = false diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt index 3d6b518a..630eabc0 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt @@ -3,5 +3,5 @@ package me.alegian.thavma.impl.client.gui.book import me.alegian.thavma.impl.common.book.PageFeature interface PageFeatureRenderer{ - fun initPageFeatures(screen: EntryScreen, features: List, currentPage: Int) + fun initPageFeatures(screen: EntryScreen, features: List) } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt index 16270e9a..dade09b5 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt @@ -11,14 +11,13 @@ import net.minecraft.network.chat.ComponentSerialization import net.minecraft.network.chat.Style import java.util.Optional -class FigureFeature(val image: Texture, val caption: Component?, override val mustStartPage: Boolean = false) : PageFeature { +class FigureFeature(val image: Texture, val caption: Component?, override val mustStartPage: Boolean = false, override val mustOccupySetPage: Boolean = false, override val preferredPageIndex: Int = 1) : PageFeature { override val type: PageFeatureType<*> get() = PageFeatureTypes.FIGURE.get() override val coversOneWholePage = false - override val mustOccupySetPage = false - override val preferredPageIndex: Int - get() = 1 + + override val pageWidth: Int get() = 256 @@ -28,7 +27,8 @@ class FigureFeature(val image: Texture, val caption: Component?, override val mu // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor val LINE_HEIGHT = font.lineHeight + 2 - val lines = font.splitter.splitLines(caption, pageWidth - 25, Style.EMPTY) + + val lines = if (caption != null) font.splitter.splitLines(caption, pageWidth - 25, Style.EMPTY) else listOf() val textureHeight = image.height val captionHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 4 / 3 @@ -39,9 +39,11 @@ class FigureFeature(val image: Texture, val caption: Component?, override val mu builder.group( Texture.CODEC.fieldOf("image").forGetter(FigureFeature::image), ComponentSerialization.CODEC.optionalFieldOf("caption").forGetter { p -> Optional.ofNullable(p.caption) }, - Codec.BOOL.optionalFieldOf("starts_page", false).forGetter(FigureFeature::mustStartPage) - ).apply(builder) { img, cap, start -> - FigureFeature(img, cap.orElse(null), start) + Codec.BOOL.optionalFieldOf("starts_page", false).forGetter(FigureFeature::mustStartPage), + Codec.BOOL.optionalFieldOf("has_set_page", false).forGetter(FigureFeature::mustOccupySetPage), + Codec.INT.optionalFieldOf("preferred_page", 1).forGetter(FigureFeature::preferredPageIndex) + ).apply(builder) { img, cap, start, index, pref -> + FigureFeature(img, cap.orElse(null), start, index, pref) } } diff --git a/src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt index 2a1f4e4a..f6b8d472 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt @@ -3,14 +3,14 @@ package me.alegian.thavma.impl.common.book import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes import net.minecraft.client.Minecraft import net.minecraft.client.gui.Font -import net.minecraft.network.chat.FormattedText +import net.minecraft.util.FormattedCharSequence /** * Is only ever used in EntryScreen.kt to break ParagraphFeatures into segments * so that they fit in their respective pages */ -class FormattedTextFeature(val text: List, val font: Font = Minecraft.getInstance().font) : PageFeature { +class FormattedTextFeature(val text: List, val font: Font = Minecraft.getInstance().font) : PageFeature { override val coversOneWholePage: Boolean get() = false override val mustStartPage: Boolean diff --git a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt index a8315a11..7edfee87 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt @@ -2,24 +2,19 @@ package me.alegian.thavma.impl.common.book import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder -import me.alegian.thavma.impl.common.book.PageFeature -import me.alegian.thavma.impl.init.data.worldgen.tree.trunk.SilverwoodTrunkPlacer import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes import net.minecraft.client.Minecraft import net.minecraft.client.gui.Font import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentSerialization import net.minecraft.network.chat.Style -import net.minecraft.resources.ResourceLocation -import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer.trunkPlacerParts -import java.util.Optional -class ParagraphFeature(val text: Component, override val mustStartPage: Boolean = false) : PageFeature { +class ParagraphFeature(val text: Component, override val mustStartPage: Boolean = false, override val mustOccupySetPage: Boolean = false, override val preferredPageIndex: Int = 1) : PageFeature { override val type: PageFeatureType<*> get() = PageFeatureTypes.PARAGRAPH.get() override val coversOneWholePage = false - override val mustOccupySetPage = false + val font: Font = Minecraft.getInstance().font @@ -34,7 +29,9 @@ class ParagraphFeature(val text: Component, override val mustStartPage: Boolean builder.group( ComponentSerialization.CODEC.fieldOf("text").forGetter(ParagraphFeature::text), //ResourceLocation.CODEC.optionalFieldOf("font", ResourceLocation.withDefaultNamespace("default")).forGetter(ParagraphFeature::font), - Codec.BOOL.optionalFieldOf("starts_page", false).forGetter(ParagraphFeature::mustStartPage) + Codec.BOOL.optionalFieldOf("starts_page", false).forGetter(ParagraphFeature::mustStartPage), + Codec.BOOL.optionalFieldOf("has_set_page", false).forGetter(ParagraphFeature::mustOccupySetPage), + Codec.INT.optionalFieldOf("preferred_page", 1).forGetter(ParagraphFeature::preferredPageIndex) ).apply(builder, ::ParagraphFeature) } diff --git a/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt index ab76dce8..fdcfa3b0 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt @@ -1,30 +1,27 @@ package me.alegian.thavma.impl.common.book +import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes import net.minecraft.resources.ResourceLocation class RecipeFeature( - val recipeRL: ResourceLocation, -) : PageFeature { + val recipeRL: ResourceLocation, override val coversOneWholePage: Boolean = true, override val mustStartPage: Boolean = true, override val mustOccupySetPage: Boolean = true, + override val preferredPageIndex: Int = 1) : PageFeature { override val type: PageFeatureType<*> get() = PageFeatureTypes.RECIPE.get() - override val coversOneWholePage: Boolean - get() = true - override val mustStartPage: Boolean - get() = true - override val mustOccupySetPage: Boolean - get() = true - - override val renderedHeight: Int - get() = TODO("Not yet implemented") + get() = 96 companion object { val CODEC = RecordCodecBuilder.mapCodec { builder -> builder.group( ResourceLocation.CODEC.fieldOf("recipeRL").forGetter(RecipeFeature::recipeRL), + Codec.BOOL.optionalFieldOf("covers_whole_page", true).forGetter(RecipeFeature::coversOneWholePage), + Codec.BOOL.optionalFieldOf("starts_page", true).forGetter(RecipeFeature::mustStartPage), + Codec.BOOL.optionalFieldOf("has_set_page", true).forGetter(RecipeFeature::mustOccupySetPage), + Codec.INT.optionalFieldOf("preferred_page", 1).forGetter(RecipeFeature::preferredPageIndex) ).apply(builder, ::RecipeFeature) } diff --git a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt index 44eca10e..11304387 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt @@ -1,5 +1,6 @@ package me.alegian.thavma.impl.common.book +import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes @@ -9,13 +10,14 @@ import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentSerialization import net.minecraft.network.chat.Style -class TitleFeature(val text: Component, ): PageFeature { +class TitleFeature(val text: Component, override val mustStartPage: Boolean = false, override val mustOccupySetPage: Boolean = false, + override val preferredPageIndex: Int = 1): PageFeature { override val type: PageFeatureType<*> get() = PageFeatureTypes.TITLE.get() override val coversOneWholePage = false - override val mustStartPage = false - override val mustOccupySetPage = false + + val font: Font = Minecraft.getInstance().font // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor @@ -27,7 +29,10 @@ class TitleFeature(val text: Component, ): PageFeature { companion object { val CODEC = RecordCodecBuilder.mapCodec { builder -> builder.group( - ComponentSerialization.CODEC.fieldOf("text").forGetter(TitleFeature::text) + ComponentSerialization.CODEC.fieldOf("text").forGetter(TitleFeature::text), + Codec.BOOL.optionalFieldOf("starts_page", false).forGetter(TitleFeature::mustStartPage), + Codec.BOOL.optionalFieldOf("has_set_page", false).forGetter(TitleFeature::mustStartPage), + Codec.INT.optionalFieldOf("preferred_page", 1).forGetter(TitleFeature::preferredPageIndex) ).apply(builder, ::TitleFeature) } diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index 329c7850..670f02bc 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -1,12 +1,18 @@ package me.alegian.thavma.impl.init.data.providers import me.alegian.thavma.impl.Thavma +import me.alegian.thavma.impl.client.texture.Texture import me.alegian.thavma.impl.common.aspect.Aspect import me.alegian.thavma.impl.common.book.CraftingPage import me.alegian.thavma.impl.common.book.DynamicPage +import me.alegian.thavma.impl.common.book.FigureFeature import me.alegian.thavma.impl.common.book.Page import me.alegian.thavma.impl.common.book.PageFeature +import me.alegian.thavma.impl.common.book.PageFeatureType +import me.alegian.thavma.impl.common.book.ParagraphFeature +import me.alegian.thavma.impl.common.book.RecipeFeature import me.alegian.thavma.impl.common.book.TextPage +import me.alegian.thavma.impl.common.book.TitleFeature import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION import me.alegian.thavma.impl.common.research.ResearchCategory import me.alegian.thavma.impl.common.research.ResearchEntry @@ -35,6 +41,7 @@ import net.minecraft.data.PackOutput import net.minecraft.data.worldgen.BootstrapContext import net.minecraft.network.chat.Component import net.minecraft.resources.ResourceKey +import net.minecraft.resources.ResourceLocation import net.minecraft.tags.ItemTags import net.minecraft.world.entity.EquipmentSlotGroup import net.minecraft.world.item.ItemStack @@ -54,7 +61,8 @@ import org.joml.Vector2i import java.util.* import java.util.concurrent.CompletableFuture -class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: CompletableFuture) : DatapackBuiltinEntriesProvider(output, registries, builder, setOf(Thavma.MODID)) { +class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: CompletableFuture) : + DatapackBuiltinEntriesProvider(output, registries, builder, setOf(Thavma.MODID)) { companion object { private val builder: RegistrySetBuilder = RegistrySetBuilder() .add(Registries.CONFIGURED_FEATURE) { ctx -> @@ -130,19 +138,37 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab // .build(ctx) ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, -3), false, Items.TURTLE_HELMET.defaultInstance) - .research() - .addPage(simpleTextPage(2, true)) - .addPage(simpleTextPage(2, true)) - .addPage(simpleTextPage(2, true)) - .defaultKnown() - .build(ctx) + .research() + .addPageFeature(makeTitleFeature(true, false)) + .addPageFeature(makeParagraphFeature(false, false)) + .addPageFeature(makeParagraphFeature()) + .addPageFeature(makeTitleFeature()) + .addPageFeature(makeParagraphFeature(true)) + .addPageFeature(makeTitleFeature(true)) + .addPageFeature(makeTitleFeature(true, true, 0)) + .addPageFeature(makeParagraphFeature()) +// .addPage(simpleTextPage(2, true)) +// .addPage(simpleTextPage(2, true)) +// .addPage(simpleTextPage(2, true)) + .defaultKnown() + .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.TREES, Vector2i(0, -3), false, T7Blocks.GREATWOOD_LOG.get().asItem().defaultInstance) + ResearchEntryBuilder( + ResearchEntries.Thavma.TREES, + Vector2i(0, -3), + false, + T7Blocks.GREATWOOD_LOG.get().asItem().defaultInstance + ) .research(lockedAspect(2, 0, Aspects.HERBA), lockedAspect(2, 4, Aspects.HERBA)) .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.ORES, Vector2i(2, -4), false, T7Items.SHARDS[Aspects.AQUA]!!.get().defaultInstance) + ResearchEntryBuilder( + ResearchEntries.Thavma.ORES, + Vector2i(2, -4), + false, + T7Items.SHARDS[Aspects.AQUA]!!.get().defaultInstance + ) .research(lockedAspect(2, 0, Aspects.TERRA), lockedAspect(2, 4, Aspects.AETHER)) .addChild(ResearchEntries.Thavma.ARCANE_LENS) .build(ctx) @@ -163,27 +189,57 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab // .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) // .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_PROFICIENCY, Vector2i(-1, -1), false, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) + ResearchEntryBuilder( + ResearchEntries.Thavma.RESEARCH_PROFICIENCY, + Vector2i(-1, -1), + false, + T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance + ) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.ALCHEMY, Vector2i(-2, 2), true, T7Blocks.CRUCIBLE.get().asItem().defaultInstance) + ResearchEntryBuilder( + ResearchEntries.Thavma.ALCHEMY, + Vector2i(-2, 2), + true, + T7Blocks.CRUCIBLE.get().asItem().defaultInstance + ) .research(lockedAspect(2, 0, Aspects.AQUA), lockedAspect(2, 4, Aspects.ALKIMIA)) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.WANDS, Vector2i(-2, 4), true, T7Items.wandOrThrow(WandPlatingMaterials.THAVMITE.get(), WandCoreMaterials.SILVERWOOD.get()).defaultInstance) + ResearchEntryBuilder( + ResearchEntries.Thavma.WANDS, + Vector2i(-2, 4), + true, + T7Items.wandOrThrow(WandPlatingMaterials.THAVMITE.get(), WandCoreMaterials.SILVERWOOD.get()).defaultInstance + ) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.INSTRUMENTUM)) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.INFUSION, Vector2i(2, 2), true, T7Blocks.MATRIX.get().asItem().defaultInstance) + ResearchEntryBuilder( + ResearchEntries.Thavma.INFUSION, + Vector2i(2, 2), + true, + T7Blocks.MATRIX.get().asItem().defaultInstance + ) .research(lockedAspect(2, 0, Aspects.TERRA), lockedAspect(2, 4, Aspects.AETHER)) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.TECHNOLOGY, Vector2i(2, 4), true, T7Items.GOGGLES.get().defaultInstance) + ResearchEntryBuilder( + ResearchEntries.Thavma.TECHNOLOGY, + Vector2i(2, 4), + true, + T7Items.GOGGLES.get().defaultInstance + ) .research(lockedAspect(2, 0, Aspects.INSTRUMENTUM), lockedAspect(2, 4, Aspects.CIVILIS)) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Alchemy.ALCHEMY, Vector2i(0, 0), false, T7Blocks.CRUCIBLE.get().asItem().defaultInstance) + ResearchEntryBuilder( + ResearchEntries.Alchemy.ALCHEMY, + Vector2i(0, 0), + false, + T7Blocks.CRUCIBLE.get().asItem().defaultInstance + ) .research(lockedAspect(2, 0, Aspects.AQUA), lockedAspect(2, 4, Aspects.ALKIMIA)) .defaultKnown() .build(ctx) @@ -199,6 +255,7 @@ private class ResearchEntryBuilder( ) { private val children = mutableListOf>() private val pageFeatures = mutableListOf() + private val pages = mutableListOf() private val socketStates = mutableListOf() private var defaultKnown = false @@ -212,6 +269,11 @@ private class ResearchEntryBuilder( return this } + inline fun addPageFeature(crossinline makeFeature: (ResourceKey, Int) -> T): ResearchEntryBuilder { + pageFeatures.add(makeFeature(key, pageFeatures.filterIsInstance().size)) + return this + } + fun research(vararg states: SocketState): ResearchEntryBuilder { socketStates.addAll(states) return this @@ -227,11 +289,28 @@ private class ResearchEntryBuilder( val entryRegistry = ctx.lookup(T7DatapackRegistries.RESEARCH_ENTRY) val categoryHolder = categoryRegistry.getOrThrow(cat) val childrenHolders = children.map { entryRegistry.getOrThrow(it) } - ctx.register(key, ResearchEntry(categoryHolder, pos, preferX, childrenHolders, pageFeatures, icon, Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), socketStates, defaultKnown)) + ctx.register( + key, + ResearchEntry( + categoryHolder, + pos, + preferX, + childrenHolders, + pageFeatures, + icon, + Component.translatable(ResearchEntry.translationId(key)).withStyle(Rarity.UNCOMMON.styleModifier), + socketStates, + defaultKnown + ) + ) } } -private fun BootstrapContext.registerCategory(key: ResourceKey, icon: ItemStack, sortIndex: Float) { +private fun BootstrapContext.registerCategory( + key: ResourceKey, + icon: ItemStack, + sortIndex: Float +) { register(key, ResearchCategory(Component.translatable(ResearchCategory.translationId(key)), sortIndex, icon)) } @@ -245,27 +324,50 @@ private fun simpleTextPage(paragraphCount: Int, hasTitle: Boolean): (ResourceKey } } -private fun simpleDynamicPages(vararg features: PageFeature): (ResourceKey) -> List { +private fun makeParagraphFeature(mustStartPage: Boolean = false, mustOccupySetPage: Boolean = false, preferredPageIndex: Int = 1): (ResourceKey, Int) -> ParagraphFeature { + return { entryKey, paragraphIndex -> + val baseId = ResearchEntry.translationId(entryKey) + ParagraphFeature(Component.translatable(ParagraphFeature.translationId(baseId, paragraphIndex)), + mustStartPage, mustOccupySetPage, preferredPageIndex + ) + } +} - // tady prostě mám přístup k těm jednotlivejm features, z nich poskládám seznam - // dynamických stránek a ty renderuju pomocí zvláštního DynamicPageRenderer (TODO) - // zahrnout nejlépe všechnu tu logiku co mám v těch Features - // nebo taky vůbec ne? logika bude až v rendereru (nějakou zvláštní metodu tam na konec (třeba do companion objektu) - // kterou budu volat nahoře kterou se to rozkouskuje na části +private fun makeTitleFeature(mustStartPage: Boolean = false, mustOccupySetPage: Boolean = false, preferredPageIndex: Int = 1): (ResourceKey, Int) -> TitleFeature{ + return { entryKey, titleIndex -> + val baseId = ResearchEntry.translationId(entryKey) + TitleFeature(Component.translatable(TitleFeature.translationId(baseId, titleIndex)), + mustStartPage, mustOccupySetPage, preferredPageIndex + ) + } +} - return { entryKey -> - val baseId = ResearchEntry.translationId(entryKey) - DynamicPage( - features.toList() - ) +private fun makeFigureFeature(image: Texture, giveCaption: Boolean, mustStartPage: Boolean = false, mustOccupySetPage: Boolean = false, preferredPageIndex: Int = 1): (ResourceKey, Int) -> FigureFeature{ + return if (giveCaption) { entryKey, figureIndex -> + val baseId = ResearchEntry.translationId(entryKey) + FigureFeature(image, Component.translatable(FigureFeature.translationId(baseId, figureIndex)), + mustStartPage, mustOccupySetPage, preferredPageIndex + ) + } else { entryKey, figureIndex -> + FigureFeature(image, null, mustStartPage, mustOccupySetPage) + } +} - } +private fun makeRecipeFeature(recipeRL: ResourceLocation, coversOneWholePage: Boolean = true, mustStartPage: Boolean = true, mustOccupySetPage: Boolean = true, preferredPageIndex: Int = 1): (ResourceKey, Int) -> RecipeFeature{ + return { entryKey, titleIndex -> + RecipeFeature(recipeRL, coversOneWholePage, mustStartPage, mustOccupySetPage, + preferredPageIndex + ) + } } private fun simpleTitle(pageIndex: Int, baseId: String) = Component.translatable(TextPage.titleTranslationId(baseId, pageIndex)).withStyle(ChatFormatting.BOLD) -private fun simpleParagraphs(count: Int, pageIndex: Int, baseId: String) = List(count) { Component.translatable(TextPage.paragraphTranslationId(baseId, pageIndex, it)) } +private fun simpleParagraphs(count: Int, pageIndex: Int, baseId: String) = + List(count) { Component.translatable(TextPage.paragraphTranslationId(baseId, pageIndex, it)) } + +private fun lockedAspect(row: Int, col: Int, a: DeferredAspect) = + SocketState(Indices(row, col), a.get(), false, true) -private fun lockedAspect(row: Int, col: Int, a: DeferredAspect) = SocketState(Indices(row, col), a.get(), false, true) private fun broken(row: Int, col: Int) = SocketState(Indices(row, col), null, true, true) diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index 01f4ad8b..b71bf304 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -11,7 +11,10 @@ import me.alegian.thavma.impl.client.gui.tooltip.AspectClientTooltipComponent import me.alegian.thavma.impl.common.block.HungryChestBlock import me.alegian.thavma.impl.common.block.ResearchTableBlock import me.alegian.thavma.impl.common.block.WorkbenchBlock +import me.alegian.thavma.impl.common.book.FigureFeature +import me.alegian.thavma.impl.common.book.ParagraphFeature import me.alegian.thavma.impl.common.book.TextPage +import me.alegian.thavma.impl.common.book.TitleFeature import me.alegian.thavma.impl.common.recipe.translationId import me.alegian.thavma.impl.common.research.ResearchCategory import me.alegian.thavma.impl.common.research.ResearchEntry @@ -277,27 +280,48 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( addCategory(ResearchCategories.STORY, "???") addEntry(ResearchEntries.Story.TEST, "A Courtesy Call") - addTextPage( - ResearchEntries.Story.TEST, 0, - "A Courtesy Call 1", - "Lorem ipsum %s 1 sit amet,", - "this story a great meaning haveth." - ) - - addTextPage( - ResearchEntries.Story.TEST, 1, - "A Courtesy Call 2", - "Lorem dolor 2 sit amet,", - "this story a great meaning haveth.", - "" - ) - - addTextPage( - ResearchEntries.Story.TEST, 2, - "A Courtesy Call 3", - "Lorem lotrumatum dolor 3 sit amet,", - "this story a great meaning haveth." - ) +// addTextPage( +// ResearchEntries.Story.TEST, 0, +// "A Courtesy Call 1", +// "Lorem ipsum %s 1 sit amet,", +// "this story a great meaning haveth." +// ) +// +// addTextPage( +// ResearchEntries.Story.TEST, 1, +// "A Courtesy Call 2", +// "Lorem dolor 2 sit amet,", +// "this story a great meaning haveth.", +// "" +// ) +// +// addTextPage( +// ResearchEntries.Story.TEST, 2, +// "A Courtesy Call 3", +// "Lorem lotrumatum dolor 3 sit amet,", +// "this story a great meaning haveth." +// ) + + addTitleFeature(ResearchEntries.Story.TEST, 0,"A courtesy call starts the page") + addParagraphFeature(ResearchEntries.Story.TEST, 0, """ + As fate would have it, this paragraph will probably not start a page because it does not + have the mustStartPage flag set to true, which is actually rather a worrisome tragedy. + Somebody had better do something about it soon. + """) + addParagraphFeature(ResearchEntries.Story.TEST, 1, """ + This is an experiment simply due to the fact that I am testing out double-trim-indent behaviour! + If this fails and I am left to contemplate my existence in the bottomless abyss that my mind is, + I will be making phone calls real fast real soon. No manager is safe from this bad birch. + (I got my Karen haircut done yesterday so while you mongrels crawl about and lick your "gracious" masters' fingers, + I will bee the queen out of every damn bastard who makes me think badly of me, myself included.) + """.trimIndent()) + addTitleFeature(ResearchEntries.Story.TEST, 1, "This title might appear in the middle") + addParagraphFeature(ResearchEntries.Story.TEST, 2, """ + On the other (also doubly trimmed hand), this paragraph should start a new page always. + """.trimIndent()) + addTitleFeature(ResearchEntries.Story.TEST, 2,"(start of page)") + addTitleFeature(ResearchEntries.Story.TEST, 3, "this is page number 1! Surprise!") + addParagraphFeature(ResearchEntries.Story.TEST, 3, "Just another random little paragraph :D") addTextPage( ResearchEntries.Thavma.THAVMA, 0, @@ -354,14 +378,35 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( add(RecipeViewerDescriptions.ROTTEN_BRAIN, "Sometimes dropped by angry zombies.") add(RecipeViewerDescriptions.BOOK, "Right click a bookcase with a wand to obtain!") - add(RecipeViewerDescriptions.ARCANE_WORKBENCH, "Right click a crafting table with a wand to convert it to an arcane workbench.") + add( + RecipeViewerDescriptions.ARCANE_WORKBENCH, + "Right click a crafting table with a wand to convert it to an arcane workbench." + ) add(RecipeViewerDescriptions.CRUCIBLE, "Right click a cauldron with a wand to convert it to a crucible.") - add(RecipeViewerDescriptions.RESEARCH_TABLE, "Can be formed by placing 2 tables next to each other, and right clicking one with a wand.") - add(RecipeViewerDescriptions.INFUSED_STONES, "A piece of stone, infused with a primal element. Found in the overworld, at any height.") - add(RecipeViewerDescriptions.GREATWOOD, "Greatwoods are very tall, ancient trees. They are somewhat rare, but they can spawn in all overworld biomes.") - add(RecipeViewerDescriptions.SILVERWOOD, "Silverwoods are magical trees, with uniquely blue leaves. They are very rare, but they can spawn in all overworld biomes.") - add(RecipeViewerDescriptions.PILLAR, "Formed by right clicking the Infusion Matrix, after completing the Infusion Multiblock.") - add(RecipeViewerDescriptions.RESEARCH_SCROLL, "Obtained by clicking any unknown entry in the \"Elements of Thavma\"") + add( + RecipeViewerDescriptions.RESEARCH_TABLE, + "Can be formed by placing 2 tables next to each other, and right clicking one with a wand." + ) + add( + RecipeViewerDescriptions.INFUSED_STONES, + "A piece of stone, infused with a primal element. Found in the overworld, at any height." + ) + add( + RecipeViewerDescriptions.GREATWOOD, + "Greatwoods are very tall, ancient trees. They are somewhat rare, but they can spawn in all overworld biomes." + ) + add( + RecipeViewerDescriptions.SILVERWOOD, + "Silverwoods are magical trees, with uniquely blue leaves. They are very rare, but they can spawn in all overworld biomes." + ) + add( + RecipeViewerDescriptions.PILLAR, + "Formed by right clicking the Infusion Matrix, after completing the Infusion Multiblock." + ) + add( + RecipeViewerDescriptions.RESEARCH_SCROLL, + "Obtained by clicking any unknown entry in the \"Elements of Thavma\"" + ) add(RecipeViewerAliases.BOOK, "Book") add(RecipeViewerAliases.ORE, "Ore") @@ -395,18 +440,33 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( add(ResearchCategory.translationId(key), name) } - private fun addTextPage(entryKey: ResourceKey, pageIndex: Int, title: String?, vararg paragraphs: String) { + private fun addTextPage( + entryKey: ResourceKey, + pageIndex: Int, + title: String?, + vararg paragraphs: String + ) { val baseId = ResearchEntry.translationId(entryKey) if (title != null) add(TextPage.titleTranslationId(baseId, pageIndex), title) for (parIndex in paragraphs.indices) - add(TextPage.paragraphTranslationId(baseId, pageIndex, parIndex), paragraphs[parIndex].trimIndent().replace("\n", " ")) + add( + TextPage.paragraphTranslationId(baseId, pageIndex, parIndex), + paragraphs[parIndex].trimIndent().replace("\n", " ") + ) } - private fun addParagraphFeature(entryKey: ResourceKey, featureIndex: Int, text: String?){ - - } + private fun addParagraphFeature(entryKey: ResourceKey, featureIndex: Int, text: String) { + val baseId = ResearchEntry.translationId(entryKey) + add(ParagraphFeature.translationId(baseId, featureIndex), text.trimIndent().replace("\n", " ")) + } - private fun addTitleFeature(entryKey: ResourceKey, featureIndex: Int, text: String?){ + private fun addTitleFeature(entryKey: ResourceKey, featureIndex: Int, text: String) { + val baseId = ResearchEntry.translationId(entryKey) + add(TitleFeature.translationId(baseId, featureIndex), text.trimIndent().replace("\n", " ")) + } - } + private fun addFigureFeature(entryKey: ResourceKey, featureIndex: Int, text: String){ + val baseId = ResearchEntry.translationId(entryKey) + add(FigureFeature.translationId(baseId, featureIndex), text.trimIndent().replace("\n", " ")) + } } From c2bb5d06d3a50d76fb82f30d4aacbb42aa6ac93c Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Sun, 10 May 2026 21:32:49 +0200 Subject: [PATCH 12/19] Test#1 actually registered PageFeatureTypes, but getting null font --- .../java/me/alegian/thavma/impl/Thavma.kt | 1 + .../thavma/impl/common/book/PageFeature.kt | 2 +- .../impl/common/event/T7CommonModEvents.kt | 1 + .../T7DatapackBuiltinEntriesProvider.kt | 50 +++++++++---------- .../init/data/providers/T7LanguageProvider.kt | 42 ++++++++-------- 5 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/main/java/me/alegian/thavma/impl/Thavma.kt b/src/main/java/me/alegian/thavma/impl/Thavma.kt index b14253ac..d97a950a 100644 --- a/src/main/java/me/alegian/thavma/impl/Thavma.kt +++ b/src/main/java/me/alegian/thavma/impl/Thavma.kt @@ -36,6 +36,7 @@ object Thavma { T7Attributes.REGISTRAR.register(KFF_MOD_BUS) T7GlobalLootModifierSerializers.REGISTRAR.register(KFF_MOD_BUS) PageTypes.REGISTRAR.register(KFF_MOD_BUS) + PageFeatureTypes.REGISTRAR.register(KFF_MOD_BUS) T7Features.REGISTRAR.register(KFF_MOD_BUS) registerCommonModEvents() diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt index aac3d5af..1dbc1653 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt @@ -17,6 +17,6 @@ interface PageFeature { companion object{ val CODEC = T7Registries.PAGE_FEATURE_TYPE.byNameCodec().dispatch({ pageFeature -> pageFeature.type }, { type -> type.codec }) - fun translationId(baseId: String, featureIndex: Int) = "$baseId.pageFeature$featureIndex" + //fun translationId(baseId: String, featureIndex: Int) = "$baseId.page_feature$featureIndex" } } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/event/T7CommonModEvents.kt b/src/main/java/me/alegian/thavma/impl/common/event/T7CommonModEvents.kt index c8e153a9..48736e27 100644 --- a/src/main/java/me/alegian/thavma/impl/common/event/T7CommonModEvents.kt +++ b/src/main/java/me/alegian/thavma/impl/common/event/T7CommonModEvents.kt @@ -40,6 +40,7 @@ private fun registerRegistries(event: NewRegistryEvent) { event.register(T7Registries.WAND_CORE) event.register(T7Registries.ASPECT) event.register(T7Registries.PAGE_TYPE) + event.register(T7Registries.PAGE_FEATURE_TYPE) } private fun registerDatapackRegistries(event: DataPackRegistryEvent.NewRegistry) { diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index 670f02bc..c9ac9442 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -128,14 +128,14 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab ctx.registerCategory(ResearchCategories.STORY, Items.WRITABLE_BOOK.defaultInstance, 2f) } .add(T7DatapackRegistries.RESEARCH_ENTRY) { ctx -> -// ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) -// .addPage(simpleTextPage(3, true)) -// .addPage(simpleTextPage(1, false)) -// .addChild(ResearchEntries.Thavma.TREES) -// .addChild(ResearchEntries.Thavma.ORES) -// .defaultKnown() -// .build(ctx) + ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) + .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) + .addPage(simpleTextPage(3, true)) + .addPage(simpleTextPage(1, false)) + .addChild(ResearchEntries.Thavma.TREES) + .addChild(ResearchEntries.Thavma.ORES) + .defaultKnown() + .build(ctx) ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, -3), false, Items.TURTLE_HELMET.defaultInstance) .research() @@ -147,9 +147,9 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .addPageFeature(makeTitleFeature(true)) .addPageFeature(makeTitleFeature(true, true, 0)) .addPageFeature(makeParagraphFeature()) -// .addPage(simpleTextPage(2, true)) -// .addPage(simpleTextPage(2, true)) -// .addPage(simpleTextPage(2, true)) + .addPage(simpleTextPage(2, true)) + .addPage(simpleTextPage(2, true)) + .addPage(simpleTextPage(2, true)) .defaultKnown() .build(ctx) @@ -173,21 +173,21 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .addChild(ResearchEntries.Thavma.ARCANE_LENS) .build(ctx) -// ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) -// .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) -// .addPage(simpleTextPage(3, true)) -// .build(ctx) + ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) + .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) + .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) + .addPage(simpleTextPage(3, true)) + .build(ctx) -// ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) -// .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) -// .addPage { _, _ -> CraftingPage(Recipes.CHEST) } -// .addChild(ResearchEntries.Thavma.WANDS) -// .addChild(ResearchEntries.Thavma.TECHNOLOGY) -// .addChild(ResearchEntries.Thavma.ALCHEMY) -// .addChild(ResearchEntries.Thavma.INFUSION) -// .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) -// .build(ctx) + ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) + .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) + .addPage { _, _ -> CraftingPage(Recipes.CHEST) } + .addChild(ResearchEntries.Thavma.WANDS) + .addChild(ResearchEntries.Thavma.TECHNOLOGY) + .addChild(ResearchEntries.Thavma.ALCHEMY) + .addChild(ResearchEntries.Thavma.INFUSION) + .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) + .build(ctx) ResearchEntryBuilder( ResearchEntries.Thavma.RESEARCH_PROFICIENCY, diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index b71bf304..57ff4846 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -280,27 +280,27 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( addCategory(ResearchCategories.STORY, "???") addEntry(ResearchEntries.Story.TEST, "A Courtesy Call") -// addTextPage( -// ResearchEntries.Story.TEST, 0, -// "A Courtesy Call 1", -// "Lorem ipsum %s 1 sit amet,", -// "this story a great meaning haveth." -// ) -// -// addTextPage( -// ResearchEntries.Story.TEST, 1, -// "A Courtesy Call 2", -// "Lorem dolor 2 sit amet,", -// "this story a great meaning haveth.", -// "" -// ) -// -// addTextPage( -// ResearchEntries.Story.TEST, 2, -// "A Courtesy Call 3", -// "Lorem lotrumatum dolor 3 sit amet,", -// "this story a great meaning haveth." -// ) + addTextPage( + ResearchEntries.Story.TEST, 0, + "A Courtesy Call 1", + "Lorem ipsum %s 1 sit amet,", + "this story a great meaning haveth." + ) + + addTextPage( + ResearchEntries.Story.TEST, 1, + "A Courtesy Call 2", + "Lorem dolor 2 sit amet,", + "this story a great meaning haveth.", + "" + ) + + addTextPage( + ResearchEntries.Story.TEST, 2, + "A Courtesy Call 3", + "Lorem lotrumatum dolor 3 sit amet,", + "this story a great meaning haveth." + ) addTitleFeature(ResearchEntries.Story.TEST, 0,"A courtesy call starts the page") addParagraphFeature(ResearchEntries.Story.TEST, 0, """ From 3cd12c970ad6fb212be5922df58d31e1da54a53f Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Tue, 12 May 2026 16:20:54 +0200 Subject: [PATCH 13/19] Basic paragraph and title dynamic rendering done, polishing and figures are next. --- .../resources/assets/thavma/lang/en_us.json | 8 + .../research/entry/alchemy/alchemy.json | 2 +- .../research/entry/story/story_test.json | 83 +++++----- .../thavma/research/entry/thavma/alchemy.json | 2 +- .../research/entry/thavma/arcane_lens.json | 21 +-- .../research/entry/thavma/infusion.json | 2 +- .../thavma/research/entry/thavma/ores.json | 2 +- .../entry/thavma/research_proficiency.json | 2 +- .../research/entry/thavma/research_table.json | 7 +- .../research/entry/thavma/technology.json | 2 +- .../thavma/research/entry/thavma/thavma.json | 29 +--- .../thavma/research/entry/thavma/trees.json | 2 +- .../thavma/research/entry/thavma/wands.json | 2 +- .../thavma/impl/client/gui/book/BookScreen.kt | 2 +- .../gui/book/DynamicFeaturesRenderer.kt | 56 +++---- .../client/gui/book/DynamicRenderingHelper.kt | 143 ++++++++++++++---- .../impl/client/gui/book/EntryScreen.kt | 16 +- .../thavma/impl/client/gui/book/GridHelper.kt | 2 +- .../client/gui/book/PageFeatureRenderer.kt | 2 +- .../impl/client/util/GuiGraphicsExtensions.kt | 4 + .../thavma/impl/common/book/FigureFeature.kt | 24 +-- .../impl/common/book/FormattedTextFeature.kt | 17 ++- .../thavma/impl/common/book/PageFeature.kt | 6 +- .../impl/common/book/ParagraphFeature.kt | 17 ++- .../thavma/impl/common/book/RecipeFeature.kt | 6 +- .../thavma/impl/common/book/TitleFeature.kt | 25 +-- .../T7DatapackBuiltinEntriesProvider.kt | 16 +- .../init/data/providers/T7LanguageProvider.kt | 4 - .../impl/init/registries/T7Registries.kt | 2 +- .../resources/assets/thavma/font/default.json | 11 ++ .../resources/assets/thavma/font/default.ttf | Bin 0 -> 14488 bytes 31 files changed, 292 insertions(+), 225 deletions(-) create mode 100644 src/main/resources/assets/thavma/font/default.json create mode 100644 src/main/resources/assets/thavma/font/default.ttf diff --git a/src/generated/resources/assets/thavma/lang/en_us.json b/src/generated/resources/assets/thavma/lang/en_us.json index b6b22a9b..3ad52213 100644 --- a/src/generated/resources/assets/thavma/lang/en_us.json +++ b/src/generated/resources/assets/thavma/lang/en_us.json @@ -176,6 +176,14 @@ "research/entry.thavma.story.story_test.page2.paragraph0": "Lorem lotrumatum dolor 3 sit amet,", "research/entry.thavma.story.story_test.page2.paragraph1": "this story a great meaning haveth.", "research/entry.thavma.story.story_test.page2.title": "A Courtesy Call 3", + "research/entry.thavma.story.story_test.paragraph_feature0": "As fate would have it, this paragraph will probably not start a page because it does not have the mustStartPage flag set to true, which is actually rather a worrisome tragedy. Somebody had better do something about it soon.", + "research/entry.thavma.story.story_test.paragraph_feature1": "This is an experiment simply due to the fact that I am testing out double-trim-indent behaviour! If this fails and I am left to contemplate my existence in the bottomless abyss that my mind is, I will be making phone calls real fast real soon. No manager is safe from this bad birch. (I got my Karen haircut done yesterday so while you mongrels crawl about and lick your \"gracious\" masters' fingers, I will bee the queen out of every damn bastard who makes me think badly of me, myself included.)", + "research/entry.thavma.story.story_test.paragraph_feature2": "On the other (also doubly trimmed hand), this paragraph should start a new page always.", + "research/entry.thavma.story.story_test.paragraph_feature3": "Just another random little paragraph :D", + "research/entry.thavma.story.story_test.title_feature0": "A courtesy call starts the page", + "research/entry.thavma.story.story_test.title_feature1": "This title might appear in the middle", + "research/entry.thavma.story.story_test.title_feature2": "(start of page)", + "research/entry.thavma.story.story_test.title_feature3": "this is page number 1! Surprise!", "research/entry.thavma.thavma.alchemy": "Alchemy", "research/entry.thavma.thavma.arcane_lens": "The Arcane Lens", "research/entry.thavma.thavma.arcane_lens.page0.paragraph0": "The part of the book I can read describes an arcane tool that \"allows the user to see\", whatever that might mean. I have a feeling that crafting it could assist my work in unsealing the other pages.", diff --git a/src/generated/resources/data/thavma/thavma/research/entry/alchemy/alchemy.json b/src/generated/resources/data/thavma/thavma/research/entry/alchemy/alchemy.json index e5abb074..4c760f17 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/alchemy/alchemy.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/alchemy/alchemy.json @@ -26,7 +26,7 @@ "count": 1, "id": "thavma:crucible" }, - "pages": [], + "pageFeatures": [], "position": [ 0, 0 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json b/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json index 17b97fbc..9b3e9f65 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json @@ -7,56 +7,69 @@ "count": 1, "id": "minecraft:turtle_helmet" }, - "pages": [ + "pageFeatures": [ { - "type": "thavma:text", - "paragraphs": [ - { - "translate": "research/entry.thavma.story.story_test.page0.paragraph0" - }, - { - "translate": "research/entry.thavma.story.story_test.page0.paragraph1" - } - ], - "title": { + "type": "thavma:title", + "starts_page": true, + "text": { "bold": true, - "translate": "research/entry.thavma.story.story_test.page0.title" + "translate": "research/entry.thavma.story.story_test.title_feature0" } }, { - "type": "thavma:text", - "paragraphs": [ - { - "translate": "research/entry.thavma.story.story_test.page1.paragraph0" - }, - { - "translate": "research/entry.thavma.story.story_test.page1.paragraph1" - } - ], - "title": { + "type": "thavma:paragraph", + "text": { + "translate": "research/entry.thavma.story.story_test.paragraph_feature0" + } + }, + { + "type": "thavma:paragraph", + "text": { + "translate": "research/entry.thavma.story.story_test.paragraph_feature1" + } + }, + { + "type": "thavma:title", + "text": { + "bold": true, + "translate": "research/entry.thavma.story.story_test.title_feature1" + } + }, + { + "type": "thavma:paragraph", + "starts_page": true, + "text": { + "translate": "research/entry.thavma.story.story_test.paragraph_feature2" + } + }, + { + "type": "thavma:title", + "starts_page": true, + "text": { "bold": true, - "translate": "research/entry.thavma.story.story_test.page1.title" + "translate": "research/entry.thavma.story.story_test.title_feature2" } }, { - "type": "thavma:text", - "paragraphs": [ - { - "translate": "research/entry.thavma.story.story_test.page2.paragraph0" - }, - { - "translate": "research/entry.thavma.story.story_test.page2.paragraph1" - } - ], - "title": { + "type": "thavma:title", + "has_set_page": true, + "preferred_page": 0, + "starts_page": true, + "text": { "bold": true, - "translate": "research/entry.thavma.story.story_test.page2.title" + "translate": "research/entry.thavma.story.story_test.title_feature3" + } + }, + { + "type": "thavma:paragraph", + "text": { + "translate": "research/entry.thavma.story.story_test.paragraph_feature3" } } ], "position": [ 0, - -3 + 0 ], "preferX": false, "title": { diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/alchemy.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/alchemy.json index cf144174..9af0e3d1 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/alchemy.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/alchemy.json @@ -26,7 +26,7 @@ "count": 1, "id": "thavma:crucible" }, - "pages": [], + "pageFeatures": [], "position": [ -2, 2 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/arcane_lens.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/arcane_lens.json index b1cf4745..33183a96 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/arcane_lens.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/arcane_lens.json @@ -36,26 +36,7 @@ "count": 1, "id": "thavma:arcane_lens" }, - "pages": [ - { - "type": "thavma:text", - "paragraphs": [ - { - "translate": "research/entry.thavma.thavma.arcane_lens.page0.paragraph0" - }, - { - "translate": "research/entry.thavma.thavma.arcane_lens.page0.paragraph1" - }, - { - "translate": "research/entry.thavma.thavma.arcane_lens.page0.paragraph2" - } - ], - "title": { - "bold": true, - "translate": "research/entry.thavma.thavma.arcane_lens.page0.title" - } - } - ], + "pageFeatures": [], "position": [ 2, -2 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/infusion.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/infusion.json index ca774a8a..fa0d7078 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/infusion.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/infusion.json @@ -26,7 +26,7 @@ "count": 1, "id": "thavma:infusion_matrix" }, - "pages": [], + "pageFeatures": [], "position": [ 2, 2 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/ores.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/ores.json index 2dee4be2..ea1a35f3 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/ores.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/ores.json @@ -28,7 +28,7 @@ "count": 1, "id": "thavma:aqua_shard" }, - "pages": [], + "pageFeatures": [], "position": [ 2, -4 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json index 382dde7e..36d325ae 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json @@ -26,7 +26,7 @@ "count": 1, "id": "thavma:research_table" }, - "pages": [], + "pageFeatures": [], "position": [ -1, -1 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json index 1edfaf76..928ed808 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json @@ -32,12 +32,7 @@ "count": 1, "id": "thavma:research_table" }, - "pages": [ - { - "type": "thavma:crafting", - "recipeRL": "minecraft:chest" - } - ], + "pageFeatures": [], "position": [ 0, 0 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/technology.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/technology.json index 03f96e16..e7999939 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/technology.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/technology.json @@ -26,7 +26,7 @@ "count": 1, "id": "thavma:goggles" }, - "pages": [], + "pageFeatures": [], "position": [ 2, 4 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json index 8c91c2af..a4906b9e 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json @@ -29,34 +29,7 @@ "count": 1, "id": "thavma:book" }, - "pages": [ - { - "type": "thavma:text", - "paragraphs": [ - { - "translate": "research/entry.thavma.thavma.thavma.page0.paragraph0" - }, - { - "translate": "research/entry.thavma.thavma.thavma.page0.paragraph1" - }, - { - "translate": "research/entry.thavma.thavma.thavma.page0.paragraph2" - } - ], - "title": { - "bold": true, - "translate": "research/entry.thavma.thavma.thavma.page0.title" - } - }, - { - "type": "thavma:text", - "paragraphs": [ - { - "translate": "research/entry.thavma.thavma.thavma.page1.paragraph0" - } - ] - } - ], + "pageFeatures": [], "position": [ 0, -6 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/trees.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/trees.json index 1d03df95..b47d0111 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/trees.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/trees.json @@ -28,7 +28,7 @@ "count": 1, "id": "thavma:greatwood_log" }, - "pages": [], + "pageFeatures": [], "position": [ 0, -3 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/wands.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/wands.json index 77ccd882..6dff452d 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/wands.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/wands.json @@ -26,7 +26,7 @@ "count": 1, "id": "thavma:thavmite_silverwood_wand" }, - "pages": [], + "pageFeatures": [], "position": [ -2, 4 diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt index 6099f4bd..dc9e9819 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt @@ -30,7 +30,7 @@ class BookScreen : Screen(Component.literal("book")) { selectorOffset = cornerHeight + selectorGap val categoryRegistry = clientRegistry(T7DatapackRegistries.RESEARCH_CATEGORY) - currentCategory = categoryRegistry?.getOrThrow(ResearchCategories.THAVMA) + currentCategory = categoryRegistry?.getOrThrow(ResearchCategories.STORY) categoryRegistry?.forEach { tabs[it] = addRenderableOnly(TabRenderable(this)) } diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt index a176518b..5021c727 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt @@ -15,7 +15,10 @@ import net.minecraft.client.Minecraft object DynamicFeaturesRenderer : PageFeatureRenderer { private val SEPARATOR = Texture("gui/book/separator", 128, 16, 128, 16) - override fun initPageFeatures(screen: EntryScreen, features: List) { + override fun initPageFeatures(screen: EntryScreen, features: List, maxWidth: Int) { + + val font = Minecraft.getInstance().font + val LINE_HEIGHT = font.lineHeight + 2 Column({ size = grow() @@ -24,7 +27,7 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { for (feature in features) { when (feature) { is TitleFeature -> { - Title(feature) + Title(feature, maxWidth) Separator() } @@ -34,44 +37,23 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { relativeRenderable { guiGraphics, _, _, _ -> guiGraphics.usePose { for (line in feature.text) { - guiGraphics.drawString(feature.font, line) - translateXY(0, feature.font.lineHeight) + //guiGraphics.drawString(feature.font, line) + guiGraphics.drawString(font, line) + //translateXY(0, feature.font.lineHeight) + translateXY(0, LINE_HEIGHT) } - translateXY(0, feature.font.lineHeight * 2 / 3) + //translateXY(0, feature.font.lineHeight * 2 / 3) + translateXY(0, LINE_HEIGHT * 2 / 3) } } } + is FigureFeature -> Image(feature) } } } } -// Column({ -// size = grow() -// gap = 4 -// }) { -// if (page.title != null) { -// Title(page.title) -// Separator() -// } -// Row({ -// size = grow() -// }) { -// relativeRenderable { guiGraphics, _, _, _ -> -// guiGraphics.usePose { -// for (paragraph in page.paragraphs) { -// for (line in font.splitter.splitLines(paragraph, size.x.toInt(), Style.EMPTY)) { -// guiGraphics.drawString(Minecraft.getInstance().font, Component.literal(line.string)) -// translateXY(0, LINE_HEIGHT) -// } -// translateXY(0, LINE_HEIGHT * 2 / 3) -// } -// } -// } -// } -// } -// } private fun Separator() { Row({ @@ -82,15 +64,21 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { } } - private fun Title(title: TitleFeature) { + private fun Title(title: TitleFeature, maxWidth: Int) { val font = Minecraft.getInstance().font + val lines = font.split(title.text, maxWidth) Row({ width = grow() - height = fixed(font.lineHeight) + height = fixed((font.lineHeight + 2) * lines.size) }) { relativeRenderable { guiGraphics, _, _, _ -> - guiGraphics.drawCenteredString(font, title.text, size.x / 2) + guiGraphics.usePose { + for (line in lines) { + guiGraphics.drawCenteredString(font, line, size.x / 2) + translateXY(0, font.lineHeight) + } + } } } } @@ -101,9 +89,9 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { height = fixed(figure.textureHeight) }) { TextureBox(figure.image) {} - } } } +} diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt index 4c1d2eb9..3aacf5d2 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt @@ -4,6 +4,27 @@ import me.alegian.thavma.impl.common.book.FigureFeature import me.alegian.thavma.impl.common.book.FormattedTextFeature import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.book.ParagraphFeature +import me.alegian.thavma.impl.common.book.RecipeFeature +import me.alegian.thavma.impl.common.book.TitleFeature +import net.minecraft.client.gui.Font +import net.minecraft.network.chat.Component +import kotlin.math.min + +private fun PageFeature.renderedHeight(pageWidth: Int, font: Font): Int { + val lineHeight = font.lineHeight + 2 + return when (this) { + is ParagraphFeature -> font.split(this.text, pageWidth).size * lineHeight + is TitleFeature -> font.split(this.text, pageWidth).size * lineHeight + 16 + is FigureFeature -> if (caption != null) font.split( + this.caption, + pageWidth + ).size * lineHeight + this.textureHeight else this.textureHeight + + is RecipeFeature -> 96 + is FormattedTextFeature -> text.size * lineHeight + else -> throw IllegalArgumentException("This PageFeature $this does not have renderedHeight implemented yet") + } +} /** * Return a list of PageFeatures containing a list of lines - FormattedCharSequence @@ -13,49 +34,71 @@ import me.alegian.thavma.impl.common.book.ParagraphFeature */ fun spliceParagraphOrFigure( input: PageFeature, maxPageHeight: Int, - currentHeight: Int + currentHeight: Int, maxPageWidth: Int, font: Font ): List { val result = mutableListOf() + println("maxHeight is $maxPageHeight, pageWidth is $maxPageWidth, processing page feature $input, current height is $currentHeight") if (input is ParagraphFeature) { // how many lines fit in the current page - val lineHeight = input.LINE_HEIGHT + val lineHeight = font.lineHeight + 2 + println("lineheight is $lineHeight") // so that we get at least 2 lines at the end of the first page val linesRemainingAtStart: Int = (maxPageHeight - currentHeight) / lineHeight + println("lines remaining until end of page are $linesRemainingAtStart") + // so that we get at least 2 lines at the start of the last page // (making use of rounding down when dividing integers) val numOfLinesCoveringFullPages: Int = - (input.renderedHeight - linesRemainingAtStart * lineHeight) / maxPageHeight / lineHeight - val linesCroppingOutAtEnd: Int = - (input.renderedHeight - linesRemainingAtStart * lineHeight - numOfLinesCoveringFullPages * lineHeight) / lineHeight - + (input.renderedHeight( + maxPageWidth, + font + ) - linesRemainingAtStart * lineHeight) / maxPageHeight * maxPageHeight / lineHeight + println("number of lines covering whole pages is $numOfLinesCoveringFullPages") + var linesCroppingOutAtEnd: Int = + (input.renderedHeight( + maxPageWidth, + font + ) - linesRemainingAtStart * lineHeight - numOfLinesCoveringFullPages * lineHeight) / lineHeight + if (linesCroppingOutAtEnd < 0) linesCroppingOutAtEnd = 0 + println("number of lines cropping out at end are $linesCroppingOutAtEnd") val maxLinesPerPage = maxPageHeight / lineHeight + println("max lines per page is $maxLinesPerPage") val numOfFullPagesCovered = numOfLinesCoveringFullPages / maxLinesPerPage - val lines = input.font.split(input.text, input.pageWidth) + println("number of full pages is $numOfFullPagesCovered") + val lines = font.split(input.text, maxPageWidth) + println("the text was split into this number of lines: ${lines.size}") + val realLinesRemaining: Int = min(linesRemainingAtStart, lines.size) + println("given the length of the text, this many lines are at the start: ${realLinesRemaining}") when { - lines.size == 1 && currentHeight + lineHeight <= maxPageHeight -> result += FormattedTextFeature(lines) +// lines.size == 1 && currentHeight + lineHeight <= maxPageHeight -> result += FormattedTextFeature(lines) +// +// lines.size == 1 -> result += listOf( +// FormattedTextFeature(listOf()), +// FormattedTextFeature(lines) +// ) - lines.size == 1 -> result += listOf( - FormattedTextFeature(listOf()), - FormattedTextFeature(lines) + lines.size <= linesRemainingAtStart && currentHeight + lineHeight * lines.size <= maxPageHeight -> result += FormattedTextFeature( + lines ) linesRemainingAtStart != 1 && linesCroppingOutAtEnd != 1 -> { // separate into "underhang", middle and overhang - val start = lines.slice(0 until linesRemainingAtStart) + val start = lines.slice(0 until realLinesRemaining) result += FormattedTextFeature(start) for (i in 0 until numOfFullPagesCovered) { - val middle = + val fullPage = lines.slice(linesRemainingAtStart + i * maxLinesPerPage until linesRemainingAtStart + (i + 1) * maxLinesPerPage) - result += FormattedTextFeature(middle) + result += FormattedTextFeature(fullPage) } val end = - lines.slice(lines.size - 1 - linesCroppingOutAtEnd until lines.size - 1) + lines.slice(lines.size - linesCroppingOutAtEnd until lines.size) + //lines.slice(linesRemainingAtStart + numOfFullPagesCovered * maxLinesPerPage until lines.size - 1) result += FormattedTextFeature(end) } @@ -63,8 +106,8 @@ fun spliceParagraphOrFigure( // add an empty paragraph to current page and continue on the next result += FormattedTextFeature(listOf()) for (i in 0 until numOfFullPagesCovered) { - val page = lines.slice(i * maxLinesPerPage until (i + 1) * maxLinesPerPage) - result += FormattedTextFeature(page) + val fullPage = lines.slice(i * maxLinesPerPage until (i + 1) * maxLinesPerPage) + result += FormattedTextFeature(fullPage) } val finish = lines.slice(numOfFullPagesCovered * maxLinesPerPage until lines.size) @@ -79,7 +122,7 @@ fun spliceParagraphOrFigure( when { caption == null && currentHeight + textureHeight <= maxPageHeight -> result += input caption == null -> { - result += listOf() + result += FormattedTextFeature(listOf()) result += this } @@ -89,7 +132,8 @@ fun spliceParagraphOrFigure( spliceParagraphOrFigure( ParagraphFeature(caption), maxPageHeight, - currentHeight + textureHeight + currentHeight + textureHeight, + maxPageWidth, font ) ) } @@ -97,7 +141,15 @@ fun spliceParagraphOrFigure( else -> { result += FormattedTextFeature(listOf()) result += FigureFeature(image, null) - result.addAll(spliceParagraphOrFigure(ParagraphFeature(caption), maxPageHeight, textureHeight)) + result.addAll( + spliceParagraphOrFigure( + ParagraphFeature(caption), + maxPageHeight, + textureHeight, + maxPageWidth, + font + ) + ) } } } @@ -110,24 +162,42 @@ fun spliceParagraphOrFigure( * Returns a list of lists of features where every index represents a page. * Features in the same list belong together on one page. */ -fun pagifyFeatures(features: List, maxHeight: Int): List> { +fun pagifyFeatures(features: List, maxHeight: Int, pageWidth: Int, font: Font): List> { // maxHeight is height of background texture minus padding (32 top 42 bottom) //val maxHeight = this@EntryScreen.height - 74 + println("maxHeight is $maxHeight, pageWidth is $pageWidth") val partition = features.partition { !it.mustOccupySetPage } val pages = mutableListOf>() val buffer = mutableListOf() - fun currentHeight() = buffer.sumOf { it.renderedHeight } + fun currentHeight() = buffer.sumOf { it.renderedHeight(pageWidth, font) } fun submitBufferAndClear() { - pages += buffer + pages.add(buffer.toList()) + println("===== Just submitted from buffer =====") + buffer.forEach { println(it) } buffer.clear() } + println("The list of features contains:") + for (i in features) println(i) + println("Divided into partitions of length ${partition.first.size} and ${partition.second.size}") + println(partition.first) + println(partition.second) + // deal with elements without predetermined order (bulk of the logic) for (feature in partition.first) { + println("Processing feature $feature in initial pagifyFeatures(), current height ${currentHeight()}") with(feature) { when { - (this !is ParagraphFeature && this !is FigureFeature) && renderedHeight > maxHeight -> throw IllegalArgumentException( - "The size of the element ${this::class.simpleName} is too large at $renderedHeight while allowed $maxHeight." + (this !is ParagraphFeature && this !is FigureFeature) && renderedHeight( + pageWidth, + font + ) > maxHeight -> throw IllegalArgumentException( + "The size of the element ${this::class.simpleName} is too large at ${ + renderedHeight( + pageWidth, + font + ) + } while allowed $maxHeight." ) coversOneWholePage -> { @@ -138,7 +208,8 @@ fun pagifyFeatures(features: List, maxHeight: Int): List { if (buffer.isNotEmpty()) submitBufferAndClear() if (this is ParagraphFeature || this is FigureFeature) { - val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight()) + println("Currently have this in the result: $pages") + val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight(), pageWidth, font) if (processed.size < 2) buffer += processed.first() // only need to check this much since buffer is empty (starts page) else if (this is ParagraphFeature) { @@ -161,7 +232,7 @@ fun pagifyFeatures(features: List, maxHeight: Int): List { - val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight()) + val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight(), pageWidth, font) if (processed.size < 2) buffer += processed.first() else if (this is ParagraphFeature) { buffer += processed.first() @@ -207,9 +278,17 @@ fun pagifyFeatures(features: List, maxHeight: Int): List buffer += this + currentHeight() + renderedHeight(pageWidth, font) <= maxHeight -> buffer += this // the next check might be redundant: - buffer.isEmpty() -> throw IllegalArgumentException("The size of the element ${this::class.simpleName} is too large at $renderedHeight while allowed $maxHeight.") + buffer.isEmpty() -> throw IllegalArgumentException( + "The size of the element ${this::class.simpleName} is too large at ${ + renderedHeight( + pageWidth, + font + ) + } while allowed $maxHeight." + ) + else -> { submitBufferAndClear() buffer += this @@ -221,9 +300,15 @@ fun pagifyFeatures(features: List, maxHeight: Int): List) : Screen(Component.literal("Book Entry")) { +class EntryScreen(entry: Holder) : Screen(Component.literal("Book Entry")) { companion object { private val BG = Texture("gui/book/background", 510, 282, 512, 512) } private var currentPage = 0 + private val fontify = Minecraft.getInstance().font + + private var maxWidth = BG.width/2 - 80 // maxHeight is height of background texture minus padding (32 top 42 bottom) - var pages = pagifyFeatures(entry.value().pageFeatures, this.height - 74) + var pages = pagifyFeatures(entry.value().pageFeatures, BG.height - 74, maxWidth, fontify) override fun init() { super.init() @@ -100,16 +104,10 @@ class EntryScreen(private val entry: Holder) : Screen(Component.l renderer.initPage(this, page) } -// private fun initPageFeatures(features: List) { -// val renderer = DynamicFeaturesRenderer as PageFeatureRenderer -// renderer.initPageFeatures(this, features, currentPage) -// } - - private fun initPageFeatures(features: List?) { if (features != null) { val renderer = DynamicFeaturesRenderer - renderer.initPageFeatures(this, features) + renderer.initPageFeatures(this, features, maxWidth) } } diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt index 72f50fcc..5e712bb1 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt @@ -34,7 +34,7 @@ fun PoseStack.renderConnectionRecursive(dx: Int, dy: Int, guiGraphics: GuiGraphi val preference = if (preferX) -1f else 1f if (absDx + absDy <= 1f) return - else if (absDx > 2 && absDy > 2) throw IllegalStateException() + else if (absDx > 2 && absDy > 2) throw IllegalStateException("Yup the problem is here in GridHelper.kt") else if (!invert && (preferX && absDx > absDy && absDy > 0 || !preferX && absDy > absDx && absDx > 0)) { translateXY(dx, dy) renderConnectionRecursive(-dx, -dy, guiGraphics, preferX, true) diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt index 630eabc0..107c7fc0 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt @@ -3,5 +3,5 @@ package me.alegian.thavma.impl.client.gui.book import me.alegian.thavma.impl.common.book.PageFeature interface PageFeatureRenderer{ - fun initPageFeatures(screen: EntryScreen, features: List) + fun initPageFeatures(screen: EntryScreen, features: List, maxWidth: Int) } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/client/util/GuiGraphicsExtensions.kt b/src/main/java/me/alegian/thavma/impl/client/util/GuiGraphicsExtensions.kt index ed8fffed..08290a54 100644 --- a/src/main/java/me/alegian/thavma/impl/client/util/GuiGraphicsExtensions.kt +++ b/src/main/java/me/alegian/thavma/impl/client/util/GuiGraphicsExtensions.kt @@ -18,6 +18,10 @@ fun GuiGraphics.drawCenteredString(font: Font, text: Component, centerX: Float, drawString(font, text, (centerX - font.width(text) / 2f).toInt(), 0, color, false) } +fun GuiGraphics.drawCenteredString(font: Font, text: FormattedCharSequence, centerX: Float, color: Int = 0) { + drawString(font, text, (centerX - font.width(text) / 2f).toInt(), 0, color, false) +} + fun GuiGraphics.drawString(font: Font, text: Component, color: Int = 0) { drawString(font, text, 0, 0, color, false) } diff --git a/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt index dade09b5..db1c0228 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt @@ -4,12 +4,9 @@ import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import me.alegian.thavma.impl.client.texture.Texture import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Font import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentSerialization -import net.minecraft.network.chat.Style -import java.util.Optional +import java.util.* class FigureFeature(val image: Texture, val caption: Component?, override val mustStartPage: Boolean = false, override val mustOccupySetPage: Boolean = false, override val preferredPageIndex: Int = 1) : PageFeature { override val type: PageFeatureType<*> @@ -18,21 +15,24 @@ class FigureFeature(val image: Texture, val caption: Component?, override val mu override val coversOneWholePage = false +// +// override val pageWidth: Int +// get() = 256 - override val pageWidth: Int - get() = 256 - - val font: Font = Minecraft.getInstance().font + //val font: Font = Minecraft.getInstance().font // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor - val LINE_HEIGHT = font.lineHeight + 2 + //val LINE_HEIGHT = font.lineHeight + 2 + //val LINE_HEIGHT = 11 - val lines = if (caption != null) font.splitter.splitLines(caption, pageWidth - 25, Style.EMPTY) else listOf() + //val lines = if (caption != null) font.splitter.splitLines(caption, pageWidth - 25, Style.EMPTY) else listOf() val textureHeight = image.height - val captionHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 4 / 3 - override val renderedHeight = textureHeight + captionHeight + //val captionHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 4 / 3 + + //val captionHeight = if (caption != null) LINE_HEIGHT * caption.string.length*5/115 + LINE_HEIGHT * 4 / 3 else 0 + //override val renderedHeight = textureHeight + captionHeight companion object { val CODEC = RecordCodecBuilder.mapCodec { builder -> diff --git a/src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt index f6b8d472..8d263093 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/FormattedTextFeature.kt @@ -1,8 +1,6 @@ package me.alegian.thavma.impl.common.book import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Font import net.minecraft.util.FormattedCharSequence /** @@ -10,7 +8,7 @@ import net.minecraft.util.FormattedCharSequence * so that they fit in their respective pages */ -class FormattedTextFeature(val text: List, val font: Font = Minecraft.getInstance().font) : PageFeature { +class FormattedTextFeature(val text: List, val isTitle: Boolean = false) : PageFeature { override val coversOneWholePage: Boolean get() = false override val mustStartPage: Boolean @@ -21,10 +19,19 @@ class FormattedTextFeature(val text: List, val font: Font override val type: PageFeatureType<*> get() = PageFeatureTypes.PARAGRAPH.get() + override fun toString(): String { + return "FormattedTextFeature with number of lines ${text.size}" + } + + //val font: Font = Minecraft.getInstance().font + //val DEFAULT_FONT = ResourceLocation.fromNamespaceAndPath("thavma","font:default.ttf") + //val x: Font? = null + // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor - val LINE_HEIGHT = font.lineHeight + 2 + //val LINE_HEIGHT = font.lineHeight + 2 + //val LINE_HEIGHT = 11 //val lines = font.splitter.splitLines(text, pageWidth - 25, Style.EMPTY) - override val renderedHeight = LINE_HEIGHT * text.size + LINE_HEIGHT * 2 / 3 + //override val renderedHeight = LINE_HEIGHT * text.size + LINE_HEIGHT * 2 / 3 } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt index 1dbc1653..9255cd37 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/PageFeature.kt @@ -8,9 +8,9 @@ interface PageFeature { val mustOccupySetPage: Boolean val preferredPageIndex: Int get() = 1 - val renderedHeight: Int - val pageWidth: Int - get() = 256 - 25 + //val renderedHeight: Int + //val pageWidth: Int + //get() = 256 - 25 val type: PageFeatureType<*> diff --git a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt index 7edfee87..f554253d 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/ParagraphFeature.kt @@ -3,11 +3,8 @@ package me.alegian.thavma.impl.common.book import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Font import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentSerialization -import net.minecraft.network.chat.Style class ParagraphFeature(val text: Component, override val mustStartPage: Boolean = false, override val mustOccupySetPage: Boolean = false, override val preferredPageIndex: Int = 1) : PageFeature { override val type: PageFeatureType<*> @@ -15,14 +12,20 @@ class ParagraphFeature(val text: Component, override val mustStartPage: Boolean override val coversOneWholePage = false - val font: Font = Minecraft.getInstance().font + override fun toString(): String { + return "ParagraphFeature with text $text" + } + + //val font: Font = Minecraft.getInstance().font // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor - val LINE_HEIGHT = font.lineHeight + 2 - val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) + //val LINE_HEIGHT = font.lineHeight + 2 + //val LINE_HEIGHT = 11 + //val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) - override val renderedHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 2 / 3 + //override val renderedHeight = LINE_HEIGHT * lines.size + LINE_HEIGHT * 2 / 3 + //override val renderedHeight = LINE_HEIGHT * text.string.length*5/115 + LINE_HEIGHT * 2 / 3 companion object { val CODEC = RecordCodecBuilder.mapCodec { builder -> diff --git a/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt index fdcfa3b0..d49e3297 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/RecipeFeature.kt @@ -11,8 +11,10 @@ class RecipeFeature( override val type: PageFeatureType<*> get() = PageFeatureTypes.RECIPE.get() - override val renderedHeight: Int - get() = 96 + //val font: Font = Minecraft.getInstance().font + +// override val renderedHeight: Int +// get() = 96 companion object { val CODEC = RecordCodecBuilder.mapCodec { builder -> diff --git a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt index 11304387..48eab757 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/TitleFeature.kt @@ -2,36 +2,39 @@ package me.alegian.thavma.impl.common.book import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder -import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.init.registries.deferred.PageFeatureTypes -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Font import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentSerialization -import net.minecraft.network.chat.Style class TitleFeature(val text: Component, override val mustStartPage: Boolean = false, override val mustOccupySetPage: Boolean = false, override val preferredPageIndex: Int = 1): PageFeature { - override val type: PageFeatureType<*> + + override val type: PageFeatureType<*> get() = PageFeatureTypes.TITLE.get() - override val coversOneWholePage = false + override val coversOneWholePage = false + + override fun toString(): String { + return "TitleFeature with text $text" + } - val font: Font = Minecraft.getInstance().font + //val font: Font = Minecraft.getInstance().font // if a recalibrated font size is used, I can multiply or divide by rendering scaling factor - val LINE_HEIGHT = font.lineHeight + 2 - val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) + //val LINE_HEIGHT = font.lineHeight + 2 + //val LINE_HEIGHT = 11 + //val lines = font.splitter.splitLines(text, pageWidth, Style.EMPTY) - override val renderedHeight = LINE_HEIGHT * lines.size + 16 + //override val renderedHeight = LINE_HEIGHT * lines.size + 16 + //override val renderedHeight = 11 + 16 companion object { val CODEC = RecordCodecBuilder.mapCodec { builder -> builder.group( ComponentSerialization.CODEC.fieldOf("text").forGetter(TitleFeature::text), Codec.BOOL.optionalFieldOf("starts_page", false).forGetter(TitleFeature::mustStartPage), - Codec.BOOL.optionalFieldOf("has_set_page", false).forGetter(TitleFeature::mustStartPage), + Codec.BOOL.optionalFieldOf("has_set_page", false).forGetter(TitleFeature::mustOccupySetPage), Codec.INT.optionalFieldOf("preferred_page", 1).forGetter(TitleFeature::preferredPageIndex) ).apply(builder, ::TitleFeature) } diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index c9ac9442..cde33c41 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -137,19 +137,19 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .defaultKnown() .build(ctx) - ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, -3), false, Items.TURTLE_HELMET.defaultInstance) + ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, 0), false, Items.TURTLE_HELMET.defaultInstance) .research() - .addPageFeature(makeTitleFeature(true, false)) + .addPageFeature(makeTitleFeature(true)) .addPageFeature(makeParagraphFeature(false, false)) .addPageFeature(makeParagraphFeature()) - .addPageFeature(makeTitleFeature()) + .addPageFeature(makeTitleFeature(false)) .addPageFeature(makeParagraphFeature(true)) - .addPageFeature(makeTitleFeature(true)) + .addPageFeature(makeTitleFeature(true, false)) .addPageFeature(makeTitleFeature(true, true, 0)) .addPageFeature(makeParagraphFeature()) - .addPage(simpleTextPage(2, true)) - .addPage(simpleTextPage(2, true)) - .addPage(simpleTextPage(2, true)) +// .addPage(simpleTextPage(2, true)) +// .addPage(simpleTextPage(2, true)) +// .addPage(simpleTextPage(2, true)) .defaultKnown() .build(ctx) @@ -336,7 +336,7 @@ private fun makeParagraphFeature(mustStartPage: Boolean = false, mustOccupySetPa private fun makeTitleFeature(mustStartPage: Boolean = false, mustOccupySetPage: Boolean = false, preferredPageIndex: Int = 1): (ResourceKey, Int) -> TitleFeature{ return { entryKey, titleIndex -> val baseId = ResearchEntry.translationId(entryKey) - TitleFeature(Component.translatable(TitleFeature.translationId(baseId, titleIndex)), + TitleFeature(Component.translatable(TitleFeature.translationId(baseId, titleIndex)).withStyle(ChatFormatting.BOLD), mustStartPage, mustOccupySetPage, preferredPageIndex ) } diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index 57ff4846..481f7eb4 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -5,7 +5,6 @@ import me.alegian.thavma.impl.client.T7KeyMappings import me.alegian.thavma.impl.client.gui.layer.ArcaneLensLayer import me.alegian.thavma.impl.client.gui.research_table.AspectWidget import me.alegian.thavma.impl.client.gui.research_table.ButtonWidget -import me.alegian.thavma.impl.client.gui.research_table.ResearchScreen import me.alegian.thavma.impl.client.gui.research_table.SocketWidget import me.alegian.thavma.impl.client.gui.tooltip.AspectClientTooltipComponent import me.alegian.thavma.impl.common.block.HungryChestBlock @@ -103,12 +102,9 @@ import me.alegian.thavma.impl.init.registries.deferred.WandPlatingMaterials.ORIC import me.alegian.thavma.impl.init.registries.deferred.WandPlatingMaterials.THAVMITE import me.alegian.thavma.impl.integration.RecipeViewerAliases import me.alegian.thavma.impl.integration.RecipeViewerDescriptions -import net.minecraft.ChatFormatting import net.minecraft.Util import net.minecraft.core.registries.Registries import net.minecraft.data.PackOutput -import net.minecraft.network.chat.Component -import net.minecraft.network.chat.MutableComponent import net.minecraft.resources.ResourceKey import net.minecraft.world.entity.ai.attributes.Attribute import net.minecraft.world.item.crafting.RecipeType diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt b/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt index 467bff0a..e9c61a7d 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/T7Registries.kt @@ -27,7 +27,7 @@ object T7Registries { .maxId(Int.MAX_VALUE) .create() - val PAGE_FEATURE_TYPE = RegistryBuilder(ResourceKey.createRegistryKey>(rl("page_feature"))) + val PAGE_FEATURE_TYPE = RegistryBuilder(ResourceKey.createRegistryKey>(rl("page_feature_type"))) .maxId(Int.MAX_VALUE) .create() } diff --git a/src/main/resources/assets/thavma/font/default.json b/src/main/resources/assets/thavma/font/default.json new file mode 100644 index 00000000..dbad25b0 --- /dev/null +++ b/src/main/resources/assets/thavma/font/default.json @@ -0,0 +1,11 @@ +{ + "providers": [ + { + "type": "ttf", + "file": "thavma:default.ttf", + "shift": [0, 0], + "size": 11.0, + "oversample": 2.0 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/thavma/font/default.ttf b/src/main/resources/assets/thavma/font/default.ttf new file mode 100644 index 0000000000000000000000000000000000000000..85c14725a3bf6d67aaf0f03292f9b763c1654f07 GIT binary patch literal 14488 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 Z*c*kN@$GAW{e3oGeYSI$CI24pe*xM150(G` literal 0 HcmV?d00001 From abb15b9509009bc6d59d93eb3adf719638f68235 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Tue, 12 May 2026 21:20:20 +0200 Subject: [PATCH 14/19] I actually did it, adjustable font size has got nothing on me! --- .../resources/assets/thavma/lang/en_us.json | 8 +- .../research/entry/story/story_test.json | 35 ++++++ .../gui/book/DynamicFeaturesRenderer.kt | 61 +++++++++- .../client/gui/book/DynamicRenderingHelper.kt | 15 +-- .../impl/client/gui/book/EntryScreen.kt | 11 +- .../client/gui/layout/LayoutExtensions.kt | 22 ++++ .../thavma/impl/common/book/FigureFeature.kt | 4 + .../T7DatapackBuiltinEntriesProvider.kt | 9 +- .../init/data/providers/T7LanguageProvider.kt | 110 ++++++++++++++++-- .../textures/gui/images/important_image.png | Bin 0 -> 41205 bytes .../textures/gui/images/important_image2.png | Bin 0 -> 11542 bytes 11 files changed, 236 insertions(+), 39 deletions(-) create mode 100644 src/main/resources/assets/thavma/textures/gui/images/important_image.png create mode 100644 src/main/resources/assets/thavma/textures/gui/images/important_image2.png diff --git a/src/generated/resources/assets/thavma/lang/en_us.json b/src/generated/resources/assets/thavma/lang/en_us.json index 3ad52213..d296c4e7 100644 --- a/src/generated/resources/assets/thavma/lang/en_us.json +++ b/src/generated/resources/assets/thavma/lang/en_us.json @@ -166,6 +166,7 @@ "research/category.thavma.thavma": "Thavma", "research/entry.thavma.alchemy.alchemy": "Alchemy", "research/entry.thavma.story.story_test": "A Courtesy Call", + "research/entry.thavma.story.story_test.figure_feature0": "We're no strangers to love You know the rules and so do I A full commitment's what I'm thinking of You wouldn't get this from any other guy I just wanna tell you how I'm feeling Gotta make you understand Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you We've known each other for so long Your heart's been aching, but you're too shy to say it Inside, we both know what's been going on We know the game, and we're gonna play it And if you ask me how I'm feeling Don't tell me you're too blind to see Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you Ooh (Give you up) Ooh-ooh (Give you up) Ooh (Never gonna give, never gonna give) Give you up Ooh-ooh (Never gonna give, never gonna give) Give you up We've known each other for so long Your heart's been aching, but you're too shy to say it Inside, we both know what's been going on We know the game, and we're gonna play it I just wanna tell you how I'm feeling Gotta make you understand Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you Never gonna give you up Never gonna let you down Never gonna run around and desert you Never gonna make you cry Never gonna say goodbye Never gonna tell a lie and hurt you", "research/entry.thavma.story.story_test.page0.paragraph0": "Lorem ipsum %s 1 sit amet,", "research/entry.thavma.story.story_test.page0.paragraph1": "this story a great meaning haveth.", "research/entry.thavma.story.story_test.page0.title": "A Courtesy Call 1", @@ -177,13 +178,14 @@ "research/entry.thavma.story.story_test.page2.paragraph1": "this story a great meaning haveth.", "research/entry.thavma.story.story_test.page2.title": "A Courtesy Call 3", "research/entry.thavma.story.story_test.paragraph_feature0": "As fate would have it, this paragraph will probably not start a page because it does not have the mustStartPage flag set to true, which is actually rather a worrisome tragedy. Somebody had better do something about it soon.", - "research/entry.thavma.story.story_test.paragraph_feature1": "This is an experiment simply due to the fact that I am testing out double-trim-indent behaviour! If this fails and I am left to contemplate my existence in the bottomless abyss that my mind is, I will be making phone calls real fast real soon. No manager is safe from this bad birch. (I got my Karen haircut done yesterday so while you mongrels crawl about and lick your \"gracious\" masters' fingers, I will bee the queen out of every damn bastard who makes me think badly of me, myself included.)", - "research/entry.thavma.story.story_test.paragraph_feature2": "On the other (also doubly trimmed hand), this paragraph should start a new page always.", + "research/entry.thavma.story.story_test.paragraph_feature1": "This is an experiment simply due to the fact that I am testing out double-trim-indent behaviour! If this fails and I am left to contemplate my existence in the bottomless abyss that my mind is, I will be making phone calls real fast real soon. (I got my Karen haircut done yesterday so while you mongrels crawl about, I will bee the queen out of every damn bastard who makes me think badly of me, myself included.)", + "research/entry.thavma.story.story_test.paragraph_feature2": "On the other hand, this paragraph should start a new page always. It has the mustStartPage property set to true :3", "research/entry.thavma.story.story_test.paragraph_feature3": "Just another random little paragraph :D", + "research/entry.thavma.story.story_test.paragraph_feature4": "And BAM! smack that paragraph right in the middle without it being an image caption! (Both the image and this have a preferred page index set to 5 -> always page number 6)", "research/entry.thavma.story.story_test.title_feature0": "A courtesy call starts the page", "research/entry.thavma.story.story_test.title_feature1": "This title might appear in the middle", "research/entry.thavma.story.story_test.title_feature2": "(start of page)", - "research/entry.thavma.story.story_test.title_feature3": "this is page number 1! Surprise!", + "research/entry.thavma.story.story_test.title_feature3": "this is page number 1! Surprise! Even though this Title is added as the fourth one in the DataPackBuiltinEntriesProvider and LanguageProvider after a ton of paragraphs and titles and figures etc., it has a preferred page index 0!", "research/entry.thavma.thavma.alchemy": "Alchemy", "research/entry.thavma.thavma.arcane_lens": "The Arcane Lens", "research/entry.thavma.thavma.arcane_lens.page0.paragraph0": "The part of the book I can read describes an arcane tool that \"allows the user to see\", whatever that might mean. I have a feeling that crafting it could assist my work in unsealing the other pages.", diff --git a/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json b/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json index 9b3e9f65..814d9b23 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json @@ -28,6 +28,21 @@ "translate": "research/entry.thavma.story.story_test.paragraph_feature1" } }, + { + "type": "thavma:figure", + "caption": { + "color": "dark_aqua", + "italic": true, + "translate": "research/entry.thavma.story.story_test.figure_feature0" + }, + "image": { + "canvas_height": 101, + "canvas_width": 180, + "height": 101, + "location": "thavma:textures/gui/images/important_image.png", + "width": 180 + } + }, { "type": "thavma:title", "text": { @@ -65,6 +80,26 @@ "text": { "translate": "research/entry.thavma.story.story_test.paragraph_feature3" } + }, + { + "type": "thavma:figure", + "has_set_page": true, + "image": { + "canvas_height": 77, + "canvas_width": 87, + "height": 77, + "location": "thavma:textures/gui/images/important_image2.png", + "width": 87 + }, + "preferred_page": 5 + }, + { + "type": "thavma:paragraph", + "has_set_page": true, + "preferred_page": 5, + "text": { + "translate": "research/entry.thavma.story.story_test.paragraph_feature4" + } } ], "position": [ diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt index 5021c727..475a96df 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt @@ -9,6 +9,7 @@ import me.alegian.thavma.impl.client.util.usePose import me.alegian.thavma.impl.common.book.FigureFeature import me.alegian.thavma.impl.common.book.FormattedTextFeature import me.alegian.thavma.impl.common.book.PageFeature +import me.alegian.thavma.impl.common.book.ParagraphFeature import me.alegian.thavma.impl.common.book.TitleFeature import net.minecraft.client.Minecraft @@ -20,9 +21,12 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { val font = Minecraft.getInstance().font val LINE_HEIGHT = font.lineHeight + 2 + Column({ + //println("size before setting initial column is $size") size = grow() gap = 4 + //println("size after initial column is $size") }) { for (feature in features) { when (feature) { @@ -32,7 +36,11 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { } is FormattedTextFeature -> Row({ - size = grow() + //println("size before setting row of FormattedTextFeature is $size") + //size = grow() + width = grow() + height = fixed((font.lineHeight + 2) * feature.text.size + (font.lineHeight + 2) * 2 / 3) + //println("size after FormattedTextFeature is $size") }) { relativeRenderable { guiGraphics, _, _, _ -> guiGraphics.usePose { @@ -48,7 +56,29 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { } } - is FigureFeature -> Image(feature) + is FigureFeature -> Image(feature, maxWidth) + + is ParagraphFeature -> Row({ + //println("size before setting row of FormattedTextFeature is $size") + //size = grow() + val lines = font.split(feature.text, maxWidth) + width = grow() + height = fixed((font.lineHeight + 2) * lines.size + (font.lineHeight + 2) * 2 / 3) + //println("size after FormattedTextFeature is $size") + }) { + relativeRenderable { guiGraphics, _, _, _ -> + guiGraphics.usePose { + for (line in font.split(feature.text, maxWidth)) { + //guiGraphics.drawString(feature.font, line) + guiGraphics.drawString(font, line) + //translateXY(0, feature.font.lineHeight) + translateXY(0, LINE_HEIGHT) + } + //translateXY(0, feature.font.lineHeight * 2 / 3) + translateXY(0, LINE_HEIGHT * 2 / 3) + } + } + } } } } @@ -57,7 +87,9 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { private fun Separator() { Row({ + //println("width before setting row of SEPARATOR is $width") width = grow() + //println("width after separator is $width") alignMain = Alignment.CENTER }) { TextureBox(SEPARATOR) {} @@ -69,26 +101,43 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { val lines = font.split(title.text, maxWidth) Row({ + //println("width before setting row of TitleFeature is $width") width = grow() + //println("width after TitleFeature is $width") height = fixed((font.lineHeight + 2) * lines.size) }) { relativeRenderable { guiGraphics, _, _, _ -> guiGraphics.usePose { - for (line in lines) { + for ((index, line) in lines.withIndex()) { guiGraphics.drawCenteredString(font, line, size.x / 2) - translateXY(0, font.lineHeight) + if (index != lines.size - 1) translateXY(0, font.lineHeight) } } } } } - private fun Image(figure: FigureFeature) { +// private fun Image(figure: FigureFeature, maxWidth: Int) { +// Row({ +// width = grow() +// height = fixed(figure.textureHeight) +// }) { +// relativeRenderable { guiGraphics, _, _, _ -> +// guiGraphics.usePose { +// translateXY((maxWidth - figure.image.width) / 2, 0) +// TextureBox(figure.image) {} +// } +// } +// } +// } +//} + + private fun Image(figure: FigureFeature, maxWidth: Int) { Row({ width = grow() height = fixed(figure.textureHeight) }) { - TextureBox(figure.image) {} + CenteredTextureBox(figure.image, maxWidth) {} } } } diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt index 3aacf5d2..162e193d 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt @@ -74,13 +74,6 @@ fun spliceParagraphOrFigure( println("given the length of the text, this many lines are at the start: ${realLinesRemaining}") when { -// lines.size == 1 && currentHeight + lineHeight <= maxPageHeight -> result += FormattedTextFeature(lines) -// -// lines.size == 1 -> result += listOf( -// FormattedTextFeature(listOf()), -// FormattedTextFeature(lines) -// ) - lines.size <= linesRemainingAtStart && currentHeight + lineHeight * lines.size <= maxPageHeight -> result += FormattedTextFeature( lines ) @@ -98,7 +91,6 @@ fun spliceParagraphOrFigure( val end = lines.slice(lines.size - linesCroppingOutAtEnd until lines.size) - //lines.slice(linesRemainingAtStart + numOfFullPagesCovered * maxLinesPerPage until lines.size - 1) result += FormattedTextFeature(end) } @@ -127,7 +119,7 @@ fun spliceParagraphOrFigure( } currentHeight + textureHeight <= maxPageHeight -> { - result += FigureFeature(image, null) + result += FigureFeature(image, null, input.mustStartPage, input.mustOccupySetPage, input.preferredPageIndex) result.addAll( spliceParagraphOrFigure( ParagraphFeature(caption), @@ -140,7 +132,7 @@ fun spliceParagraphOrFigure( else -> { result += FormattedTextFeature(listOf()) - result += FigureFeature(image, null) + result += FigureFeature(image, null, input.mustStartPage, input.mustOccupySetPage, input.preferredPageIndex) result.addAll( spliceParagraphOrFigure( ParagraphFeature(caption), @@ -163,8 +155,6 @@ fun spliceParagraphOrFigure( * Features in the same list belong together on one page. */ fun pagifyFeatures(features: List, maxHeight: Int, pageWidth: Int, font: Font): List> { - // maxHeight is height of background texture minus padding (32 top 42 bottom) - //val maxHeight = this@EntryScreen.height - 74 println("maxHeight is $maxHeight, pageWidth is $pageWidth") val partition = features.partition { !it.mustOccupySetPage } val pages = mutableListOf>() @@ -305,6 +295,7 @@ fun pagifyFeatures(features: List, maxHeight: Int, pageWidth: Int, // finally add features with pre-determined positions (cannot be paragraphs) // these features have to be ordered correctly in the Research Entry builder + println("The grouping of the second partition is ${partition.second.groupBy { it.preferredPageIndex }}") partition.second.groupBy { it.preferredPageIndex }.forEach { pages.add(it.key, it.value) } println("the final state of pages is") diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt index aba61597..56584e9c 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt @@ -10,6 +10,7 @@ import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.screens.Screen import net.minecraft.core.Holder import net.minecraft.network.chat.Component +import net.minecraft.world.phys.Vec2 class EntryScreen(entry: Holder) : Screen(Component.literal("Book Entry")) { companion object { @@ -19,10 +20,11 @@ class EntryScreen(entry: Holder) : Screen(Component.literal("Book private var currentPage = 0 private val fontify = Minecraft.getInstance().font - private var maxWidth = BG.width/2 - 80 + private var maxWidth = BG.width/2 - 65 + private var maxHeight = BG.height - 90 // maxHeight is height of background texture minus padding (32 top 42 bottom) - var pages = pagifyFeatures(entry.value().pageFeatures, BG.height - 74, maxWidth, fontify) + var pages = pagifyFeatures(entry.value().pageFeatures, maxHeight, maxWidth, fontify) override fun init() { super.init() @@ -52,7 +54,8 @@ class EntryScreen(entry: Holder) : Screen(Component.literal("Book }) { afterLayout { - addRenderableWidget(PageTurningWidget(position, false) { + //addRenderableWidget(PageTurningWidget(position, false) { + addRenderableWidget(PageTurningWidget(Vec2(position.x - maxWidth + 40, position.y + maxHeight + 30), false) { // reinitiate the screen for this research entry when clicked // with an updated page // clearWidgets() is essential, also clears underline formatting! @@ -76,7 +79,7 @@ class EntryScreen(entry: Holder) : Screen(Component.literal("Book }) { afterLayout { - addRenderableWidget(PageTurningWidget(position, true) { + addRenderableWidget(PageTurningWidget(Vec2(position.x - 10, position.y + maxHeight + 30), true) { // reinitiate the screen for this research entry when clicked // with an updated page // clearWidgets() is essential, also clears underline formatting! diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/layout/LayoutExtensions.kt b/src/main/java/me/alegian/thavma/impl/client/gui/layout/LayoutExtensions.kt index 13425938..88591884 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/layout/LayoutExtensions.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/layout/LayoutExtensions.kt @@ -52,6 +52,19 @@ fun relativeRenderable(renderable: Renderable) { } } +fun relativeCenteredRenderable(renderable: Renderable, maxWidth: Int, texture: Texture) { + val screen = LayoutExtensions.currScreen ?: throw IllegalStateException("Thavma Exception: cannot add renderable without setting LayoutExtensions.currScreen first!") + afterLayout { + screen.renderables.add(Renderable { guiGraphics, mouseX, mouseY, partialTick -> + guiGraphics.usePose { + translateXY(position.x + (maxWidth - texture.width)/2, position.y) + renderable.render(guiGraphics, mouseX, mouseY, partialTick) + } + }) + } +} + + fun TextureBox(texture: Texture, children: T7LayoutElement.() -> Unit) = Row({ width = fixed(texture.width) @@ -61,6 +74,15 @@ fun TextureBox(texture: Texture, children: T7LayoutElement.() -> Unit) = children() } +fun CenteredTextureBox(texture: Texture, maxWidth: Int ,children: T7LayoutElement.() -> Unit) = + Row({ + width = fixed(texture.width) + height = fixed(texture.height) + }) { + relativeCenteredRenderable(renderableTexture(texture), maxWidth, texture) + children() + } + private fun T7LayoutElement.slotSetup(slot: Slot) { if (slot !is DynamicSlot<*>) return slot.actualX = position.x diff --git a/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt b/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt index db1c0228..ec5079ef 100644 --- a/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt +++ b/src/main/java/me/alegian/thavma/impl/common/book/FigureFeature.kt @@ -14,6 +14,10 @@ class FigureFeature(val image: Texture, val caption: Component?, override val mu override val coversOneWholePage = false + override fun toString(): String { + return "FigureFeature with caption $caption, mustOccupySetPage set to $mustOccupySetPage and preferred page index $preferredPageIndex" + } + // // override val pageWidth: Int diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index cde33c41..8ba3779a 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -57,6 +57,7 @@ import net.minecraft.world.item.enchantment.effects.AddValue import net.minecraft.world.level.storage.loot.predicates.DamageSourceCondition import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider import net.neoforged.neoforge.registries.NeoForgeRegistries +import org.apache.logging.log4j.core.tools.picocli.CommandLine.Help.Ansi.Style import org.joml.Vector2i import java.util.* import java.util.concurrent.CompletableFuture @@ -142,11 +143,14 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .addPageFeature(makeTitleFeature(true)) .addPageFeature(makeParagraphFeature(false, false)) .addPageFeature(makeParagraphFeature()) + .addPageFeature(makeFigureFeature(Texture("gui/images/important_image", 180, 101, 180, 101), true)) .addPageFeature(makeTitleFeature(false)) .addPageFeature(makeParagraphFeature(true)) .addPageFeature(makeTitleFeature(true, false)) .addPageFeature(makeTitleFeature(true, true, 0)) .addPageFeature(makeParagraphFeature()) + .addPageFeature(makeFigureFeature(Texture("gui/images/important_image2", 87, 77, 87, 77),false,false,true,5)) + .addPageFeature(makeParagraphFeature(false,true,5)) // .addPage(simpleTextPage(2, true)) // .addPage(simpleTextPage(2, true)) // .addPage(simpleTextPage(2, true)) @@ -345,11 +349,12 @@ private fun makeTitleFeature(mustStartPage: Boolean = false, mustOccupySetPage: private fun makeFigureFeature(image: Texture, giveCaption: Boolean, mustStartPage: Boolean = false, mustOccupySetPage: Boolean = false, preferredPageIndex: Int = 1): (ResourceKey, Int) -> FigureFeature{ return if (giveCaption) { entryKey, figureIndex -> val baseId = ResearchEntry.translationId(entryKey) - FigureFeature(image, Component.translatable(FigureFeature.translationId(baseId, figureIndex)), + FigureFeature(image, Component.translatable(FigureFeature.translationId(baseId, figureIndex)).withStyle( + ChatFormatting.ITALIC).withStyle(ChatFormatting.DARK_AQUA), mustStartPage, mustOccupySetPage, preferredPageIndex ) } else { entryKey, figureIndex -> - FigureFeature(image, null, mustStartPage, mustOccupySetPage) + FigureFeature(image, null, mustStartPage, mustOccupySetPage, preferredPageIndex) } } diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index 481f7eb4..bc3aa39c 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -298,26 +298,112 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( "this story a great meaning haveth." ) - addTitleFeature(ResearchEntries.Story.TEST, 0,"A courtesy call starts the page") - addParagraphFeature(ResearchEntries.Story.TEST, 0, """ + addTitleFeature(ResearchEntries.Story.TEST, 0, "A courtesy call starts the page") + addParagraphFeature( + ResearchEntries.Story.TEST, 0, """ As fate would have it, this paragraph will probably not start a page because it does not have the mustStartPage flag set to true, which is actually rather a worrisome tragedy. Somebody had better do something about it soon. - """) - addParagraphFeature(ResearchEntries.Story.TEST, 1, """ + """ + ) + addParagraphFeature( + ResearchEntries.Story.TEST, 1, """ This is an experiment simply due to the fact that I am testing out double-trim-indent behaviour! If this fails and I am left to contemplate my existence in the bottomless abyss that my mind is, - I will be making phone calls real fast real soon. No manager is safe from this bad birch. - (I got my Karen haircut done yesterday so while you mongrels crawl about and lick your "gracious" masters' fingers, + I will be making phone calls real fast real soon. + (I got my Karen haircut done yesterday so while you mongrels crawl about, I will bee the queen out of every damn bastard who makes me think badly of me, myself included.) - """.trimIndent()) + """.trimIndent() + ) + addFigureFeature( + ResearchEntries.Story.TEST, 0, """ +We're no strangers to love +You know the rules and so do I +A full commitment's what I'm thinking of +You wouldn't get this from any other guy +I just wanna tell you how I'm feeling +Gotta make you understand +Never gonna give you up +Never gonna let you down +Never gonna run around and desert you +Never gonna make you cry +Never gonna say goodbye +Never gonna tell a lie and hurt you +We've known each other for so long +Your heart's been aching, but you're too shy to say it +Inside, we both know what's been going on +We know the game, and we're gonna play it +And if you ask me how I'm feeling +Don't tell me you're too blind to see +Never gonna give you up +Never gonna let you down +Never gonna run around and desert you +Never gonna make you cry +Never gonna say goodbye +Never gonna tell a lie and hurt you +Never gonna give you up +Never gonna let you down +Never gonna run around and desert you +Never gonna make you cry +Never gonna say goodbye +Never gonna tell a lie and hurt you +Ooh (Give you up) +Ooh-ooh (Give you up) +Ooh (Never gonna give, never gonna give) +Give you up +Ooh-ooh (Never gonna give, never gonna give) +Give you up +We've known each other for so long +Your heart's been aching, but you're too shy to say it +Inside, we both know what's been going on +We know the game, and we're gonna play it +I just wanna tell you how I'm feeling +Gotta make you understand +Never gonna give you up +Never gonna let you down +Never gonna run around and desert you +Never gonna make you cry +Never gonna say goodbye +Never gonna tell a lie and hurt you +Never gonna give you up +Never gonna let you down +Never gonna run around and desert you +Never gonna make you cry +Never gonna say goodbye +Never gonna tell a lie and hurt you +Never gonna give you up +Never gonna let you down +Never gonna run around and desert you +Never gonna make you cry +Never gonna say goodbye +Never gonna tell a lie and hurt you +Never gonna give you up +Never gonna let you down +Never gonna run around and desert you +Never gonna make you cry +Never gonna say goodbye +Never gonna tell a lie and hurt you + """.trimIndent() + ) addTitleFeature(ResearchEntries.Story.TEST, 1, "This title might appear in the middle") - addParagraphFeature(ResearchEntries.Story.TEST, 2, """ - On the other (also doubly trimmed hand), this paragraph should start a new page always. + addParagraphFeature( + ResearchEntries.Story.TEST, 2, """ + On the other hand, this paragraph should start a new page always. It has the mustStartPage property set to true :3 + """.trimIndent() + ) + addTitleFeature(ResearchEntries.Story.TEST, 2, "(start of page)") + addTitleFeature(ResearchEntries.Story.TEST, 3, """ + this is page number 1! Surprise! Even though this Title is added as the + fourth one in the DataPackBuiltinEntriesProvider and LanguageProvider + after a ton of paragraphs and titles and figures etc., + it has a preferred page index 0! """.trimIndent()) - addTitleFeature(ResearchEntries.Story.TEST, 2,"(start of page)") - addTitleFeature(ResearchEntries.Story.TEST, 3, "this is page number 1! Surprise!") addParagraphFeature(ResearchEntries.Story.TEST, 3, "Just another random little paragraph :D") + addParagraphFeature(ResearchEntries.Story.TEST, 4, """ + And BAM! smack that paragraph right in the middle without it + being an image caption! (Both the image and this have a + preferred page index set to 5 -> always page number 6) + """.trimIndent()) addTextPage( ResearchEntries.Thavma.THAVMA, 0, @@ -461,7 +547,7 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( add(TitleFeature.translationId(baseId, featureIndex), text.trimIndent().replace("\n", " ")) } - private fun addFigureFeature(entryKey: ResourceKey, featureIndex: Int, text: String){ + private fun addFigureFeature(entryKey: ResourceKey, featureIndex: Int, text: String) { val baseId = ResearchEntry.translationId(entryKey) add(FigureFeature.translationId(baseId, featureIndex), text.trimIndent().replace("\n", " ")) } diff --git a/src/main/resources/assets/thavma/textures/gui/images/important_image.png b/src/main/resources/assets/thavma/textures/gui/images/important_image.png new file mode 100644 index 0000000000000000000000000000000000000000..1de24135647954b4f617a7d7705ae50798eed9ff GIT binary patch literal 41205 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 IM6N<$f@X>z7XSbN literal 0 HcmV?d00001 From a9ea1219c34bbfeb28add569f5e1e62b11796f00 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Tue, 26 May 2026 00:43:23 +0200 Subject: [PATCH 15/19] Adjustable font size done, need to discuss config options. --- .../java/me/alegian/thavma/config/Config.kt | 22 +++++ .../java/me/alegian/thavma/impl/Thavma.kt | 2 + .../gui/book/DynamicFeaturesRenderer.kt | 52 +++++++--- .../client/gui/book/DynamicRenderingHelper.kt | 61 +++++++----- .../impl/client/gui/book/EntryScreen.kt | 6 +- .../client/gui/book/PageFeatureRenderer.kt | 3 +- .../T7DatapackBuiltinEntriesProvider.kt | 98 +++++++++++++++---- .../init/data/providers/T7LanguageProvider.kt | 4 +- 8 files changed, 180 insertions(+), 68 deletions(-) create mode 100644 src/main/java/me/alegian/thavma/config/Config.kt diff --git a/src/main/java/me/alegian/thavma/config/Config.kt b/src/main/java/me/alegian/thavma/config/Config.kt new file mode 100644 index 00000000..586fc3b8 --- /dev/null +++ b/src/main/java/me/alegian/thavma/config/Config.kt @@ -0,0 +1,22 @@ +package me.alegian.thavma.config + +import net.neoforged.neoforge.common.ModConfigSpec + +object Config { + private val BUILDER = ModConfigSpec.Builder() + + val SPEC: ModConfigSpec + var FONT_SIZE_MULTIPLIER: ModConfigSpec.DoubleValue + + + init { + BUILDER.push("general_settings") + + FONT_SIZE_MULTIPLIER = BUILDER + .comment("A number to multiply font size with") + .defineInRange("fontSizeMultiplier", 1.0, 0.1, 2.0) + + BUILDER.pop() + SPEC = BUILDER.build() + } +} \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/Thavma.kt b/src/main/java/me/alegian/thavma/impl/Thavma.kt index d97a950a..b6f2f415 100644 --- a/src/main/java/me/alegian/thavma/impl/Thavma.kt +++ b/src/main/java/me/alegian/thavma/impl/Thavma.kt @@ -16,6 +16,8 @@ import thedarkcolour.kotlinforforge.neoforge.forge.MOD_BUS as KFF_MOD_BUS object Thavma { const val MODID: String = "thavma" + //modContainer.registerConfig(ModConfig.Type.COMMON, MyModConfig.SPEC) + init { T7ArmorMaterials.REGISTRAR.register(KFF_MOD_BUS) T7Blocks.REGISTRAR.register(KFF_MOD_BUS) diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt index 475a96df..9f6b8531 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicFeaturesRenderer.kt @@ -12,14 +12,23 @@ import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.book.ParagraphFeature import me.alegian.thavma.impl.common.book.TitleFeature import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Font +import net.minecraft.util.Mth.ceil +import javax.sound.sampled.Line object DynamicFeaturesRenderer : PageFeatureRenderer { private val SEPARATOR = Texture("gui/book/separator", 128, 16, 128, 16) - override fun initPageFeatures(screen: EntryScreen, features: List, maxWidth: Int) { + override fun initPageFeatures( + screen: EntryScreen, + features: List, + maxWidth: Int, + font: Font, + scale: Float + ) { - val font = Minecraft.getInstance().font - val LINE_HEIGHT = font.lineHeight + 2 + //val font = Minecraft.getInstance().font + val LINE_HEIGHT = ceil((font.lineHeight * scale + 2)) Column({ @@ -31,7 +40,7 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { for (feature in features) { when (feature) { is TitleFeature -> { - Title(feature, maxWidth) + Title(feature, maxWidth, font, scale) Separator() } @@ -39,42 +48,53 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { //println("size before setting row of FormattedTextFeature is $size") //size = grow() width = grow() - height = fixed((font.lineHeight + 2) * feature.text.size + (font.lineHeight + 2) * 2 / 3) + //height = fixed(LINE_HEIGHT * feature.text.size + ceil(LINE_HEIGHT.toFloat() * (scale+1)) * 2f / 3) + height = fixed(LINE_HEIGHT * (feature.text.size + 0.5f)) //println("size after FormattedTextFeature is $size") }) { relativeRenderable { guiGraphics, _, _, _ -> + //guiGraphics.pose().pushPose() + guiGraphics.pose().scale(scale, scale, 1.0f) guiGraphics.usePose { for (line in feature.text) { //guiGraphics.drawString(feature.font, line) guiGraphics.drawString(font, line) //translateXY(0, feature.font.lineHeight) - translateXY(0, LINE_HEIGHT) + translateXY(0, LINE_HEIGHT/scale) } //translateXY(0, feature.font.lineHeight * 2 / 3) + //guiGraphics.pose().popPose() translateXY(0, LINE_HEIGHT * 2 / 3) } + } } + is FigureFeature -> Image(feature, maxWidth) is ParagraphFeature -> Row({ //println("size before setting row of FormattedTextFeature is $size") //size = grow() - val lines = font.split(feature.text, maxWidth) + val lines = font.split(feature.text, (maxWidth / scale).toInt()) width = grow() - height = fixed((font.lineHeight + 2) * lines.size + (font.lineHeight + 2) * 2 / 3) + //height = fixed(LINE_HEIGHT * lines.size + ceil(LINE_HEIGHT.toFloat() * (scale+1)) * 2f / 3) + height = fixed(LINE_HEIGHT * (lines.size + 0.5f)) //println("size after FormattedTextFeature is $size") }) { relativeRenderable { guiGraphics, _, _, _ -> + //guiGraphics.pose().pushPose() + //val scale = 1f + guiGraphics.pose().scale(scale, scale, 1.0f) guiGraphics.usePose { - for (line in font.split(feature.text, maxWidth)) { + for (line in font.split(feature.text, (maxWidth / scale).toInt())) { //guiGraphics.drawString(feature.font, line) guiGraphics.drawString(font, line) //translateXY(0, feature.font.lineHeight) - translateXY(0, LINE_HEIGHT) + translateXY(0, LINE_HEIGHT/scale) } //translateXY(0, feature.font.lineHeight * 2 / 3) + //guiGraphics.pose().popPose() translateXY(0, LINE_HEIGHT * 2 / 3) } } @@ -96,21 +116,21 @@ object DynamicFeaturesRenderer : PageFeatureRenderer { } } - private fun Title(title: TitleFeature, maxWidth: Int) { - val font = Minecraft.getInstance().font - val lines = font.split(title.text, maxWidth) + private fun Title(title: TitleFeature, maxWidth: Int, font: Font, scale: Float) { + val lines = font.split(title.text, (maxWidth / scale).toInt()) Row({ //println("width before setting row of TitleFeature is $width") width = grow() //println("width after TitleFeature is $width") - height = fixed((font.lineHeight + 2) * lines.size) + height = fixed(ceil((font.lineHeight * scale + 2)) * lines.size) }) { relativeRenderable { guiGraphics, _, _, _ -> + guiGraphics.pose().scale(scale, scale, 1.0f) guiGraphics.usePose { for ((index, line) in lines.withIndex()) { - guiGraphics.drawCenteredString(font, line, size.x / 2) - if (index != lines.size - 1) translateXY(0, font.lineHeight) + guiGraphics.drawCenteredString(font, line, size.x/scale / 2) + if (index != lines.size - 1) translateXY(0, ceil((font.lineHeight * scale + 2))/scale) } } } diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt index 162e193d..8ca507cf 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/DynamicRenderingHelper.kt @@ -1,5 +1,6 @@ package me.alegian.thavma.impl.client.gui.book +import me.alegian.thavma.config.Config import me.alegian.thavma.impl.common.book.FigureFeature import me.alegian.thavma.impl.common.book.FormattedTextFeature import me.alegian.thavma.impl.common.book.PageFeature @@ -7,22 +8,22 @@ import me.alegian.thavma.impl.common.book.ParagraphFeature import me.alegian.thavma.impl.common.book.RecipeFeature import me.alegian.thavma.impl.common.book.TitleFeature import net.minecraft.client.gui.Font -import net.minecraft.network.chat.Component +import net.minecraft.util.Mth.ceil +import net.minecraft.util.Mth.floor import kotlin.math.min -private fun PageFeature.renderedHeight(pageWidth: Int, font: Font): Int { - val lineHeight = font.lineHeight + 2 +private fun PageFeature.renderedHeight(pageWidth: Int, font: Font, scale: Float): Int { + val lineHeight = ceil((font.lineHeight * scale) + 2) return when (this) { - is ParagraphFeature -> font.split(this.text, pageWidth).size * lineHeight - is TitleFeature -> font.split(this.text, pageWidth).size * lineHeight + 16 + is ParagraphFeature -> font.split(this.text, floor(pageWidth/scale)).size * lineHeight + is TitleFeature -> font.split(this.text, (pageWidth/scale).toInt()).size * lineHeight + 16 is FigureFeature -> if (caption != null) font.split( this.caption, - pageWidth + (pageWidth/scale).toInt() ).size * lineHeight + this.textureHeight else this.textureHeight - is RecipeFeature -> 96 is FormattedTextFeature -> text.size * lineHeight - else -> throw IllegalArgumentException("This PageFeature $this does not have renderedHeight implemented yet") + else -> throw IllegalArgumentException("This PageFeature $this does not have renderedHeight() implemented yet") } } @@ -34,14 +35,14 @@ private fun PageFeature.renderedHeight(pageWidth: Int, font: Font): Int { */ fun spliceParagraphOrFigure( input: PageFeature, maxPageHeight: Int, - currentHeight: Int, maxPageWidth: Int, font: Font + currentHeight: Int, maxPageWidth: Int, font: Font, scale: Float ): List { val result = mutableListOf() println("maxHeight is $maxPageHeight, pageWidth is $maxPageWidth, processing page feature $input, current height is $currentHeight") if (input is ParagraphFeature) { // how many lines fit in the current page - val lineHeight = font.lineHeight + 2 + val lineHeight = ceil((font.lineHeight * scale + 2)) //* Config.FONT_SIZE_MULTIPLIER.get() println("lineheight is $lineHeight") // so that we get at least 2 lines at the end of the first page @@ -54,13 +55,15 @@ fun spliceParagraphOrFigure( val numOfLinesCoveringFullPages: Int = (input.renderedHeight( maxPageWidth, - font + font, + scale ) - linesRemainingAtStart * lineHeight) / maxPageHeight * maxPageHeight / lineHeight println("number of lines covering whole pages is $numOfLinesCoveringFullPages") var linesCroppingOutAtEnd: Int = (input.renderedHeight( maxPageWidth, - font + font, + scale ) - linesRemainingAtStart * lineHeight - numOfLinesCoveringFullPages * lineHeight) / lineHeight if (linesCroppingOutAtEnd < 0) linesCroppingOutAtEnd = 0 println("number of lines cropping out at end are $linesCroppingOutAtEnd") @@ -68,7 +71,7 @@ fun spliceParagraphOrFigure( println("max lines per page is $maxLinesPerPage") val numOfFullPagesCovered = numOfLinesCoveringFullPages / maxLinesPerPage println("number of full pages is $numOfFullPagesCovered") - val lines = font.split(input.text, maxPageWidth) + val lines = font.split(input.text, (maxPageWidth/scale).toInt()) println("the text was split into this number of lines: ${lines.size}") val realLinesRemaining: Int = min(linesRemainingAtStart, lines.size) println("given the length of the text, this many lines are at the start: ${realLinesRemaining}") @@ -119,27 +122,30 @@ fun spliceParagraphOrFigure( } currentHeight + textureHeight <= maxPageHeight -> { - result += FigureFeature(image, null, input.mustStartPage, input.mustOccupySetPage, input.preferredPageIndex) + //result += FigureFeature(image, null, input.mustStartPage, input.mustOccupySetPage, input.preferredPageIndex) + result += this result.addAll( spliceParagraphOrFigure( ParagraphFeature(caption), maxPageHeight, currentHeight + textureHeight, - maxPageWidth, font + maxPageWidth, font, scale ) ) } else -> { result += FormattedTextFeature(listOf()) - result += FigureFeature(image, null, input.mustStartPage, input.mustOccupySetPage, input.preferredPageIndex) + //result += FigureFeature(image, null, input.mustStartPage, input.mustOccupySetPage, input.preferredPageIndex) + result += this result.addAll( spliceParagraphOrFigure( ParagraphFeature(caption), maxPageHeight, textureHeight, maxPageWidth, - font + font, + scale ) ) } @@ -154,12 +160,12 @@ fun spliceParagraphOrFigure( * Returns a list of lists of features where every index represents a page. * Features in the same list belong together on one page. */ -fun pagifyFeatures(features: List, maxHeight: Int, pageWidth: Int, font: Font): List> { +fun pagifyFeatures(features: List, maxHeight: Int, pageWidth: Int, font: Font, scale: Float): List> { println("maxHeight is $maxHeight, pageWidth is $pageWidth") val partition = features.partition { !it.mustOccupySetPage } val pages = mutableListOf>() val buffer = mutableListOf() - fun currentHeight() = buffer.sumOf { it.renderedHeight(pageWidth, font) } + fun currentHeight() = buffer.sumOf { it.renderedHeight(pageWidth, font, scale) } fun submitBufferAndClear() { pages.add(buffer.toList()) println("===== Just submitted from buffer =====") @@ -180,12 +186,14 @@ fun pagifyFeatures(features: List, maxHeight: Int, pageWidth: Int, when { (this !is ParagraphFeature && this !is FigureFeature) && renderedHeight( pageWidth, - font + font, + scale ) > maxHeight -> throw IllegalArgumentException( "The size of the element ${this::class.simpleName} is too large at ${ renderedHeight( pageWidth, - font + font, + scale ) } while allowed $maxHeight." ) @@ -199,7 +207,7 @@ fun pagifyFeatures(features: List, maxHeight: Int, pageWidth: Int, if (buffer.isNotEmpty()) submitBufferAndClear() if (this is ParagraphFeature || this is FigureFeature) { println("Currently have this in the result: $pages") - val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight(), pageWidth, font) + val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight(), pageWidth, font, scale) if (processed.size < 2) buffer += processed.first() // only need to check this much since buffer is empty (starts page) else if (this is ParagraphFeature) { @@ -222,7 +230,7 @@ fun pagifyFeatures(features: List, maxHeight: Int, pageWidth: Int, } (this is ParagraphFeature || this is FigureFeature) -> { - val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight(), pageWidth, font) + val processed = spliceParagraphOrFigure(this, maxHeight, currentHeight(), pageWidth, font, scale) if (processed.size < 2) buffer += processed.first() else if (this is ParagraphFeature) { buffer += processed.first() @@ -268,13 +276,14 @@ fun pagifyFeatures(features: List, maxHeight: Int, pageWidth: Int, } } - currentHeight() + renderedHeight(pageWidth, font) <= maxHeight -> buffer += this + currentHeight() + renderedHeight(pageWidth, font, scale) <= maxHeight -> buffer += this // the next check might be redundant: buffer.isEmpty() -> throw IllegalArgumentException( "The size of the element ${this::class.simpleName} is too large at ${ renderedHeight( pageWidth, - font + font, + scale ) } while allowed $maxHeight." ) @@ -293,7 +302,7 @@ fun pagifyFeatures(features: List, maxHeight: Int, pageWidth: Int, println("current state of pages before adding predetermined stuff is") for (i in pages) println(i) - // finally add features with pre-determined positions (cannot be paragraphs) + // finally add features with pre-determined positions (cannot be long paragraphs) // these features have to be ordered correctly in the Research Entry builder println("The grouping of the second partition is ${partition.second.groupBy { it.preferredPageIndex }}") partition.second.groupBy { it.preferredPageIndex }.forEach { pages.add(it.key, it.value) } diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt index 56584e9c..b9245a1a 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryScreen.kt @@ -23,8 +23,10 @@ class EntryScreen(entry: Holder) : Screen(Component.literal("Book private var maxWidth = BG.width/2 - 65 private var maxHeight = BG.height - 90 + private val scale = 1.75f + // maxHeight is height of background texture minus padding (32 top 42 bottom) - var pages = pagifyFeatures(entry.value().pageFeatures, maxHeight, maxWidth, fontify) + var pages = pagifyFeatures(entry.value().pageFeatures, maxHeight, maxWidth, fontify, scale) override fun init() { super.init() @@ -110,7 +112,7 @@ class EntryScreen(entry: Holder) : Screen(Component.literal("Book private fun initPageFeatures(features: List?) { if (features != null) { val renderer = DynamicFeaturesRenderer - renderer.initPageFeatures(this, features, maxWidth) + renderer.initPageFeatures(this, features, maxWidth, fontify, scale) } } diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt index 107c7fc0..15fdeccb 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/PageFeatureRenderer.kt @@ -1,7 +1,8 @@ package me.alegian.thavma.impl.client.gui.book import me.alegian.thavma.impl.common.book.PageFeature +import net.minecraft.client.gui.Font interface PageFeatureRenderer{ - fun initPageFeatures(screen: EntryScreen, features: List, maxWidth: Int) + fun initPageFeatures(screen: EntryScreen, features: List, maxWidth: Int, font: Font, scale: Float) } \ No newline at end of file diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index 8ba3779a..881a9d0d 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -4,11 +4,9 @@ import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.client.texture.Texture import me.alegian.thavma.impl.common.aspect.Aspect import me.alegian.thavma.impl.common.book.CraftingPage -import me.alegian.thavma.impl.common.book.DynamicPage import me.alegian.thavma.impl.common.book.FigureFeature import me.alegian.thavma.impl.common.book.Page import me.alegian.thavma.impl.common.book.PageFeature -import me.alegian.thavma.impl.common.book.PageFeatureType import me.alegian.thavma.impl.common.book.ParagraphFeature import me.alegian.thavma.impl.common.book.RecipeFeature import me.alegian.thavma.impl.common.book.TextPage @@ -57,7 +55,6 @@ import net.minecraft.world.item.enchantment.effects.AddValue import net.minecraft.world.level.storage.loot.predicates.DamageSourceCondition import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider import net.neoforged.neoforge.registries.NeoForgeRegistries -import org.apache.logging.log4j.core.tools.picocli.CommandLine.Help.Ansi.Style import org.joml.Vector2i import java.util.* import java.util.concurrent.CompletableFuture @@ -140,17 +137,17 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, 0), false, Items.TURTLE_HELMET.defaultInstance) .research() - .addPageFeature(makeTitleFeature(true)) + .addPageFeature(makeTitleFeature()) .addPageFeature(makeParagraphFeature(false, false)) .addPageFeature(makeParagraphFeature()) - .addPageFeature(makeFigureFeature(Texture("gui/images/important_image", 180, 101, 180, 101), true)) + .addPageFeature(makeFigureFeature(Texture("gui/images/important_image", 180, 101, 180, 101), true, false, false, 1, ChatFormatting.DARK_AQUA, ChatFormatting.ITALIC)) .addPageFeature(makeTitleFeature(false)) .addPageFeature(makeParagraphFeature(true)) .addPageFeature(makeTitleFeature(true, false)) .addPageFeature(makeTitleFeature(true, true, 0)) .addPageFeature(makeParagraphFeature()) - .addPageFeature(makeFigureFeature(Texture("gui/images/important_image2", 87, 77, 87, 77),false,false,true,5)) - .addPageFeature(makeParagraphFeature(false,true,5)) + .addPageFeature(makeFigureFeature(Texture("gui/images/important_image2", 87, 77, 87, 77), false, false, true, 5)) + .addPageFeature(makeParagraphFeature(false, true, 5)) // .addPage(simpleTextPage(2, true)) // .addPage(simpleTextPage(2, true)) // .addPage(simpleTextPage(2, true)) @@ -177,13 +174,23 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .addChild(ResearchEntries.Thavma.ARCANE_LENS) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.ARCANE_LENS, Vector2i(2, -2), false, T7Items.ARCANE_LENS.get().defaultInstance) + ResearchEntryBuilder( + ResearchEntries.Thavma.ARCANE_LENS, + Vector2i(2, -2), + false, + T7Items.ARCANE_LENS.get().defaultInstance + ) .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) .addPage(simpleTextPage(3, true)) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.RESEARCH_TABLE, Vector2i(0, 0), true, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance) + ResearchEntryBuilder( + ResearchEntries.Thavma.RESEARCH_TABLE, + Vector2i(0, 0), + true, + T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance + ) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) .addPage { _, _ -> CraftingPage(Recipes.CHEST) } .addChild(ResearchEntries.Thavma.WANDS) @@ -328,39 +335,88 @@ private fun simpleTextPage(paragraphCount: Int, hasTitle: Boolean): (ResourceKey } } -private fun makeParagraphFeature(mustStartPage: Boolean = false, mustOccupySetPage: Boolean = false, preferredPageIndex: Int = 1): (ResourceKey, Int) -> ParagraphFeature { +private fun makeParagraphFeature( + mustStartPage: Boolean = false, + mustOccupySetPage: Boolean = false, + preferredPageIndex: Int = 1 +): (ResourceKey, Int) -> ParagraphFeature { return { entryKey, paragraphIndex -> val baseId = ResearchEntry.translationId(entryKey) - ParagraphFeature(Component.translatable(ParagraphFeature.translationId(baseId, paragraphIndex)), + ParagraphFeature( + Component.translatable(ParagraphFeature.translationId(baseId, paragraphIndex)), mustStartPage, mustOccupySetPage, preferredPageIndex ) } } -private fun makeTitleFeature(mustStartPage: Boolean = false, mustOccupySetPage: Boolean = false, preferredPageIndex: Int = 1): (ResourceKey, Int) -> TitleFeature{ +private fun makeStyledParagraphFeature( + mustStartPage: Boolean = false, + mustOccupySetPage: Boolean = false, + preferredPageIndex: Int = 1, + vararg styles: ChatFormatting? +): (ResourceKey, Int) -> ParagraphFeature { + return { entryKey, paragraphIndex -> + val baseId = ResearchEntry.translationId(entryKey) + val content = Component.translatable(ParagraphFeature.translationId(baseId, paragraphIndex)) + for (i in styles) { + content.apply { + if (i != null) { + this.withStyle(i) + } + } + } + ParagraphFeature(content, mustStartPage, mustOccupySetPage, preferredPageIndex) + } +} + +private fun makeTitleFeature( + mustStartPage: Boolean = true, + mustOccupySetPage: Boolean = false, + preferredPageIndex: Int = 1 +): (ResourceKey, Int) -> TitleFeature { return { entryKey, titleIndex -> val baseId = ResearchEntry.translationId(entryKey) - TitleFeature(Component.translatable(TitleFeature.translationId(baseId, titleIndex)).withStyle(ChatFormatting.BOLD), + TitleFeature( + Component.translatable(TitleFeature.translationId(baseId, titleIndex)).withStyle(ChatFormatting.BOLD), mustStartPage, mustOccupySetPage, preferredPageIndex ) } } -private fun makeFigureFeature(image: Texture, giveCaption: Boolean, mustStartPage: Boolean = false, mustOccupySetPage: Boolean = false, preferredPageIndex: Int = 1): (ResourceKey, Int) -> FigureFeature{ +private fun makeFigureFeature( + image: Texture, + giveCaption: Boolean, + mustStartPage: Boolean = false, + mustOccupySetPage: Boolean = false, + preferredPageIndex: Int = 1, + vararg styles: ChatFormatting? +): (ResourceKey, Int) -> FigureFeature { return if (giveCaption) { entryKey, figureIndex -> val baseId = ResearchEntry.translationId(entryKey) - FigureFeature(image, Component.translatable(FigureFeature.translationId(baseId, figureIndex)).withStyle( - ChatFormatting.ITALIC).withStyle(ChatFormatting.DARK_AQUA), - mustStartPage, mustOccupySetPage, preferredPageIndex - ) - } else { entryKey, figureIndex -> + val content = Component.translatable(FigureFeature.translationId(baseId, figureIndex)) + for (i in styles) { + content.apply { + if (i != null) { + this.withStyle(i) + } + } + } + FigureFeature(image, content, mustStartPage, mustOccupySetPage, preferredPageIndex) + } else { _, _ -> FigureFeature(image, null, mustStartPage, mustOccupySetPage, preferredPageIndex) } } -private fun makeRecipeFeature(recipeRL: ResourceLocation, coversOneWholePage: Boolean = true, mustStartPage: Boolean = true, mustOccupySetPage: Boolean = true, preferredPageIndex: Int = 1): (ResourceKey, Int) -> RecipeFeature{ +private fun makeRecipeFeature( + recipeRL: ResourceLocation, + coversOneWholePage: Boolean = true, + mustStartPage: Boolean = true, + mustOccupySetPage: Boolean = true, + preferredPageIndex: Int = 1 +): (ResourceKey, Int) -> RecipeFeature { return { entryKey, titleIndex -> - RecipeFeature(recipeRL, coversOneWholePage, mustStartPage, mustOccupySetPage, + RecipeFeature( + recipeRL, coversOneWholePage, mustStartPage, mustOccupySetPage, preferredPageIndex ) } diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index bc3aa39c..55421b15 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -140,7 +140,7 @@ class T7LanguageProvider(output: PackOutput, locale: String) : LanguageProvider( add(ORICHALCUM_NUGGET.get(), "Orichalcum Nugget") add(RESEARCH_SCROLL.get(), "Research Scroll") add(ARCANE_LENS.get(), "Arcane Lens") - add(BOOK.get(), "Elements of Thavma") + add(BOOK.get(), "Elements") add(T7Items.BASIC_AMULET.get(), "Basic Amulet") add(T7Items.BASIC_BELT.get(), "Basic Belt") @@ -413,7 +413,7 @@ Never gonna tell a lie and hurt you flew into my hands! I can sense great power within it. """, """ - The cover reads "Elements of Thavma", but a lot of its pages appear blank, sealed by some magic. + The cover reads "Elements", but a lot of its pages appear blank, sealed by some magic. """, """ To read them, I will first need to break that seal. It won't be easy... but From a18357d16dbabe448ba928e32d50c88442503e62 Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Tue, 26 May 2026 21:24:35 +0200 Subject: [PATCH 16/19] Started working towards parallax bookscreen dragging. --- .../thavma/impl/client/gui/book/BookScreen.kt | 62 +++++++++++++++---- .../impl/client/gui/book/TabRenderable.kt | 20 +++++- .../impl/common/research/ResearchEntry.kt | 4 ++ .../registries/deferred/ResearchEntries.kt | 2 + 4 files changed, 74 insertions(+), 14 deletions(-) diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt index dc9e9819..74fea5f6 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt @@ -3,11 +3,15 @@ package me.alegian.thavma.impl.client.gui.book import me.alegian.thavma.impl.client.clientRegistry import me.alegian.thavma.impl.common.entity.knowsResearch import me.alegian.thavma.impl.common.research.ResearchCategory +import me.alegian.thavma.impl.common.research.ResearchEntry import me.alegian.thavma.impl.init.registries.T7DatapackRegistries import me.alegian.thavma.impl.init.registries.deferred.ResearchCategories +import me.alegian.thavma.impl.init.registries.deferred.ResearchEntries +import me.alegian.thavma.impl.init.registries.deferred.ResearchEntries.CATEGORIES import net.minecraft.client.Minecraft import net.minecraft.client.gui.screens.Screen import net.minecraft.network.chat.Component +import kotlin.streams.toList class BookScreen : Screen(Component.literal("book")) { companion object { @@ -18,33 +22,58 @@ class BookScreen : Screen(Component.literal("book")) { private var isScrolling = false private var currentCategory: ResearchCategory? = null private val tabs = mutableMapOf() + private val backgrounds = mutableListOf() val currentTab get() = tabs[currentCategory] ?: tabs.toList().first().second private var selectorOffset = 0 private val entryWidgets = mutableListOf() + private val entries = clientRegistry(T7DatapackRegistries.RESEARCH_ENTRY)?.holders()?.toList() + val haha = entries.apply { println(this) } + private var currentEntries = entries?.filter { it.value().category.value() == currentCategory } + //actualEntries[currentCategory] ?: actualEntries.toList().first().second + private val hehe = currentEntries.apply { println(this) } + var relevance = entryWidgets.filter { entryWidget -> currentEntries?.map{ it.value() }?.contains(entryWidget.entry.value()) == true } + override fun init() { super.init() val player = Minecraft.getInstance().player ?: return + println("the list of actual entries is") + println(entries) + println("the current category is $currentCategory") + + entryWidgets.clear() selectorOffset = cornerHeight + selectorGap val categoryRegistry = clientRegistry(T7DatapackRegistries.RESEARCH_CATEGORY) currentCategory = categoryRegistry?.getOrThrow(ResearchCategories.STORY) - categoryRegistry?.forEach { - tabs[it] = addRenderableOnly(TabRenderable(this)) - } - clientRegistry(T7DatapackRegistries.RESEARCH_ENTRY)?.holders()?.forEach { - val tab = tabs[it.value().category.value()] - var shown = player.knowsResearch(it) - for (p in it.value().parents(player.level())) - if (player.knowsResearch(p)) shown = true - - if (tab != null && shown) - entryWidgets.add(addRenderableWidget(EntryWidget(this, tab, it))) + currentEntries = entries?.filter { it.value().category.value() == currentCategory } + + println("and current entries are") + println(currentEntries) + categoryRegistry?.forEach { category -> + tabs[category] = addRenderableOnly(TabRenderable(this, category, entries?.filter { it.value().category.value() == category }, player)) } +// clientRegistry(T7DatapackRegistries.RESEARCH_ENTRY)?.holders()?.forEach { +// val tab = tabs[it.value().category.value()] +// var shown = player.knowsResearch(it) +// for (p in it.value().parents(player.level())) +// if (player.knowsResearch(p)) shown = true +// +// if (tab != null && shown) +// entryWidgets.add(addRenderableWidget(EntryWidget(this, tab, it))) +// } + + // moved EntryWidget creation logic into TabRenderable.kt, here only adding them via + // the protected method addRenderableWidget + tabs.forEach { tab -> entryWidgets.addAll(tab.value.entryWidgets.map { addRenderableWidget(it) }) } + println("And the entry widgets added are") + println(entryWidgets) updateEntryWidgets() + relevance = entryWidgets.filter { entryWidget -> currentEntries?.map{ it.value() }?.contains(entryWidget.entry.value()) == true } + addRenderableOnly(FrameRenderable) clientRegistry(T7DatapackRegistries.RESEARCH_CATEGORY) ?.sortedBy { it.index } @@ -54,6 +83,8 @@ class BookScreen : Screen(Component.literal("book")) { private fun addSelectorWidget(category: ResearchCategory) { addRenderableWidget(TabSelectorWidget(0, selectorOffset, category) { currentCategory = category + currentEntries = entries?.filter { it.value().category.value() == currentCategory } + relevance = entryWidgets.filter { entryWidget -> currentEntries?.map{ it.value() }?.contains(entryWidget.entry.value()) == true } updateEntryWidgets() }) selectorOffset += TabSelectorWidget.TEXTURE.height + selectorGap @@ -68,6 +99,13 @@ class BookScreen : Screen(Component.literal("book")) { } override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, dragX: Double, dragY: Double): Boolean { + + val dimensionX = relevance.maxOf { it.x } - relevance.minOf { it.x } + val dimensionY = relevance.maxOf { it.y } - relevance.minOf { it.y } + + //println(dimensionX) + //println(dimensionY) + if (button != 0) { this.isScrolling = false return false @@ -75,7 +113,7 @@ class BookScreen : Screen(Component.literal("book")) { if (!this.isScrolling) { this.isScrolling = true } else { - currentTab.drag(dragX, dragY) + currentTab.drag(dragX/(dimensionX-1), dragY/(dimensionY-1)) } return true diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt index 674cda24..585799a3 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt @@ -2,12 +2,18 @@ package me.alegian.thavma.impl.client.gui.book import me.alegian.thavma.impl.client.texture.Texture import me.alegian.thavma.impl.client.util.* +import me.alegian.thavma.impl.common.entity.knowsResearch +import me.alegian.thavma.impl.common.research.ResearchCategory +import me.alegian.thavma.impl.common.research.ResearchEntry +import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.components.Renderable +import net.minecraft.core.Holder +import net.minecraft.world.entity.player.Player import kotlin.math.pow // represents the renderable content of a tab in the book -class TabRenderable(val screen: BookScreen) : Renderable { +class TabRenderable(val screen: BookScreen, val category: ResearchCategory, val entries: List>?, val player: Player) : Renderable { companion object{ private const val ZOOM_MULTIPLIER = 1.25 private const val maxScrollX = 300.0 @@ -17,6 +23,16 @@ class TabRenderable(val screen: BookScreen) : Renderable { val TEXTURE: Texture = Texture("gui/book/tab_bg", 512, 512) } + val entryWidgets = mutableListOf() + + val haha = entries?.forEach { + var shown = player.knowsResearch(it) + for (p in it.value().parents(player.level())) + if (player.knowsResearch(p)) shown = true + if (shown) + entryWidgets.add(EntryWidget(screen, this, it)) + } + var scrollX = 0.0 private set var scrollY = 0.0 @@ -69,4 +85,4 @@ class TabRenderable(val screen: BookScreen) : Renderable { graphics.disableCrop() } -} \ No newline at end of file +} diff --git a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt index a799e4c0..481506d3 100644 --- a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt +++ b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt @@ -31,6 +31,10 @@ class ResearchEntry( val defaultResearchState: List, val defaultKnown: Boolean ) { + override fun toString(): String { + return "Entry ${title.string} in ${this.category}" + } + fun parents(level: Level) = parentsMap.computeIfAbsent(this) { _ -> val registry = level.registry(T7DatapackRegistries.RESEARCH_ENTRY) diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt index fc7d78da..93391530 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt @@ -4,6 +4,7 @@ import me.alegian.thavma.impl.common.research.ResearchCategory import me.alegian.thavma.impl.common.research.ResearchEntry import me.alegian.thavma.impl.init.registries.T7DatapackRegistries import net.minecraft.resources.ResourceKey +import net.minecraft.resources.ResourceLocation object ResearchEntries { // used in datagen @@ -29,6 +30,7 @@ object ResearchEntries { object Story { val TEST = register("story_test", ResearchCategories.STORY) } + } // prepend category id to entry id to avoid duplicates From ee7073edb5ab760b6189899da3844103a6bb793b Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Thu, 28 May 2026 00:03:52 +0200 Subject: [PATCH 17/19] Added parallax-scrolling, some tuning of the parameters may be in order. --- .../resources/assets/thavma/lang/en_us.json | 6 ++- .../research/entry/story/story_test.json | 4 +- .../research/entry/story/story_test2.json | 27 ++++++++++ .../thavma/research/entry/thavma/alchemy.json | 2 +- .../research/entry/thavma/infusion.json | 2 +- .../entry/thavma/research_proficiency.json | 2 +- .../research/entry/thavma/research_table.json | 8 +-- .../research/entry/thavma/technology.json | 2 +- .../thavma/research/entry/thavma/thavma.json | 5 +- .../thavma/research/entry/thavma/wands.json | 2 +- .../thavma/impl/client/gui/book/BookScreen.kt | 37 +++++++------ .../impl/client/gui/book/EntryWidget.kt | 54 +++++++++++++------ .../thavma/impl/client/gui/book/GridHelper.kt | 2 +- .../impl/client/gui/book/TabRenderable.kt | 35 +++++++----- .../T7DatapackBuiltinEntriesProvider.kt | 40 +++++++++----- .../init/data/providers/T7LanguageProvider.kt | 15 ++++-- .../registries/deferred/ResearchEntries.kt | 4 +- 17 files changed, 163 insertions(+), 84 deletions(-) create mode 100644 src/generated/resources/data/thavma/thavma/research/entry/story/story_test2.json diff --git a/src/generated/resources/assets/thavma/lang/en_us.json b/src/generated/resources/assets/thavma/lang/en_us.json index d296c4e7..bf4bdb8b 100644 --- a/src/generated/resources/assets/thavma/lang/en_us.json +++ b/src/generated/resources/assets/thavma/lang/en_us.json @@ -82,7 +82,7 @@ "item.thavma.basic_amulet": "Basic Amulet", "item.thavma.basic_belt": "Basic Belt", "item.thavma.basic_ring": "Basic Ring", - "item.thavma.book": "Elements of Thavma", + "item.thavma.book": "Elements", "item.thavma.charm_of_the_dawn": "Charm of the Dawn", "item.thavma.eye_of_warden": "Eye of Warden", "item.thavma.fabric": "Infused Fabric", @@ -186,6 +186,8 @@ "research/entry.thavma.story.story_test.title_feature1": "This title might appear in the middle", "research/entry.thavma.story.story_test.title_feature2": "(start of page)", "research/entry.thavma.story.story_test.title_feature3": "this is page number 1! Surprise! Even though this Title is added as the fourth one in the DataPackBuiltinEntriesProvider and LanguageProvider after a ton of paragraphs and titles and figures etc., it has a preferred page index 0!", + "research/entry.thavma.story.story_test2": "I will not be denied", + "research/entry.thavma.story.story_test2.paragraph_feature0": "When faced with setback, we must challenge our assumptions.", "research/entry.thavma.thavma.alchemy": "Alchemy", "research/entry.thavma.thavma.arcane_lens": "The Arcane Lens", "research/entry.thavma.thavma.arcane_lens.page0.paragraph0": "The part of the book I can read describes an arcane tool that \"allows the user to see\", whatever that might mean. I have a feeling that crafting it could assist my work in unsealing the other pages.", @@ -199,7 +201,7 @@ "research/entry.thavma.thavma.technology": "Technology", "research/entry.thavma.thavma.thavma": "Thavma", "research/entry.thavma.thavma.thavma.page0.paragraph0": "I was merely toying with that wand -if it can even be called that- when this tome flew into my hands! I can sense great power within it.", - "research/entry.thavma.thavma.thavma.page0.paragraph1": "The cover reads \"Elements of Thavma\", but a lot of its pages appear blank, sealed by some magic.", + "research/entry.thavma.thavma.thavma.page0.paragraph1": "The cover reads \"Elements\", but a lot of its pages appear blank, sealed by some magic.", "research/entry.thavma.thavma.thavma.page0.paragraph2": "To read them, I will first need to break that seal. It won't be easy... but I have a feeling it will be worth my efforts.", "research/entry.thavma.thavma.thavma.page0.title": "Thavma", "research/entry.thavma.thavma.thavma.page1.paragraph0": "I will document all my findings inside the book, so that I can recall them later.", diff --git a/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json b/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json index 814d9b23..c1e8e5b8 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/story/story_test.json @@ -1,6 +1,8 @@ { "category": "thavma:story", - "children": [], + "children": [ + "thavma:story/story_test2" + ], "defaultKnown": true, "defaultResearchState": [], "icon": { diff --git a/src/generated/resources/data/thavma/thavma/research/entry/story/story_test2.json b/src/generated/resources/data/thavma/thavma/research/entry/story/story_test2.json new file mode 100644 index 00000000..7b12ea55 --- /dev/null +++ b/src/generated/resources/data/thavma/thavma/research/entry/story/story_test2.json @@ -0,0 +1,27 @@ +{ + "category": "thavma:story", + "children": [], + "defaultKnown": false, + "defaultResearchState": [], + "icon": { + "count": 1, + "id": "minecraft:fishing_rod" + }, + "pageFeatures": [ + { + "type": "thavma:paragraph", + "text": { + "translate": "research/entry.thavma.story.story_test2.paragraph_feature0" + } + } + ], + "position": [ + 0, + 3 + ], + "preferX": false, + "title": { + "color": "yellow", + "translate": "research/entry.thavma.story.story_test2" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/alchemy.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/alchemy.json index 9af0e3d1..642ed273 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/alchemy.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/alchemy.json @@ -1,7 +1,7 @@ { "category": "thavma:thavma", "children": [], - "defaultKnown": false, + "defaultKnown": true, "defaultResearchState": [ { "aspect": "thavma:aqua", diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/infusion.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/infusion.json index fa0d7078..04c01e92 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/infusion.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/infusion.json @@ -1,7 +1,7 @@ { "category": "thavma:thavma", "children": [], - "defaultKnown": false, + "defaultKnown": true, "defaultResearchState": [ { "aspect": "thavma:terra", diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json index 36d325ae..a3953117 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json @@ -1,7 +1,7 @@ { "category": "thavma:thavma", "children": [], - "defaultKnown": false, + "defaultKnown": true, "defaultResearchState": [ { "aspect": "thavma:aether", diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json index 928ed808..f5b2c991 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json @@ -1,12 +1,6 @@ { "category": "thavma:thavma", - "children": [ - "thavma:thavma/wands", - "thavma:thavma/technology", - "thavma:thavma/alchemy", - "thavma:thavma/infusion", - "thavma:thavma/research_proficiency" - ], + "children": [], "defaultKnown": false, "defaultResearchState": [ { diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/technology.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/technology.json index e7999939..e1db149b 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/technology.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/technology.json @@ -1,7 +1,7 @@ { "category": "thavma:thavma", "children": [], - "defaultKnown": false, + "defaultKnown": true, "defaultResearchState": [ { "aspect": "thavma:instrumentum", diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json index a4906b9e..32a6d621 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json @@ -1,9 +1,6 @@ { "category": "thavma:thavma", - "children": [ - "thavma:thavma/trees", - "thavma:thavma/ores" - ], + "children": [], "defaultKnown": true, "defaultResearchState": [ { diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/wands.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/wands.json index 6dff452d..28fae594 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/wands.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/wands.json @@ -1,7 +1,7 @@ { "category": "thavma:thavma", "children": [], - "defaultKnown": false, + "defaultKnown": true, "defaultResearchState": [ { "aspect": "thavma:aether", diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt index 74fea5f6..8d6a23f3 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt @@ -1,17 +1,12 @@ package me.alegian.thavma.impl.client.gui.book import me.alegian.thavma.impl.client.clientRegistry -import me.alegian.thavma.impl.common.entity.knowsResearch import me.alegian.thavma.impl.common.research.ResearchCategory -import me.alegian.thavma.impl.common.research.ResearchEntry import me.alegian.thavma.impl.init.registries.T7DatapackRegistries import me.alegian.thavma.impl.init.registries.deferred.ResearchCategories -import me.alegian.thavma.impl.init.registries.deferred.ResearchEntries -import me.alegian.thavma.impl.init.registries.deferred.ResearchEntries.CATEGORIES import net.minecraft.client.Minecraft import net.minecraft.client.gui.screens.Screen import net.minecraft.network.chat.Component -import kotlin.streams.toList class BookScreen : Screen(Component.literal("book")) { companion object { @@ -30,9 +25,12 @@ class BookScreen : Screen(Component.literal("book")) { private val entries = clientRegistry(T7DatapackRegistries.RESEARCH_ENTRY)?.holders()?.toList() val haha = entries.apply { println(this) } private var currentEntries = entries?.filter { it.value().category.value() == currentCategory } - //actualEntries[currentCategory] ?: actualEntries.toList().first().second + + //actualEntries[currentCategory] ?: actualEntries.toList().first().second private val hehe = currentEntries.apply { println(this) } - var relevance = entryWidgets.filter { entryWidget -> currentEntries?.map{ it.value() }?.contains(entryWidget.entry.value()) == true } + var currentWidgets = entryWidgets.filter { entryWidget -> + currentEntries?.map { it.value() }?.contains(entryWidget.entry.value()) == true + } override fun init() { super.init() @@ -53,7 +51,14 @@ class BookScreen : Screen(Component.literal("book")) { println("and current entries are") println(currentEntries) categoryRegistry?.forEach { category -> - tabs[category] = addRenderableOnly(TabRenderable(this, category, entries?.filter { it.value().category.value() == category }, player)) + tabs[category] = addRenderableOnly( + TabRenderable( + this, + category, + entries?.filter { it.value().category.value() == category }, + player + ) + ) } // clientRegistry(T7DatapackRegistries.RESEARCH_ENTRY)?.holders()?.forEach { // val tab = tabs[it.value().category.value()] @@ -72,7 +77,9 @@ class BookScreen : Screen(Component.literal("book")) { println(entryWidgets) updateEntryWidgets() - relevance = entryWidgets.filter { entryWidget -> currentEntries?.map{ it.value() }?.contains(entryWidget.entry.value()) == true } + currentWidgets = entryWidgets.filter { entryWidget -> + currentEntries?.map { it.value() }?.contains(entryWidget.entry.value()) == true + } addRenderableOnly(FrameRenderable) clientRegistry(T7DatapackRegistries.RESEARCH_CATEGORY) @@ -84,7 +91,9 @@ class BookScreen : Screen(Component.literal("book")) { addRenderableWidget(TabSelectorWidget(0, selectorOffset, category) { currentCategory = category currentEntries = entries?.filter { it.value().category.value() == currentCategory } - relevance = entryWidgets.filter { entryWidget -> currentEntries?.map{ it.value() }?.contains(entryWidget.entry.value()) == true } + currentWidgets = entryWidgets.filter { entryWidget -> + currentEntries?.map { it.value() }?.contains(entryWidget.entry.value()) == true + } updateEntryWidgets() }) selectorOffset += TabSelectorWidget.TEXTURE.height + selectorGap @@ -100,12 +109,6 @@ class BookScreen : Screen(Component.literal("book")) { override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, dragX: Double, dragY: Double): Boolean { - val dimensionX = relevance.maxOf { it.x } - relevance.minOf { it.x } - val dimensionY = relevance.maxOf { it.y } - relevance.minOf { it.y } - - //println(dimensionX) - //println(dimensionY) - if (button != 0) { this.isScrolling = false return false @@ -113,7 +116,7 @@ class BookScreen : Screen(Component.literal("book")) { if (!this.isScrolling) { this.isScrolling = true } else { - currentTab.drag(dragX/(dimensionX-1), dragY/(dimensionY-1)) + currentTab.drag(dragX, dragY) } return true diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt index 17b13116..4289ed67 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt @@ -3,13 +3,12 @@ package me.alegian.thavma.impl.client.gui.book import com.mojang.blaze3d.systems.RenderSystem import me.alegian.thavma.impl.client.ClientHelper import me.alegian.thavma.impl.client.clientSound +import me.alegian.thavma.impl.client.gui.book.TabRenderable.Companion.maxScrollX +import me.alegian.thavma.impl.client.gui.book.TabRenderable.Companion.maxScrollY import me.alegian.thavma.impl.client.gui.tooltip.T7Tooltip import me.alegian.thavma.impl.client.pushScreen import me.alegian.thavma.impl.client.texture.Texture -import me.alegian.thavma.impl.client.util.resetRenderSystemColor -import me.alegian.thavma.impl.client.util.scaleXY -import me.alegian.thavma.impl.client.util.translateXY -import me.alegian.thavma.impl.client.util.usePose +import me.alegian.thavma.impl.client.util.* import me.alegian.thavma.impl.common.entity.knowsResearch import me.alegian.thavma.impl.common.payload.ResearchScrollPayload import me.alegian.thavma.impl.common.research.ResearchEntry @@ -24,14 +23,22 @@ import net.minecraft.core.Holder import net.minecraft.network.chat.Component import net.minecraft.sounds.SoundEvents import net.minecraft.sounds.SoundSource +import net.minecraft.world.entity.player.Player import net.neoforged.neoforge.network.PacketDistributor +import kotlin.math.abs +import kotlin.math.sin /** * By default, connections prefer to connect to children along the Y axis. * entry.preferX makes connections prefer the X axis. * Straight lines will ignore this preference */ -class EntryWidget(private val screen: BookScreen, val tab: TabRenderable, val entry: Holder) : +class EntryWidget( + private val screen: BookScreen, + val tab: TabRenderable, + val entry: Holder, + val player: Player +) : AbstractWidget(0, 0, CELL_SIZE, CELL_SIZE, entry.value().title) { private var gaveScroll = false val knowsResearch = @@ -45,18 +52,20 @@ class EntryWidget(private val screen: BookScreen, val tab: TabRenderable, val en init { val components = mutableListOf(entry.value().title) - if (!knowsParents) components.add(Component.translatable(ResearchEntry.PARENTS_UNKNOWN_TRANSLATION).withStyle(ChatFormatting.GRAY)) + if (!knowsParents) components.add( + Component.translatable(ResearchEntry.PARENTS_UNKNOWN_TRANSLATION).withStyle(ChatFormatting.GRAY) + ) tooltip = T7Tooltip(components) } private val pos = entry.value().position override fun getX(): Int { - return ((pos.x * CELL_SIZE - CELL_SIZE / 2 - tab.scrollX) / tab.zoomFactor() + screen.width / 2).toInt() + return ((pos.x * CELL_SIZE - CELL_SIZE / 2 - tab.scrollX / (300 / (tab.average + 1))) / tab.zoomFactor() + screen.width / 2).toInt() } override fun getY(): Int { - return ((pos.y * CELL_SIZE - CELL_SIZE / 2 - tab.scrollY) / tab.zoomFactor() + screen.height / 2).toInt() + return ((pos.y * CELL_SIZE - CELL_SIZE / 2 - tab.scrollY / (300 / (tab.average + 1))) / tab.zoomFactor() + screen.height / 2).toInt() } override fun getWidth(): Int { @@ -69,15 +78,22 @@ class EntryWidget(private val screen: BookScreen, val tab: TabRenderable, val en override fun renderWidget(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) { this.isHovered = guiGraphics.containsPointInScissor(mouseX, mouseY) - && mouseX >= x - && mouseY >= y - && mouseX < x + getWidth() - && mouseY < y + getHeight() + && mouseX >= x + && mouseY >= y + && mouseX < x + getWidth() + && mouseY < y + getHeight() + + val corner = FrameRenderable.CORNER_TEXTURE + val edge = FrameRenderable.EDGE_TEXTURE + guiGraphics.enableCrop(corner.width / 2 + edge.height / 2, corner.height / 2 + edge.height / 2) guiGraphics.usePose { translateXY(screen.width / 2, screen.height / 2) scaleXY(1 / tab.zoomFactor()) - translateXY(-tab.scrollX, -tab.scrollY) + translateXY( + -tab.scrollX / (300 / (tab.average + 10)), + -tab.scrollY / (300 / (tab.average + 10)) + ) scaleXY(CELL_SIZE) translateXY(pos.x, pos.y) @@ -94,6 +110,7 @@ class EntryWidget(private val screen: BookScreen, val tab: TabRenderable, val en } RenderSystem.enableCull() } + guiGraphics.disableCrop() } override fun onClick(mouseX: Double, mouseY: Double, button: Int) { @@ -101,7 +118,10 @@ class EntryWidget(private val screen: BookScreen, val tab: TabRenderable, val en PacketDistributor.sendToServer(ResearchScrollPayload(entry)) clientSound(SoundEvents.BOOK_PAGE_TURN, SoundSource.AMBIENT, 1f, 1f) gaveScroll = true - tooltip = T7Tooltip(entry.value().title, Component.translatable(ResearchEntry.SCROLL_GIVEN_TRANSLATION).withStyle(ChatFormatting.GRAY)) + tooltip = T7Tooltip( + entry.value().title, + Component.translatable(ResearchEntry.SCROLL_GIVEN_TRANSLATION).withStyle(ChatFormatting.GRAY) + ) return } if (knowsResearch) @@ -113,8 +133,10 @@ class EntryWidget(private val screen: BookScreen, val tab: TabRenderable, val en private fun renderEntry(guiGraphics: GuiGraphics) { var brightness = 1f - if (!knowsResearch) brightness = 0.4f - RenderSystem.setShaderColor(brightness, brightness, brightness, 1f) + var alpha = 1f + if (!knowsResearch) brightness = 0.55f + if (!knowsResearch) alpha = (abs(sin(player.level().gameTime / 8.0f)) / 4 * 3 + 0.25f) + RenderSystem.setShaderColor(brightness, brightness, brightness, alpha) renderGridElement( guiGraphics, diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt index 5e712bb1..f9c0300a 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt @@ -34,7 +34,7 @@ fun PoseStack.renderConnectionRecursive(dx: Int, dy: Int, guiGraphics: GuiGraphi val preference = if (preferX) -1f else 1f if (absDx + absDy <= 1f) return - else if (absDx > 2 && absDy > 2) throw IllegalStateException("Yup the problem is here in GridHelper.kt") + else if (absDx > 2 && absDy > 2) return //throw IllegalStateException("Yup the problem is here in GridHelper.kt") else if (!invert && (preferX && absDx > absDy && absDy > 0 || !preferX && absDy > absDx && absDx > 0)) { translateXY(dx, dy) renderConnectionRecursive(-dx, -dy, guiGraphics, preferX, true) diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt index 585799a3..923e9467 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt @@ -13,11 +13,16 @@ import net.minecraft.world.entity.player.Player import kotlin.math.pow // represents the renderable content of a tab in the book -class TabRenderable(val screen: BookScreen, val category: ResearchCategory, val entries: List>?, val player: Player) : Renderable { - companion object{ +class TabRenderable( + val screen: BookScreen, + val category: ResearchCategory, + val entries: List>?, + val player: Player +) : Renderable { + companion object { private const val ZOOM_MULTIPLIER = 1.25 - private const val maxScrollX = 300.0 - private const val maxScrollY = 300.0 + const val maxScrollX = 600.0 + const val maxScrollY = 300.0 private const val minZoom = 0.0 private const val maxZoom = 5.0 val TEXTURE: Texture = Texture("gui/book/tab_bg", 512, 512) @@ -25,12 +30,14 @@ class TabRenderable(val screen: BookScreen, val category: ResearchCategory, val val entryWidgets = mutableListOf() - val haha = entries?.forEach { - var shown = player.knowsResearch(it) - for (p in it.value().parents(player.level())) - if (player.knowsResearch(p)) shown = true - if (shown) - entryWidgets.add(EntryWidget(screen, this, it)) + init { + entries?.forEach { + var shown = player.knowsResearch(it) + for (p in it.value().parents(player.level())) + if (player.knowsResearch(p)) shown = true + if (shown) + entryWidgets.add(EntryWidget(screen, this, it, player)) + } } var scrollX = 0.0 @@ -39,9 +46,13 @@ class TabRenderable(val screen: BookScreen, val category: ResearchCategory, val private set private var zoom = 2.0 // TODO: this is actually inverse zoom + val dimensionX = entryWidgets.maxOf { it.x } - entryWidgets.minOf { it.x } + val dimensionY = entryWidgets.maxOf { it.y } - entryWidgets.minOf { it.y } + val average = (dimensionY + dimensionX) / 2 + fun drag(x: Double, y: Double) { - val rawScrollX = scrollX - zoomFactor() * x - val rawScrollY = scrollY - zoomFactor() * y + val rawScrollX = scrollX - zoomFactor() * x * 1200 / (average * 7 + 50) + val rawScrollY = scrollY - zoomFactor() * y * 1200 / (average * 7 + 50) scrollX = rawScrollX.coerceIn(-maxScrollX, maxScrollX) scrollY = rawScrollY.coerceIn(-maxScrollY, maxScrollY) diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index 881a9d0d..e3ec91ea 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -52,6 +52,7 @@ import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition import net.minecraft.world.item.enchantment.EnchantmentEffectComponents import net.minecraft.world.item.enchantment.LevelBasedValue import net.minecraft.world.item.enchantment.effects.AddValue +import net.minecraft.world.level.block.Blocks import net.minecraft.world.level.storage.loot.predicates.DamageSourceCondition import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider import net.neoforged.neoforge.registries.NeoForgeRegistries @@ -126,14 +127,6 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab ctx.registerCategory(ResearchCategories.STORY, Items.WRITABLE_BOOK.defaultInstance, 2f) } .add(T7DatapackRegistries.RESEARCH_ENTRY) { ctx -> - ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) - .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) - .addPage(simpleTextPage(3, true)) - .addPage(simpleTextPage(1, false)) - .addChild(ResearchEntries.Thavma.TREES) - .addChild(ResearchEntries.Thavma.ORES) - .defaultKnown() - .build(ctx) ResearchEntryBuilder(ResearchEntries.Story.TEST, Vector2i(0, 0), false, Items.TURTLE_HELMET.defaultInstance) .research() @@ -151,6 +144,22 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab // .addPage(simpleTextPage(2, true)) // .addPage(simpleTextPage(2, true)) // .addPage(simpleTextPage(2, true)) + .defaultKnown() + .addChild(ResearchEntries.Story.TEST2) + .build(ctx) + + ResearchEntryBuilder(ResearchEntries.Story.TEST2, Vector2i(0,3), false, Items.FISHING_ROD.defaultInstance) + .research() + .addPageFeature(makeParagraphFeature()) + //.defaultKnown() + .build(ctx) + + ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) + .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) + .addPage(simpleTextPage(3, true)) + .addPage(simpleTextPage(1, false)) + //.addChild(ResearchEntries.Thavma.TREES) + //.addChild(ResearchEntries.Thavma.ORES) .defaultKnown() .build(ctx) @@ -193,11 +202,11 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab ) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) .addPage { _, _ -> CraftingPage(Recipes.CHEST) } - .addChild(ResearchEntries.Thavma.WANDS) - .addChild(ResearchEntries.Thavma.TECHNOLOGY) - .addChild(ResearchEntries.Thavma.ALCHEMY) - .addChild(ResearchEntries.Thavma.INFUSION) - .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) + //.addChild(ResearchEntries.Thavma.WANDS) + //.addChild(ResearchEntries.Thavma.TECHNOLOGY) + //.addChild(ResearchEntries.Thavma.ALCHEMY) + //.addChild(ResearchEntries.Thavma.INFUSION) + //.addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) .build(ctx) ResearchEntryBuilder( @@ -207,6 +216,7 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance ) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) + .defaultKnown() .build(ctx) ResearchEntryBuilder( @@ -216,6 +226,7 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab T7Blocks.CRUCIBLE.get().asItem().defaultInstance ) .research(lockedAspect(2, 0, Aspects.AQUA), lockedAspect(2, 4, Aspects.ALKIMIA)) + .defaultKnown() .build(ctx) ResearchEntryBuilder( @@ -225,6 +236,7 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab T7Items.wandOrThrow(WandPlatingMaterials.THAVMITE.get(), WandCoreMaterials.SILVERWOOD.get()).defaultInstance ) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.INSTRUMENTUM)) + .defaultKnown() .build(ctx) ResearchEntryBuilder( @@ -234,6 +246,7 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab T7Blocks.MATRIX.get().asItem().defaultInstance ) .research(lockedAspect(2, 0, Aspects.TERRA), lockedAspect(2, 4, Aspects.AETHER)) + .defaultKnown() .build(ctx) ResearchEntryBuilder( @@ -243,6 +256,7 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab T7Items.GOGGLES.get().defaultInstance ) .research(lockedAspect(2, 0, Aspects.INSTRUMENTUM), lockedAspect(2, 4, Aspects.CIVILIS)) + .defaultKnown() .build(ctx) ResearchEntryBuilder( diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index 55421b15..85f26bb5 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -392,18 +392,25 @@ Never gonna tell a lie and hurt you """.trimIndent() ) addTitleFeature(ResearchEntries.Story.TEST, 2, "(start of page)") - addTitleFeature(ResearchEntries.Story.TEST, 3, """ + addTitleFeature( + ResearchEntries.Story.TEST, 3, """ this is page number 1! Surprise! Even though this Title is added as the fourth one in the DataPackBuiltinEntriesProvider and LanguageProvider after a ton of paragraphs and titles and figures etc., it has a preferred page index 0! - """.trimIndent()) + """.trimIndent() + ) addParagraphFeature(ResearchEntries.Story.TEST, 3, "Just another random little paragraph :D") - addParagraphFeature(ResearchEntries.Story.TEST, 4, """ + addParagraphFeature( + ResearchEntries.Story.TEST, 4, """ And BAM! smack that paragraph right in the middle without it being an image caption! (Both the image and this have a preferred page index set to 5 -> always page number 6) - """.trimIndent()) + """.trimIndent() + ) + + addEntry(ResearchEntries.Story.TEST2, "I will not be denied") + addParagraphFeature(ResearchEntries.Story.TEST2, 0, "When faced with setback, we must challenge our assumptions.") addTextPage( ResearchEntries.Thavma.THAVMA, 0, diff --git a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt index 93391530..10a237d2 100644 --- a/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt +++ b/src/main/java/me/alegian/thavma/impl/init/registries/deferred/ResearchEntries.kt @@ -4,7 +4,6 @@ import me.alegian.thavma.impl.common.research.ResearchCategory import me.alegian.thavma.impl.common.research.ResearchEntry import me.alegian.thavma.impl.init.registries.T7DatapackRegistries import net.minecraft.resources.ResourceKey -import net.minecraft.resources.ResourceLocation object ResearchEntries { // used in datagen @@ -28,7 +27,8 @@ object ResearchEntries { } object Story { - val TEST = register("story_test", ResearchCategories.STORY) + val TEST = register("story_test", ResearchCategories.STORY) + val TEST2 = register("story_test2", ResearchCategories.STORY) } } From 5d961250d09cf0c3c6d6ecfd0f54acecfd8b632c Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Thu, 28 May 2026 17:01:03 +0200 Subject: [PATCH 18/19] Fixed the awful research entry crash bug and also added fading in and out for unknown research! --- .../resources/assets/thavma/lang/en_us.json | 18 ++-- .../research/entry/thavma/arcane_lens.json | 31 +++++- .../thavma/research/entry/thavma/ores.json | 2 +- .../entry/thavma/research_proficiency.json | 8 +- .../research/entry/thavma/research_table.json | 9 +- .../thavma/research/entry/thavma/thavma.json | 43 ++++++++- .../thavma/research/entry/thavma/trees.json | 6 +- .../impl/client/gui/book/EntryWidget.kt | 29 ++++-- .../thavma/impl/client/gui/book/GridHelper.kt | 95 ++++++++++++++++--- .../impl/client/gui/book/TabRenderable.kt | 4 +- .../impl/common/research/ResearchEntry.kt | 2 +- .../T7DatapackBuiltinEntriesProvider.kt | 67 +++++++++---- .../init/data/providers/T7LanguageProvider.kt | 86 +++++++++++------ 13 files changed, 304 insertions(+), 96 deletions(-) diff --git a/src/generated/resources/assets/thavma/lang/en_us.json b/src/generated/resources/assets/thavma/lang/en_us.json index bf4bdb8b..890d70ac 100644 --- a/src/generated/resources/assets/thavma/lang/en_us.json +++ b/src/generated/resources/assets/thavma/lang/en_us.json @@ -190,21 +190,21 @@ "research/entry.thavma.story.story_test2.paragraph_feature0": "When faced with setback, we must challenge our assumptions.", "research/entry.thavma.thavma.alchemy": "Alchemy", "research/entry.thavma.thavma.arcane_lens": "The Arcane Lens", - "research/entry.thavma.thavma.arcane_lens.page0.paragraph0": "The part of the book I can read describes an arcane tool that \"allows the user to see\", whatever that might mean. I have a feeling that crafting it could assist my work in unsealing the other pages.", - "research/entry.thavma.thavma.arcane_lens.page0.paragraph1": "The blueprint describes a hexagonal device, much like a prism, made with those colorful crystals I found lying in a cave.", - "research/entry.thavma.thavma.arcane_lens.page0.paragraph2": "I should look at the world through its lens, maybe it will uncover something useful.", - "research/entry.thavma.thavma.arcane_lens.page0.title": "The Arcane Lens", + "research/entry.thavma.thavma.arcane_lens.paragraph_feature0": "The part of the book I can read describes an arcane tool that \"allows the user to see\", whatever that might mean. I have a feeling that crafting it could assist my work in unsealing the other pages.", + "research/entry.thavma.thavma.arcane_lens.paragraph_feature1": "The blueprint describes a hexagonal device, much like a prism, made with those colorful crystals I found lying in a cave.", + "research/entry.thavma.thavma.arcane_lens.paragraph_feature2": "I should look at the world through its lens, maybe it will uncover something useful.", + "research/entry.thavma.thavma.arcane_lens.title_feature0": "The Arcane Lens", "research/entry.thavma.thavma.infusion": "Infusion", "research/entry.thavma.thavma.ores": "Ores", "research/entry.thavma.thavma.research_proficiency": "Research Proficiency", "research/entry.thavma.thavma.research_table": "Research Table", "research/entry.thavma.thavma.technology": "Technology", "research/entry.thavma.thavma.thavma": "Thavma", - "research/entry.thavma.thavma.thavma.page0.paragraph0": "I was merely toying with that wand -if it can even be called that- when this tome flew into my hands! I can sense great power within it.", - "research/entry.thavma.thavma.thavma.page0.paragraph1": "The cover reads \"Elements\", but a lot of its pages appear blank, sealed by some magic.", - "research/entry.thavma.thavma.thavma.page0.paragraph2": "To read them, I will first need to break that seal. It won't be easy... but I have a feeling it will be worth my efforts.", - "research/entry.thavma.thavma.thavma.page0.title": "Thavma", - "research/entry.thavma.thavma.thavma.page1.paragraph0": "I will document all my findings inside the book, so that I can recall them later.", + "research/entry.thavma.thavma.thavma.paragraph_feature0": "I was merely toying with that wand -if it can even be called that- when this tome flew into my hands! I can sense great power within it.", + "research/entry.thavma.thavma.thavma.paragraph_feature1": "The cover reads \"Elements\", but a lot of its pages appear blank, sealed by some magic.", + "research/entry.thavma.thavma.thavma.paragraph_feature2": "To read them, I will first need to break that seal. It won't be easy... but I have a feeling it will be worth my efforts.", + "research/entry.thavma.thavma.thavma.paragraph_feature3": "I will document all my findings inside the book, so that I can recall them later.", + "research/entry.thavma.thavma.thavma.title_feature0": "Thavma", "research/entry.thavma.thavma.trees": "Trees", "research/entry.thavma.thavma.wands": "Wands", "tag.block.thavma.crucible_heat_sources": "Crucible Block Heat Sources", diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/arcane_lens.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/arcane_lens.json index 33183a96..97d93c82 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/arcane_lens.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/arcane_lens.json @@ -3,7 +3,7 @@ "children": [ "thavma:thavma/research_table" ], - "defaultKnown": false, + "defaultKnown": true, "defaultResearchState": [ { "aspect": "thavma:lux", @@ -36,7 +36,34 @@ "count": 1, "id": "thavma:arcane_lens" }, - "pageFeatures": [], + "pageFeatures": [ + { + "type": "thavma:title", + "starts_page": true, + "text": { + "bold": true, + "translate": "research/entry.thavma.thavma.arcane_lens.title_feature0" + } + }, + { + "type": "thavma:paragraph", + "text": { + "translate": "research/entry.thavma.thavma.arcane_lens.paragraph_feature0" + } + }, + { + "type": "thavma:paragraph", + "text": { + "translate": "research/entry.thavma.thavma.arcane_lens.paragraph_feature1" + } + }, + { + "type": "thavma:paragraph", + "text": { + "translate": "research/entry.thavma.thavma.arcane_lens.paragraph_feature2" + } + } + ], "position": [ 2, -2 diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/ores.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/ores.json index ea1a35f3..41a0ced4 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/ores.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/ores.json @@ -33,7 +33,7 @@ 2, -4 ], - "preferX": false, + "preferX": true, "title": { "color": "yellow", "translate": "research/entry.thavma.thavma.ores" diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json index a3953117..652b2c49 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_proficiency.json @@ -1,6 +1,8 @@ { "category": "thavma:thavma", - "children": [], + "children": [ + "thavma:thavma/research_table" + ], "defaultKnown": true, "defaultResearchState": [ { @@ -28,8 +30,8 @@ }, "pageFeatures": [], "position": [ - -1, - -1 + -2, + -2 ], "preferX": false, "title": { diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json index f5b2c991..cf612293 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/research_table.json @@ -1,7 +1,12 @@ { "category": "thavma:thavma", - "children": [], - "defaultKnown": false, + "children": [ + "thavma:thavma/wands", + "thavma:thavma/technology", + "thavma:thavma/alchemy", + "thavma:thavma/infusion" + ], + "defaultKnown": true, "defaultResearchState": [ { "aspect": "thavma:aether", diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json index 32a6d621..2f9591fa 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/thavma.json @@ -1,6 +1,9 @@ { "category": "thavma:thavma", - "children": [], + "children": [ + "thavma:thavma/trees", + "thavma:thavma/ores" + ], "defaultKnown": true, "defaultResearchState": [ { @@ -26,10 +29,44 @@ "count": 1, "id": "thavma:book" }, - "pageFeatures": [], + "pageFeatures": [ + { + "type": "thavma:title", + "starts_page": true, + "text": { + "bold": true, + "translate": "research/entry.thavma.thavma.thavma.title_feature0" + } + }, + { + "type": "thavma:paragraph", + "text": { + "translate": "research/entry.thavma.thavma.thavma.paragraph_feature0" + } + }, + { + "type": "thavma:paragraph", + "text": { + "translate": "research/entry.thavma.thavma.thavma.paragraph_feature1" + } + }, + { + "type": "thavma:paragraph", + "text": { + "translate": "research/entry.thavma.thavma.thavma.paragraph_feature2" + } + }, + { + "type": "thavma:paragraph", + "starts_page": true, + "text": { + "translate": "research/entry.thavma.thavma.thavma.paragraph_feature3" + } + } + ], "position": [ 0, - -6 + -5 ], "preferX": false, "title": { diff --git a/src/generated/resources/data/thavma/thavma/research/entry/thavma/trees.json b/src/generated/resources/data/thavma/thavma/research/entry/thavma/trees.json index b47d0111..267f2959 100644 --- a/src/generated/resources/data/thavma/thavma/research/entry/thavma/trees.json +++ b/src/generated/resources/data/thavma/thavma/research/entry/thavma/trees.json @@ -1,7 +1,7 @@ { "category": "thavma:thavma", "children": [ - "thavma:thavma/research_table" + "thavma:thavma/research_proficiency" ], "defaultKnown": false, "defaultResearchState": [ @@ -30,8 +30,8 @@ }, "pageFeatures": [], "position": [ - 0, - -3 + -2, + -4 ], "preferX": false, "title": { diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt index 4289ed67..f046af52 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt @@ -61,11 +61,11 @@ class EntryWidget( private val pos = entry.value().position override fun getX(): Int { - return ((pos.x * CELL_SIZE - CELL_SIZE / 2 - tab.scrollX / (300 / (tab.average + 1))) / tab.zoomFactor() + screen.width / 2).toInt() + return ((pos.x * CELL_SIZE - CELL_SIZE / 2 - tab.scrollX / (300 / (tab.average * 1.5 + 10))) / tab.zoomFactor() + screen.width / 2).toInt() } override fun getY(): Int { - return ((pos.y * CELL_SIZE - CELL_SIZE / 2 - tab.scrollY / (300 / (tab.average + 1))) / tab.zoomFactor() + screen.height / 2).toInt() + return ((pos.y * CELL_SIZE - CELL_SIZE / 2 - tab.scrollY / (300 / (tab.average * 1.5 + 10))) / tab.zoomFactor() + screen.height / 2).toInt() } override fun getWidth(): Int { @@ -91,8 +91,8 @@ class EntryWidget( translateXY(screen.width / 2, screen.height / 2) scaleXY(1 / tab.zoomFactor()) translateXY( - -tab.scrollX / (300 / (tab.average + 10)), - -tab.scrollY / (300 / (tab.average + 10)) + -tab.scrollX / (300 / (tab.average * 1.5 + 10)), + -tab.scrollY / (300 / (tab.average * 1.5 + 10)) ) scaleXY(CELL_SIZE) translateXY(pos.x, pos.y) @@ -103,11 +103,14 @@ class EntryWidget( // allows negative size drawing, which greatly simplifies math RenderSystem.disableCull() for (child in children) { - val dv = child.value().position - pos + val dvx = child.value().position.x - pos.x + val dvy = child.value().position.y - pos.y + val knowsChild = ClientHelper.player()?.knowsResearch(child) ?: false guiGraphics.usePose { - renderConnectionRecursive(dv.x, dv.y, guiGraphics, child.value().preferX, false) + renderConnectionRecursive(dvx, dvy, guiGraphics, child.value().preferX, false, player, entry, knowsChild) } } + guiGraphics.flush() RenderSystem.enableCull() } guiGraphics.disableCrop() @@ -136,23 +139,31 @@ class EntryWidget( var alpha = 1f if (!knowsResearch) brightness = 0.55f if (!knowsResearch) alpha = (abs(sin(player.level().gameTime / 8.0f)) / 4 * 3 + 0.25f) - RenderSystem.setShaderColor(brightness, brightness, brightness, alpha) + //RenderSystem.setShaderColor(brightness, brightness, brightness, alpha) renderGridElement( guiGraphics, 1f, 1f, TEXTURE.location, - false + false, + player, + knowsResearch ) + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + guiGraphics.setColor(brightness, brightness, brightness, alpha) + guiGraphics.usePose { scaleXY(1f / CELL_SIZE) // back to pixel space scaleXY(2 * 0.7) // items are 16x, nodes are 32x, but we don't want full size guiGraphics.renderItem(entry.value().icon, -8, -8) } - resetRenderSystemColor() + guiGraphics.setColor(1f, 1f, 1f, 1f) + RenderSystem.disableBlend() + //resetRenderSystemColor() } override fun playDownSound(handler: SoundManager) { diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt index f9c0300a..3e9270a6 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt @@ -1,15 +1,23 @@ package me.alegian.thavma.impl.client.gui.book +import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.PoseStack import com.mojang.math.Axis +import me.alegian.thavma.impl.client.ClientHelper import me.alegian.thavma.impl.client.texture.Texture +import me.alegian.thavma.impl.client.util.resetRenderSystemColor import me.alegian.thavma.impl.client.util.translateXY import me.alegian.thavma.impl.client.util.usePose +import me.alegian.thavma.impl.common.entity.knowsResearch +import me.alegian.thavma.impl.common.research.ResearchEntry import net.minecraft.client.gui.GuiGraphics +import net.minecraft.core.Holder import net.minecraft.resources.ResourceLocation +import net.minecraft.world.entity.player.Player import org.joml.Vector3f import kotlin.math.abs import kotlin.math.sign +import kotlin.math.sin val CELL_SIZE = EntryWidget.TEXTURE.width val ARROW_HEAD = Texture("gui/book/arrow_head", 32, 32) @@ -25,31 +33,41 @@ val CORNER_2X2 = Texture("gui/book/corner_2x2", 96, 96) * along x or y axis) * Uses recursion to draw long connections. */ -fun PoseStack.renderConnectionRecursive(dx: Int, dy: Int, guiGraphics: GuiGraphics, preferX: Boolean, invert: Boolean) { +fun PoseStack.renderConnectionRecursive( + dx: Int, + dy: Int, + guiGraphics: GuiGraphics, + preferX: Boolean, + invert: Boolean, + player: Player, + entry: Holder, + knowsChild: Boolean +) { val absDx = abs(dx) val absDy = abs(dy) val signX = dx.sign val signY = dy.sign val inversion = if (invert) -1f else 1f val preference = if (preferX) -1f else 1f + //val knowsResearch = ClientHelper.player()?.knowsResearch(entry) ?: false if (absDx + absDy <= 1f) return - else if (absDx > 2 && absDy > 2) return //throw IllegalStateException("Yup the problem is here in GridHelper.kt") + else if (absDx > 2 && absDy > 2) throw IllegalStateException("Yup the problem is here in GridHelper.kt") else if (!invert && (preferX && absDx > absDy && absDy > 0 || !preferX && absDy > absDx && absDx > 0)) { translateXY(dx, dy) - renderConnectionRecursive(-dx, -dy, guiGraphics, preferX, true) + renderConnectionRecursive(-dx, -dy, guiGraphics, preferX, true, player, entry, knowsChild) } else if (absDx == absDy) { translateXY(dx / 2f, dy / 2f) translateXY(preference * signX * inversion / 2f, -preference * signY * inversion / 2f) - renderCorner(guiGraphics, dx * inversion, dy * inversion, preferX) + renderCorner(guiGraphics, dx * inversion, dy * inversion, preferX, player, knowsChild) } else if (absDx > absDy) { translateXY(signX, 0) - renderLine(guiGraphics, signX * inversion, 1f, false) - renderConnectionRecursive(dx - signX, dy, guiGraphics, preferX, invert) + renderLine(guiGraphics, signX * inversion, 1f, false, player, knowsChild) + renderConnectionRecursive(dx - signX, dy, guiGraphics, preferX, invert, player, entry, knowsChild) } else { translateXY(0, signY) - renderLine(guiGraphics, 1f, signY * inversion, true) - renderConnectionRecursive(dx, dy - signY, guiGraphics, preferX, invert) + renderLine(guiGraphics, 1f, signY * inversion, true, player, knowsChild) + renderConnectionRecursive(dx, dy - signY, guiGraphics, preferX, invert, player, entry, knowsChild) } } @@ -58,20 +76,49 @@ fun PoseStack.renderConnectionRecursive(dx: Int, dy: Int, guiGraphics: GuiGraphi * Vertical lines are reflected horizontals. * Uses negative dimensions for orientation. */ -private fun renderLine(guiGraphics: GuiGraphics, signX: Float, signY: Float, vertical: Boolean) = +private fun renderLine( + guiGraphics: GuiGraphics, + signX: Float, + signY: Float, + vertical: Boolean, + player: Player, + knowsChild: Boolean +) { + var brightness = 1f + var alpha = 1f + if (!knowsChild) brightness = 0.55f + if (!knowsChild) alpha = (abs(sin(player.level().gameTime / 8.0f)) / 4 * 3 + 0.25f) + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + guiGraphics.setColor(brightness, brightness, brightness, alpha) + renderGridElement( guiGraphics, signX, signY, LINE.location, - vertical + vertical, + player, + knowsChild ) + guiGraphics.setColor(1f, 1f, 1f, 1f) + RenderSystem.disableBlend() +} + + /** * Only 2x2 and 1x1 corners are supported. Only one texture * is ever used. Uses negative dimensions & reflections for orientation. */ -private fun renderCorner(guiGraphics: GuiGraphics, dx: Float, dy: Float, reflect: Boolean) { +private fun renderCorner( + guiGraphics: GuiGraphics, + dx: Float, + dy: Float, + reflect: Boolean, + player: Player, + knowsChild: Boolean +) { val textureLoc = if (abs(dx) == 1f) CORNER_1X1.location else CORNER_2X2.location @@ -81,8 +128,11 @@ private fun renderCorner(guiGraphics: GuiGraphics, dx: Float, dy: Float, reflect dx, dy, textureLoc, - reflect + reflect, + player, + knowsChild ) + } /** @@ -92,11 +142,30 @@ private fun renderCorner(guiGraphics: GuiGraphics, dx: Float, dy: Float, reflect * The reflect param reflects the object across the y=x axis. * It supports negative size drawing, and adjusts the reflection axis accordingly */ -fun renderGridElement(graphics: GuiGraphics, width: Float, height: Float, textureLoc: ResourceLocation, reflect: Boolean) { +fun renderGridElement( + graphics: GuiGraphics, + width: Float, + height: Float, + textureLoc: ResourceLocation, + reflect: Boolean, + player: Player, + knowsResearch: Boolean +) { + var brightness = 1f + var alpha = 1f + if (!knowsResearch) brightness = 0.55f + if (!knowsResearch) alpha = (abs(sin(player.level().gameTime / 8.0f)) / 4 * 3 + 0.25f) + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + graphics.setColor(brightness, brightness, brightness, alpha) + graphics.usePose { if (reflect) mulPose(Axis.of(Vector3f(sign(width), sign(height), 0f)).rotationDegrees(180f)) scale(width, height, 1f) translateXY(-0.5f, -0.5f) graphics.blit(textureLoc, 0, 0, 0, 0f, 0f, 1, 1, 1, 1) } + + graphics.setColor(1f, 1f, 1f, 1f) + RenderSystem.disableBlend() } diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt index 923e9467..98e255c9 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt @@ -51,8 +51,8 @@ class TabRenderable( val average = (dimensionY + dimensionX) / 2 fun drag(x: Double, y: Double) { - val rawScrollX = scrollX - zoomFactor() * x * 1200 / (average * 7 + 50) - val rawScrollY = scrollY - zoomFactor() * y * 1200 / (average * 7 + 50) + val rawScrollX = scrollX - zoomFactor() * x * 1200 / (average * 10 + 50) + val rawScrollY = scrollY - zoomFactor() * y * 1200 / (average * 10 + 50) scrollX = rawScrollX.coerceIn(-maxScrollX, maxScrollX) scrollY = rawScrollY.coerceIn(-maxScrollY, maxScrollY) diff --git a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt index 481506d3..b6724b30 100644 --- a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt +++ b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt @@ -32,7 +32,7 @@ class ResearchEntry( val defaultKnown: Boolean ) { override fun toString(): String { - return "Entry ${title.string} in ${this.category}" + return "Entry ${title.string} in ${this.category.value().title}" } fun parents(level: Level) = diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index e3ec91ea..5518ad54 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -133,50 +133,70 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab .addPageFeature(makeTitleFeature()) .addPageFeature(makeParagraphFeature(false, false)) .addPageFeature(makeParagraphFeature()) - .addPageFeature(makeFigureFeature(Texture("gui/images/important_image", 180, 101, 180, 101), true, false, false, 1, ChatFormatting.DARK_AQUA, ChatFormatting.ITALIC)) + .addPageFeature( + makeFigureFeature( + Texture("gui/images/important_image", 180, 101, 180, 101), + true, + false, + false, + 1, + ChatFormatting.DARK_AQUA, + ChatFormatting.ITALIC + ) + ) .addPageFeature(makeTitleFeature(false)) .addPageFeature(makeParagraphFeature(true)) .addPageFeature(makeTitleFeature(true, false)) .addPageFeature(makeTitleFeature(true, true, 0)) .addPageFeature(makeParagraphFeature()) - .addPageFeature(makeFigureFeature(Texture("gui/images/important_image2", 87, 77, 87, 77), false, false, true, 5)) + .addPageFeature( + makeFigureFeature( + Texture("gui/images/important_image2", 87, 77, 87, 77), + false, + false, + true, + 5 + ) + ) .addPageFeature(makeParagraphFeature(false, true, 5)) -// .addPage(simpleTextPage(2, true)) -// .addPage(simpleTextPage(2, true)) -// .addPage(simpleTextPage(2, true)) .defaultKnown() .addChild(ResearchEntries.Story.TEST2) .build(ctx) - ResearchEntryBuilder(ResearchEntries.Story.TEST2, Vector2i(0,3), false, Items.FISHING_ROD.defaultInstance) + ResearchEntryBuilder(ResearchEntries.Story.TEST2, Vector2i(0, 3), false, Items.FISHING_ROD.defaultInstance) .research() .addPageFeature(makeParagraphFeature()) //.defaultKnown() .build(ctx) - ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -6), false, T7Items.BOOK.get().defaultInstance) + ResearchEntryBuilder(ResearchEntries.Thavma.THAVMA, Vector2i(0, -5), false, T7Items.BOOK.get().defaultInstance) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.AETHER)) - .addPage(simpleTextPage(3, true)) - .addPage(simpleTextPage(1, false)) - //.addChild(ResearchEntries.Thavma.TREES) - //.addChild(ResearchEntries.Thavma.ORES) + .addPageFeature(makeTitleFeature()) + .addPageFeature(makeParagraphFeature()) + .addPageFeature(makeParagraphFeature()) + .addPageFeature(makeParagraphFeature()) + .addPageFeature(makeParagraphFeature(true)) + //.addPage(simpleTextPage(3, true)) + //.addPage(simpleTextPage(1, false)) + .addChild(ResearchEntries.Thavma.TREES) + .addChild(ResearchEntries.Thavma.ORES) .defaultKnown() .build(ctx) ResearchEntryBuilder( ResearchEntries.Thavma.TREES, - Vector2i(0, -3), + Vector2i(-2, -4), false, T7Blocks.GREATWOOD_LOG.get().asItem().defaultInstance ) .research(lockedAspect(2, 0, Aspects.HERBA), lockedAspect(2, 4, Aspects.HERBA)) - .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) + .addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) .build(ctx) ResearchEntryBuilder( ResearchEntries.Thavma.ORES, Vector2i(2, -4), - false, + true, T7Items.SHARDS[Aspects.AQUA]!!.get().defaultInstance ) .research(lockedAspect(2, 0, Aspects.TERRA), lockedAspect(2, 4, Aspects.AETHER)) @@ -190,8 +210,13 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab T7Items.ARCANE_LENS.get().defaultInstance ) .research(lockedAspect(2, 0, Aspects.LUX), lockedAspect(2, 4, Aspects.AETHER), broken(2, 2)) + .addPageFeature(makeTitleFeature()) + .addPageFeature(makeParagraphFeature()) + .addPageFeature(makeParagraphFeature()) + .addPageFeature(makeParagraphFeature()) + .defaultKnown() + //.addPage(simpleTextPage(3, true)) .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) - .addPage(simpleTextPage(3, true)) .build(ctx) ResearchEntryBuilder( @@ -202,20 +227,22 @@ class T7DatapackBuiltinEntriesProvider(output: PackOutput, registries: Completab ) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) .addPage { _, _ -> CraftingPage(Recipes.CHEST) } - //.addChild(ResearchEntries.Thavma.WANDS) - //.addChild(ResearchEntries.Thavma.TECHNOLOGY) - //.addChild(ResearchEntries.Thavma.ALCHEMY) - //.addChild(ResearchEntries.Thavma.INFUSION) + .addChild(ResearchEntries.Thavma.WANDS) + .addChild(ResearchEntries.Thavma.TECHNOLOGY) + .addChild(ResearchEntries.Thavma.ALCHEMY) + .addChild(ResearchEntries.Thavma.INFUSION) //.addChild(ResearchEntries.Thavma.RESEARCH_PROFICIENCY) + .defaultKnown() .build(ctx) ResearchEntryBuilder( ResearchEntries.Thavma.RESEARCH_PROFICIENCY, - Vector2i(-1, -1), + Vector2i(-2, -2), false, T7Blocks.RESEARCH_TABLE.get().asItem().defaultInstance ) .research(lockedAspect(2, 0, Aspects.AETHER), lockedAspect(2, 4, Aspects.HERBA)) + .addChild(ResearchEntries.Thavma.RESEARCH_TABLE) .defaultKnown() .build(ctx) diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index 85f26bb5..a7066e02 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -412,46 +412,76 @@ Never gonna tell a lie and hurt you addEntry(ResearchEntries.Story.TEST2, "I will not be denied") addParagraphFeature(ResearchEntries.Story.TEST2, 0, "When faced with setback, we must challenge our assumptions.") - addTextPage( - ResearchEntries.Thavma.THAVMA, 0, - "Thavma", - """ + addTitleFeature(ResearchEntries.Thavma.THAVMA, 0, "Thavma") + addParagraphFeature(ResearchEntries.Thavma.THAVMA, 0, """ I was merely toying with that wand -if it can even be called that- when this tome flew into my hands! I can sense great power within it. - """, - """ + """) + addParagraphFeature(ResearchEntries.Thavma.THAVMA, 1, """ The cover reads "Elements", but a lot of its pages appear blank, sealed by some magic. - """, - """ + """) + addParagraphFeature(ResearchEntries.Thavma.THAVMA, 2, """ To read them, I will first need to break that seal. It won't be easy... but I have a feeling it will be worth my efforts. - """ - ) - - addTextPage( - ResearchEntries.Thavma.THAVMA, 1, - null, - """ + """) + addParagraphFeature(ResearchEntries.Thavma.THAVMA, 3, """ I will document all my findings inside the book, so that I can recall them later. - """ - ) - - addTextPage( - ResearchEntries.Thavma.ARCANE_LENS, 0, - "The Arcane Lens", - """ + """) + +// addTextPage( +// ResearchEntries.Thavma.THAVMA, 0, +// "Thavma", +// """ +// I was merely toying with that wand -if it can even be called that- when this tome +// flew into my hands! I can sense great power within it. +// """, +// """ +// The cover reads "Elements", but a lot of its pages appear blank, sealed by some magic. +// """, +// """ +// To read them, I will first need to break that seal. It won't be easy... but +// I have a feeling it will be worth my efforts. +// """ +// ) + +// addTextPage( +// ResearchEntries.Thavma.THAVMA, 1, +// null, +// """ +// I will document all my findings inside the book, so that I can recall them later. +// """ +// ) + + addTitleFeature(ResearchEntries.Thavma.ARCANE_LENS,0,"The Arcane Lens") + addParagraphFeature(ResearchEntries.Thavma.ARCANE_LENS, 0, """ The part of the book I can read describes an arcane tool that "allows the user to see", whatever that might mean. I have a feeling that crafting it could assist my work in unsealing the other pages. - """, - """ + """) + addParagraphFeature(ResearchEntries.Thavma.ARCANE_LENS, 1, """ The blueprint describes a hexagonal device, much like a prism, made with those colorful crystals I found lying in a cave. - """, - """ + """) + addParagraphFeature(ResearchEntries.Thavma.ARCANE_LENS, 2, """ I should look at the world through its lens, maybe it will uncover something useful. - """ - ) + """) + +// addTextPage( +// ResearchEntries.Thavma.ARCANE_LENS, 0, +// "The Arcane Lens", +// """ +// The part of the book I can read describes an arcane tool that "allows the user +// to see", whatever that might mean. I have a feeling that crafting it could assist +// my work in unsealing the other pages. +// """, +// """ +// The blueprint describes a hexagonal device, much like a prism, +// made with those colorful crystals I found lying in a cave. +// """, +// """ +// I should look at the world through its lens, maybe it will uncover something useful. +// """ +// ) add(RESEARCH_SCROLL.get().completedTranslation(), "Completed Research") add(ResearchEntry.TOAST_TRANSLATION, "Research Complete!") From ac1c7ac9cf8bd64eb922a5924f7d56007ad39cff Mon Sep 17 00:00:00 2001 From: KorrAn34 Date: Thu, 28 May 2026 21:22:55 +0200 Subject: [PATCH 19/19] Code cleanup --- .../thavma/impl/client/gui/book/BookScreen.kt | 14 ------ .../impl/client/gui/book/EntryWidget.kt | 5 --- .../thavma/impl/client/gui/book/GridHelper.kt | 15 ------- .../impl/client/gui/book/TabRenderable.kt | 1 - .../impl/common/research/ResearchEntry.kt | 30 +++++++------ .../T7DatapackBuiltinEntriesProvider.kt | 12 +---- .../init/data/providers/T7LanguageProvider.kt | 44 ++++++++++++------- 7 files changed, 47 insertions(+), 74 deletions(-) diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt index 8d6a23f3..7f995315 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/BookScreen.kt @@ -17,17 +17,12 @@ class BookScreen : Screen(Component.literal("book")) { private var isScrolling = false private var currentCategory: ResearchCategory? = null private val tabs = mutableMapOf() - private val backgrounds = mutableListOf() val currentTab get() = tabs[currentCategory] ?: tabs.toList().first().second private var selectorOffset = 0 private val entryWidgets = mutableListOf() private val entries = clientRegistry(T7DatapackRegistries.RESEARCH_ENTRY)?.holders()?.toList() - val haha = entries.apply { println(this) } private var currentEntries = entries?.filter { it.value().category.value() == currentCategory } - - //actualEntries[currentCategory] ?: actualEntries.toList().first().second - private val hehe = currentEntries.apply { println(this) } var currentWidgets = entryWidgets.filter { entryWidget -> currentEntries?.map { it.value() }?.contains(entryWidget.entry.value()) == true } @@ -60,15 +55,6 @@ class BookScreen : Screen(Component.literal("book")) { ) ) } -// clientRegistry(T7DatapackRegistries.RESEARCH_ENTRY)?.holders()?.forEach { -// val tab = tabs[it.value().category.value()] -// var shown = player.knowsResearch(it) -// for (p in it.value().parents(player.level())) -// if (player.knowsResearch(p)) shown = true -// -// if (tab != null && shown) -// entryWidgets.add(addRenderableWidget(EntryWidget(this, tab, it))) -// } // moved EntryWidget creation logic into TabRenderable.kt, here only adding them via // the protected method addRenderableWidget diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt index f046af52..4fa0a676 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/EntryWidget.kt @@ -3,8 +3,6 @@ package me.alegian.thavma.impl.client.gui.book import com.mojang.blaze3d.systems.RenderSystem import me.alegian.thavma.impl.client.ClientHelper import me.alegian.thavma.impl.client.clientSound -import me.alegian.thavma.impl.client.gui.book.TabRenderable.Companion.maxScrollX -import me.alegian.thavma.impl.client.gui.book.TabRenderable.Companion.maxScrollY import me.alegian.thavma.impl.client.gui.tooltip.T7Tooltip import me.alegian.thavma.impl.client.pushScreen import me.alegian.thavma.impl.client.texture.Texture @@ -12,7 +10,6 @@ import me.alegian.thavma.impl.client.util.* import me.alegian.thavma.impl.common.entity.knowsResearch import me.alegian.thavma.impl.common.payload.ResearchScrollPayload import me.alegian.thavma.impl.common.research.ResearchEntry -import me.alegian.thavma.impl.common.util.minus import net.minecraft.ChatFormatting import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.components.AbstractWidget @@ -139,7 +136,6 @@ class EntryWidget( var alpha = 1f if (!knowsResearch) brightness = 0.55f if (!knowsResearch) alpha = (abs(sin(player.level().gameTime / 8.0f)) / 4 * 3 + 0.25f) - //RenderSystem.setShaderColor(brightness, brightness, brightness, alpha) renderGridElement( guiGraphics, @@ -163,7 +159,6 @@ class EntryWidget( guiGraphics.setColor(1f, 1f, 1f, 1f) RenderSystem.disableBlend() - //resetRenderSystemColor() } override fun playDownSound(handler: SoundManager) { diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt index 3e9270a6..b2472ba9 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/GridHelper.kt @@ -3,12 +3,9 @@ package me.alegian.thavma.impl.client.gui.book import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.PoseStack import com.mojang.math.Axis -import me.alegian.thavma.impl.client.ClientHelper import me.alegian.thavma.impl.client.texture.Texture -import me.alegian.thavma.impl.client.util.resetRenderSystemColor import me.alegian.thavma.impl.client.util.translateXY import me.alegian.thavma.impl.client.util.usePose -import me.alegian.thavma.impl.common.entity.knowsResearch import me.alegian.thavma.impl.common.research.ResearchEntry import net.minecraft.client.gui.GuiGraphics import net.minecraft.core.Holder @@ -49,7 +46,6 @@ fun PoseStack.renderConnectionRecursive( val signY = dy.sign val inversion = if (invert) -1f else 1f val preference = if (preferX) -1f else 1f - //val knowsResearch = ClientHelper.player()?.knowsResearch(entry) ?: false if (absDx + absDy <= 1f) return else if (absDx > 2 && absDy > 2) throw IllegalStateException("Yup the problem is here in GridHelper.kt") @@ -84,14 +80,6 @@ private fun renderLine( player: Player, knowsChild: Boolean ) { - var brightness = 1f - var alpha = 1f - if (!knowsChild) brightness = 0.55f - if (!knowsChild) alpha = (abs(sin(player.level().gameTime / 8.0f)) / 4 * 3 + 0.25f) - RenderSystem.enableBlend() - RenderSystem.defaultBlendFunc() - guiGraphics.setColor(brightness, brightness, brightness, alpha) - renderGridElement( guiGraphics, signX, @@ -101,9 +89,6 @@ private fun renderLine( player, knowsChild ) - - guiGraphics.setColor(1f, 1f, 1f, 1f) - RenderSystem.disableBlend() } diff --git a/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt b/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt index 98e255c9..d7bd4bb2 100644 --- a/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt +++ b/src/main/java/me/alegian/thavma/impl/client/gui/book/TabRenderable.kt @@ -5,7 +5,6 @@ import me.alegian.thavma.impl.client.util.* import me.alegian.thavma.impl.common.entity.knowsResearch import me.alegian.thavma.impl.common.research.ResearchCategory import me.alegian.thavma.impl.common.research.ResearchEntry -import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.components.Renderable import net.minecraft.core.Holder diff --git a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt index b6724b30..324203e0 100644 --- a/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt +++ b/src/main/java/me/alegian/thavma/impl/common/research/ResearchEntry.kt @@ -3,7 +3,6 @@ package me.alegian.thavma.impl.common.research import com.mojang.serialization.Codec import com.mojang.serialization.codecs.RecordCodecBuilder import me.alegian.thavma.impl.Thavma -import me.alegian.thavma.impl.common.book.Page import me.alegian.thavma.impl.common.book.PageFeature import me.alegian.thavma.impl.common.util.T7ExtraCodecs import me.alegian.thavma.impl.common.util.registry @@ -21,15 +20,15 @@ import org.joml.Vector2i private val parentsMap = mutableMapOf>>() class ResearchEntry( - val category: Holder, - val position: Vector2i, - val preferX: Boolean, - val children: List>, - val pageFeatures: List, - val icon: ItemStack, - val title: Component, - val defaultResearchState: List, - val defaultKnown: Boolean + val category: Holder, + val position: Vector2i, + val preferX: Boolean, + val children: List>, + val pageFeatures: List, + val icon: ItemStack, + val title: Component, + val defaultResearchState: List, + val defaultKnown: Boolean ) { override fun toString(): String { return "Entry ${title.string} in ${this.category.value().title}" @@ -38,16 +37,18 @@ class ResearchEntry( fun parents(level: Level) = parentsMap.computeIfAbsent(this) { _ -> val registry = level.registry(T7DatapackRegistries.RESEARCH_ENTRY) - registry.holders().filter { e -> e.value().children.map{it.value()}.contains(this) }.toList() + registry.holders().filter { e -> e.value().children.map { it.value() }.contains(this) }.toList() } companion object { val CODEC: Codec = RecordCodecBuilder.create { it.group( - RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_CATEGORY, ResearchCategory.CODEC).fieldOf("category").forGetter(ResearchEntry::category), + RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_CATEGORY, ResearchCategory.CODEC).fieldOf("category") + .forGetter(ResearchEntry::category), T7ExtraCodecs.VECTOR2I.fieldOf("position").forGetter(ResearchEntry::position), Codec.BOOL.fieldOf("preferX").forGetter(ResearchEntry::preferX), - RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_ENTRY, CODEC).listOf().fieldOf("children").forGetter(ResearchEntry::children), + RegistryFileCodec.create(T7DatapackRegistries.RESEARCH_ENTRY, CODEC).listOf().fieldOf("children") + .forGetter(ResearchEntry::children), PageFeature.CODEC.listOf().fieldOf("pageFeatures").forGetter(ResearchEntry::pageFeatures), ItemStack.STRICT_CODEC.fieldOf("icon").forGetter(ResearchEntry::icon), ComponentSerialization.CODEC.fieldOf("title").forGetter(ResearchEntry::title), @@ -56,7 +57,8 @@ class ResearchEntry( ).apply(it, ::ResearchEntry) } - fun translationId(entryKey: ResourceKey) = Util.makeDescriptionId(T7DatapackRegistries.RESEARCH_ENTRY.location().path, entryKey.location()) + fun translationId(entryKey: ResourceKey) = + Util.makeDescriptionId(T7DatapackRegistries.RESEARCH_ENTRY.location().path, entryKey.location()) val TOAST_TRANSLATION = "research." + Thavma.MODID + ".toast" val SCROLL_GIVEN_TRANSLATION = "research." + Thavma.MODID + ".scroll_given" diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt index 5518ad54..d21fc9b8 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7DatapackBuiltinEntriesProvider.kt @@ -3,14 +3,7 @@ package me.alegian.thavma.impl.init.data.providers import me.alegian.thavma.impl.Thavma import me.alegian.thavma.impl.client.texture.Texture import me.alegian.thavma.impl.common.aspect.Aspect -import me.alegian.thavma.impl.common.book.CraftingPage -import me.alegian.thavma.impl.common.book.FigureFeature -import me.alegian.thavma.impl.common.book.Page -import me.alegian.thavma.impl.common.book.PageFeature -import me.alegian.thavma.impl.common.book.ParagraphFeature -import me.alegian.thavma.impl.common.book.RecipeFeature -import me.alegian.thavma.impl.common.book.TextPage -import me.alegian.thavma.impl.common.book.TitleFeature +import me.alegian.thavma.impl.common.book.* import me.alegian.thavma.impl.common.enchantment.ShriekResistance.LOCATION import me.alegian.thavma.impl.common.research.ResearchCategory import me.alegian.thavma.impl.common.research.ResearchEntry @@ -52,7 +45,6 @@ import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition import net.minecraft.world.item.enchantment.EnchantmentEffectComponents import net.minecraft.world.item.enchantment.LevelBasedValue import net.minecraft.world.item.enchantment.effects.AddValue -import net.minecraft.world.level.block.Blocks import net.minecraft.world.level.storage.loot.predicates.DamageSourceCondition import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider import net.neoforged.neoforge.registries.NeoForgeRegistries @@ -455,7 +447,7 @@ private fun makeRecipeFeature( mustOccupySetPage: Boolean = true, preferredPageIndex: Int = 1 ): (ResourceKey, Int) -> RecipeFeature { - return { entryKey, titleIndex -> + return { _, _ -> RecipeFeature( recipeRL, coversOneWholePage, mustStartPage, mustOccupySetPage, preferredPageIndex diff --git a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt index a7066e02..71e1e330 100644 --- a/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt +++ b/src/main/java/me/alegian/thavma/impl/init/data/providers/T7LanguageProvider.kt @@ -413,20 +413,28 @@ Never gonna tell a lie and hurt you addParagraphFeature(ResearchEntries.Story.TEST2, 0, "When faced with setback, we must challenge our assumptions.") addTitleFeature(ResearchEntries.Thavma.THAVMA, 0, "Thavma") - addParagraphFeature(ResearchEntries.Thavma.THAVMA, 0, """ + addParagraphFeature( + ResearchEntries.Thavma.THAVMA, 0, """ I was merely toying with that wand -if it can even be called that- when this tome flew into my hands! I can sense great power within it. - """) - addParagraphFeature(ResearchEntries.Thavma.THAVMA, 1, """ + """ + ) + addParagraphFeature( + ResearchEntries.Thavma.THAVMA, 1, """ The cover reads "Elements", but a lot of its pages appear blank, sealed by some magic. - """) - addParagraphFeature(ResearchEntries.Thavma.THAVMA, 2, """ + """ + ) + addParagraphFeature( + ResearchEntries.Thavma.THAVMA, 2, """ To read them, I will first need to break that seal. It won't be easy... but I have a feeling it will be worth my efforts. - """) - addParagraphFeature(ResearchEntries.Thavma.THAVMA, 3, """ + """ + ) + addParagraphFeature( + ResearchEntries.Thavma.THAVMA, 3, """ I will document all my findings inside the book, so that I can recall them later. - """) + """ + ) // addTextPage( // ResearchEntries.Thavma.THAVMA, 0, @@ -452,19 +460,25 @@ Never gonna tell a lie and hurt you // """ // ) - addTitleFeature(ResearchEntries.Thavma.ARCANE_LENS,0,"The Arcane Lens") - addParagraphFeature(ResearchEntries.Thavma.ARCANE_LENS, 0, """ + addTitleFeature(ResearchEntries.Thavma.ARCANE_LENS, 0, "The Arcane Lens") + addParagraphFeature( + ResearchEntries.Thavma.ARCANE_LENS, 0, """ The part of the book I can read describes an arcane tool that "allows the user to see", whatever that might mean. I have a feeling that crafting it could assist my work in unsealing the other pages. - """) - addParagraphFeature(ResearchEntries.Thavma.ARCANE_LENS, 1, """ + """ + ) + addParagraphFeature( + ResearchEntries.Thavma.ARCANE_LENS, 1, """ The blueprint describes a hexagonal device, much like a prism, made with those colorful crystals I found lying in a cave. - """) - addParagraphFeature(ResearchEntries.Thavma.ARCANE_LENS, 2, """ + """ + ) + addParagraphFeature( + ResearchEntries.Thavma.ARCANE_LENS, 2, """ I should look at the world through its lens, maybe it will uncover something useful. - """) + """ + ) // addTextPage( // ResearchEntries.Thavma.ARCANE_LENS, 0,