mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-24 16:07:01 +00:00
Allow placing printouts in lecterns
- Add a new custom lectern block, that is used to hold the printed pages. We have to roll quite a lot of custom logic, so this is much cleaner than trying to mixin to the existing lectern code. - Add a new (entity) model for printed pages and books placed on a lectern. I did originally think about just rendering the item (or the in-hand/map version), but I think this is a bit more consistent with vanilla. However, we do still need to sync the item to the client (mostly to get the current page count!). There is a risk of chunkbanning here, but I think it's much harder than vanilla, due to the significantly reduced page limit.
This commit is contained in:
parent
7e53c19d74
commit
aa8078ddeb
@ -13,6 +13,7 @@ import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller;
|
|||||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||||
import dan200.computercraft.client.gui.*;
|
import dan200.computercraft.client.gui.*;
|
||||||
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||||
|
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||||
import dan200.computercraft.client.render.RenderTypes;
|
import dan200.computercraft.client.render.RenderTypes;
|
||||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
||||||
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
|
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
|
||||||
@ -73,6 +74,7 @@ public final class ClientRegistry {
|
|||||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
|
||||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
|
||||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TurtleBlockEntityRenderer::new);
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TurtleBlockEntityRenderer::new);
|
||||||
|
BlockEntityRenderers.register(ModRegistry.BlockEntities.LECTERN.get(), CustomLecternRenderer::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.client.model;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||||
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
|
import net.minecraft.client.model.geom.ModelPart;
|
||||||
|
import net.minecraft.client.model.geom.PartPose;
|
||||||
|
import net.minecraft.client.model.geom.builders.CubeListBuilder;
|
||||||
|
import net.minecraft.client.model.geom.builders.MeshDefinition;
|
||||||
|
import net.minecraft.client.resources.model.Material;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.inventory.InventoryMenu;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A model for {@linkplain PrintoutItem printouts} placed on a lectern.
|
||||||
|
* <p>
|
||||||
|
* This provides two models, {@linkplain #renderPages(PoseStack, VertexConsumer, int, int, int) one for a variable
|
||||||
|
* number of pages}, and {@linkplain #renderBook(PoseStack, VertexConsumer, int, int) one for books}.
|
||||||
|
*
|
||||||
|
* @see CustomLecternRenderer
|
||||||
|
*/
|
||||||
|
public class LecternPrintoutModel {
|
||||||
|
public static final ResourceLocation TEXTURE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "entity/printout");
|
||||||
|
public static final Material MATERIAL = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE);
|
||||||
|
|
||||||
|
private static final int TEXTURE_WIDTH = 32;
|
||||||
|
private static final int TEXTURE_HEIGHT = 32;
|
||||||
|
|
||||||
|
private static final String PAGE_1 = "page_1";
|
||||||
|
private static final String PAGE_2 = "page_2";
|
||||||
|
private static final String PAGE_3 = "page_3";
|
||||||
|
private static final List<String> PAGES = List.of(PAGE_1, PAGE_2, PAGE_3);
|
||||||
|
|
||||||
|
private final ModelPart pagesRoot;
|
||||||
|
private final ModelPart bookRoot;
|
||||||
|
private final ModelPart[] pages;
|
||||||
|
|
||||||
|
public LecternPrintoutModel() {
|
||||||
|
pagesRoot = buildPages();
|
||||||
|
bookRoot = buildBook();
|
||||||
|
pages = PAGES.stream().map(pagesRoot::getChild).toArray(ModelPart[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModelPart buildPages() {
|
||||||
|
var mesh = new MeshDefinition();
|
||||||
|
var parts = mesh.getRoot();
|
||||||
|
parts.addOrReplaceChild(
|
||||||
|
PAGE_1,
|
||||||
|
CubeListBuilder.create().texOffs(0, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
|
||||||
|
PartPose.ZERO
|
||||||
|
);
|
||||||
|
|
||||||
|
parts.addOrReplaceChild(
|
||||||
|
PAGE_2,
|
||||||
|
CubeListBuilder.create().texOffs(12, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
|
||||||
|
PartPose.offsetAndRotation(-0.125f, 0, 1.5f, (float) Math.PI * (1f / 16), 0, 0)
|
||||||
|
);
|
||||||
|
parts.addOrReplaceChild(
|
||||||
|
PAGE_3,
|
||||||
|
CubeListBuilder.create().texOffs(12, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
|
||||||
|
PartPose.offsetAndRotation(-0.25f, 0, -1.5f, (float) -Math.PI * (2f / 16), 0, 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModelPart buildBook() {
|
||||||
|
var mesh = new MeshDefinition();
|
||||||
|
var parts = mesh.getRoot();
|
||||||
|
|
||||||
|
parts.addOrReplaceChild(
|
||||||
|
"spine",
|
||||||
|
CubeListBuilder.create().texOffs(12, 15).addBox(-0.005f, -5.0f, -0.5f, 0, 10, 1.0f),
|
||||||
|
PartPose.ZERO
|
||||||
|
);
|
||||||
|
|
||||||
|
var angle = (float) Math.toRadians(5);
|
||||||
|
parts.addOrReplaceChild(
|
||||||
|
"left",
|
||||||
|
CubeListBuilder.create()
|
||||||
|
.texOffs(0, 10).addBox(0, -5.0f, -6.0f, 0, 10, 6.0f)
|
||||||
|
.texOffs(0, 0).addBox(0.005f, -4.0f, -5.0f, 1.0f, 8.0f, 5.0f),
|
||||||
|
PartPose.offsetAndRotation(-0.005f, 0, -0.5f, 0, -angle, 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
parts.addOrReplaceChild(
|
||||||
|
"right",
|
||||||
|
CubeListBuilder.create()
|
||||||
|
.texOffs(14, 10).addBox(0, -5.0f, 0, 0, 10, 6.0f)
|
||||||
|
.texOffs(0, 0).addBox(0.005f, -4.0f, 0, 1.0f, 8.0f, 5.0f),
|
||||||
|
PartPose.offsetAndRotation(-0.005f, 0, 0.5f, 0, angle, 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderBook(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay) {
|
||||||
|
bookRoot.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPages(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, int pageCount) {
|
||||||
|
if (pageCount > pages.length) pageCount = pages.length;
|
||||||
|
var i = 0;
|
||||||
|
for (; i < pageCount; i++) pages[i].visible = true;
|
||||||
|
for (; i < pages.length; i++) pages[i].visible = false;
|
||||||
|
|
||||||
|
pagesRoot.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.client.render;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.math.Axis;
|
||||||
|
import dan200.computercraft.client.model.LecternPrintoutModel;
|
||||||
|
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
|
||||||
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||||
|
import net.minecraft.client.renderer.blockentity.LecternRenderer;
|
||||||
|
import net.minecraft.world.level.block.LecternBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A block entity renderer for our {@linkplain CustomLecternBlockEntity lectern}.
|
||||||
|
* <p>
|
||||||
|
* This largely follows {@link LecternRenderer}, but with support for multiple types of item.
|
||||||
|
*/
|
||||||
|
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> {
|
||||||
|
private final LecternPrintoutModel printoutModel;
|
||||||
|
|
||||||
|
public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
|
||||||
|
printoutModel = new LecternPrintoutModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(CustomLecternBlockEntity lectern, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) {
|
||||||
|
poseStack.pushPose();
|
||||||
|
poseStack.translate(0.5f, 1.0625f, 0.5f);
|
||||||
|
poseStack.mulPose(Axis.YP.rotationDegrees(-lectern.getBlockState().getValue(LecternBlock.FACING).getClockWise().toYRot()));
|
||||||
|
poseStack.mulPose(Axis.ZP.rotationDegrees(67.5f));
|
||||||
|
poseStack.translate(0, -0.125f, 0);
|
||||||
|
|
||||||
|
var item = lectern.getItem();
|
||||||
|
if (item.getItem() instanceof PrintoutItem printout) {
|
||||||
|
var vertexConsumer = LecternPrintoutModel.MATERIAL.buffer(buffer, RenderType::entitySolid);
|
||||||
|
if (printout.getType() == PrintoutItem.Type.BOOK) {
|
||||||
|
printoutModel.renderBook(poseStack, vertexConsumer, packedLight, packedOverlay);
|
||||||
|
} else {
|
||||||
|
printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutItem.getPageCount(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
package dan200.computercraft.data.client;
|
package dan200.computercraft.data.client;
|
||||||
|
|
||||||
import dan200.computercraft.client.gui.GuiSprites;
|
import dan200.computercraft.client.gui.GuiSprites;
|
||||||
|
import dan200.computercraft.client.model.LecternPrintoutModel;
|
||||||
import dan200.computercraft.data.DataProviders;
|
import dan200.computercraft.data.DataProviders;
|
||||||
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
|
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
|
||||||
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
|
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
|
||||||
@ -30,7 +31,8 @@ public final class ClientDataProviders {
|
|||||||
generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> {
|
generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> {
|
||||||
out.accept(new ResourceLocation("blocks"), List.of(
|
out.accept(new ResourceLocation("blocks"), List.of(
|
||||||
new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()),
|
new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()),
|
||||||
new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty())
|
new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty()),
|
||||||
|
new SingleFile(LecternPrintoutModel.TEXTURE, Optional.empty())
|
||||||
));
|
));
|
||||||
out.accept(GuiSprites.SPRITE_SHEET, Stream.of(
|
out.accept(GuiSprites.SPRITE_SHEET, Stream.of(
|
||||||
// Buttons
|
// Buttons
|
||||||
|
8
projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
generated
Normal file
8
projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"facing=east": {"model": "minecraft:block/lectern", "y": 90},
|
||||||
|
"facing=north": {"model": "minecraft:block/lectern", "y": 0},
|
||||||
|
"facing=south": {"model": "minecraft:block/lectern", "y": 180},
|
||||||
|
"facing=west": {"model": "minecraft:block/lectern", "y": 270}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"sources": [
|
"sources": [
|
||||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"},
|
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"},
|
||||||
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"}
|
{"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"},
|
||||||
|
{"type": "minecraft:single", "resource": "computercraft:entity/printout"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
12
projects/common/src/generated/resources/data/computercraft/loot_tables/blocks/lectern.json
generated
Normal file
12
projects/common/src/generated/resources/data/computercraft/loot_tables/blocks/lectern.json
generated
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"type": "minecraft:block",
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"bonus_rolls": 0.0,
|
||||||
|
"conditions": [{"condition": "minecraft:survives_explosion"}],
|
||||||
|
"entries": [{"type": "minecraft:item", "name": "minecraft:lectern"}],
|
||||||
|
"rolls": 1.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"random_sequence": "computercraft:blocks/lectern"
|
||||||
|
}
|
@ -23,6 +23,7 @@ import net.minecraft.data.models.BlockModelGenerators;
|
|||||||
import net.minecraft.data.models.blockstates.*;
|
import net.minecraft.data.models.blockstates.*;
|
||||||
import net.minecraft.data.models.model.*;
|
import net.minecraft.data.models.model.*;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||||
import net.minecraft.world.level.block.state.properties.Property;
|
import net.minecraft.world.level.block.state.properties.Property;
|
||||||
@ -100,6 +101,11 @@ class BlockModelProvider {
|
|||||||
registerTurtleUpgrade(generators, "block/turtle_speaker", "block/turtle_speaker_face");
|
registerTurtleUpgrade(generators, "block/turtle_speaker", "block/turtle_speaker_face");
|
||||||
registerTurtleModem(generators, "block/turtle_modem_normal", "block/wireless_modem_normal_face");
|
registerTurtleModem(generators, "block/turtle_modem_normal", "block/wireless_modem_normal_face");
|
||||||
registerTurtleModem(generators, "block/turtle_modem_advanced", "block/wireless_modem_advanced_face");
|
registerTurtleModem(generators, "block/turtle_modem_advanced", "block/wireless_modem_advanced_face");
|
||||||
|
|
||||||
|
generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(
|
||||||
|
ModRegistry.Blocks.LECTERN.get(),
|
||||||
|
Variant.variant().with(VariantProperties.MODEL, ModelLocationUtils.getModelLocation(Blocks.LECTERN))
|
||||||
|
).with(createHorizontalFacingDispatch()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerDiskDrive(BlockModelGenerators generators) {
|
private static void registerDiskDrive(BlockModelGenerators generators) {
|
||||||
|
@ -284,7 +284,9 @@ public final class LanguageProvider implements DataProvider {
|
|||||||
return Stream.of(
|
return Stream.of(
|
||||||
RegistryWrappers.BLOCKS.stream()
|
RegistryWrappers.BLOCKS.stream()
|
||||||
.filter(x -> RegistryWrappers.BLOCKS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
.filter(x -> RegistryWrappers.BLOCKS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||||
.map(Block::getDescriptionId),
|
.map(Block::getDescriptionId)
|
||||||
|
// Exclude blocks that just reuse vanilla translations, such as the lectern.
|
||||||
|
.filter(x -> !x.startsWith("block.minecraft.")),
|
||||||
RegistryWrappers.ITEMS.stream()
|
RegistryWrappers.ITEMS.stream()
|
||||||
.filter(x -> RegistryWrappers.ITEMS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
.filter(x -> RegistryWrappers.ITEMS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
|
||||||
.map(Item::getDescriptionId),
|
.map(Item::getDescriptionId),
|
||||||
|
@ -15,6 +15,7 @@ import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
|
|||||||
import net.minecraft.advancements.critereon.StatePropertiesPredicate;
|
import net.minecraft.advancements.critereon.StatePropertiesPredicate;
|
||||||
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.storage.loot.LootPool;
|
import net.minecraft.world.level.storage.loot.LootPool;
|
||||||
import net.minecraft.world.level.storage.loot.LootTable;
|
import net.minecraft.world.level.storage.loot.LootTable;
|
||||||
@ -57,6 +58,8 @@ class LootTableProvider {
|
|||||||
computerDrop(add, ModRegistry.Blocks.TURTLE_NORMAL);
|
computerDrop(add, ModRegistry.Blocks.TURTLE_NORMAL);
|
||||||
computerDrop(add, ModRegistry.Blocks.TURTLE_ADVANCED);
|
computerDrop(add, ModRegistry.Blocks.TURTLE_ADVANCED);
|
||||||
|
|
||||||
|
blockDrop(add, ModRegistry.Blocks.LECTERN, LootItem.lootTableItem(Items.LECTERN), ExplosionCondition.survivesExplosion());
|
||||||
|
|
||||||
add.accept(ModRegistry.Blocks.CABLE.get().getLootTable(), LootTable
|
add.accept(ModRegistry.Blocks.CABLE.get().getLootTable(), LootTable
|
||||||
.lootTable()
|
.lootTable()
|
||||||
.withPool(LootPool.lootPool()
|
.withPool(LootPool.lootPool()
|
||||||
|
@ -102,8 +102,14 @@ class TagProvider {
|
|||||||
ModRegistry.Items.MONITOR_ADVANCED.get()
|
ModRegistry.Items.MONITOR_ADVANCED.get()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Allow printed books to be placed in bookshelves.
|
||||||
tags.tag(ItemTags.BOOKSHELF_BOOKS).add(ModRegistry.Items.PRINTED_BOOK.get());
|
tags.tag(ItemTags.BOOKSHELF_BOOKS).add(ModRegistry.Items.PRINTED_BOOK.get());
|
||||||
|
|
||||||
|
// Allow any printout to be placed on lecterns. See also PrintoutItem and CustomLecternBlock.
|
||||||
|
tags.tag(ItemTags.LECTERN_BOOKS).add(
|
||||||
|
ModRegistry.Items.PRINTED_PAGE.get(), ModRegistry.Items.PRINTED_PAGES.get(), ModRegistry.Items.PRINTED_BOOK.get()
|
||||||
|
);
|
||||||
|
|
||||||
tags.tag(ComputerCraftTags.Items.TURTLE_CAN_PLACE)
|
tags.tag(ComputerCraftTags.Items.TURTLE_CAN_PLACE)
|
||||||
.add(Items.GLASS_BOTTLE)
|
.add(Items.GLASS_BOTTLE)
|
||||||
.addTag(ItemTags.BOATS);
|
.addTag(ItemTags.BOATS);
|
||||||
|
@ -40,6 +40,8 @@ import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
|
|||||||
import dan200.computercraft.shared.details.BlockDetails;
|
import dan200.computercraft.shared.details.BlockDetails;
|
||||||
import dan200.computercraft.shared.details.ItemDetails;
|
import dan200.computercraft.shared.details.ItemDetails;
|
||||||
import dan200.computercraft.shared.integration.PermissionRegistry;
|
import dan200.computercraft.shared.integration.PermissionRegistry;
|
||||||
|
import dan200.computercraft.shared.lectern.CustomLecternBlock;
|
||||||
|
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
|
||||||
import dan200.computercraft.shared.media.PrintoutMenu;
|
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
import dan200.computercraft.shared.media.items.DiskItem;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
@ -100,10 +102,12 @@ import net.minecraft.world.item.crafting.CustomRecipe;
|
|||||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.SoundType;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||||
import net.minecraft.world.level.material.MapColor;
|
import net.minecraft.world.level.material.MapColor;
|
||||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
||||||
|
|
||||||
@ -171,6 +175,10 @@ public final class ModRegistry {
|
|||||||
public static final RegistryEntry<WiredModemFullBlock> WIRED_MODEM_FULL = REGISTRY.register("wired_modem_full",
|
public static final RegistryEntry<WiredModemFullBlock> WIRED_MODEM_FULL = REGISTRY.register("wired_modem_full",
|
||||||
() -> new WiredModemFullBlock(modemProperties().mapColor(MapColor.STONE)));
|
() -> new WiredModemFullBlock(modemProperties().mapColor(MapColor.STONE)));
|
||||||
public static final RegistryEntry<CableBlock> CABLE = REGISTRY.register("cable", () -> new CableBlock(modemProperties().mapColor(MapColor.STONE)));
|
public static final RegistryEntry<CableBlock> CABLE = REGISTRY.register("cable", () -> new CableBlock(modemProperties().mapColor(MapColor.STONE)));
|
||||||
|
|
||||||
|
public static final RegistryEntry<CustomLecternBlock> LECTERN = REGISTRY.register("lectern", () -> new CustomLecternBlock(
|
||||||
|
BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.5F).sound(SoundType.WOOD).ignitedByLava()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BlockEntities {
|
public static class BlockEntities {
|
||||||
@ -212,6 +220,8 @@ public final class ModRegistry {
|
|||||||
ofBlock(Blocks.WIRELESS_MODEM_NORMAL, (p, s) -> new WirelessModemBlockEntity(BlockEntities.WIRELESS_MODEM_NORMAL.get(), p, s, false));
|
ofBlock(Blocks.WIRELESS_MODEM_NORMAL, (p, s) -> new WirelessModemBlockEntity(BlockEntities.WIRELESS_MODEM_NORMAL.get(), p, s, false));
|
||||||
public static final RegistryEntry<BlockEntityType<WirelessModemBlockEntity>> WIRELESS_MODEM_ADVANCED =
|
public static final RegistryEntry<BlockEntityType<WirelessModemBlockEntity>> WIRELESS_MODEM_ADVANCED =
|
||||||
ofBlock(Blocks.WIRELESS_MODEM_ADVANCED, (p, s) -> new WirelessModemBlockEntity(BlockEntities.WIRELESS_MODEM_ADVANCED.get(), p, s, true));
|
ofBlock(Blocks.WIRELESS_MODEM_ADVANCED, (p, s) -> new WirelessModemBlockEntity(BlockEntities.WIRELESS_MODEM_ADVANCED.get(), p, s, true));
|
||||||
|
|
||||||
|
public static final RegistryEntry<BlockEntityType<CustomLecternBlockEntity>> LECTERN = ofBlock(Blocks.LECTERN, CustomLecternBlockEntity::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Items {
|
public static final class Items {
|
||||||
|
@ -4,16 +4,17 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.container;
|
package dan200.computercraft.shared.container;
|
||||||
|
|
||||||
import net.minecraft.core.NonNullList;
|
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
import net.minecraft.world.ContainerHelper;
|
import net.minecraft.world.ContainerHelper;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic implementation of {@link Container} which operates on a {@linkplain #getContents() list of stacks}.
|
* A basic implementation of {@link Container} which operates on a {@linkplain #getContents() list of stacks}.
|
||||||
*/
|
*/
|
||||||
public interface BasicContainer extends Container {
|
public interface BasicContainer extends Container {
|
||||||
NonNullList<ItemStack> getContents();
|
List<ItemStack> getContents();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default int getContainerSize() {
|
default int getContainerSize() {
|
||||||
|
@ -0,0 +1,142 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.lectern;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.stats.Stats;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.item.context.UseOnContext;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.LecternBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends {@link LecternBlock} with support for {@linkplain PrintoutItem printouts}.
|
||||||
|
* <p>
|
||||||
|
* Unlike the vanilla lectern, this block is never empty. If the book is removed from the lectern, it converts back to
|
||||||
|
* its vanilla version (see {@link #clearLectern(Level, BlockPos, BlockState)}).
|
||||||
|
*
|
||||||
|
* @see PrintoutItem#useOn(UseOnContext) Placing books into a lectern.
|
||||||
|
*/
|
||||||
|
public class CustomLecternBlock extends LecternBlock {
|
||||||
|
public CustomLecternBlock(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
registerDefaultState(defaultBlockState().setValue(HAS_BOOK, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace a vanilla lectern with a custom one.
|
||||||
|
*
|
||||||
|
* @param level The current level.
|
||||||
|
* @param pos The position of the lectern.
|
||||||
|
* @param blockState The current state of the lectern.
|
||||||
|
* @param item The item to place in the custom lectern.
|
||||||
|
*/
|
||||||
|
public static void replaceLectern(Level level, BlockPos pos, BlockState blockState, ItemStack item) {
|
||||||
|
level.setBlockAndUpdate(pos, ModRegistry.Blocks.LECTERN.get().defaultBlockState()
|
||||||
|
.setValue(HAS_BOOK, true)
|
||||||
|
.setValue(FACING, blockState.getValue(FACING))
|
||||||
|
.setValue(POWERED, blockState.getValue(POWERED)));
|
||||||
|
|
||||||
|
if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity be) be.setItem(item.split(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a custom lectern and replace it with an empty vanilla one.
|
||||||
|
*
|
||||||
|
* @param level The current level.
|
||||||
|
* @param pos The position of the lectern.
|
||||||
|
* @param blockState The current state of the lectern.
|
||||||
|
*/
|
||||||
|
static void clearLectern(Level level, BlockPos pos, BlockState blockState) {
|
||||||
|
level.setBlockAndUpdate(pos, Blocks.LECTERN.defaultBlockState()
|
||||||
|
.setValue(HAS_BOOK, false)
|
||||||
|
.setValue(FACING, blockState.getValue(FACING))
|
||||||
|
.setValue(POWERED, blockState.getValue(POWERED)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public ItemStack getCloneItemStack(BlockGetter level, BlockPos pos, BlockState state) {
|
||||||
|
return new ItemStack(Items.LECTERN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||||
|
// If we've no lectern, remove it.
|
||||||
|
if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern && lectern.getItem().isEmpty()) {
|
||||||
|
clearLectern(level, pos, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.tick(state, level, pos, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
|
||||||
|
if (state.is(newState.getBlock())) return;
|
||||||
|
|
||||||
|
if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern) {
|
||||||
|
dropItem(level, pos, state, lectern.getItem().copy());
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onRemove(state, level, pos, newState, isMoving);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dropItem(Level level, BlockPos pos, BlockState state, ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return;
|
||||||
|
|
||||||
|
var direction = state.getValue(FACING);
|
||||||
|
var dx = 0.25 * direction.getStepX();
|
||||||
|
var dz = 0.25 * direction.getStepZ();
|
||||||
|
var entity = new ItemEntity(level, pos.getX() + 0.5 + dx, pos.getY() + 1, pos.getZ() + 0.5 + dz, stack);
|
||||||
|
entity.setDefaultPickUpDelay();
|
||||||
|
level.addFreshEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescriptionId() {
|
||||||
|
return Blocks.LECTERN.getDescriptionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomLecternBlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
return new CustomLecternBlockEntity(pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) {
|
||||||
|
return level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern ? lectern.getRedstoneSignal() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||||
|
if (!level.isClientSide && level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern) {
|
||||||
|
if (player.isSecondaryUseActive()) {
|
||||||
|
// When shift+clicked with an empty hand, drop the item and replace with the normal lectern.
|
||||||
|
clearLectern(level, pos, state);
|
||||||
|
} else {
|
||||||
|
// Otherwise open the screen.
|
||||||
|
player.openMenu(lectern);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.awardStat(Stats.INTERACT_WITH_LECTERN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.lectern;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.container.BasicContainer;
|
||||||
|
import dan200.computercraft.shared.container.SingleContainerData;
|
||||||
|
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||||
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
|
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.protocol.Packet;
|
||||||
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.Container;
|
||||||
|
import net.minecraft.world.MenuProvider;
|
||||||
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.inventory.ContainerData;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.block.LecternBlock;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The block entity for our {@link CustomLecternBlock}.
|
||||||
|
*
|
||||||
|
* @see LecternBlockEntity
|
||||||
|
*/
|
||||||
|
public final class CustomLecternBlockEntity extends BlockEntity implements MenuProvider {
|
||||||
|
private static final String NBT_ITEM = "Item";
|
||||||
|
private static final String NBT_PAGE = "Page";
|
||||||
|
|
||||||
|
private ItemStack item = ItemStack.EMPTY;
|
||||||
|
private int page, pageCount;
|
||||||
|
|
||||||
|
public CustomLecternBlockEntity(BlockPos pos, BlockState blockState) {
|
||||||
|
super(ModRegistry.BlockEntities.LECTERN.get(), pos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getItem() {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setItem(ItemStack item) {
|
||||||
|
this.item = item;
|
||||||
|
itemChanged();
|
||||||
|
BlockEntityHelpers.updateBlock(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getRedstoneSignal() {
|
||||||
|
if (item.getItem() instanceof PrintoutItem) {
|
||||||
|
var progress = pageCount > 1 ? (float) page / (pageCount - 1) : 1F;
|
||||||
|
return Mth.floor(progress * 14f) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after the item has changed. This sets up the state for the new item.
|
||||||
|
*/
|
||||||
|
private void itemChanged() {
|
||||||
|
if (item.getItem() instanceof PrintoutItem) {
|
||||||
|
pageCount = PrintoutItem.getPageCount(item);
|
||||||
|
page = Mth.clamp(page, 0, pageCount - 1);
|
||||||
|
} else {
|
||||||
|
pageCount = page = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current page, emitting a redstone pulse if needed.
|
||||||
|
*
|
||||||
|
* @param page The new page.
|
||||||
|
*/
|
||||||
|
private void setPage(int page) {
|
||||||
|
if (this.page == page) return;
|
||||||
|
|
||||||
|
this.page = page;
|
||||||
|
setChanged();
|
||||||
|
if (getLevel() != null) LecternBlock.signalPageChange(getLevel(), getBlockPos(), getBlockState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load(CompoundTag tag) {
|
||||||
|
super.load(tag);
|
||||||
|
|
||||||
|
item = tag.contains(NBT_ITEM, Tag.TAG_COMPOUND) ? ItemStack.of(tag.getCompound(NBT_ITEM)) : ItemStack.EMPTY;
|
||||||
|
page = tag.getInt(NBT_PAGE);
|
||||||
|
itemChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void saveAdditional(CompoundTag tag) {
|
||||||
|
super.saveAdditional(tag);
|
||||||
|
|
||||||
|
if (!item.isEmpty()) tag.put(NBT_ITEM, item.save(new CompoundTag()));
|
||||||
|
if (item.getItem() instanceof PrintoutItem) tag.putInt(NBT_PAGE, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
||||||
|
return ClientboundBlockEntityDataPacket.create(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag getUpdateTag() {
|
||||||
|
var tag = super.getUpdateTag();
|
||||||
|
tag.put(NBT_ITEM, item.save(new CompoundTag()));
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) {
|
||||||
|
var item = getItem();
|
||||||
|
if (item.getItem() instanceof PrintoutItem) {
|
||||||
|
return new PrintoutMenu(
|
||||||
|
containerId, new LecternContainer(), 0,
|
||||||
|
p -> Container.stillValidBlockEntity(this, player, Container.DEFAULT_DISTANCE_LIMIT),
|
||||||
|
new PrintoutContainerData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getDisplayName() {
|
||||||
|
return getItem().getDisplayName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A read-only container storing the lectern's contents.
|
||||||
|
*/
|
||||||
|
private final class LecternContainer implements BasicContainer {
|
||||||
|
private final List<ItemStack> itemView = new AbstractList<>() {
|
||||||
|
@Override
|
||||||
|
public ItemStack get(int index) {
|
||||||
|
if (index != 0) throw new IndexOutOfBoundsException("Inventory only has one slot");
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ItemStack> getContents() {
|
||||||
|
return itemView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChanged() {
|
||||||
|
// Should never happen, so a no-op.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stillValid(Player player) {
|
||||||
|
return !isRemoved();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ContainerData} for a {@link PrintoutMenu}. This provides a read/write view of the current page.
|
||||||
|
*/
|
||||||
|
private final class PrintoutContainerData implements SingleContainerData {
|
||||||
|
@Override
|
||||||
|
public int get() {
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int index, int value) {
|
||||||
|
if (index == 0) setPage(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ package dan200.computercraft.shared.media.items;
|
|||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.lectern.CustomLecternBlock;
|
||||||
import dan200.computercraft.shared.media.PrintoutMenu;
|
import dan200.computercraft.shared.media.PrintoutMenu;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
@ -16,7 +17,10 @@ import net.minecraft.world.entity.player.Player;
|
|||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.TooltipFlag;
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
import net.minecraft.world.item.context.UseOnContext;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.LecternBlock;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -50,6 +54,22 @@ public class PrintoutItem extends Item {
|
|||||||
if (title != null && !title.isEmpty()) list.add(Component.literal(title));
|
if (title != null && !title.isEmpty()) list.add(Component.literal(title));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionResult useOn(UseOnContext context) {
|
||||||
|
var level = context.getLevel();
|
||||||
|
var blockPos = context.getClickedPos();
|
||||||
|
var blockState = level.getBlockState(blockPos);
|
||||||
|
if (blockState.is(Blocks.LECTERN) && !blockState.getValue(LecternBlock.HAS_BOOK)) {
|
||||||
|
// If we have an empty lectern, place our book into it.
|
||||||
|
if (!level.isClientSide) {
|
||||||
|
CustomLecternBlock.replaceLectern(level, blockPos, blockState, context.getItemInHand());
|
||||||
|
}
|
||||||
|
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||||
|
} else {
|
||||||
|
return InteractionResult.PASS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
||||||
var stack = player.getItemInHand(hand);
|
var stack = player.getItemInHand(hand);
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 212 B |
@ -0,0 +1,3 @@
|
|||||||
|
SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
4
projects/fabric/src/generated/resources/data/minecraft/tags/items/lectern_books.json
generated
Normal file
4
projects/fabric/src/generated/resources/data/minecraft/tags/items/lectern_books.json
generated
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"replace": false,
|
||||||
|
"values": ["computercraft:printed_page", "computercraft:printed_pages", "computercraft:printed_book"]
|
||||||
|
}
|
1
projects/forge/src/generated/resources/data/minecraft/tags/items/lectern_books.json
generated
Normal file
1
projects/forge/src/generated/resources/data/minecraft/tags/items/lectern_books.json
generated
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"values": ["computercraft:printed_page", "computercraft:printed_pages", "computercraft:printed_book"]}
|
Loading…
Reference in New Issue
Block a user