diff --git a/docs/ASSET_PROVENANCE.md b/docs/ASSET_PROVENANCE.md index ba85adb..b5979ad 100644 --- a/docs/ASSET_PROVENANCE.md +++ b/docs/ASSET_PROVENANCE.md @@ -23,7 +23,8 @@ project's GNU LGPL v3 license. Rivencraft itself uses the same license. | `assets/rivencraft/textures/block/ink_mixer_top.png`, `ink_mixer_side.png`, `ink_mixer_bottom.png` | `assets/mystcraft/textures/blocks/inkmixer_top.png`, `inkmixer_side.png`, `inkmixer_bottom.png` | `9bc8ddc061845df0cd5ea47f4fdef773cff786ae` | Namespace/path rename only; Rivencraft supplies its own block model | | `assets/rivencraft/textures/item/linking_book_unwritten.png` | `assets/mystcraft/textures/items/unlinked.png` | `9bc8ddc061845df0cd5ea47f4fdef773cff786ae` | Namespace/path rename only | | `assets/rivencraft/textures/item/page_link_panel.png` | `page_background.png` and `PageBuilder.BasicPageSprite` | `9bc8ddc061845df0cd5ea47f4fdef773cff786ae` | Reproduces the legacy 160 px scale, black panel at `(25,30)`–`(135,75)`, and 128 px resample | -| `assets/rivencraft/textures/item/page_symbol*.png` and `page_unresolved.png` | `page_background.png`, `textures/symbolcomponents.png`, `WordData`, `ModSymbols`, and `PageBuilder.PageSprite` | `9bc8ddc061845df0cd5ea47f4fdef773cff786ae` | Static 1.21.1 renditions of the legacy runtime-composed four-word symbol pages; `normal_time` uses the legacy Full Length poem | +| `assets/rivencraft/textures/gui/symbolcomponents.png` | `assets/mystcraft/textures/symbolcomponents.png` | `9bc8ddc061845df0cd5ea47f4fdef773cff786ae` | Byte-identical shared 512×512 component sheet for client-side page composition | +| Runtime-composed symbol pages (`SymbolPageTextures`) | `page_background.png`, `textures/symbolcomponents.png`, `WordData`, `ModSymbols`, and `PageBuilder.PageSprite` | `9bc8ddc061845df0cd5ea47f4fdef773cff786ae` | Replaces the six pre-generated symbol PNGs; layers four poem words on the shared page background and caches bounded client textures | Mystcraft Legacy identifies XCompWiz as its project developer and includes the full LGPL v3 text in its root `LICENSE` file. Rivencraft's root `LICENSE` diff --git a/docs/SYMBOL_SEMANTICS.md b/docs/SYMBOL_SEMANTICS.md index 6b4ffd8..5c91404 100644 --- a/docs/SYMBOL_SEMANTICS.md +++ b/docs/SYMBOL_SEMANTICS.md @@ -11,12 +11,17 @@ Milestone 5 issue #29 adds typed meaning to the existing data-pack symbol catalo "semantics": { "role": "terrain", "value": "example:highlands" - } + }, + "poem": "Terrain Form Tradition Flow" } ``` `name` and `description` are translation keys. `category` is for display and sorting; it does not determine grammar. `semantics.role` and its typed `value` are server-owned. The terrain value above names an Age runtime profile that a later compiler must resolve and validate. A syntactically valid resource ID alone does not guarantee the profile or feature exists. +`poem` is optional, client-safe artwork metadata: exactly four alphabetic words, ordered top, right, bottom, left. The client layers their glyphs from the shared Legacy component sheet at runtime. GUI thumbnails use the full transparent artwork over their page backgrounds; item icons use the same artwork reduced from 16 to 13 pixels across, over the page texture. Unregistered words use a stable generated component combination; when a symbol has no poem, the six original built-in IDs retain their Legacy poems and other IDs produce four stable fallback words. This affects appearance only, never Age generation. Generated images are cached up to 256 entries and discarded on resource reload. + +The Writing Desk's 88 collection tabs use Legacy's number components, chiefly the bottom row of `symbolcomponents.png`, rather than word sigils. Legacy's `ModWords.constructNumber` combines four unit marks (1–4) with four five-step marks (5–20) to form 1–24 and uses a separate zero mark. Rivencraft extends these marks positionally in base 25 for tabs 25–88 (for example, 25 is 1 followed by 0). Hovering still reveals the decimal number for accessibility. The former arbitrary-word glyph generator remains useful for rendering text: on page-free Descriptive Books, the generated Age description now appears as a sequence of layered word glyphs instead of Minecraft's enchantment font. The Age name stays readable. + | Role | Value type and allowed values | | --- | --- | | `terrain` | Canonical namespaced Age profile ID. | diff --git a/gradle.properties b/gradle.properties index 91ea795..ee7c787 100644 --- a/gradle.properties +++ b/gradle.properties @@ -36,7 +36,7 @@ mod_name=Rivencraft # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0-or-later # The mod version. Rivencraft uses the x.y.z.b policy documented in README.md. -mod_version=0.4.1.0 +mod_version=0.4.1.5 immersive_portals_version=6.0.7 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. diff --git a/src/main/java/net/dnorberg/rivencraft/client/ClientEvents.java b/src/main/java/net/dnorberg/rivencraft/client/ClientEvents.java index 7cb2c55..a6732af 100644 --- a/src/main/java/net/dnorberg/rivencraft/client/ClientEvents.java +++ b/src/main/java/net/dnorberg/rivencraft/client/ClientEvents.java @@ -13,6 +13,10 @@ import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.client.event.EntityRenderersEvent; import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; +import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; +import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; +import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent; +import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; import net.dnorberg.rivencraft.client.screen.NotebookScreen; import net.dnorberg.rivencraft.client.screen.WritingDeskScreen; import net.dnorberg.rivencraft.client.screen.BookBinderScreen; @@ -38,6 +42,24 @@ public final class ClientEvents { event.register(ModMenus.INK_MIXER.get(), InkMixerScreen::new); } + @SubscribeEvent + public static void registerPageItemRenderer(RegisterClientExtensionsEvent event) { + event.registerItem(new IClientItemExtensions() { + private final SymbolPageItemRenderer renderer = new SymbolPageItemRenderer(); + + @Override + public BlockEntityWithoutLevelRenderer getCustomRenderer() { + return renderer; + } + }, ModItems.PAGE.get()); + } + + @SubscribeEvent + public static void registerPageTextureReload(RegisterClientReloadListenersEvent event) { + event.registerReloadListener((net.minecraft.server.packs.resources.ResourceManagerReloadListener) + manager -> SymbolPageTextures.clear()); + } + @SubscribeEvent public static void clientSetup(FMLClientSetupEvent event) { event.enqueueWork(() -> ItemProperties.register( diff --git a/src/main/java/net/dnorberg/rivencraft/client/SymbolPageItemRenderer.java b/src/main/java/net/dnorberg/rivencraft/client/SymbolPageItemRenderer.java new file mode 100644 index 0000000..2a37c9a --- /dev/null +++ b/src/main/java/net/dnorberg/rivencraft/client/SymbolPageItemRenderer.java @@ -0,0 +1,47 @@ +package net.dnorberg.rivencraft.client; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.dnorberg.rivencraft.Rivencraft; +import net.dnorberg.rivencraft.item.PageItem; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; + +/** Flat page model whose symbol artwork is resolved from the stack at render time. */ +public final class SymbolPageItemRenderer extends BlockEntityWithoutLevelRenderer { + public SymbolPageItemRenderer() { + super(Minecraft.getInstance().getBlockEntityRenderDispatcher(), Minecraft.getInstance().getEntityModels()); + } + + @Override + public void renderByItem(ItemStack stack, ItemDisplayContext context, PoseStack pose, + MultiBufferSource buffers, int light, int overlay) { + ResourceLocation texture = switch (PageItem.content(stack).kind()) { + case BLANK -> texture("page"); + case LINK_PANEL -> texture("page_link_panel"); + case UNRESOLVED_SYMBOL -> texture("page_unresolved"); + case SYMBOL -> SymbolPageTextures.forItem(stack); + }; + VertexConsumer vertices = buffers.getBuffer(RenderType.entityCutoutNoCull(texture)); + var matrix = pose.last().pose(); + vertex(vertices, matrix, 0, 0, 0, 1, light, overlay); + vertex(vertices, matrix, 1, 0, 1, 1, light, overlay); + vertex(vertices, matrix, 1, 1, 1, 0, light, overlay); + vertex(vertices, matrix, 0, 1, 0, 0, light, overlay); + } + + private static void vertex(VertexConsumer vertices, org.joml.Matrix4f matrix, + float x, float y, float u, float v, int light, int overlay) { + vertices.addVertex(matrix, x, y, 0.5F).setColor(255, 255, 255, 255) + .setUv(u, v).setOverlay(overlay).setLight(light).setNormal(0, 0, 1); + } + + private static ResourceLocation texture(String name) { + return ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, "textures/item/" + name + ".png"); + } +} diff --git a/src/main/java/net/dnorberg/rivencraft/client/SymbolPageTextures.java b/src/main/java/net/dnorberg/rivencraft/client/SymbolPageTextures.java new file mode 100644 index 0000000..38a2799 --- /dev/null +++ b/src/main/java/net/dnorberg/rivencraft/client/SymbolPageTextures.java @@ -0,0 +1,240 @@ +package net.dnorberg.rivencraft.client; + +import com.mojang.blaze3d.platform.NativeImage; +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import net.dnorberg.rivencraft.Rivencraft; +import net.dnorberg.rivencraft.item.PageItem; +import net.dnorberg.rivencraft.registry.ModDataComponents; +import net.dnorberg.rivencraft.writing.PageContent; +import net.dnorberg.rivencraft.writing.SymbolDisplayMetadata; +import net.dnorberg.rivencraft.writing.SymbolPoems; +import net.dnorberg.rivencraft.writing.SymbolNumerals; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.world.item.ItemStack; + +/** Client-only, bounded cache of Legacy-style page compositions; no per-symbol PNGs. */ +public final class SymbolPageTextures { + private static final int SIZE = 128; + private static final int GLYPH = 51; + // A 16 px item icon displays a 13 px symbol: three pixels narrower overall. + private static final int ITEM_SYMBOL_SIZE = 13; + private static final int ICON_SIZE = 16; + private static final int CACHE_LIMIT = 256; + private static final ResourceLocation BASE = texture("item/page"); + private static final ResourceLocation COMPONENTS = texture("gui/symbolcomponents"); + private static final ResourceLocation FALLBACK = texture("item/page_symbol"); + private static final Map CACHE = new LinkedHashMap<>(16, .75F, true); + private static final Map WORD_CACHE = new LinkedHashMap<>(16, .75F, true); + private static NativeImage base; + private static NativeImage components; + private static int nextId; + + // WordData from Mystcraft Legacy 0.13.7.06. Other words get a stable generated glyph. + private static final Map WORDS = Map.ofEntries( + Map.entry("Terrain", new int[] {6,10,12,13,16,19,24,25,27}), + Map.entry("Form", new int[] {4,6,8,9,10,11,13,14,16,23}), + Map.entry("Tradition", new int[] {4,6,7,8,9,14,17,18,20}), + Map.entry("Flow", new int[] {4,7,12,13,17,18}), + Map.entry("Constraint", new int[] {5,6,9,11,13,20,22,23}), + Map.entry("Nature", new int[] {5,6,8,10,11,12,15,16,17,22}), + Map.entry("Sustain", new int[] {6,16,19,20,21,23}), + Map.entry("Ethereal", new int[] {4,6,8,13,14,16,18,21,23}), + Map.entry("Dynamic", new int[] {4,5,6,12,13,17,18,22,23}), + Map.entry("Cycle", new int[] {4,5,7,16,17,18}), + Map.entry("Balance", new int[] {4,6,9,10,16,19}), + Map.entry("Transform", new int[] {4,5,6,8,11,14,18,20,21}), + Map.entry("Time", new int[] {4,5,7,12,13,19,20,21,22,23}), + Map.entry("System", new int[] {10,12,14,15,16,20,21,22}), + Map.entry("Void", new int[] {4,5,6,16,17,18,23}) + ); + + private SymbolPageTextures() {} + + /** Transparent full-size artwork for GUI thumbnails drawn over their own page background. */ + public static ResourceLocation forPage(ItemStack stack) { + return forPage(stack, false); + } + + /** Full page texture with artwork reduced by three pixels at 16 px item-icon scale. */ + public static ResourceLocation forItem(ItemStack stack) { + return forPage(stack, true); + } + + private static ResourceLocation forPage(ItemStack stack, boolean item) { + PageContent content = PageItem.content(stack); + if (content.kind() == PageContent.Kind.UNRESOLVED_SYMBOL) return texture("item/page_unresolved"); + if (content.kind() != PageContent.Kind.SYMBOL) return FALLBACK; + SymbolDisplayMetadata metadata = stack.get(ModDataComponents.PAGE_SYMBOL_DISPLAY.get()); + ResourceLocation id = content.symbol().orElseThrow().id(); + return forSymbol(id, metadata == null ? "" : metadata.poem(), item); + } + + private static ResourceLocation forSymbol(ResourceLocation id, String poem, boolean item) { + if (poem.length() > 96 || !poem.matches("[A-Za-z]+(?: [A-Za-z]+){3}")) poem = ""; + if (poem.isEmpty()) poem = SymbolPoems.builtIn(id); + String key = id + "|" + poem + (item ? "|item" : "|thumbnail"); + ResourceLocation cached = CACHE.get(key); + if (cached != null) return cached; + try { + loadSources(Minecraft.getInstance().getResourceManager()); + NativeImage image = compose(id, poem, item); + ResourceLocation location = ResourceLocation.fromNamespaceAndPath( + Rivencraft.MOD_ID, "generated/page_" + nextId++); + Minecraft.getInstance().getTextureManager().register(location, new DynamicTexture(image)); + CACHE.put(key, location); + if (CACHE.size() > CACHE_LIMIT) { + String eldest = CACHE.keySet().iterator().next(); + Minecraft.getInstance().getTextureManager().release(CACHE.remove(eldest)); + } + return location; + } catch (IOException exception) { + Rivencraft.LOGGER.error("Could not compose symbol page {}", id, exception); + return FALLBACK; + } + } + + public static void clear() { + var textures = Minecraft.getInstance().getTextureManager(); + CACHE.values().forEach(textures::release); + CACHE.clear(); + WORD_CACHE.clear(); + if (base != null) base.close(); + if (components != null) components.close(); + base = null; + components = null; + } + + private static void loadSources(ResourceManager manager) throws IOException { + if (base != null && components != null) return; + try (InputStream page = manager.getResourceOrThrow(BASE).open(); + InputStream sheet = manager.getResourceOrThrow(COMPONENTS).open()) { + base = NativeImage.read(page); + components = NativeImage.read(sheet); + } + } + + private static NativeImage compose(ResourceLocation id, String poem, boolean item) { + // GUI variants have no parchment layer, so their untouched pixels must be transparent. + NativeImage image = new NativeImage(SIZE, SIZE, true); + if (item) { + for (int y = 0; y < SIZE; y++) for (int x = 0; x < SIZE; x++) { + image.setPixelRGBA(x, y, base.getPixelRGBA(x * base.getWidth() / SIZE, + y * base.getHeight() / SIZE)); + } + } + String[] words = poem.isBlank() ? generatedWords(id) : poem.split(" "); + int[] offsetsX = {38, 77, 38, 0}; + int[] offsetsY = {0, 38, 77, 38}; + int glyphSize = item ? GLYPH * ITEM_SYMBOL_SIZE / ICON_SIZE : GLYPH; + for (int word = 0; word < 4; word++) { + int originX = item ? scaleForItem(offsetsX[word]) : offsetsX[word]; + int originY = item ? scaleForItem(offsetsY[word]) : offsetsY[word]; + for (int component : glyphs(words[word])) { + for (int y = 0; y < glyphSize; y++) for (int x = 0; x < glyphSize; x++) { + int pixel = components.getPixelRGBA((component % 8) * 64 + x * 64 / glyphSize, + (component / 8) * 64 + y * 64 / glyphSize); + int alpha = pixel >>> 24; + if (alpha != 0) image.blendPixel(originX + x, originY + y, + (alpha << 24) | 0x001A212B); + } + } + } + return image; + } + + private static int scaleForItem(int coordinate) { + return SIZE / 2 + Math.round((coordinate - SIZE / 2F) * ITEM_SYMBOL_SIZE / ICON_SIZE); + } + + private static String[] generatedWords(ResourceLocation id) { + String stem = id.toString(); + return new String[] {stem + 'a', stem + 'b', stem + 'c', stem + 'd'}; + } + + private static int[] glyphs(String word) { + int[] known = WORDS.get(word); + if (known != null) return known; + String key = word.toLowerCase(Locale.ROOT); + int[] cached = WORD_CACHE.get(key); + if (cached != null) return cached; + Random random = new Random(key.hashCode()); + int[] components = new int[random.nextInt(10) + 3]; + for (int index = 0; index < components.length; index++) components[index] = random.nextInt(20) + 4; + WORD_CACHE.put(key, components); + if (WORD_CACHE.size() > CACHE_LIMIT) WORD_CACHE.remove(WORD_CACHE.keySet().iterator().next()); + return components; + } + + /** Draw a word as a layered Legacy glyph, or a number with Legacy's number components. */ + public static void drawWordGlyph(GuiGraphics graphics, String word, int x, int y, int size) { + if (word.matches("[0-9]+")) { + try { + drawNumeral(graphics, Integer.parseInt(word), x, y, size); + return; + } catch (NumberFormatException ignored) { + // Oversized numerals still receive a stable word glyph. + } + } + graphics.setColor(0.13F, 0.10F, 0.08F, 1F); + for (int component : glyphs(word)) drawComponent(graphics, component, x, y, size); + graphics.setColor(1F, 1F, 1F, 1F); + } + + /** Legacy's single-number marks; numbers above 24 use positional base 25. */ + public static void drawNumeral(GuiGraphics graphics, int number, int x, int y, int size) { + int[] digits = SymbolNumerals.digitsFor(number); + int digitSize = digits.length == 1 ? size : Math.max(8, size * 3 / 4); + graphics.setColor(0.13F, 0.10F, 0.08F, 1F); + for (int digit : digits) { + for (int component : SymbolNumerals.componentsForDigit(digit)) + drawComponent(graphics, component, x, y + (size - digitSize) / 2, digitSize); + x += digitSize + 1; + } + graphics.setColor(1F, 1F, 1F, 1F); + } + + /** Render translated prose as a sequence of word glyphs within a fixed-width book page. */ + public static int drawGlyphText(GuiGraphics graphics, String text, int x, int y, int maxWidth) { + int cursor = x; + boolean drew = false; + for (String word : text.split("[^\\p{L}\\p{N}]+")) { + if (word.isEmpty()) continue; + int width = word.matches("[0-9]+") ? numeralWidth(word, 12) : 12; + if (cursor != x && cursor + width > x + maxWidth) { + cursor = x; + y += 13; + } + drawWordGlyph(graphics, word, cursor, y, 12); + cursor += width + 3; + drew = true; + } + return y + (drew ? 13 : 0); + } + + private static int numeralWidth(String word, int size) { + try { + int count = SymbolNumerals.digitsFor(Integer.parseInt(word)).length; + return count == 1 ? size : count * (size * 3 / 4 + 1) - 1; + } catch (NumberFormatException ignored) { + return size; + } + } + + private static void drawComponent(GuiGraphics graphics, int component, int x, int y, int size) { + graphics.blit(COMPONENTS, x, y, size, size, + (component % 8) * 64, (component / 8) * 64, 64, 64, 512, 512); + } + + private static ResourceLocation texture(String path) { + return ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, "textures/" + path + ".png"); + } +} diff --git a/src/main/java/net/dnorberg/rivencraft/client/screen/BookBinderScreen.java b/src/main/java/net/dnorberg/rivencraft/client/screen/BookBinderScreen.java index b1f80b6..7c90bf0 100644 --- a/src/main/java/net/dnorberg/rivencraft/client/screen/BookBinderScreen.java +++ b/src/main/java/net/dnorberg/rivencraft/client/screen/BookBinderScreen.java @@ -146,7 +146,7 @@ public final class BookBinderScreen extends AbstractContainerScreen { } case LINK_PANEL -> graphics.fill(x + 5, y + 6, x + 22, y + 17, 0xFF000000); case SYMBOL, UNRESOLVED_SYMBOL -> graphics.blit(NotebookScreen.symbolTexture(page), - x + 1, y + 5, 24, 24, 16, 16, 96, 96, 128, 128); + x + 1, y + 5, 24, 24, 0, 0, 128, 128, 128, 128); } } diff --git a/src/main/java/net/dnorberg/rivencraft/client/screen/DescriptiveBookScreen.java b/src/main/java/net/dnorberg/rivencraft/client/screen/DescriptiveBookScreen.java index 189bc49..2b938b0 100644 --- a/src/main/java/net/dnorberg/rivencraft/client/screen/DescriptiveBookScreen.java +++ b/src/main/java/net/dnorberg/rivencraft/client/screen/DescriptiveBookScreen.java @@ -5,6 +5,7 @@ import java.util.Locale; import net.dnorberg.rivencraft.book.AgeReference; import net.dnorberg.rivencraft.book.AgeTraits; import net.dnorberg.rivencraft.client.ClientBookExploreOverlay; +import net.dnorberg.rivencraft.client.SymbolPageTextures; import net.dnorberg.rivencraft.network.UseDescriptiveBookPayload; import net.dnorberg.rivencraft.network.BindDescriptiveBookPayload; import net.dnorberg.rivencraft.network.ModNetworking; @@ -37,7 +38,6 @@ import org.jetbrains.annotations.Nullable; public final class DescriptiveBookScreen extends Screen implements ExplorableBookScreen { private static final int BOOK_TEXT_COLOR = 0xFF3B281B; private static final int UNRESOLVED_TEXT_COLOR = 0xFF9B302C; - private static final ResourceLocation ENCHANTMENT_FONT = ResourceLocation.withDefaultNamespace("alt"); private static final int BOOK_WIDTH = 327; private static final int BOOK_HEIGHT = 199; private static final int LINK_PANEL_X = 173; @@ -211,7 +211,7 @@ public final class DescriptiveBookScreen extends Screen implements ExplorableBoo Component profileName = generationProfile.getNamespace().equals("rivencraft") ? Component.translatable("generator.rivencraft." + generationProfile.getPath()) : Component.literal(generationProfile.toString()); - detailsY = drawWrappedWithoutShadow(graphics, + detailsY = drawDescription(graphics, Component.translatable("screen.rivencraft.age.generator", profileName), x, detailsY, 110) + 2; } @@ -415,10 +415,16 @@ public final class DescriptiveBookScreen extends Screen implements ExplorableBoo } private int drawTrait(GuiGraphics graphics, String key, Component value, int x, int y) { - return drawWrappedWithoutShadow(graphics, + return drawDescription(graphics, Component.translatable("screen.rivencraft.age.trait." + key, value), x, y, 110) + 2; } + private int drawDescription(GuiGraphics graphics, Component text, int x, int y, int maxWidth) { + return pageFree && age != null + ? SymbolPageTextures.drawGlyphText(graphics, text.getString(), x, y, maxWidth) + : drawWrappedWithoutShadow(graphics, text, x, y, maxWidth); + } + private static Component traitName(String group, Enum value) { return Component.translatable("trait.rivencraft." + group + "." + value.name().toLowerCase(Locale.ROOT)); @@ -429,9 +435,7 @@ public final class DescriptiveBookScreen extends Screen implements ExplorableBoo } private int drawWrappedWithoutShadow(GuiGraphics graphics, Component text, int x, int y, int maxWidth, int color) { - Component displayed = pageFree && age != null && pageIndex == 0 - ? text.copy().withStyle(style -> style.withFont(ENCHANTMENT_FONT)) : text; - for (FormattedCharSequence line : font.split(displayed, maxWidth)) { + for (FormattedCharSequence line : font.split(text, maxWidth)) { graphics.drawString(font, line, x, y, color, false); y += font.lineHeight; } diff --git a/src/main/java/net/dnorberg/rivencraft/client/screen/NotebookScreen.java b/src/main/java/net/dnorberg/rivencraft/client/screen/NotebookScreen.java index 90a0b3d..b3b0791 100644 --- a/src/main/java/net/dnorberg/rivencraft/client/screen/NotebookScreen.java +++ b/src/main/java/net/dnorberg/rivencraft/client/screen/NotebookScreen.java @@ -3,6 +3,7 @@ package net.dnorberg.rivencraft.client.screen; import java.util.Locale; import net.dnorberg.rivencraft.Rivencraft; import net.dnorberg.rivencraft.item.PageItem; +import net.dnorberg.rivencraft.client.SymbolPageTextures; import net.dnorberg.rivencraft.menu.NotebookMenu; import net.dnorberg.rivencraft.registry.ModItems; import net.minecraft.client.gui.GuiGraphics; @@ -26,14 +27,6 @@ public final class NotebookScreen extends AbstractContainerScreen private static final int PAGE_STEP_Y = 41; private static final int PAGE_COLUMNS = 5; private static final ResourceLocation PAGE_ATLAS = texture("gui/notebook_pages"); - private static final ResourceLocation GENERIC_SYMBOL = texture("item/page_symbol"); - private static final ResourceLocation UNRESOLVED_SYMBOL = texture("item/page_unresolved"); - private static final ResourceLocation STANDARD_TERRAIN = texture("item/page_symbol_terrain_standard"); - private static final ResourceLocation NATIVE_BIOMES = texture("item/page_symbol_biome_native"); - private static final ResourceLocation NORMAL_LIGHTING = texture("item/page_symbol_lighting_normal"); - private static final ResourceLocation NORMAL_WEATHER = texture("item/page_symbol_weather_normal"); - private static final ResourceLocation NORMAL_TIME = texture("item/page_symbol_celestial_normal_time"); - private static final ResourceLocation CAVES = texture("item/page_symbol_feature_caves"); private EditBox textInput; private boolean draggingScrollbar; private boolean surfaceClickPendingRelease; @@ -262,7 +255,7 @@ public final class NotebookScreen extends AbstractContainerScreen case BLANK -> { } case LINK_PANEL -> graphics.fill(x + 5, y + 6, x + 25, y + 20, 0xFF000000); case SYMBOL, UNRESOLVED_SYMBOL -> graphics.blit(symbolTexture(page), x + 1, y + 6, - 28, 28, 16, 16, 96, 96, 128, 128); + 28, 28, 0, 0, 128, 128, 128, 128); } } if (dimmed) { @@ -274,23 +267,7 @@ public final class NotebookScreen extends AbstractContainerScreen } static ResourceLocation symbolTexture(ItemStack page) { - var content = PageItem.content(page); - if (content.kind() == net.dnorberg.rivencraft.writing.PageContent.Kind.UNRESOLVED_SYMBOL) { - return UNRESOLVED_SYMBOL; - } - return symbolTexture(content.symbol().orElseThrow().id()); - } - - static ResourceLocation symbolTexture(ResourceLocation id) { - return switch (id.toString()) { - case "rivencraft:terrain/standard" -> STANDARD_TERRAIN; - case "rivencraft:biome/native" -> NATIVE_BIOMES; - case "rivencraft:lighting/normal" -> NORMAL_LIGHTING; - case "rivencraft:weather/normal" -> NORMAL_WEATHER; - case "rivencraft:celestial/normal_time" -> NORMAL_TIME; - case "rivencraft:feature/caves" -> CAVES; - default -> GENERIC_SYMBOL; - }; + return SymbolPageTextures.forPage(page); } private static ResourceLocation texture(String path) { diff --git a/src/main/java/net/dnorberg/rivencraft/client/screen/WritingDeskScreen.java b/src/main/java/net/dnorberg/rivencraft/client/screen/WritingDeskScreen.java index a2f6fea..4ddca24 100644 --- a/src/main/java/net/dnorberg/rivencraft/client/screen/WritingDeskScreen.java +++ b/src/main/java/net/dnorberg/rivencraft/client/screen/WritingDeskScreen.java @@ -5,6 +5,7 @@ import java.util.Locale; import java.util.ArrayList; import java.util.Comparator; import net.dnorberg.rivencraft.Rivencraft; +import net.dnorberg.rivencraft.client.SymbolPageTextures; import net.dnorberg.rivencraft.item.PageItem; import net.dnorberg.rivencraft.menu.WritingDeskMenu; import net.dnorberg.rivencraft.block.entity.WritingDeskBlockEntity; @@ -148,7 +149,8 @@ public final class WritingDeskScreen extends AbstractContainerScreen= 177 && localY < 186) graphics.renderTooltip(font, Component.translatable("screen.rivencraft.writing_desk.next_tabs"), mouseX, mouseY); + else if (mouseX < leftPos + 34 && localY >= 29 && localY < 177) { + int tab = menu.firstVisibleTab() + (localY - 29) / 37; + graphics.renderTooltip(font, Component.translatable("screen.rivencraft.writing_desk.tab_symbol", + tab + 1), mouseX, mouseY); + } } } @@ -270,7 +277,7 @@ public final class WritingDeskScreen extends AbstractContainerScreen { } case LINK_PANEL -> graphics.fill(x + 5, y + 6, x + 25, y + 20, 0xFF000000); case SYMBOL, UNRESOLVED_SYMBOL -> graphics.blit(NotebookScreen.symbolTexture(page), x + 1, y + 6, - 28, 28, 16, 16, 96, 96, 128, 128); + 28, 28, 0, 0, 128, 128, 128, 128); } } diff --git a/src/main/java/net/dnorberg/rivencraft/gametest/WritingDataGameTests.java b/src/main/java/net/dnorberg/rivencraft/gametest/WritingDataGameTests.java index d83dc84..d8c176f 100644 --- a/src/main/java/net/dnorberg/rivencraft/gametest/WritingDataGameTests.java +++ b/src/main/java/net/dnorberg/rivencraft/gametest/WritingDataGameTests.java @@ -3,6 +3,7 @@ package net.dnorberg.rivencraft.gametest; import com.google.gson.JsonParser; import com.mojang.serialization.JsonOps; import io.netty.buffer.Unpooled; +import java.util.Arrays; import java.util.List; import java.util.UUID; import net.dnorberg.rivencraft.book.AgeReference; @@ -11,6 +12,8 @@ import net.dnorberg.rivencraft.writing.PageContent; import net.dnorberg.rivencraft.writing.SymbolCatalog; import net.dnorberg.rivencraft.writing.SymbolDefinition; import net.dnorberg.rivencraft.writing.SymbolSemantics; +import net.dnorberg.rivencraft.writing.SymbolPoems; +import net.dnorberg.rivencraft.writing.SymbolNumerals; import net.dnorberg.rivencraft.writing.SymbolReference; import net.dnorberg.rivencraft.writing.WritingEntry; import net.dnorberg.rivencraft.writing.NotebookContents; @@ -109,6 +112,20 @@ public final class WritingDataGameTests { helper.assertValueEqual(builtIn.schemaVersion(), 2, "Built-in symbol must use semantic schema v2"); helper.assertValueEqual(builtIn.semantics().orElseThrow().role(), SymbolSemantics.Role.TERRAIN, "Built-in terrain symbol must declare its grammar role"); + helper.assertValueEqual(builtIn.poem(), "Terrain Form Tradition Flow", + "Built-in page artwork must carry its four-word Legacy poem"); + helper.assertValueEqual(SymbolPoems.builtIn(reference("rivencraft:terrain/standard").id()), builtIn.poem(), + "Pre-artwork saved starter pages must retain their original poem"); + helper.assertTrue(Arrays.equals(SymbolNumerals.componentsForDigit(0), new int[] {1}), + "Zero must use Legacy's dedicated number mark"); + helper.assertTrue(Arrays.equals(SymbolNumerals.componentsForDigit(1), new int[] {56}), + "One must use Legacy's first bottom-row mark"); + helper.assertTrue(Arrays.equals(SymbolNumerals.componentsForDigit(24), new int[] {63, 59}), + "Twenty-four must combine Legacy's five and unit marks"); + helper.assertTrue(Arrays.equals(SymbolNumerals.digitsFor(25), new int[] {1, 0}), + "Twenty-five must start a second base-25 place"); + helper.assertTrue(Arrays.equals(SymbolNumerals.digitsFor(88), new int[] {3, 13}), + "Last writing-desk tab must retain its numeric value"); String display = "\"name\":\"symbol.example.test\"," + "\"description\":\"symbol.example.test.description\"," @@ -146,6 +163,10 @@ public final class WritingDataGameTests { .encodeStart(JsonOps.INSTANCE, clientJson).result().orElseThrow(); helper.assertFalse(encoded.getAsJsonObject().has("semantics"), "Client display payload must never contain generation semantics"); + helper.assertValueEqual(encoded.getAsJsonObject().get("poem").getAsString(), builtIn.poem(), + "Client display payload must carry page artwork metadata"); + rejectDefinition(helper, "{\"schema_version\":2," + display + + ",\"semantics\":{\"role\":\"weather\",\"value\":\"normal\"},\"poem\":\"only two\"}", "poem"); helper.succeed(); } diff --git a/src/main/java/net/dnorberg/rivencraft/item/PageItem.java b/src/main/java/net/dnorberg/rivencraft/item/PageItem.java index d715233..bfb8579 100644 --- a/src/main/java/net/dnorberg/rivencraft/item/PageItem.java +++ b/src/main/java/net/dnorberg/rivencraft/item/PageItem.java @@ -8,10 +8,10 @@ import net.dnorberg.rivencraft.writing.PageContent; import net.dnorberg.rivencraft.writing.SymbolCatalog; import net.dnorberg.rivencraft.writing.SymbolDisplayMetadata; import net.dnorberg.rivencraft.writing.SymbolReference; +import net.dnorberg.rivencraft.writing.SymbolPoems; import net.minecraft.ChatFormatting; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; -import net.minecraft.world.item.component.CustomModelData; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; @@ -42,7 +42,7 @@ public final class PageItem extends Item { page.set(ModDataComponents.PAGE_SYMBOL_DISPLAY.get(), new SymbolDisplayMetadata( reference.id(), key, key + ".description", net.minecraft.resources.ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, path.split("/")[0]), - true)); + true, SymbolPoems.builtIn(reference.id()))); return page; } @@ -118,7 +118,6 @@ public final class PageItem extends Item { private static ItemStack create(PageContent content) { ItemStack stack = new ItemStack(ModItems.PAGE.get()); stack.set(ModDataComponents.PAGE_CONTENT.get(), content); - stack.set(DataComponents.CUSTOM_MODEL_DATA, new CustomModelData(modelIndex(content))); if (content.kind() == PageContent.Kind.SYMBOL) { stack.set(ModDataComponents.PAGE_SYMBOL_DISPLAY.get(), SymbolCatalog.display(content.symbol().orElseThrow())); @@ -134,20 +133,4 @@ public final class PageItem extends Item { : Component.translatable("item.rivencraft.page.unresolved"); } - private static int modelIndex(PageContent content) { - return switch (content.kind()) { - case BLANK -> 0; - case LINK_PANEL -> 1; - case UNRESOLVED_SYMBOL -> 2; - case SYMBOL -> switch (content.symbol().orElseThrow().id().toString()) { - case "rivencraft:terrain/standard" -> 100; - case "rivencraft:biome/native" -> 101; - case "rivencraft:lighting/normal" -> 102; - case "rivencraft:weather/normal" -> 103; - case "rivencraft:celestial/normal_time" -> 104; - case "rivencraft:feature/caves" -> 105; - default -> 3; - }; - }; - } } diff --git a/src/main/java/net/dnorberg/rivencraft/writing/SymbolDefinition.java b/src/main/java/net/dnorberg/rivencraft/writing/SymbolDefinition.java index d7ec70a..321956c 100644 --- a/src/main/java/net/dnorberg/rivencraft/writing/SymbolDefinition.java +++ b/src/main/java/net/dnorberg/rivencraft/writing/SymbolDefinition.java @@ -15,18 +15,20 @@ public record SymbolDefinition( String nameTranslationKey, String descriptionTranslationKey, ResourceLocation category, - Optional semantics + Optional semantics, + String poem ) { public static final int CURRENT_SCHEMA_VERSION = 2; private static final Pattern TRANSLATION_KEY = Pattern.compile("[a-z0-9_.-]+"); private static final Set V2_FIELDS = Set.of( - "schema_version", "name", "description", "category", "semantics"); + "schema_version", "name", "description", "category", "semantics", "poem"); public SymbolDefinition { Objects.requireNonNull(nameTranslationKey, "nameTranslationKey"); Objects.requireNonNull(descriptionTranslationKey, "descriptionTranslationKey"); Objects.requireNonNull(category, "category"); semantics = Objects.requireNonNull(semantics, "semantics"); + poem = Objects.requireNonNull(poem, "poem"); if (schemaVersion != 1 && schemaVersion != CURRENT_SCHEMA_VERSION) { throw new IllegalArgumentException("Unsupported schema_version " + schemaVersion); } @@ -38,6 +40,10 @@ public record SymbolDefinition( } validateTranslationKey(nameTranslationKey, "name"); validateTranslationKey(descriptionTranslationKey, "description"); + if (!poem.isEmpty() && (poem.length() > 96 + || !poem.matches("[A-Za-z]+(?: [A-Za-z]+){3}"))) { + throw new IllegalArgumentException("'poem' must contain exactly four alphabetic words"); + } } /** v1 remains display-only; v2 has an exact field set and typed semantics. */ @@ -66,11 +72,12 @@ public record SymbolDefinition( Optional semantics = version == CURRENT_SCHEMA_VERSION ? Optional.of(SymbolSemantics.parse(object.get("semantics"))) : Optional.empty(); - return new SymbolDefinition(version, name, description, category, semantics); + String poem = object.has("poem") ? requiredString(object, "poem") : ""; + return new SymbolDefinition(version, name, description, category, semantics, poem); } public SymbolDisplayMetadata display(ResourceLocation id) { - return new SymbolDisplayMetadata(id, nameTranslationKey, descriptionTranslationKey, category, true); + return new SymbolDisplayMetadata(id, nameTranslationKey, descriptionTranslationKey, category, true, poem); } static String requiredString(JsonObject object, String field) { diff --git a/src/main/java/net/dnorberg/rivencraft/writing/SymbolDisplayMetadata.java b/src/main/java/net/dnorberg/rivencraft/writing/SymbolDisplayMetadata.java index f462b20..1648f60 100644 --- a/src/main/java/net/dnorberg/rivencraft/writing/SymbolDisplayMetadata.java +++ b/src/main/java/net/dnorberg/rivencraft/writing/SymbolDisplayMetadata.java @@ -14,14 +14,16 @@ public record SymbolDisplayMetadata( String nameTranslationKey, String descriptionTranslationKey, ResourceLocation category, - boolean resolved + boolean resolved, + String poem ) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( ResourceLocation.CODEC.fieldOf("id").forGetter(SymbolDisplayMetadata::id), Codec.STRING.fieldOf("name").forGetter(SymbolDisplayMetadata::nameTranslationKey), Codec.STRING.fieldOf("description").forGetter(SymbolDisplayMetadata::descriptionTranslationKey), ResourceLocation.CODEC.fieldOf("category").forGetter(SymbolDisplayMetadata::category), - Codec.BOOL.fieldOf("resolved").forGetter(SymbolDisplayMetadata::resolved) + Codec.BOOL.fieldOf("resolved").forGetter(SymbolDisplayMetadata::resolved), + Codec.STRING.optionalFieldOf("poem", "").forGetter(SymbolDisplayMetadata::poem) ).apply(instance, SymbolDisplayMetadata::new)); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ResourceLocation.STREAM_CODEC, SymbolDisplayMetadata::id, @@ -29,6 +31,7 @@ public record SymbolDisplayMetadata( ByteBufCodecs.STRING_UTF8, SymbolDisplayMetadata::descriptionTranslationKey, ResourceLocation.STREAM_CODEC, SymbolDisplayMetadata::category, ByteBufCodecs.BOOL, SymbolDisplayMetadata::resolved, + ByteBufCodecs.STRING_UTF8, SymbolDisplayMetadata::poem, SymbolDisplayMetadata::new ); @@ -38,7 +41,8 @@ public record SymbolDisplayMetadata( "symbol.rivencraft.unresolved", "symbol.rivencraft.unresolved.description", ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, "unresolved"), - false + false, + "" ); } } diff --git a/src/main/java/net/dnorberg/rivencraft/writing/SymbolNumerals.java b/src/main/java/net/dnorberg/rivencraft/writing/SymbolNumerals.java new file mode 100644 index 0000000..27d2b2e --- /dev/null +++ b/src/main/java/net/dnorberg/rivencraft/writing/SymbolNumerals.java @@ -0,0 +1,30 @@ +package net.dnorberg.rivencraft.writing; + +/** + * Number components from Mystcraft Legacy's ModWords#constructNumber. + * Legacy registers 0–25; larger Rivencraft tab numbers use positional base 25. + */ +public final class SymbolNumerals { + private SymbolNumerals() {} + + public static int[] digitsFor(int number) { + if (number < 0) throw new IllegalArgumentException("Numeral must be non-negative"); + if (number < 25) return new int[] {number}; + int count = 0; + for (int remaining = number; remaining > 0; remaining /= 25) count++; + int[] digits = new int[count]; + for (int remaining = number, index = count - 1; index >= 0; index--, remaining /= 25) + digits[index] = remaining % 25; + return digits; + } + + public static int[] componentsForDigit(int digit) { + if (digit < 0 || digit >= 25) throw new IllegalArgumentException("Numeral digit must be in [0, 24]"); + if (digit == 0) return new int[] {1}; + int fifths = digit / 5; + int remainder = digit % 5; + if (fifths == 0) return new int[] {55 + remainder}; + if (remainder == 0) return new int[] {59 + fifths}; + return new int[] {59 + fifths, 55 + remainder}; + } +} diff --git a/src/main/java/net/dnorberg/rivencraft/writing/SymbolPoems.java b/src/main/java/net/dnorberg/rivencraft/writing/SymbolPoems.java new file mode 100644 index 0000000..b61f566 --- /dev/null +++ b/src/main/java/net/dnorberg/rivencraft/writing/SymbolPoems.java @@ -0,0 +1,21 @@ +package net.dnorberg.rivencraft.writing; + +import net.minecraft.resources.ResourceLocation; + +/** Legacy poems for existing starter pages, including stacks saved before poem metadata existed. */ +public final class SymbolPoems { + private SymbolPoems() {} + + public static String builtIn(ResourceLocation id) { + if (!id.getNamespace().equals("rivencraft")) return ""; + return switch (id.getPath()) { + case "terrain/standard" -> "Terrain Form Tradition Flow"; + case "biome/native" -> "Constraint Nature Tradition Sustain"; + case "lighting/normal" -> "Ethereal Dynamic Cycle Balance"; + case "weather/normal" -> "Sustain Dynamic Tradition Balance"; + case "celestial/normal_time" -> "Transform Time System Balance"; + case "feature/caves" -> "Terrain Transform Void Flow"; + default -> ""; + }; + } +} diff --git a/src/main/resources/assets/rivencraft/lang/en_us.json b/src/main/resources/assets/rivencraft/lang/en_us.json index f29ed11..c8bb90b 100644 --- a/src/main/resources/assets/rivencraft/lang/en_us.json +++ b/src/main/resources/assets/rivencraft/lang/en_us.json @@ -42,6 +42,7 @@ "screen.rivencraft.writing_desk.all_categories": "All categories", "screen.rivencraft.writing_desk.previous_tabs": "Previous four tabs (Page Up)", "screen.rivencraft.writing_desk.next_tabs": "Next four tabs (Page Down)", + "screen.rivencraft.writing_desk.tab_symbol": "Tab %s", "screen.rivencraft.writing_desk.no_symbols": "No available symbols", "item.rivencraft.ink_vial.remaining": "Ink: %s/%s", "item.rivencraft.ink_vial.usage": "Use with paper in the other hand to prepare a Link Panel.", diff --git a/src/main/resources/assets/rivencraft/models/item/page.json b/src/main/resources/assets/rivencraft/models/item/page.json index 6ebfeba..776e88c 100644 --- a/src/main/resources/assets/rivencraft/models/item/page.json +++ b/src/main/resources/assets/rivencraft/models/item/page.json @@ -1,17 +1,13 @@ { - "parent": "minecraft:item/generated", - "textures": { - "layer0": "rivencraft:item/page" - }, - "overrides": [ - { "predicate": { "custom_model_data": 1 }, "model": "rivencraft:item/page_link_panel" }, - { "predicate": { "custom_model_data": 2 }, "model": "rivencraft:item/page_unresolved" }, - { "predicate": { "custom_model_data": 3 }, "model": "rivencraft:item/page_symbol" }, - { "predicate": { "custom_model_data": 100 }, "model": "rivencraft:item/page_symbol_terrain_standard" }, - { "predicate": { "custom_model_data": 101 }, "model": "rivencraft:item/page_symbol_biome_native" }, - { "predicate": { "custom_model_data": 102 }, "model": "rivencraft:item/page_symbol_lighting_normal" }, - { "predicate": { "custom_model_data": 103 }, "model": "rivencraft:item/page_symbol_weather_normal" }, - { "predicate": { "custom_model_data": 104 }, "model": "rivencraft:item/page_symbol_celestial_normal_time" }, - { "predicate": { "custom_model_data": 105 }, "model": "rivencraft:item/page_symbol_feature_caves" } - ] + "parent": "builtin/entity", + "gui_light": "front", + "display": { + "ground": { "translation": [0, 2, 0], "scale": [0.5, 0.5, 0.5] }, + "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7] }, + "thirdperson_righthand": { "translation": [0, 3, 1], "scale": [0.55, 0.55, 0.55] }, + "thirdperson_lefthand": { "translation": [0, 3, 1], "scale": [0.55, 0.55, 0.55] }, + "firstperson_righthand": { "rotation": [0, -90, 25], "translation": [1.13, 3.2, 1.13], "scale": [0.68, 0.68, 0.68] }, + "firstperson_lefthand": { "rotation": [0, 90, -25], "translation": [1.13, 3.2, 1.13], "scale": [0.68, 0.68, 0.68] }, + "fixed": { "rotation": [0, 180, 0] } + } } diff --git a/src/main/resources/assets/rivencraft/models/item/page_symbol_biome_native.json b/src/main/resources/assets/rivencraft/models/item/page_symbol_biome_native.json deleted file mode 100644 index 251bac9..0000000 --- a/src/main/resources/assets/rivencraft/models/item/page_symbol_biome_native.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { "layer0": "rivencraft:item/page_symbol_biome_native" } -} diff --git a/src/main/resources/assets/rivencraft/models/item/page_symbol_celestial_normal_time.json b/src/main/resources/assets/rivencraft/models/item/page_symbol_celestial_normal_time.json deleted file mode 100644 index 38bb083..0000000 --- a/src/main/resources/assets/rivencraft/models/item/page_symbol_celestial_normal_time.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { "layer0": "rivencraft:item/page_symbol_celestial_normal_time" } -} diff --git a/src/main/resources/assets/rivencraft/models/item/page_symbol_feature_caves.json b/src/main/resources/assets/rivencraft/models/item/page_symbol_feature_caves.json deleted file mode 100644 index 621bce9..0000000 --- a/src/main/resources/assets/rivencraft/models/item/page_symbol_feature_caves.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { "layer0": "rivencraft:item/page_symbol_feature_caves" } -} diff --git a/src/main/resources/assets/rivencraft/models/item/page_symbol_lighting_normal.json b/src/main/resources/assets/rivencraft/models/item/page_symbol_lighting_normal.json deleted file mode 100644 index 747cbfb..0000000 --- a/src/main/resources/assets/rivencraft/models/item/page_symbol_lighting_normal.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { "layer0": "rivencraft:item/page_symbol_lighting_normal" } -} diff --git a/src/main/resources/assets/rivencraft/models/item/page_symbol_terrain_standard.json b/src/main/resources/assets/rivencraft/models/item/page_symbol_terrain_standard.json deleted file mode 100644 index 562ebd7..0000000 --- a/src/main/resources/assets/rivencraft/models/item/page_symbol_terrain_standard.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { "layer0": "rivencraft:item/page_symbol_terrain_standard" } -} diff --git a/src/main/resources/assets/rivencraft/models/item/page_symbol_weather_normal.json b/src/main/resources/assets/rivencraft/models/item/page_symbol_weather_normal.json deleted file mode 100644 index 07c4ed6..0000000 --- a/src/main/resources/assets/rivencraft/models/item/page_symbol_weather_normal.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { "layer0": "rivencraft:item/page_symbol_weather_normal" } -} diff --git a/src/main/resources/assets/rivencraft/textures/gui/symbolcomponents.png b/src/main/resources/assets/rivencraft/textures/gui/symbolcomponents.png new file mode 100644 index 0000000..c067640 Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/gui/symbolcomponents.png differ diff --git a/src/main/resources/assets/rivencraft/textures/item/page_symbol_biome_native.png b/src/main/resources/assets/rivencraft/textures/item/page_symbol_biome_native.png deleted file mode 100644 index cfd7e6a..0000000 Binary files a/src/main/resources/assets/rivencraft/textures/item/page_symbol_biome_native.png and /dev/null differ diff --git a/src/main/resources/assets/rivencraft/textures/item/page_symbol_celestial_normal_time.png b/src/main/resources/assets/rivencraft/textures/item/page_symbol_celestial_normal_time.png deleted file mode 100644 index eb763e9..0000000 Binary files a/src/main/resources/assets/rivencraft/textures/item/page_symbol_celestial_normal_time.png and /dev/null differ diff --git a/src/main/resources/assets/rivencraft/textures/item/page_symbol_feature_caves.png b/src/main/resources/assets/rivencraft/textures/item/page_symbol_feature_caves.png deleted file mode 100644 index 9895464..0000000 Binary files a/src/main/resources/assets/rivencraft/textures/item/page_symbol_feature_caves.png and /dev/null differ diff --git a/src/main/resources/assets/rivencraft/textures/item/page_symbol_lighting_normal.png b/src/main/resources/assets/rivencraft/textures/item/page_symbol_lighting_normal.png deleted file mode 100644 index e5a43dc..0000000 Binary files a/src/main/resources/assets/rivencraft/textures/item/page_symbol_lighting_normal.png and /dev/null differ diff --git a/src/main/resources/assets/rivencraft/textures/item/page_symbol_terrain_standard.png b/src/main/resources/assets/rivencraft/textures/item/page_symbol_terrain_standard.png deleted file mode 100644 index baacfc2..0000000 Binary files a/src/main/resources/assets/rivencraft/textures/item/page_symbol_terrain_standard.png and /dev/null differ diff --git a/src/main/resources/assets/rivencraft/textures/item/page_symbol_weather_normal.png b/src/main/resources/assets/rivencraft/textures/item/page_symbol_weather_normal.png deleted file mode 100644 index b3ad202..0000000 Binary files a/src/main/resources/assets/rivencraft/textures/item/page_symbol_weather_normal.png and /dev/null differ diff --git a/src/main/resources/data/rivencraft/rivencraft/symbols/biome/native.json b/src/main/resources/data/rivencraft/rivencraft/symbols/biome/native.json index ba42ebd..4f477d2 100644 --- a/src/main/resources/data/rivencraft/rivencraft/symbols/biome/native.json +++ b/src/main/resources/data/rivencraft/rivencraft/symbols/biome/native.json @@ -3,5 +3,6 @@ "name": "symbol.rivencraft.biome.native", "description": "symbol.rivencraft.biome.native.description", "category": "rivencraft:biome", - "semantics": { "role": "biome_controller", "value": "native" } + "semantics": { "role": "biome_controller", "value": "native" }, + "poem": "Constraint Nature Tradition Sustain" } diff --git a/src/main/resources/data/rivencraft/rivencraft/symbols/celestial/normal_time.json b/src/main/resources/data/rivencraft/rivencraft/symbols/celestial/normal_time.json index a441947..f71648f 100644 --- a/src/main/resources/data/rivencraft/rivencraft/symbols/celestial/normal_time.json +++ b/src/main/resources/data/rivencraft/rivencraft/symbols/celestial/normal_time.json @@ -3,5 +3,6 @@ "name": "symbol.rivencraft.celestial.normal_time", "description": "symbol.rivencraft.celestial.normal_time.description", "category": "rivencraft:celestial", - "semantics": { "role": "celestial", "value": "normal_time" } + "semantics": { "role": "celestial", "value": "normal_time" }, + "poem": "Transform Time System Balance" } diff --git a/src/main/resources/data/rivencraft/rivencraft/symbols/feature/caves.json b/src/main/resources/data/rivencraft/rivencraft/symbols/feature/caves.json index b9f3822..f2bbfee 100644 --- a/src/main/resources/data/rivencraft/rivencraft/symbols/feature/caves.json +++ b/src/main/resources/data/rivencraft/rivencraft/symbols/feature/caves.json @@ -3,5 +3,6 @@ "name": "symbol.rivencraft.feature.caves", "description": "symbol.rivencraft.feature.caves.description", "category": "rivencraft:feature", - "semantics": { "role": "feature", "value": "rivencraft:caves" } + "semantics": { "role": "feature", "value": "rivencraft:caves" }, + "poem": "Terrain Transform Void Flow" } diff --git a/src/main/resources/data/rivencraft/rivencraft/symbols/lighting/normal.json b/src/main/resources/data/rivencraft/rivencraft/symbols/lighting/normal.json index 6ce9239..76cfd8c 100644 --- a/src/main/resources/data/rivencraft/rivencraft/symbols/lighting/normal.json +++ b/src/main/resources/data/rivencraft/rivencraft/symbols/lighting/normal.json @@ -3,5 +3,6 @@ "name": "symbol.rivencraft.lighting.normal", "description": "symbol.rivencraft.lighting.normal.description", "category": "rivencraft:lighting", - "semantics": { "role": "lighting", "value": "normal" } + "semantics": { "role": "lighting", "value": "normal" }, + "poem": "Ethereal Dynamic Cycle Balance" } diff --git a/src/main/resources/data/rivencraft/rivencraft/symbols/terrain/standard.json b/src/main/resources/data/rivencraft/rivencraft/symbols/terrain/standard.json index ccb289d..1696d59 100644 --- a/src/main/resources/data/rivencraft/rivencraft/symbols/terrain/standard.json +++ b/src/main/resources/data/rivencraft/rivencraft/symbols/terrain/standard.json @@ -3,5 +3,6 @@ "name": "symbol.rivencraft.terrain.standard", "description": "symbol.rivencraft.terrain.standard.description", "category": "rivencraft:terrain", - "semantics": { "role": "terrain", "value": "rivencraft:standard" } + "semantics": { "role": "terrain", "value": "rivencraft:standard" }, + "poem": "Terrain Form Tradition Flow" } diff --git a/src/main/resources/data/rivencraft/rivencraft/symbols/weather/normal.json b/src/main/resources/data/rivencraft/rivencraft/symbols/weather/normal.json index 4c60c14..94476fa 100644 --- a/src/main/resources/data/rivencraft/rivencraft/symbols/weather/normal.json +++ b/src/main/resources/data/rivencraft/rivencraft/symbols/weather/normal.json @@ -3,5 +3,6 @@ "name": "symbol.rivencraft.weather.normal", "description": "symbol.rivencraft.weather.normal.description", "category": "rivencraft:weather", - "semantics": { "role": "weather", "value": "normal" } + "semantics": { "role": "weather", "value": "normal" }, + "poem": "Sustain Dynamic Tradition Balance" }