diff --git a/README.md b/README.md index f0e2bde..8afd39e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ and randomly seeded, persistent vanilla-like Ages created from page-free Descriptive Books. Instability and full symbol-to-world grammar interpretation have not been ported yet. +Milestone 4 now includes stackable blank Pages plus server-created symbol, +unresolved-symbol, and baseline link-panel states. Page visuals follow the +legacy runtime-composition rules; written-page creation will become available +through the Writing Desk work that follows. + ## Linking Book Craft a Linking Book from a vanilla book and an ender pearl, then use it in the diff --git a/docs/ASSET_PROVENANCE.md b/docs/ASSET_PROVENANCE.md new file mode 100644 index 0000000..57bb3a3 --- /dev/null +++ b/docs/ASSET_PROVENANCE.md @@ -0,0 +1,15 @@ +# Asset provenance + +Rivencraft reuses selected assets from +[Mystcraft Legacy](https://github.com/Mystcraft/Mystcraft-Legacy) under that +project's GNU LGPL v3 license. Rivencraft itself uses the same license. + +| Rivencraft asset | Upstream asset | Upstream revision | Changes | +| --- | --- | --- | --- | +| `assets/rivencraft/textures/item/page.png` | `assets/mystcraft/textures/items/page_background.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 | + +Mystcraft Legacy identifies XCompWiz as its project developer and includes the +full LGPL v3 text in its root `LICENSE` file. Rivencraft's root `LICENSE` +provides the same license terms. diff --git a/docs/PORTING.md b/docs/PORTING.md index 4a36824..0350d02 100644 --- a/docs/PORTING.md +++ b/docs/PORTING.md @@ -70,6 +70,7 @@ not interpret symbols into Age generation, which remains Milestone 5 work. ## Provenance rules - New code uses the `net.dnorberg.rivencraft` package. +- Reused legacy art is recorded in [ASSET_PROVENANCE.md](ASSET_PROVENANCE.md). - Adapted Mystcraft code retains its upstream copyright notice and is tracked in a nearby source comment or commit. - Generated, binary, and third-party dependency content is not copied from the diff --git a/docs/WRITING_DATA.md b/docs/WRITING_DATA.md index 2297d9c..bc061c6 100644 --- a/docs/WRITING_DATA.md +++ b/docs/WRITING_DATA.md @@ -44,3 +44,23 @@ Removing a data pack never removes its IDs from saved writing. A missing ID is resolved as client-safe **Unresolved Symbol** display metadata while retaining the original namespaced ID for diagnostics and later restoration. This is deliberately different from treating an unknown symbol as valid grammar. + +## Pages + +The single stackable `rivencraft:page` item has a bounded, persistent +`rivencraft:page_content` component. Its explicit states are blank, symbol, +unresolved symbol, and baseline link panel. Symbol and unresolved-symbol pages +retain their namespaced symbol reference; unresolved pages can be inspected and +removed but are rejected as usable writing until their data pack returns. + +Only server-side writing operations may turn a blank page into a known symbol +page or link panel. The shared `PageItem` creation and validation gates reject +new pages for identifiers absent from the server catalog. Later Writing Desk +and Book Binder packets must call these gates rather than accepting page +contents supplied by a client. + +The item model uses server-assigned custom-model data derived from the symbol +identifier. Built-in pages reproduce Mystcraft Legacy's four-word poem +composition from its page background and symbol-component sheet. Data-pack +symbols without a bundled resource-pack model use the legacy question-mark +fallback while keeping their real identifier and display metadata. diff --git a/gradle.properties b/gradle.properties index 40104cb..69a52aa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -35,7 +35,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.3.1.0 +mod_version=0.3.2.0 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/gametest/WritingDataGameTests.java b/src/main/java/net/dnorberg/rivencraft/gametest/WritingDataGameTests.java index 80b269f..961ebe2 100644 --- a/src/main/java/net/dnorberg/rivencraft/gametest/WritingDataGameTests.java +++ b/src/main/java/net/dnorberg/rivencraft/gametest/WritingDataGameTests.java @@ -5,13 +5,18 @@ import io.netty.buffer.Unpooled; import java.util.List; import net.dnorberg.rivencraft.Rivencraft; import net.dnorberg.rivencraft.writing.OrderedWriting; +import net.dnorberg.rivencraft.writing.PageContent; import net.dnorberg.rivencraft.writing.SymbolCatalog; import net.dnorberg.rivencraft.writing.SymbolReference; import net.dnorberg.rivencraft.writing.WritingEntry; +import net.dnorberg.rivencraft.item.PageItem; +import net.dnorberg.rivencraft.registry.ModDataComponents; import net.minecraft.gametest.framework.GameTest; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.gametest.GameTestHolder; import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; @@ -63,6 +68,56 @@ public final class WritingDataGameTests { helper.succeed(); } + @GameTest(template = "empty") + public static void pageStatesPersistAndNetworkRoundTrip(GameTestHelper helper) { + SymbolReference known = reference("rivencraft:terrain/standard"); + SymbolReference removed = reference("example:removed_symbol"); + List states = List.of( + PageContent.BLANK, + PageContent.symbol(known), + PageContent.unresolved(removed), + PageContent.LINK_PANEL + ); + + for (PageContent state : states) { + var json = PageContent.CODEC.encodeStart(JsonOps.INSTANCE, state).result().orElseThrow(); + PageContent persistentCopy = PageContent.CODEC.parse(JsonOps.INSTANCE, json).result().orElseThrow(); + helper.assertValueEqual(persistentCopy, state, "Page content must persist exactly"); + + FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer()); + PageContent.STREAM_CODEC.encode(buffer, state); + helper.assertValueEqual(PageContent.STREAM_CODEC.decode(buffer), state, + "Page content must network-sync exactly"); + } + helper.succeed(); + } + + @GameTest(template = "empty") + public static void pageItemPreservesContentAndRejectsForgedUnknownSymbols(GameTestHelper helper) { + SymbolReference known = reference("rivencraft:terrain/standard"); + SymbolReference unknown = reference("example:forged_symbol"); + ItemStack page = PageItem.createSymbolPage(known); + CompoundTag serialized = (CompoundTag) page.saveOptional(helper.getLevel().registryAccess()); + ItemStack restored = ItemStack.parseOptional(helper.getLevel().registryAccess(), serialized); + helper.assertValueEqual(restored.get(ModDataComponents.PAGE_CONTENT.get()), PageContent.symbol(known), + "Symbol page content must survive an ItemStack save/load"); + helper.assertValueEqual( + restored.get(ModDataComponents.PAGE_SYMBOL_DISPLAY.get()), + SymbolCatalog.display(known), + "Server-selected display metadata must survive for remote clients" + ); + + ItemStack recovered = PageItem.createRecoveredPage(unknown); + helper.assertFalse(PageItem.isUsableWriting(recovered), + "An unresolved page must remain recoverable without becoming valid writing"); + try { + PageItem.createSymbolPage(unknown); + helper.fail("The server must reject new pages for unknown symbols"); + } catch (IllegalArgumentException expected) { + helper.succeed(); + } + } + private static SymbolReference reference(String id) { return new SymbolReference(ResourceLocation.parse(id)); } diff --git a/src/main/java/net/dnorberg/rivencraft/item/PageItem.java b/src/main/java/net/dnorberg/rivencraft/item/PageItem.java new file mode 100644 index 0000000..7ff7fa3 --- /dev/null +++ b/src/main/java/net/dnorberg/rivencraft/item/PageItem.java @@ -0,0 +1,131 @@ +package net.dnorberg.rivencraft.item; + +import java.util.List; +import net.dnorberg.rivencraft.registry.ModDataComponents; +import net.dnorberg.rivencraft.registry.ModItems; +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.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; + +/** One stackable Page item whose server-owned component identifies its writing state. */ +public final class PageItem extends Item { + public PageItem(Properties properties) { + super(properties); + } + + public static ItemStack createSymbolPage(SymbolReference reference) { + if (!SymbolCatalog.isKnown(reference)) { + throw new IllegalArgumentException("Cannot create a page for unknown symbol " + reference.id()); + } + return create(PageContent.symbol(reference)); + } + + public static ItemStack createLinkPanelPage() { + return create(PageContent.LINK_PANEL); + } + + /** Restores saved unknown references without granting them validity for new writing operations. */ + public static ItemStack createRecoveredPage(SymbolReference reference) { + return create(PageContent.unresolved(reference)); + } + + public static PageContent content(ItemStack stack) { + PageContent content = stack.get(ModDataComponents.PAGE_CONTENT.get()); + return content == null ? PageContent.BLANK : content; + } + + /** Server-side gate for Desk and Binder inputs; unresolved pages remain removable but not usable. */ + public static boolean isUsableWriting(ItemStack stack) { + if (!stack.is(ModItems.PAGE.get())) { + return false; + } + PageContent resolved = content(stack).resolve(); + return resolved.kind() == PageContent.Kind.BLANK + || resolved.kind() == PageContent.Kind.LINK_PANEL + || resolved.isResolvedSymbol(); + } + + @Override + public Component getName(ItemStack stack) { + PageContent content = content(stack); + return switch (content.kind()) { + case BLANK -> Component.translatable("item.rivencraft.page.blank"); + case LINK_PANEL -> Component.translatable("item.rivencraft.page.link_panel"); + case UNRESOLVED_SYMBOL -> Component.translatable("item.rivencraft.page.unresolved"); + case SYMBOL -> symbolName(stack, content.symbol().orElseThrow()); + }; + } + + @Override + public void appendHoverText(ItemStack stack, TooltipContext context, + List tooltipComponents, TooltipFlag tooltipFlag) { + PageContent content = content(stack); + if (content.kind() == PageContent.Kind.BLANK) { + tooltipComponents.add(Component.translatable("item.rivencraft.page.blank.description") + .withStyle(ChatFormatting.GRAY)); + return; + } + if (content.kind() == PageContent.Kind.LINK_PANEL) { + tooltipComponents.add(Component.translatable("item.rivencraft.page.link_panel.description") + .withStyle(ChatFormatting.GRAY)); + return; + } + SymbolReference reference = content.symbol().orElseThrow(); + SymbolDisplayMetadata metadata = content.kind() == PageContent.Kind.SYMBOL + ? stack.getOrDefault(ModDataComponents.PAGE_SYMBOL_DISPLAY.get(), SymbolCatalog.display(reference)) + : SymbolDisplayMetadata.unresolved(reference); + if (metadata.resolved()) { + tooltipComponents.add(Component.translatable(metadata.descriptionTranslationKey()) + .withStyle(ChatFormatting.GRAY)); + } else { + tooltipComponents.add(Component.translatable( + "symbol.rivencraft.unresolved.description", reference.unresolvedName()) + .withStyle(ChatFormatting.RED)); + } + tooltipComponents.add(Component.literal(reference.id().toString()).withStyle(ChatFormatting.DARK_GRAY)); + } + + 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())); + } + return stack; + } + + private static Component symbolName(ItemStack stack, SymbolReference reference) { + SymbolDisplayMetadata metadata = stack.getOrDefault( + ModDataComponents.PAGE_SYMBOL_DISPLAY.get(), SymbolCatalog.display(reference)); + return metadata.resolved() + ? Component.translatable(metadata.nameTranslationKey()) + : 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/registry/ModCreativeTabs.java b/src/main/java/net/dnorberg/rivencraft/registry/ModCreativeTabs.java index 91d4902..c418f47 100644 --- a/src/main/java/net/dnorberg/rivencraft/registry/ModCreativeTabs.java +++ b/src/main/java/net/dnorberg/rivencraft/registry/ModCreativeTabs.java @@ -19,6 +19,7 @@ public final class ModCreativeTabs { .displayItems((parameters, output) -> { output.accept(ModItems.DESCRIPTIVE_BOOK.get()); output.accept(ModItems.LINKING_BOOK.get()); + output.accept(ModItems.PAGE.get()); }) .build()); diff --git a/src/main/java/net/dnorberg/rivencraft/registry/ModDataComponents.java b/src/main/java/net/dnorberg/rivencraft/registry/ModDataComponents.java index 6415a23..a356657 100644 --- a/src/main/java/net/dnorberg/rivencraft/registry/ModDataComponents.java +++ b/src/main/java/net/dnorberg/rivencraft/registry/ModDataComponents.java @@ -3,6 +3,8 @@ package net.dnorberg.rivencraft.registry; import net.dnorberg.rivencraft.Rivencraft; import net.dnorberg.rivencraft.book.LinkTarget; import net.dnorberg.rivencraft.book.AgeReference; +import net.dnorberg.rivencraft.writing.PageContent; +import net.dnorberg.rivencraft.writing.SymbolDisplayMetadata; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceLocation; @@ -39,6 +41,18 @@ public final class ModDataComponents { .networkSynchronized(ByteBufCodecs.STRING_UTF8) .cacheEncoding() .build()); + public static final DeferredHolder, DataComponentType> PAGE_CONTENT = + DATA_COMPONENTS.register("page_content", () -> DataComponentType.builder() + .persistent(PageContent.CODEC) + .networkSynchronized(PageContent.STREAM_CODEC) + .cacheEncoding() + .build()); + public static final DeferredHolder, DataComponentType> PAGE_SYMBOL_DISPLAY = + DATA_COMPONENTS.register("page_symbol_display", () -> DataComponentType.builder() + .persistent(SymbolDisplayMetadata.CODEC) + .networkSynchronized(SymbolDisplayMetadata.STREAM_CODEC) + .cacheEncoding() + .build()); private ModDataComponents() { } diff --git a/src/main/java/net/dnorberg/rivencraft/registry/ModItems.java b/src/main/java/net/dnorberg/rivencraft/registry/ModItems.java index 7eeb238..60c0cf4 100644 --- a/src/main/java/net/dnorberg/rivencraft/registry/ModItems.java +++ b/src/main/java/net/dnorberg/rivencraft/registry/ModItems.java @@ -3,6 +3,7 @@ package net.dnorberg.rivencraft.registry; import net.dnorberg.rivencraft.Rivencraft; import net.dnorberg.rivencraft.item.DescriptiveBookItem; import net.dnorberg.rivencraft.item.LinkingBookItem; +import net.dnorberg.rivencraft.item.PageItem; import net.minecraft.world.item.Item; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.registries.DeferredItem; @@ -21,6 +22,11 @@ public final class ModItems { LinkingBookItem::new, new Item.Properties().stacksTo(1) ); + public static final DeferredItem PAGE = ITEMS.registerItem( + "page", + PageItem::new, + new Item.Properties().stacksTo(64) + ); private ModItems() { } diff --git a/src/main/java/net/dnorberg/rivencraft/writing/PageContent.java b/src/main/java/net/dnorberg/rivencraft/writing/PageContent.java new file mode 100644 index 0000000..fac5637 --- /dev/null +++ b/src/main/java/net/dnorberg/rivencraft/writing/PageContent.java @@ -0,0 +1,95 @@ +package net.dnorberg.rivencraft.writing; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.netty.buffer.ByteBuf; +import java.util.Locale; +import java.util.Objects; +import java.util.Optional; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +/** Persistent contents of a Page item. Unknown symbols remain explicit and recoverable. */ +public record PageContent(Kind kind, Optional symbol) { + public static final PageContent BLANK = new PageContent(Kind.BLANK, Optional.empty()); + public static final PageContent LINK_PANEL = new PageContent(Kind.LINK_PANEL, Optional.empty()); + private static final Codec KIND_CODEC = Codec.STRING.comapFlatMap( + value -> { + try { + return DataResult.success(Kind.valueOf(value.toUpperCase(Locale.ROOT))); + } catch (IllegalArgumentException exception) { + return DataResult.error(() -> "Unknown page content kind: " + value); + } + }, + value -> value.name().toLowerCase(Locale.ROOT) + ); + private static final Codec RECORD_CODEC = RecordCodecBuilder.create(instance -> instance.group( + KIND_CODEC.fieldOf("kind").forGetter(PageContent::kind), + SymbolReference.CODEC.optionalFieldOf("symbol").forGetter(PageContent::symbol) + ).apply(instance, PageContent::new)); + public static final Codec CODEC = RECORD_CODEC.flatXmap(PageContent::validate, PageContent::validate); + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + PageContent::encode, PageContent::decode); + + public PageContent { + Objects.requireNonNull(kind, "kind"); + symbol = Objects.requireNonNull(symbol, "symbol"); + boolean needsSymbol = kind == Kind.SYMBOL || kind == Kind.UNRESOLVED_SYMBOL; + if (needsSymbol != symbol.isPresent()) { + throw new IllegalArgumentException("Page contents do not match kind " + kind); + } + } + + public static PageContent symbol(SymbolReference symbol) { + return new PageContent(Kind.SYMBOL, Optional.of(symbol)); + } + + public static PageContent unresolved(SymbolReference symbol) { + return new PageContent(Kind.UNRESOLVED_SYMBOL, Optional.of(symbol)); + } + + /** Re-resolves persisted data against the authoritative current server catalog. */ + public PageContent resolve() { + if (kind != Kind.SYMBOL && kind != Kind.UNRESOLVED_SYMBOL) { + return this; + } + SymbolReference reference = symbol.orElseThrow(); + return SymbolCatalog.isKnown(reference) ? symbol(reference) : unresolved(reference); + } + + public boolean isResolvedSymbol() { + return kind == Kind.SYMBOL; + } + + private static DataResult validate(PageContent content) { + boolean needsSymbol = content.kind == Kind.SYMBOL || content.kind == Kind.UNRESOLVED_SYMBOL; + return needsSymbol == content.symbol.isPresent() + ? DataResult.success(content) + : DataResult.error(() -> "Page contents do not match kind " + content.kind); + } + + private static void encode(ByteBuf buffer, PageContent content) { + ByteBufCodecs.VAR_INT.encode(buffer, content.kind.ordinal()); + content.symbol.ifPresent(reference -> SymbolReference.STREAM_CODEC.encode(buffer, reference)); + } + + private static PageContent decode(ByteBuf buffer) { + int ordinal = ByteBufCodecs.VAR_INT.decode(buffer); + if (ordinal < 0 || ordinal >= Kind.values().length) { + throw new IllegalArgumentException("Unknown page content kind ordinal " + ordinal); + } + Kind kind = Kind.values()[ordinal]; + Optional symbol = kind == Kind.SYMBOL || kind == Kind.UNRESOLVED_SYMBOL + ? Optional.of(SymbolReference.STREAM_CODEC.decode(buffer)) + : Optional.empty(); + return new PageContent(kind, symbol); + } + + public enum Kind { + BLANK, + SYMBOL, + UNRESOLVED_SYMBOL, + LINK_PANEL + } +} diff --git a/src/main/resources/assets/rivencraft/lang/en_us.json b/src/main/resources/assets/rivencraft/lang/en_us.json index ba2e668..21a54a2 100644 --- a/src/main/resources/assets/rivencraft/lang/en_us.json +++ b/src/main/resources/assets/rivencraft/lang/en_us.json @@ -6,6 +6,12 @@ "item.rivencraft.linking_book.unbound": "Unbound — sneak-use to bind", "item.rivencraft.linking_book.destination": "%s (%s, %s, %s)", "item.rivencraft.linking_book.return": "Return Link", + "item.rivencraft.page": "Page", + "item.rivencraft.page.blank": "Blank Page", + "item.rivencraft.page.blank.description": "Ready for writing at a Writing Desk.", + "item.rivencraft.page.link_panel": "Link Panel Page", + "item.rivencraft.page.link_panel.description": "The baseline panel required to assemble a Descriptive Book.", + "item.rivencraft.page.unresolved": "Unresolved Symbol Page", "message.rivencraft.linking_book.bind_hint": "Sneak-use to bind this book to your current location", "message.rivencraft.linking_book.unbound": "This Linking Book is unbound", "message.rivencraft.linking_book.bound": "Linking Book bound as %s", diff --git a/src/main/resources/assets/rivencraft/models/item/page.json b/src/main/resources/assets/rivencraft/models/item/page.json new file mode 100644 index 0000000..6ebfeba --- /dev/null +++ b/src/main/resources/assets/rivencraft/models/item/page.json @@ -0,0 +1,17 @@ +{ + "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" } + ] +} diff --git a/src/main/resources/assets/rivencraft/models/item/page_link_panel.json b/src/main/resources/assets/rivencraft/models/item/page_link_panel.json new file mode 100644 index 0000000..c0d519f --- /dev/null +++ b/src/main/resources/assets/rivencraft/models/item/page_link_panel.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "rivencraft:item/page_link_panel" + } +} diff --git a/src/main/resources/assets/rivencraft/models/item/page_symbol.json b/src/main/resources/assets/rivencraft/models/item/page_symbol.json new file mode 100644 index 0000000..86d5800 --- /dev/null +++ b/src/main/resources/assets/rivencraft/models/item/page_symbol.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "rivencraft:item/page_symbol" + } +} 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 new file mode 100644 index 0000000..251bac9 --- /dev/null +++ b/src/main/resources/assets/rivencraft/models/item/page_symbol_biome_native.json @@ -0,0 +1,4 @@ +{ + "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 new file mode 100644 index 0000000..38bb083 --- /dev/null +++ b/src/main/resources/assets/rivencraft/models/item/page_symbol_celestial_normal_time.json @@ -0,0 +1,4 @@ +{ + "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 new file mode 100644 index 0000000..621bce9 --- /dev/null +++ b/src/main/resources/assets/rivencraft/models/item/page_symbol_feature_caves.json @@ -0,0 +1,4 @@ +{ + "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 new file mode 100644 index 0000000..747cbfb --- /dev/null +++ b/src/main/resources/assets/rivencraft/models/item/page_symbol_lighting_normal.json @@ -0,0 +1,4 @@ +{ + "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 new file mode 100644 index 0000000..562ebd7 --- /dev/null +++ b/src/main/resources/assets/rivencraft/models/item/page_symbol_terrain_standard.json @@ -0,0 +1,4 @@ +{ + "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 new file mode 100644 index 0000000..07c4ed6 --- /dev/null +++ b/src/main/resources/assets/rivencraft/models/item/page_symbol_weather_normal.json @@ -0,0 +1,4 @@ +{ + "parent": "minecraft:item/generated", + "textures": { "layer0": "rivencraft:item/page_symbol_weather_normal" } +} diff --git a/src/main/resources/assets/rivencraft/models/item/page_unresolved.json b/src/main/resources/assets/rivencraft/models/item/page_unresolved.json new file mode 100644 index 0000000..0270473 --- /dev/null +++ b/src/main/resources/assets/rivencraft/models/item/page_unresolved.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "rivencraft:item/page_unresolved" + } +} diff --git a/src/main/resources/assets/rivencraft/textures/item/page.png b/src/main/resources/assets/rivencraft/textures/item/page.png new file mode 100644 index 0000000..632f573 Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/item/page.png differ diff --git a/src/main/resources/assets/rivencraft/textures/item/page_link_panel.png b/src/main/resources/assets/rivencraft/textures/item/page_link_panel.png new file mode 100644 index 0000000..3691f73 Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/item/page_link_panel.png differ diff --git a/src/main/resources/assets/rivencraft/textures/item/page_symbol.png b/src/main/resources/assets/rivencraft/textures/item/page_symbol.png new file mode 100644 index 0000000..3fdbcef Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/item/page_symbol.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 new file mode 100644 index 0000000..cfd7e6a Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/item/page_symbol_biome_native.png 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 new file mode 100644 index 0000000..eb763e9 Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/item/page_symbol_celestial_normal_time.png 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 new file mode 100644 index 0000000..9895464 Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/item/page_symbol_feature_caves.png 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 new file mode 100644 index 0000000..e5a43dc Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/item/page_symbol_lighting_normal.png 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 new file mode 100644 index 0000000..baacfc2 Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/item/page_symbol_terrain_standard.png 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 new file mode 100644 index 0000000..b3ad202 Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/item/page_symbol_weather_normal.png differ diff --git a/src/main/resources/assets/rivencraft/textures/item/page_unresolved.png b/src/main/resources/assets/rivencraft/textures/item/page_unresolved.png new file mode 100644 index 0000000..3fdbcef Binary files /dev/null and b/src/main/resources/assets/rivencraft/textures/item/page_unresolved.png differ diff --git a/src/main/resources/data/rivencraft/recipe/page.json b/src/main/resources/data/rivencraft/recipe/page.json new file mode 100644 index 0000000..b90ef76 --- /dev/null +++ b/src/main/resources/data/rivencraft/recipe/page.json @@ -0,0 +1,13 @@ +{ + "type": "minecraft:crafting_shapeless", + "category": "misc", + "ingredients": [ + { + "item": "minecraft:paper" + } + ], + "result": { + "count": 1, + "id": "rivencraft:page" + } +}