Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dcd3a178e |
No files matched your search
@@ -19,12 +19,18 @@ hand and use the Ink Vial in the other to prepare a baseline Link Panel Page.
|
|||||||
Each vial holds 16 ink units, and the default link-panel cost is two units.
|
Each vial holds 16 ink units, and the default link-panel cost is two units.
|
||||||
The spent vial returns a glass bottle. Server owners can adjust the symbol-copy
|
The spent vial returns a glass bottle. Server owners can adjust the symbol-copy
|
||||||
and link-panel costs in the `writing` section of `rivencraft-server.toml`.
|
and link-panel costs in the `writing` section of `rivencraft-server.toml`.
|
||||||
Symbol-page transcription will become available through the Writing Desk.
|
The Writing Desk can copy a selected written page from its collection tabs.
|
||||||
The Writing Desk is now craftable from planks, a glass bottle, and a feather.
|
The Writing Desk is now craftable from planks, a glass bottle, and a feather.
|
||||||
Its four collection tabs, ink/source/page work slots, and inventory persist
|
Its four collection tabs, paper input, ink vial input, empty glass-bottle return,
|
||||||
|
16-unit ink meter, and inventory persist
|
||||||
through world saves; breaking it drops stored contents. The desk screen is a
|
through world saves; breaking it drops stored contents. The desk screen is a
|
||||||
functional foundation styled after Legacy's left tabs, central writing surface,
|
functional foundation styled after Legacy's left tabs, central writing surface,
|
||||||
and right inventory. Transcription controls are not implemented yet.
|
and right inventory. Put vanilla paper (or a blank Rivencraft Page) in the
|
||||||
|
upper-left slot and Ink Vials in the upper-right; a copy appears in the
|
||||||
|
lower-left result slot when the selected page is valid. Taking it consumes one
|
||||||
|
paper and, by default, one ink. An empty meter consumes a vial to refill and
|
||||||
|
returns a vanilla glass bottle to the lower-right slot. Writing new symbols
|
||||||
|
from a catalog remains future work.
|
||||||
Selecting a stored page on the desk surface shows its Legacy-proportioned
|
Selecting a stored page on the desk surface shows its Legacy-proportioned
|
||||||
portrait preview and name above the player inventory.
|
portrait preview and name above the player inventory.
|
||||||
|
|
||||||
|
|||||||
@@ -86,5 +86,8 @@ panel, hold a blank Page in one hand and use an Ink Vial in the other. The
|
|||||||
server checks the held items, blank state, and sufficient ink before consuming
|
server checks the held items, blank state, and sufficient ink before consuming
|
||||||
anything. A stack of blank Pages yields exactly one Link Panel Page per use;
|
anything. A stack of blank Pages yields exactly one Link Panel Page per use;
|
||||||
if the inventory is full, the result is dropped instead of deleted. Symbol
|
if the inventory is full, the result is dropped instead of deleted. Symbol
|
||||||
copying will be implemented by the Writing Desk. Link-property modifiers are
|
copying is implemented by the Writing Desk: it previews a copy of the selected
|
||||||
|
resolved page, then spends paper and ink only when that copy is taken. The
|
||||||
|
desk's 16-unit ink meter refills from an Ink Vial when needed and returns a
|
||||||
|
vanilla glass bottle. Link-property modifiers are
|
||||||
deferred beyond this baseline flow.
|
deferred beyond this baseline flow.
|
||||||
+1
-1
@@ -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.
|
# 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
|
mod_license=LGPL-3.0-or-later
|
||||||
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
||||||
mod_version=0.3.5.1
|
mod_version=0.3.5.2
|
||||||
immersive_portals_version=6.0.7
|
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.
|
# 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.
|
# This should match the base package used for the mod sources.
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package net.dnorberg.rivencraft.block.entity;
|
package net.dnorberg.rivencraft.block.entity;
|
||||||
|
|
||||||
|
import net.dnorberg.rivencraft.item.InkVialItem;
|
||||||
|
import net.dnorberg.rivencraft.item.PageItem;
|
||||||
|
import net.dnorberg.rivencraft.writing.WritingInkService;
|
||||||
import net.dnorberg.rivencraft.registry.ModBlockEntities;
|
import net.dnorberg.rivencraft.registry.ModBlockEntities;
|
||||||
import net.dnorberg.rivencraft.registry.ModItems;
|
import net.dnorberg.rivencraft.registry.ModItems;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
@@ -10,25 +13,29 @@ import net.minecraft.world.Container;
|
|||||||
import net.minecraft.world.ContainerHelper;
|
import net.minecraft.world.ContainerHelper;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
/** The four Legacy-like collection tabs and four work slots are owned by the block, not its viewers. */
|
/** Four collection tabs, paper, ink vials and empty-vial return are owned by the block. */
|
||||||
public final class WritingDeskBlockEntity extends BlockEntity implements Container {
|
public final class WritingDeskBlockEntity extends BlockEntity implements Container {
|
||||||
public static final int COLLECTIONS = 4;
|
public static final int COLLECTIONS = 4;
|
||||||
public static final int INK = 4;
|
public static final int INK = 4;
|
||||||
public static final int SOURCE = 5;
|
public static final int RETURN = 5;
|
||||||
public static final int PAPER = 6;
|
public static final int PAPER = 6;
|
||||||
public static final int OUTPUT = 7;
|
/** Unused by new desks; retained so old saves cannot silently lose slot contents. */
|
||||||
|
public static final int LEGACY_OUTPUT = 7;
|
||||||
public static final int SIZE = 8;
|
public static final int SIZE = 8;
|
||||||
private NonNullList<ItemStack> items = NonNullList.withSize(SIZE, ItemStack.EMPTY);
|
private NonNullList<ItemStack> items = NonNullList.withSize(SIZE, ItemStack.EMPTY);
|
||||||
private int activeCollection;
|
private int activeCollection;
|
||||||
|
private int inkRemaining;
|
||||||
|
|
||||||
public WritingDeskBlockEntity(BlockPos pos, BlockState state) {
|
public WritingDeskBlockEntity(BlockPos pos, BlockState state) {
|
||||||
super(ModBlockEntities.WRITING_DESK.get(), pos, state);
|
super(ModBlockEntities.WRITING_DESK.get(), pos, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int activeCollection() { return activeCollection; }
|
public int activeCollection() { return activeCollection; }
|
||||||
|
public int inkRemaining() { return inkRemaining; }
|
||||||
public void setActiveCollection(int index) {
|
public void setActiveCollection(int index) {
|
||||||
if (index >= 0 && index < COLLECTIONS && activeCollection != index) {
|
if (index >= 0 && index < COLLECTIONS && activeCollection != index) {
|
||||||
activeCollection = index;
|
activeCollection = index;
|
||||||
@@ -60,15 +67,49 @@ public final class WritingDeskBlockEntity extends BlockEntity implements Contain
|
|||||||
if (slot < COLLECTIONS) return stack.is(ModItems.NOTEBOOK.get()) || stack.is(ModItems.PAGE.get());
|
if (slot < COLLECTIONS) return stack.is(ModItems.NOTEBOOK.get()) || stack.is(ModItems.PAGE.get());
|
||||||
return switch (slot) {
|
return switch (slot) {
|
||||||
case INK -> stack.is(ModItems.INK_VIAL.get());
|
case INK -> stack.is(ModItems.INK_VIAL.get());
|
||||||
case SOURCE -> stack.is(ModItems.PAGE.get()) || stack.is(ModItems.NOTEBOOK.get());
|
case RETURN -> false;
|
||||||
case PAPER -> stack.is(ModItems.PAGE.get());
|
case PAPER -> stack.is(Items.PAPER)
|
||||||
case OUTPUT -> false;
|
|| stack.is(ModItems.PAGE.get()) && PageItem.content(stack).kind()
|
||||||
|
== net.dnorberg.rivencraft.writing.PageContent.Kind.BLANK;
|
||||||
|
case LEGACY_OUTPUT -> false;
|
||||||
default -> false;
|
default -> false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The output is virtual; inputs are committed only after a result can be given to a player. */
|
||||||
|
public boolean canConsumeCopyMaterials() {
|
||||||
|
ItemStack paper = items.get(PAPER);
|
||||||
|
if (!canPlaceItem(PAPER, paper) || paper.isEmpty()) return false;
|
||||||
|
int cost = WritingInkService.symbolCopyCost();
|
||||||
|
if (inkRemaining >= cost) return true;
|
||||||
|
ItemStack vial = items.get(INK);
|
||||||
|
ItemStack returned = items.get(RETURN);
|
||||||
|
return vial.is(ModItems.INK_VIAL.get()) && inkRemaining + InkVialItem.remaining(vial) >= cost
|
||||||
|
&& (returned.isEmpty() || returned.is(Items.GLASS_BOTTLE)
|
||||||
|
&& returned.getCount() < returned.getMaxStackSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean consumeCopyMaterials() {
|
||||||
|
if (!canConsumeCopyMaterials()) return false;
|
||||||
|
int cost = WritingInkService.symbolCopyCost();
|
||||||
|
if (inkRemaining < cost) {
|
||||||
|
ItemStack vial = items.get(INK);
|
||||||
|
inkRemaining += Math.clamp(InkVialItem.remaining(vial), 0, InkVialItem.CAPACITY);
|
||||||
|
vial.shrink(1);
|
||||||
|
if (vial.isEmpty()) items.set(INK, ItemStack.EMPTY);
|
||||||
|
ItemStack returned = items.get(RETURN);
|
||||||
|
if (returned.isEmpty()) items.set(RETURN, new ItemStack(Items.GLASS_BOTTLE));
|
||||||
|
else returned.grow(1);
|
||||||
|
}
|
||||||
|
inkRemaining -= cost;
|
||||||
|
removeItem(PAPER, 1);
|
||||||
|
setChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@Override public void clearContent() {
|
@Override public void clearContent() {
|
||||||
items.clear();
|
items.clear();
|
||||||
items = NonNullList.withSize(SIZE, ItemStack.EMPTY);
|
items = NonNullList.withSize(SIZE, ItemStack.EMPTY);
|
||||||
|
inkRemaining = 0;
|
||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
@Override protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
@Override protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
||||||
@@ -76,10 +117,12 @@ public final class WritingDeskBlockEntity extends BlockEntity implements Contain
|
|||||||
items = NonNullList.withSize(SIZE, ItemStack.EMPTY);
|
items = NonNullList.withSize(SIZE, ItemStack.EMPTY);
|
||||||
ContainerHelper.loadAllItems(tag, items, registries);
|
ContainerHelper.loadAllItems(tag, items, registries);
|
||||||
activeCollection = Math.clamp(tag.getInt("ActiveCollection"), 0, COLLECTIONS - 1);
|
activeCollection = Math.clamp(tag.getInt("ActiveCollection"), 0, COLLECTIONS - 1);
|
||||||
|
inkRemaining = Math.clamp(tag.getInt("InkRemaining"), 0, InkVialItem.CAPACITY);
|
||||||
}
|
}
|
||||||
@Override protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
@Override protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
||||||
super.saveAdditional(tag, registries);
|
super.saveAdditional(tag, registries);
|
||||||
ContainerHelper.saveAllItems(tag, items, registries);
|
ContainerHelper.saveAllItems(tag, items, registries);
|
||||||
tag.putInt("ActiveCollection", activeCollection);
|
tag.putInt("ActiveCollection", activeCollection);
|
||||||
|
tag.putInt("InkRemaining", inkRemaining);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,10 @@ public final class WritingDeskScreen extends AbstractContainerScreen<WritingDesk
|
|||||||
inventoryLabelY = 79;
|
inventoryLabelY = 79;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
|
||||||
|
// Legacy leaves the world visible around the desk instead of dimming it.
|
||||||
|
}
|
||||||
|
|
||||||
@Override protected void renderBg(GuiGraphics graphics, float partialTick, int mouseX, int mouseY) {
|
@Override protected void renderBg(GuiGraphics graphics, float partialTick, int mouseX, int mouseY) {
|
||||||
int x = leftPos;
|
int x = leftPos;
|
||||||
int y = topPos;
|
int y = topPos;
|
||||||
@@ -47,6 +51,11 @@ public final class WritingDeskScreen extends AbstractContainerScreen<WritingDesk
|
|||||||
slotFrame(graphics, 228, 20);
|
slotFrame(graphics, 228, 20);
|
||||||
slotFrame(graphics, 228, 57);
|
slotFrame(graphics, 228, 57);
|
||||||
slotFrame(graphics, 370, 57);
|
slotFrame(graphics, 370, 57);
|
||||||
|
graphics.fill(x + 350, y + 20, x + 364, y + 75, 0xFF303035);
|
||||||
|
graphics.fill(x + 352, y + 22, x + 362, y + 73, 0xFF15151B);
|
||||||
|
int filled = 51 * menu.inkRemaining() / 16;
|
||||||
|
if (filled > 0) graphics.fill(x + 352, y + 73 - filled, x + 362, y + 73, 0xFF6666AA);
|
||||||
|
graphics.renderOutline(x + 350, y + 20, 14, 55, 0xFF484848);
|
||||||
for (int row = 0; row < 3; row++) {
|
for (int row = 0; row < 3; row++) {
|
||||||
for (int column = 0; column < 9; column++) slotFrame(graphics, 224 + column * 18, 91 + row * 18);
|
for (int column = 0; column < 9; column++) slotFrame(graphics, 224 + column * 18, 91 + row * 18);
|
||||||
}
|
}
|
||||||
@@ -68,6 +77,11 @@ public final class WritingDeskScreen extends AbstractContainerScreen<WritingDesk
|
|||||||
super.render(graphics, mouseX, mouseY, partialTick);
|
super.render(graphics, mouseX, mouseY, partialTick);
|
||||||
renderCollection(graphics);
|
renderCollection(graphics);
|
||||||
renderTooltip(graphics, mouseX, mouseY);
|
renderTooltip(graphics, mouseX, mouseY);
|
||||||
|
if (mouseX >= leftPos + 350 && mouseX < leftPos + 364
|
||||||
|
&& mouseY >= topPos + 20 && mouseY < topPos + 75) {
|
||||||
|
graphics.renderTooltip(font, Component.translatable("screen.rivencraft.writing_desk.ink",
|
||||||
|
menu.inkRemaining(), 16), mouseX, mouseY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ItemStack> pages() {
|
private List<ItemStack> pages() {
|
||||||
@@ -133,6 +147,9 @@ public final class WritingDeskScreen extends AbstractContainerScreen<WritingDesk
|
|||||||
if (x % 31 < 30 && y % 41 < 40 && index < pages.size()
|
if (x % 31 < 30 && y % 41 < 40 && index < pages.size()
|
||||||
&& !pages.get(index).isEmpty()) {
|
&& !pages.get(index).isEmpty()) {
|
||||||
selectedPage = index;
|
selectedPage = index;
|
||||||
|
if (minecraft != null && minecraft.gameMode != null) {
|
||||||
|
minecraft.gameMode.handleInventoryButtonClick(menu.containerId, 16 + index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
package net.dnorberg.rivencraft.gametest;
|
package net.dnorberg.rivencraft.gametest;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import net.dnorberg.rivencraft.Rivencraft;
|
import net.dnorberg.rivencraft.Rivencraft;
|
||||||
import net.dnorberg.rivencraft.block.entity.WritingDeskBlockEntity;
|
import net.dnorberg.rivencraft.block.entity.WritingDeskBlockEntity;
|
||||||
import net.dnorberg.rivencraft.menu.WritingDeskMenu;
|
import net.dnorberg.rivencraft.menu.WritingDeskMenu;
|
||||||
|
import net.dnorberg.rivencraft.item.PageItem;
|
||||||
import net.dnorberg.rivencraft.registry.ModBlocks;
|
import net.dnorberg.rivencraft.registry.ModBlocks;
|
||||||
|
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||||
import net.dnorberg.rivencraft.registry.ModItems;
|
import net.dnorberg.rivencraft.registry.ModItems;
|
||||||
|
import net.dnorberg.rivencraft.writing.NotebookContents;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.gametest.framework.GameTest;
|
import net.minecraft.gametest.framework.GameTest;
|
||||||
import net.minecraft.gametest.framework.GameTestHelper;
|
import net.minecraft.gametest.framework.GameTestHelper;
|
||||||
import net.minecraft.world.entity.item.ItemEntity;
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.inventory.ClickType;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.neoforged.neoforge.gametest.GameTestHolder;
|
import net.neoforged.neoforge.gametest.GameTestHolder;
|
||||||
@@ -65,21 +71,139 @@ public final class WritingDeskGameTests {
|
|||||||
WritingDeskBlockEntity desk = (WritingDeskBlockEntity) helper.getLevel().getBlockEntity(pos);
|
WritingDeskBlockEntity desk = (WritingDeskBlockEntity) helper.getLevel().getBlockEntity(pos);
|
||||||
var player = helper.makeMockServerPlayerInLevel();
|
var player = helper.makeMockServerPlayerInLevel();
|
||||||
player.moveTo(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
|
player.moveTo(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
|
||||||
player.getInventory().setItem(9, new ItemStack(ModItems.PAGE.get(), 12));
|
player.getInventory().setItem(9, new ItemStack(Items.PAPER, 12));
|
||||||
WritingDeskMenu menu = new WritingDeskMenu(1, player.getInventory(), desk);
|
WritingDeskMenu menu = new WritingDeskMenu(1, player.getInventory(), desk);
|
||||||
helper.assertTrue(menu.stillValid(player), "Placed desk must keep its menu valid");
|
helper.assertTrue(menu.stillValid(player), "Placed desk must keep its menu valid");
|
||||||
helper.assertTrue(menu.clickMenuButton(player, 3), "Active collection control must accept a valid tab");
|
helper.assertTrue(menu.clickMenuButton(player, 3), "Active collection control must accept a valid tab");
|
||||||
helper.assertValueEqual(desk.activeCollection(), 3, "Selected tab must be stored on the desk");
|
helper.assertValueEqual(desk.activeCollection(), 3, "Selected tab must be stored on the desk");
|
||||||
menu.quickMoveStack(player, 8);
|
menu.quickMoveStack(player, 9);
|
||||||
helper.assertValueEqual(desk.getItem(WritingDeskBlockEntity.PAPER).getCount(), 12, "Shift-click must deposit all pages");
|
helper.assertValueEqual(desk.getItem(WritingDeskBlockEntity.PAPER).getCount(), 12, "Shift-click must deposit all pages");
|
||||||
helper.assertTrue(player.getInventory().getItem(9).isEmpty(), "Shift-click must consume player source stack");
|
helper.assertTrue(player.getInventory().getItem(9).isEmpty(), "Shift-click must consume player source stack");
|
||||||
menu.quickMoveStack(player, WritingDeskBlockEntity.PAPER);
|
menu.quickMoveStack(player, WritingDeskBlockEntity.PAPER);
|
||||||
helper.assertTrue(desk.getItem(WritingDeskBlockEntity.PAPER).isEmpty(), "Shift-click must empty desk source slot");
|
helper.assertTrue(desk.getItem(WritingDeskBlockEntity.PAPER).isEmpty(), "Shift-click must empty desk source slot");
|
||||||
int total = player.getInventory().items.stream().filter(stack -> stack.is(ModItems.PAGE.get()))
|
int total = player.getInventory().items.stream().filter(stack -> stack.is(Items.PAPER))
|
||||||
.mapToInt(ItemStack::getCount).sum();
|
.mapToInt(ItemStack::getCount).sum();
|
||||||
helper.assertValueEqual(total, 12, "Round-trip must not lose or duplicate pages");
|
helper.assertValueEqual(total, 12, "Round-trip must not lose or duplicate pages");
|
||||||
helper.getLevel().setBlock(pos, Blocks.AIR.defaultBlockState(), 3);
|
helper.getLevel().setBlock(pos, Blocks.AIR.defaultBlockState(), 3);
|
||||||
helper.assertFalse(menu.stillValid(player), "Removed desk must invalidate menu");
|
helper.assertFalse(menu.stillValid(player), "Removed desk must invalidate menu");
|
||||||
helper.succeed();
|
helper.succeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "empty")
|
||||||
|
public static void takingCopySpendsPaperAndOneInkThenReturnsGlassBottle(GameTestHelper helper) {
|
||||||
|
BlockPos pos = helper.absolutePos(new BlockPos(1, 1, 1));
|
||||||
|
helper.getLevel().setBlock(pos, ModBlocks.WRITING_DESK.get().defaultBlockState(), 3);
|
||||||
|
WritingDeskBlockEntity desk = (WritingDeskBlockEntity) helper.getLevel().getBlockEntity(pos);
|
||||||
|
ItemStack source = PageItem.createLinkPanelPage();
|
||||||
|
desk.setItem(0, source.copy());
|
||||||
|
desk.setItem(WritingDeskBlockEntity.PAPER, new ItemStack(Items.PAPER, 2));
|
||||||
|
desk.setItem(WritingDeskBlockEntity.INK, new ItemStack(ModItems.INK_VIAL.get()));
|
||||||
|
var player = helper.makeMockServerPlayerInLevel();
|
||||||
|
player.moveTo(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
|
||||||
|
WritingDeskMenu menu = new WritingDeskMenu(1, player.getInventory(), desk);
|
||||||
|
menu.broadcastChanges();
|
||||||
|
helper.assertTrue(menu.getSlot(WritingDeskMenu.COPY_RESULT).getItem().is(ModItems.PAGE.get()),
|
||||||
|
"A source page with paper and ink must preview one copy");
|
||||||
|
helper.assertValueEqual(desk.getItem(WritingDeskBlockEntity.PAPER).getCount(), 2,
|
||||||
|
"Previewing must not consume paper");
|
||||||
|
helper.assertValueEqual(desk.inkRemaining(), 0, "Previewing must not consume ink");
|
||||||
|
menu.clicked(WritingDeskMenu.COPY_RESULT, 0, ClickType.PICKUP, player);
|
||||||
|
helper.assertTrue(menu.getCarried().is(ModItems.PAGE.get()), "Taking result must give the copied page");
|
||||||
|
helper.assertValueEqual(desk.getItem(WritingDeskBlockEntity.PAPER).getCount(), 1,
|
||||||
|
"Taking a copy must consume one paper");
|
||||||
|
helper.assertValueEqual(desk.inkRemaining(), 15, "First copy must refill to 16 and consume one ink");
|
||||||
|
helper.assertTrue(desk.getItem(WritingDeskBlockEntity.INK).isEmpty(), "Refill must consume one vial");
|
||||||
|
helper.assertTrue(desk.getItem(WritingDeskBlockEntity.RETURN).is(Items.GLASS_BOTTLE),
|
||||||
|
"Refill must return a vanilla glass bottle");
|
||||||
|
helper.assertTrue(desk.getItem(0).is(ModItems.PAGE.get()), "Source page must remain untouched");
|
||||||
|
helper.assertTrue(menu.quickMoveStack(player, WritingDeskMenu.COPY_RESULT).is(ModItems.PAGE.get()),
|
||||||
|
"A second copy must remain available from the same ink fill");
|
||||||
|
helper.assertTrue(desk.getItem(WritingDeskBlockEntity.PAPER).isEmpty(),
|
||||||
|
"Two copies must consume exactly two paper");
|
||||||
|
helper.assertValueEqual(desk.inkRemaining(), 14, "Two copies must spend exactly two of sixteen ink");
|
||||||
|
helper.assertValueEqual(desk.getItem(WritingDeskBlockEntity.RETURN).getCount(), 1,
|
||||||
|
"A second copy must not consume another vial");
|
||||||
|
var saved = desk.saveWithoutMetadata(helper.getLevel().registryAccess());
|
||||||
|
var restored = new WritingDeskBlockEntity(pos, ModBlocks.WRITING_DESK.get().defaultBlockState());
|
||||||
|
restored.loadWithComponents(saved, helper.getLevel().registryAccess());
|
||||||
|
helper.assertValueEqual(restored.inkRemaining(), 14, "Ink meter must persist across saves");
|
||||||
|
helper.succeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "empty")
|
||||||
|
public static void shiftClickCopiesOnlyWhenResourcesAndBottleSpaceExist(GameTestHelper helper) {
|
||||||
|
BlockPos pos = helper.absolutePos(new BlockPos(1, 1, 1));
|
||||||
|
helper.getLevel().setBlock(pos, ModBlocks.WRITING_DESK.get().defaultBlockState(), 3);
|
||||||
|
WritingDeskBlockEntity desk = (WritingDeskBlockEntity) helper.getLevel().getBlockEntity(pos);
|
||||||
|
desk.setItem(0, PageItem.createLinkPanelPage());
|
||||||
|
desk.setItem(WritingDeskBlockEntity.PAPER, new ItemStack(Items.PAPER, 2));
|
||||||
|
desk.setItem(WritingDeskBlockEntity.INK, new ItemStack(ModItems.INK_VIAL.get()));
|
||||||
|
desk.setItem(WritingDeskBlockEntity.RETURN, new ItemStack(Items.GLASS_BOTTLE, 64));
|
||||||
|
var player = helper.makeMockServerPlayerInLevel();
|
||||||
|
player.moveTo(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
|
||||||
|
WritingDeskMenu menu = new WritingDeskMenu(1, player.getInventory(), desk);
|
||||||
|
helper.assertTrue(menu.quickMoveStack(player, WritingDeskMenu.COPY_RESULT).isEmpty(),
|
||||||
|
"Full bottle return must block refill without consuming supplies");
|
||||||
|
helper.assertValueEqual(desk.getItem(WritingDeskBlockEntity.PAPER).getCount(), 2,
|
||||||
|
"Blocked copy must leave paper untouched");
|
||||||
|
desk.removeItem(WritingDeskBlockEntity.RETURN, 1);
|
||||||
|
ItemStack copied = menu.quickMoveStack(player, WritingDeskMenu.COPY_RESULT);
|
||||||
|
helper.assertTrue(copied.is(ModItems.PAGE.get()), "Shift-click must transfer one copy");
|
||||||
|
helper.assertValueEqual(desk.getItem(WritingDeskBlockEntity.PAPER).getCount(), 1,
|
||||||
|
"Shift-click must consume one paper");
|
||||||
|
helper.assertValueEqual(desk.inkRemaining(), 15, "Shift-click must consume one ink");
|
||||||
|
int copies = player.getInventory().items.stream().filter(stack -> stack.is(ModItems.PAGE.get()))
|
||||||
|
.mapToInt(ItemStack::getCount).sum();
|
||||||
|
helper.assertValueEqual(copies, 1, "Shift-click must give exactly one page");
|
||||||
|
helper.succeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "empty")
|
||||||
|
public static void selectedNotebookPageControlsCopyPreview(GameTestHelper helper) {
|
||||||
|
BlockPos pos = helper.absolutePos(new BlockPos(1, 1, 1));
|
||||||
|
helper.getLevel().setBlock(pos, ModBlocks.WRITING_DESK.get().defaultBlockState(), 3);
|
||||||
|
WritingDeskBlockEntity desk = (WritingDeskBlockEntity) helper.getLevel().getBlockEntity(pos);
|
||||||
|
ItemStack notebook = new ItemStack(ModItems.NOTEBOOK.get());
|
||||||
|
notebook.set(ModDataComponents.NOTEBOOK_CONTENTS.get(),
|
||||||
|
new NotebookContents(List.of(ItemStack.EMPTY, PageItem.createLinkPanelPage())));
|
||||||
|
desk.setItem(0, notebook);
|
||||||
|
desk.setItem(WritingDeskBlockEntity.PAPER, new ItemStack(Items.PAPER));
|
||||||
|
desk.setItem(WritingDeskBlockEntity.INK, new ItemStack(ModItems.INK_VIAL.get()));
|
||||||
|
var player = helper.makeMockServerPlayerInLevel();
|
||||||
|
player.moveTo(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
|
||||||
|
WritingDeskMenu menu = new WritingDeskMenu(1, player.getInventory(), desk);
|
||||||
|
menu.broadcastChanges();
|
||||||
|
helper.assertTrue(menu.getSlot(WritingDeskMenu.COPY_RESULT).getItem().isEmpty(),
|
||||||
|
"An empty selected position must not create a copy");
|
||||||
|
helper.assertTrue(menu.clickMenuButton(player, 17), "Second page selection must be accepted");
|
||||||
|
menu.broadcastChanges();
|
||||||
|
helper.assertTrue(menu.getSlot(WritingDeskMenu.COPY_RESULT).getItem().is(ModItems.PAGE.get()),
|
||||||
|
"Selecting the populated page must show a copy");
|
||||||
|
helper.succeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "empty")
|
||||||
|
public static void twoViewersCannotTakeTheSameLastSheet(GameTestHelper helper) {
|
||||||
|
BlockPos pos = helper.absolutePos(new BlockPos(1, 1, 1));
|
||||||
|
helper.getLevel().setBlock(pos, ModBlocks.WRITING_DESK.get().defaultBlockState(), 3);
|
||||||
|
WritingDeskBlockEntity desk = (WritingDeskBlockEntity) helper.getLevel().getBlockEntity(pos);
|
||||||
|
desk.setItem(0, PageItem.createLinkPanelPage());
|
||||||
|
desk.setItem(WritingDeskBlockEntity.PAPER, new ItemStack(Items.PAPER));
|
||||||
|
desk.setItem(WritingDeskBlockEntity.INK, new ItemStack(ModItems.INK_VIAL.get()));
|
||||||
|
var first = helper.makeMockServerPlayerInLevel();
|
||||||
|
var second = helper.makeMockServerPlayerInLevel();
|
||||||
|
first.moveTo(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
|
||||||
|
second.moveTo(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
|
||||||
|
WritingDeskMenu firstMenu = new WritingDeskMenu(1, first.getInventory(), desk);
|
||||||
|
WritingDeskMenu secondMenu = new WritingDeskMenu(2, second.getInventory(), desk);
|
||||||
|
firstMenu.broadcastChanges();
|
||||||
|
secondMenu.broadcastChanges();
|
||||||
|
helper.assertTrue(firstMenu.quickMoveStack(first, WritingDeskMenu.COPY_RESULT).is(ModItems.PAGE.get()),
|
||||||
|
"First viewer should take the last available copy");
|
||||||
|
helper.assertTrue(secondMenu.quickMoveStack(second, WritingDeskMenu.COPY_RESULT).isEmpty(),
|
||||||
|
"Second viewer must not copy the already-consumed paper");
|
||||||
|
helper.assertTrue(desk.getItem(WritingDeskBlockEntity.PAPER).isEmpty(),
|
||||||
|
"Shared desk must spend paper only once");
|
||||||
|
helper.succeed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
package net.dnorberg.rivencraft.menu;
|
package net.dnorberg.rivencraft.menu;
|
||||||
|
|
||||||
import net.dnorberg.rivencraft.block.entity.WritingDeskBlockEntity;
|
import net.dnorberg.rivencraft.block.entity.WritingDeskBlockEntity;
|
||||||
|
import net.dnorberg.rivencraft.item.PageItem;
|
||||||
|
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||||
import net.dnorberg.rivencraft.registry.ModItems;
|
import net.dnorberg.rivencraft.registry.ModItems;
|
||||||
import net.dnorberg.rivencraft.registry.ModMenus;
|
import net.dnorberg.rivencraft.registry.ModMenus;
|
||||||
|
import net.dnorberg.rivencraft.writing.NotebookContents;
|
||||||
|
import net.dnorberg.rivencraft.writing.PageContent;
|
||||||
|
import java.util.List;
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
import net.minecraft.world.SimpleContainer;
|
import net.minecraft.world.SimpleContainer;
|
||||||
@@ -12,12 +17,18 @@ import net.minecraft.world.inventory.AbstractContainerMenu;
|
|||||||
import net.minecraft.world.inventory.DataSlot;
|
import net.minecraft.world.inventory.DataSlot;
|
||||||
import net.minecraft.world.inventory.Slot;
|
import net.minecraft.world.inventory.Slot;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
|
||||||
public final class WritingDeskMenu extends AbstractContainerMenu {
|
public final class WritingDeskMenu extends AbstractContainerMenu {
|
||||||
private static final int PLAYER_START = WritingDeskBlockEntity.SIZE;
|
public static final int COPY_RESULT = WritingDeskBlockEntity.SIZE;
|
||||||
|
private static final int PLAYER_START = COPY_RESULT + 1;
|
||||||
private final Container desk;
|
private final Container desk;
|
||||||
private final WritingDeskBlockEntity blockEntity;
|
private final WritingDeskBlockEntity blockEntity;
|
||||||
|
private final SimpleContainer result = new SimpleContainer(1);
|
||||||
|
private final boolean clientSide;
|
||||||
private int syncedActiveCollection;
|
private int syncedActiveCollection;
|
||||||
|
private int syncedInkRemaining;
|
||||||
|
private int selectedPage;
|
||||||
|
|
||||||
public WritingDeskMenu(int id, Inventory inventory, RegistryFriendlyByteBuf data) {
|
public WritingDeskMenu(int id, Inventory inventory, RegistryFriendlyByteBuf data) {
|
||||||
this(id, inventory, inventory.player.level().getBlockEntity(data.readBlockPos())
|
this(id, inventory, inventory.player.level().getBlockEntity(data.readBlockPos())
|
||||||
@@ -28,18 +39,25 @@ public final class WritingDeskMenu extends AbstractContainerMenu {
|
|||||||
super(ModMenus.WRITING_DESK.get(), id);
|
super(ModMenus.WRITING_DESK.get(), id);
|
||||||
this.blockEntity = blockEntity;
|
this.blockEntity = blockEntity;
|
||||||
this.desk = blockEntity == null ? new SimpleContainer(WritingDeskBlockEntity.SIZE) : blockEntity;
|
this.desk = blockEntity == null ? new SimpleContainer(WritingDeskBlockEntity.SIZE) : blockEntity;
|
||||||
|
this.clientSide = inventory.player.level().isClientSide;
|
||||||
addDataSlot(new DataSlot() {
|
addDataSlot(new DataSlot() {
|
||||||
@Override public int get() { return WritingDeskMenu.this.blockEntity == null
|
@Override public int get() { return WritingDeskMenu.this.blockEntity == null
|
||||||
? syncedActiveCollection : WritingDeskMenu.this.blockEntity.activeCollection(); }
|
? syncedActiveCollection : WritingDeskMenu.this.blockEntity.activeCollection(); }
|
||||||
@Override public void set(int value) { syncedActiveCollection = value; }
|
@Override public void set(int value) { syncedActiveCollection = value; }
|
||||||
});
|
});
|
||||||
|
addDataSlot(new DataSlot() {
|
||||||
|
@Override public int get() { return WritingDeskMenu.this.blockEntity == null
|
||||||
|
? syncedInkRemaining : WritingDeskMenu.this.blockEntity.inkRemaining(); }
|
||||||
|
@Override public void set(int value) { syncedInkRemaining = value; }
|
||||||
|
});
|
||||||
for (int i = 0; i < WritingDeskBlockEntity.COLLECTIONS; i++) {
|
for (int i = 0; i < WritingDeskBlockEntity.COLLECTIONS; i++) {
|
||||||
addSlot(new DeskSlot(desk, i, 12, 20 + i * 37, 1));
|
addSlot(new DeskSlot(desk, i, 12, 20 + i * 37, 1));
|
||||||
}
|
}
|
||||||
addSlot(new DeskSlot(desk, WritingDeskBlockEntity.INK, 370, 20, 1));
|
addSlot(new DeskSlot(desk, WritingDeskBlockEntity.INK, 370, 20, 16));
|
||||||
addSlot(new DeskSlot(desk, WritingDeskBlockEntity.SOURCE, 228, 20, 1));
|
addSlot(new DeskSlot(desk, WritingDeskBlockEntity.RETURN, 370, 57, 64));
|
||||||
addSlot(new DeskSlot(desk, WritingDeskBlockEntity.PAPER, 228, 57, 64));
|
addSlot(new DeskSlot(desk, WritingDeskBlockEntity.PAPER, 228, 20, 64));
|
||||||
addSlot(new DeskSlot(desk, WritingDeskBlockEntity.OUTPUT, 370, 57, 64));
|
addSlot(new DeskSlot(desk, WritingDeskBlockEntity.LEGACY_OUTPUT, -1000, -1000, 64));
|
||||||
|
addSlot(new CopySlot(result, 0, 228, 57));
|
||||||
for (int row = 0; row < 3; row++) {
|
for (int row = 0; row < 3; row++) {
|
||||||
for (int column = 0; column < 9; column++) {
|
for (int column = 0; column < 9; column++) {
|
||||||
addSlot(new Slot(inventory, column + row * 9 + 9, 224 + column * 18, 91 + row * 18));
|
addSlot(new Slot(inventory, column + row * 9 + 9, 224 + column * 18, 91 + row * 18));
|
||||||
@@ -51,10 +69,17 @@ public final class WritingDeskMenu extends AbstractContainerMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int activeCollection() { return syncedActiveCollection; }
|
public int activeCollection() { return syncedActiveCollection; }
|
||||||
|
public int inkRemaining() { return syncedInkRemaining; }
|
||||||
|
|
||||||
@Override public boolean clickMenuButton(Player player, int id) {
|
@Override public boolean clickMenuButton(Player player, int id) {
|
||||||
if (blockEntity == null || !stillValid(player) || id < 0 || id >= WritingDeskBlockEntity.COLLECTIONS) return false;
|
if (blockEntity == null || !stillValid(player)) return false;
|
||||||
blockEntity.setActiveCollection(id);
|
if (id >= 0 && id < WritingDeskBlockEntity.COLLECTIONS) {
|
||||||
|
blockEntity.setActiveCollection(id);
|
||||||
|
selectedPage = 0;
|
||||||
|
} else if (id >= 16 && id < 16 + NotebookContents.MAX_PAGES) {
|
||||||
|
selectedPage = id - 16;
|
||||||
|
} else return false;
|
||||||
|
refreshOutput();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,11 +89,23 @@ public final class WritingDeskMenu extends AbstractContainerMenu {
|
|||||||
|
|
||||||
@Override public ItemStack quickMoveStack(Player player, int index) {
|
@Override public ItemStack quickMoveStack(Player player, int index) {
|
||||||
if (!stillValid(player) || index < 0 || index >= slots.size()) return ItemStack.EMPTY;
|
if (!stillValid(player) || index < 0 || index >= slots.size()) return ItemStack.EMPTY;
|
||||||
|
if (index == COPY_RESULT) {
|
||||||
|
refreshOutput();
|
||||||
|
if (!canCopy()) return ItemStack.EMPTY;
|
||||||
|
ItemStack copy = selectedSource().copyWithCount(1);
|
||||||
|
ItemStack movingCopy = copy.copy();
|
||||||
|
if (!moveItemStackTo(movingCopy, PLAYER_START, slots.size(), true) || !movingCopy.isEmpty()) {
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
blockEntity.consumeCopyMaterials();
|
||||||
|
refreshOutput();
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
Slot slot = slots.get(index);
|
Slot slot = slots.get(index);
|
||||||
if (!slot.hasItem()) return ItemStack.EMPTY;
|
if (!slot.hasItem()) return ItemStack.EMPTY;
|
||||||
ItemStack moving = slot.getItem();
|
ItemStack moving = slot.getItem();
|
||||||
ItemStack original = moving.copy();
|
ItemStack original = moving.copy();
|
||||||
if (index < PLAYER_START) {
|
if (index < COPY_RESULT) {
|
||||||
if (!moveItemStackTo(moving, PLAYER_START, slots.size(), true)) return ItemStack.EMPTY;
|
if (!moveItemStackTo(moving, PLAYER_START, slots.size(), true)) return ItemStack.EMPTY;
|
||||||
} else if (!moveIntoDesk(moving)) {
|
} else if (!moveIntoDesk(moving)) {
|
||||||
return ItemStack.EMPTY;
|
return ItemStack.EMPTY;
|
||||||
@@ -81,14 +118,65 @@ public final class WritingDeskMenu extends AbstractContainerMenu {
|
|||||||
private boolean moveIntoDesk(ItemStack moving) {
|
private boolean moveIntoDesk(ItemStack moving) {
|
||||||
if (moving.is(ModItems.INK_VIAL.get())) return moveItemStackTo(moving, 4, 5, false);
|
if (moving.is(ModItems.INK_VIAL.get())) return moveItemStackTo(moving, 4, 5, false);
|
||||||
if (moving.is(ModItems.NOTEBOOK.get())) return moveItemStackTo(moving, 0, 4, false);
|
if (moving.is(ModItems.NOTEBOOK.get())) return moveItemStackTo(moving, 0, 4, false);
|
||||||
if (moving.is(ModItems.PAGE.get())) {
|
if (moving.is(Items.PAPER) || moving.is(ModItems.PAGE.get())
|
||||||
return moveItemStackTo(moving, 6, 7, false)
|
&& PageItem.content(moving).kind() == PageContent.Kind.BLANK)
|
||||||
|| moveItemStackTo(moving, 0, 4, false)
|
return moveItemStackTo(moving, WritingDeskBlockEntity.PAPER,
|
||||||
|| moveItemStackTo(moving, 5, 6, false);
|
WritingDeskBlockEntity.PAPER + 1, false);
|
||||||
}
|
if (moving.is(ModItems.PAGE.get())) return moveItemStackTo(moving, 0, 4, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ItemStack selectedSource() {
|
||||||
|
ItemStack collection = desk.getItem(blockEntity == null ? syncedActiveCollection : blockEntity.activeCollection());
|
||||||
|
if (collection.is(ModItems.PAGE.get())) return selectedPage == 0 ? collection : ItemStack.EMPTY;
|
||||||
|
if (collection.is(ModItems.NOTEBOOK.get())) {
|
||||||
|
List<ItemStack> pages = collection.getOrDefault(
|
||||||
|
ModDataComponents.NOTEBOOK_CONTENTS.get(), NotebookContents.EMPTY).pages();
|
||||||
|
return selectedPage < pages.size() ? pages.get(selectedPage) : ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canCopy() {
|
||||||
|
ItemStack source = selectedSource();
|
||||||
|
return blockEntity != null && source.is(ModItems.PAGE.get())
|
||||||
|
&& PageItem.content(source).kind() != PageContent.Kind.BLANK
|
||||||
|
&& PageItem.isUsableWriting(source) && blockEntity.canConsumeCopyMaterials();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshOutput() {
|
||||||
|
if (clientSide) return;
|
||||||
|
ItemStack preview = canCopy() ? selectedSource().copyWithCount(1) : ItemStack.EMPTY;
|
||||||
|
if (!ItemStack.matches(result.getItem(0), preview)) result.setItem(0, preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void broadcastChanges() {
|
||||||
|
refreshOutput();
|
||||||
|
super.broadcastChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void clicked(int slotId, int button, net.minecraft.world.inventory.ClickType clickType,
|
||||||
|
Player player) {
|
||||||
|
if (!stillValid(player)) {
|
||||||
|
player.closeContainer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
refreshOutput();
|
||||||
|
super.clicked(slotId, button, clickType, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class CopySlot extends Slot {
|
||||||
|
private CopySlot(Container container, int index, int x, int y) { super(container, index, x, y); }
|
||||||
|
@Override public boolean mayPlace(ItemStack stack) { return false; }
|
||||||
|
@Override public boolean mayPickup(Player player) {
|
||||||
|
return clientSide ? hasItem() : WritingDeskMenu.this.stillValid(player) && canCopy();
|
||||||
|
}
|
||||||
|
@Override public void onTake(Player player, ItemStack stack) {
|
||||||
|
if (!clientSide && blockEntity.consumeCopyMaterials()) refreshOutput();
|
||||||
|
super.onTake(player, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class DeskSlot extends Slot {
|
private static final class DeskSlot extends Slot {
|
||||||
private final int limit;
|
private final int limit;
|
||||||
private DeskSlot(Container container, int index, int x, int y, int limit) {
|
private DeskSlot(Container container, int index, int x, int y, int limit) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"item.rivencraft.ink_vial": "Ink Vial",
|
"item.rivencraft.ink_vial": "Ink Vial",
|
||||||
"block.rivencraft.writing_desk": "Writing Desk",
|
"block.rivencraft.writing_desk": "Writing Desk",
|
||||||
"container.rivencraft.writing_desk": "Writing Desk",
|
"container.rivencraft.writing_desk": "Writing Desk",
|
||||||
|
"screen.rivencraft.writing_desk.ink": "Ink: %s/%s",
|
||||||
"item.rivencraft.ink_vial.remaining": "Ink: %s/%s",
|
"item.rivencraft.ink_vial.remaining": "Ink: %s/%s",
|
||||||
"item.rivencraft.ink_vial.usage": "Use with a blank Page in the other hand to prepare a Link Panel.",
|
"item.rivencraft.ink_vial.usage": "Use with a blank Page in the other hand to prepare a Link Panel.",
|
||||||
"screen.rivencraft.notebook.search": "Search pages",
|
"screen.rivencraft.notebook.search": "Search pages",
|
||||||
|
|||||||
Reference in new issue
Block a user