From 79a617730ca17ef2137ba2d43f13bb7ea0145271 Mon Sep 17 00:00:00 2001 From: burin Date: Tue, 7 Jul 2026 13:07:28 +0800 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E9=99=84=E9=AD=94=E5=8F=B0=E6=96=B9=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加自动附魔台方块 - 添加自动附魔台方块实体 - 添加自动附魔台方块实体渲染器 - 实现渲染书本模型 - 实现流体储存 - 实现物品储存 - 实现流体渲染 --- dependencies.gradle | 3 + .../items/auto_enchanting_table.json | 6 + .../assets/anvilcraft/lang/en_ud.json | 2 + .../assets/anvilcraft/lang/en_us.json | 2 + .../blocks/auto_enchanting_table.json | 21 ++ .../tags/block/mineable/pickaxe.json | 1 + .../AutoEnchantingTableBlockEntity.java | 277 ++++++++++++++++++ .../workstation/AutoEnchantingTableBlock.java | 78 +++++ ...utoEnchantingTableBlockEntityRenderer.java | 199 +++++++++++++ .../BaseFluidHandlerHolderRenderer.java | 2 +- ...EnchantingTableBlockEntityRenderState.java | 20 ++ .../client/support/FluidRenderHelper.java | 53 ++++ .../anvilcraft/init/ModCapabilities.java | 12 + .../init/block/ModBlockEntities.java | 8 + .../anvilcraft/init/block/ModBlocks.java | 10 + .../dubhe/anvilcraft/util/MutableValue.java | 18 ++ .../blockstates/auto_enchanting_table.json | 7 + 17 files changed, 718 insertions(+), 1 deletion(-) create mode 100644 src/generated/resources/assets/anvilcraft/items/auto_enchanting_table.json create mode 100644 src/generated/resources/data/anvilcraft/loot_table/blocks/auto_enchanting_table.json create mode 100644 src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/AutoEnchantingTableBlockEntityRenderer.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/state/AutoEnchantingTableBlockEntityRenderState.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/util/MutableValue.java create mode 100644 src/main/resources/assets/anvilcraft/blockstates/auto_enchanting_table.json diff --git a/dependencies.gradle b/dependencies.gradle index 3638fc1767..2981f65c60 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -26,4 +26,7 @@ dependencies { // Jade compileClasspath(libs.jade) runtimeClasspath(libs.jade) + + compileOnly("maven.modrinth:pipez:neoforge-1.2.30+26.1.2") + runtimeOnly("maven.modrinth:pipez:neoforge-1.2.30+26.1.2") } diff --git a/src/generated/resources/assets/anvilcraft/items/auto_enchanting_table.json b/src/generated/resources/assets/anvilcraft/items/auto_enchanting_table.json new file mode 100644 index 0000000000..cba7d64307 --- /dev/null +++ b/src/generated/resources/assets/anvilcraft/items/auto_enchanting_table.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "anvilcraft:block/auto_enchanting_table" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index ad165e1fc0..6b555a56ac 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -294,6 +294,7 @@ "block.anvilcraft.advanced_comparator": "ɹoʇɐɹɐdɯoƆ pǝɔuɐʌpⱯ", "block.anvilcraft.amber_block": "ɹǝqɯⱯ ɟo ʞɔoꞁᗺ", "block.anvilcraft.arrow": "ʍoɹɹⱯ", + "block.anvilcraft.auto_enchanting_table": "ǝꞁqɐ⟘ ᵷuᴉʇuɐɥɔuƎ oʇnⱯ", "block.anvilcraft.batch_crafter": "ɹǝʇɟɐɹƆ ɥɔʇɐᗺ", "block.anvilcraft.batch_cutter": "ɹǝʇʇnƆ ɥɔʇɐᗺ", "block.anvilcraft.berry_cake_block": "ʞɔoꞁᗺ ǝʞɐƆ ʎɹɹǝᗺ", @@ -865,6 +866,7 @@ "item.anvilcraft.anvil_hammer": "ɹǝɯɯɐH ꞁᴉʌuⱯ", "item.anvilcraft.anvil_railgun": "unᵷꞁᴉɐᴚ ꞁᴉʌuⱯ", "item.anvilcraft.arrow": "ʍoɹɹⱯ", + "item.anvilcraft.auto_enchanting_table": "ǝꞁqɐ⟘ ᵷuᴉʇuɐɥɔuƎ oʇnⱯ", "item.anvilcraft.batch_crafter": "ɹǝʇɟɐɹƆ ɥɔʇɐᗺ", "item.anvilcraft.batch_cutter": "ɹǝʇʇnƆ ɥɔʇɐᗺ", "item.anvilcraft.beef_mushroom_stew": "ʍǝʇS ɯooɹɥsnW ɟǝǝᗺ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index dc10f26617..840f34133a 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -294,6 +294,7 @@ "block.anvilcraft.advanced_comparator": "Advanced Comparator", "block.anvilcraft.amber_block": "Block of Amber", "block.anvilcraft.arrow": "Arrow", + "block.anvilcraft.auto_enchanting_table": "Auto Enchanting Table", "block.anvilcraft.batch_crafter": "Batch Crafter", "block.anvilcraft.batch_cutter": "Batch Cutter", "block.anvilcraft.berry_cake_block": "Berry Cake Block", @@ -865,6 +866,7 @@ "item.anvilcraft.anvil_hammer": "Anvil Hammer", "item.anvilcraft.anvil_railgun": "Anvil Railgun", "item.anvilcraft.arrow": "Arrow", + "item.anvilcraft.auto_enchanting_table": "Auto Enchanting Table", "item.anvilcraft.batch_crafter": "Batch Crafter", "item.anvilcraft.batch_cutter": "Batch Cutter", "item.anvilcraft.beef_mushroom_stew": "Beef Mushroom Stew", diff --git a/src/generated/resources/data/anvilcraft/loot_table/blocks/auto_enchanting_table.json b/src/generated/resources/data/anvilcraft/loot_table/blocks/auto_enchanting_table.json new file mode 100644 index 0000000000..6dde90ffb5 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/loot_table/blocks/auto_enchanting_table.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "anvilcraft:auto_enchanting_table" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "anvilcraft:blocks/auto_enchanting_table" +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json b/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json index d62a5f1826..0e5cf0b7b1 100644 --- a/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json +++ b/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json @@ -51,6 +51,7 @@ "anvilcraft:item_collector", "anvilcraft:charger", "anvilcraft:discharger", + "anvilcraft:auto_enchanting_table", "anvilcraft:active_silencer", "anvilcraft:block_placer", "anvilcraft:smart_block_placer", diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java new file mode 100644 index 0000000000..a77022dc6f --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java @@ -0,0 +1,277 @@ +package dev.dubhe.anvilcraft.block.entity; + +import dev.dubhe.anvilcraft.api.power.IPowerConsumer; +import dev.dubhe.anvilcraft.api.power.PowerGrid; +import dev.dubhe.anvilcraft.init.block.ModFluids; +import lombok.Getter; +import lombok.Setter; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.transfer.ResourceHandler; +import net.neoforged.neoforge.transfer.fluid.FluidResource; +import net.neoforged.neoforge.transfer.fluid.FluidStacksResourceHandler; +import net.neoforged.neoforge.transfer.item.ItemResource; +import net.neoforged.neoforge.transfer.transaction.SnapshotJournal; +import net.neoforged.neoforge.transfer.transaction.TransactionContext; +import org.jspecify.annotations.Nullable; + +public class AutoEnchantingTableBlockEntity extends BlockEntity implements IPowerConsumer, ResourceHandler { + @Getter + @Setter + private PowerGrid grid; + private int power = 16; + + @Getter + private ItemStack inputStack = ItemStack.EMPTY; + @Getter + private ItemStack outputStack = ItemStack.EMPTY; + @Getter + @Setter + private ItemStack prologueStack = ItemStack.EMPTY; + + public int time; + public float rot; + public float oldRot; + public float targetRot; + + private final SnapshotJournal snapshotJournal = new AutoEnchantTableJournal(); + + private record AutoEnchantTableSnapshot(ItemStack input, ItemStack output) {} + + private class AutoEnchantTableJournal extends SnapshotJournal { + @Override + protected AutoEnchantTableSnapshot createSnapshot() { + return new AutoEnchantTableSnapshot( + AutoEnchantingTableBlockEntity.this.inputStack.copy(), + AutoEnchantingTableBlockEntity.this.outputStack.copy() + ); + } + + @Override + protected void revertToSnapshot(AutoEnchantTableSnapshot snapshot) { + AutoEnchantingTableBlockEntity.this.inputStack = snapshot.input; + AutoEnchantingTableBlockEntity.this.outputStack = snapshot.output; + } + + @Override + protected void onRootCommit(AutoEnchantTableSnapshot originalState) { + AutoEnchantingTableBlockEntity.this.onChange(); + } + } + + @Getter + private final FluidStacksResourceHandler fluidHandler = new FluidStacksResourceHandler(1, 32_000) { + @Override + public boolean isValid(int index, FluidResource resource) { + return resource.is(ModFluids.EXP_FLUID); + } + + @Override + protected void onContentsChanged(int index, FluidStack previousContents) { + AutoEnchantingTableBlockEntity.this.onChange(); + } + }; + + public AutoEnchantingTableBlockEntity(BlockEntityType type, BlockPos worldPosition, BlockState blockState) { + super(type, worldPosition, blockState); + } + + public static void bookAnimationTick( + final Level level, + final BlockPos worldPosition, + final BlockState state, + final AutoEnchantingTableBlockEntity entity + ) { + entity.oldRot = entity.rot; + entity.targetRot += 0.02F; + + while (entity.rot >= (float) Math.PI) { + entity.rot -= (float) (Math.PI * 2); + } + + while (entity.rot < (float) -Math.PI) { + entity.rot += (float) (Math.PI * 2); + } + + while (entity.targetRot >= (float) Math.PI) { + entity.targetRot -= (float) (Math.PI * 2); + } + + while (entity.targetRot < (float) -Math.PI) { + entity.targetRot += (float) (Math.PI * 2); + } + + float rotDir = entity.targetRot - entity.rot; + + while (rotDir >= (float) Math.PI) { + rotDir -= (float) (Math.PI * 2); + } + + while (rotDir < (float) -Math.PI) { + rotDir += (float) (Math.PI * 2); + } + + entity.rot += rotDir * 0.4F; + entity.time++; + } + + public static void serverTick( + final Level level, + final BlockPos worldPosition, + final BlockState state, + final AutoEnchantingTableBlockEntity entity + ) { + if (!entity.inputStack.isEmpty() && entity.outputStack.isEmpty()) { + entity.outputStack = entity.inputStack.copy(); + entity.inputStack = ItemStack.EMPTY; + } + } + + @Override + protected void saveAdditional(ValueOutput output) { + super.saveAdditional(output); + output.store("input", ItemStack.OPTIONAL_CODEC, this.inputStack); + output.store("output", ItemStack.OPTIONAL_CODEC, this.outputStack); + output.store("prologue", ItemStack.OPTIONAL_CODEC, this.prologueStack); + this.fluidHandler.serialize(output); + } + + @Override + protected void loadAdditional(ValueInput input) { + super.loadAdditional(input); + input.read("input", ItemStack.OPTIONAL_CODEC).ifPresent((stack) -> this.inputStack = stack); + input.read("output", ItemStack.OPTIONAL_CODEC).ifPresent((stack) -> this.outputStack = stack); + input.read("prologue", ItemStack.OPTIONAL_CODEC).ifPresent((stack) -> this.prologueStack = stack); + this.fluidHandler.deserialize(input); + } + + @Override + public @Nullable Packet getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + @Override + public CompoundTag getUpdateTag(HolderLookup.Provider registries) { + TagValueOutput output = TagValueOutput.createWithContext(new ProblemReporter.Collector(this.problemPath()), registries); + this.saveAdditional(output); + return output.buildResult(); + } + + private void onChange() { + this.setChanged(); + if (this.level != null) { + this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 2); + } + } + + @Override + public @Nullable Level getCurrentLevel() { + return this.level; + } + + @Override + public BlockPos getPos() { + return this.getBlockPos(); + } + + @Override + public int getInputPower() { + return this.power; + } + + @Override + public int size() { + return 2; + } + + @Override + public ItemResource getResource(int index) { + if (index == 0) { + return ItemResource.of(this.inputStack); + } + if (index == 1) { + return ItemResource.of(this.outputStack); + } + return ItemResource.EMPTY; + } + + @Override + public long getAmountAsLong(int index) { + if (index == 0) { + return this.inputStack.isEmpty() ? 0 : this.inputStack.count(); + } + if (index == 1) { + return this.outputStack.isEmpty() ? 0 : this.outputStack.count(); + } + return 0; + } + + @Override + public long getCapacityAsLong(int index, ItemResource resource) { + return 1; + } + + @Override + public boolean isValid(int index, ItemResource resource) { + return true; + } + + @Override + public int insert(int index, ItemResource resource, int amount, TransactionContext transaction) { + if (resource.isEmpty() || amount <= 0) { + return 0; + } + if (index != 0) { + return 0; + } + if (!this.inputStack.isEmpty()) { + return 0; + } + this.snapshotJournal.updateSnapshots(transaction); + this.inputStack = resource.toStack(1); + return 1; + } + + @Override + public int insert(ItemResource resource, int amount, TransactionContext transaction) { + return this.insert(0, resource, amount, transaction); + } + + @Override + public int extract(int index, ItemResource resource, int amount, TransactionContext transaction) { + if (resource.isEmpty() || amount <= 0) { + return 0; + } + if (index != 1) { + return 0; + } + if (this.outputStack.isEmpty()) { + return 0; + } + if (!ItemResource.of(this.outputStack).equals(resource)) { + return 0; + } + this.snapshotJournal.updateSnapshots(transaction); + this.outputStack = ItemStack.EMPTY; + return 1; + } + + @Override + public int extract(ItemResource resource, int amount, TransactionContext transaction) { + return this.extract(1, resource, amount, transaction); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java new file mode 100644 index 0000000000..a03579a3dd --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java @@ -0,0 +1,78 @@ +package dev.dubhe.anvilcraft.block.workstation; + +import com.mojang.serialization.MapCodec; +import dev.dubhe.anvilcraft.block.better.BetterBaseEntityBlock; +import dev.dubhe.anvilcraft.block.entity.AutoEnchantingTableBlockEntity; +import dev.dubhe.anvilcraft.init.block.ModBlockEntities; +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EnchantingTableBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jspecify.annotations.Nullable; + +import java.util.List; + +public class AutoEnchantingTableBlock extends BetterBaseEntityBlock { + private static final VoxelShape SHAPE = Block.column(16.0, 0.0, 12.0); + + public static final List BOOKSHELF_OFFSETS = BlockPos.betweenClosedStream(-2, 0, -2, 2, 1, 2) + .filter(pos -> Math.abs(pos.getX()) == 2 || Math.abs(pos.getZ()) == 2) + .map(BlockPos::immutable) + .toList(); + + @Override + protected MapCodec codec() { + return simpleCodec(AutoEnchantingTableBlock::new); + } + + public AutoEnchantingTableBlock(Properties properties) { + super(properties); + } + + @Override + protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + return SHAPE; + } + + @Override + public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { + super.animateTick(state, level, pos, random); + + for (BlockPos offset : BOOKSHELF_OFFSETS) { + + if (random.nextInt(16) == 0 && EnchantingTableBlock.isValidBookShelf(level, pos, offset)) { + level.addParticle( + ParticleTypes.ENCHANT, + pos.getX() + 0.5, + pos.getY() + 2.0, + pos.getZ() + 0.5, + offset.getX() + random.nextFloat() - 0.5, + offset.getY() - random.nextFloat() - 1.0F, + offset.getZ() + random.nextFloat() - 0.5 + ); + } + } + } + + @Override + public BlockEntity newBlockEntity(BlockPos worldPosition, BlockState blockState) { + return ModBlockEntities.AUTO_ENCHANTING_TABLE.create(worldPosition, blockState); + } + + @Override + public @Nullable BlockEntityTicker getTicker(Level level, BlockState blockState, BlockEntityType type) { + return level.isClientSide() + ? createTickerHelper(type, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), AutoEnchantingTableBlockEntity::bookAnimationTick) + : createTickerHelper(type, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), AutoEnchantingTableBlockEntity::serverTick); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/AutoEnchantingTableBlockEntityRenderer.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/AutoEnchantingTableBlockEntityRenderer.java new file mode 100644 index 0000000000..77cb454de3 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/AutoEnchantingTableBlockEntityRenderer.java @@ -0,0 +1,199 @@ +package dev.dubhe.anvilcraft.client.renderer.blockentity; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.block.entity.AutoEnchantingTableBlockEntity; +import dev.dubhe.anvilcraft.client.renderer.blockentity.state.AutoEnchantingTableBlockEntityRenderState; +import dev.dubhe.anvilcraft.client.support.FluidRenderHelper; +import net.minecraft.client.Minecraft; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.model.object.book.BookModel; +import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.block.FluidModel; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.blockentity.EnchantTableRenderer; +import net.minecraft.client.renderer.feature.ModelFeatureRenderer; +import net.minecraft.client.renderer.state.level.CameraRenderState; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.sprite.SpriteGetter; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.transfer.fluid.FluidResource; +import net.neoforged.neoforge.transfer.fluid.FluidStacksResourceHandler; +import org.jspecify.annotations.Nullable; + +public class AutoEnchantingTableBlockEntityRenderer + implements BlockEntityRenderer { + private final SpriteGetter sprites; + private final BookModel bookModel; + + public AutoEnchantingTableBlockEntityRenderer(BlockEntityRendererProvider.Context context) { + this.sprites = context.sprites(); + this.bookModel = new BookModel(context.bakeLayer(ModelLayers.BOOK)); + } + + @Override + public AutoEnchantingTableBlockEntityRenderState createRenderState() { + return new AutoEnchantingTableBlockEntityRenderState(); + } + + @Override + public void extractRenderState( + AutoEnchantingTableBlockEntity blockEntity, + AutoEnchantingTableBlockEntityRenderState state, + float partialTicks, + Vec3 cameraPosition, + ModelFeatureRenderer.@Nullable CrumblingOverlay breakProgress + ) { + BlockEntityRenderer.super.extractRenderState(blockEntity, state, partialTicks, cameraPosition, breakProgress); + state.time = blockEntity.time + partialTicks; + float or = blockEntity.rot - blockEntity.oldRot; + + while (or >= (float) Math.PI) { + or -= (float) (Math.PI * 2); + } + + while (or < (float) -Math.PI) { + or += (float) (Math.PI * 2); + } + + state.rotY = blockEntity.oldRot + or * partialTicks; + + final FluidStacksResourceHandler fluidHandler = blockEntity.getFluidHandler(); + FluidResource resource = fluidHandler.getResource(0); + state.amount.setValue(fluidHandler.getAmountAsInt(0)); + state.fluid = resource.getFluid(); + state.fluidStack = resource.toStack(1); + state.fluidResource = resource; + } + + @Override + public void submit( + AutoEnchantingTableBlockEntityRenderState state, + PoseStack poseStack, + SubmitNodeCollector submitNodeCollector, + CameraRenderState camera + ) { + poseStack.pushPose(); + poseStack.translate(0.5F, 0.75F, 0.5F); + float rotY = state.rotY; + poseStack.mulPose(Axis.YP.rotation(-rotY)); + poseStack.mulPose(Axis.ZP.rotationDegrees(80.0F)); + poseStack.translate(0.0F, 0.1F + Mth.sin(state.time * 0.1F) * 0.01F, 0.0F); + BookModel.State animationState = BookModel.State.forAnimation(state.time, 0, 0, 0); + submitNodeCollector.submitModel( + this.bookModel, + animationState, + poseStack, + state.lightCoords, + OverlayTexture.NO_OVERLAY, + -1, + EnchantTableRenderer.BOOK_TEXTURE, + this.sprites, + 0, + state.breakProgress + ); + poseStack.popPose(); + + if (state.amount.getValue() > 0) { + poseStack.pushPose(); + FluidModel model = FluidRenderHelper.getModel( + Minecraft.getInstance().getModelManager().getFluidStateModelSet(), + state.fluid + ); + var tintSource = model.fluidTintSource(); + if (tintSource != null) { + TextureAtlasSprite sprite = model.stillMaterial().sprite(); + int tintColor = tintSource.colorAsStack(state.fluidStack); + final int[] numbers = splitNumber(state.getAmount().getValue()); + AnvilCraft.LOGGER.debug("split: "); + for (int i : numbers) { + AnvilCraft.LOGGER.debug(" {}", i); + } + Direction[] directions = { Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH }; + for (int i = 0; i < numbers.length; i++) { + renderFluid(directions[i], numbers[i], submitNodeCollector, poseStack, sprite, state, tintColor); + } + } + poseStack.popPose(); + } + } + + private static void renderFluid( + Direction face, + int amount, + SubmitNodeCollector submitNodeCollector, + PoseStack poseStack, + TextureAtlasSprite sprite, + AutoEnchantingTableBlockEntityRenderState state, + int tintColor + ) { + float y = 0.375f; + if (amount > 0 && amount <= 2000) { + y += 0.0625f; + } else if (amount > 2000 && amount <= 4000) { + y += 0.125f; + } else if (amount > 4000 && amount <= 6000) { + y += 0.1875f; + } else if (amount > 6000 && amount <= 8000) { + y += 0.25f; + } + float[] pos = new float[6]; + if (face == Direction.EAST || face == Direction.WEST) { + pos[0] = 0; + pos[1] = 0.375f; + pos[2] = 0.4375f; + pos[3] = 1; + pos[4] = y; + pos[5] = 0.5625f; + } else if (face == Direction.SOUTH || face == Direction.NORTH) { + pos[0] = 0.4375f; + pos[1] = 0.375f; + pos[2] = 0; + pos[3] = 0.5625f; + pos[4] = y; + pos[5] = 1; + } + submitNodeCollector.submitCustomGeometry( + poseStack, + BaseFluidHandlerHolderRenderer.FLUID_RENDER_TYPE, + (pose, buffer) -> FluidRenderHelper.INSTANCE.renderFluidBox( + face, + sprite, + state.fluidResource, + pos[0], pos[1], pos[2], + pos[3], pos[4], pos[5], + tintColor, + buffer, + pose, + state.lightCoords, + false, + false + ) + ); + } + + private static int[] splitNumber(int total) { + if (total <= 0) { + return new int[0]; + } + + int full = total / 8000; + int remainder = total % 8000; + int length = remainder == 0 ? full : full + 1; + int[] result = new int[length]; + + for (int i = 0; i < full; i++) { + result[i] = 8000; + } + if (remainder != 0) { + result[full] = remainder; + } + return result; + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/BaseFluidHandlerHolderRenderer.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/BaseFluidHandlerHolderRenderer.java index 989de8009b..8574993f14 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/BaseFluidHandlerHolderRenderer.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/BaseFluidHandlerHolderRenderer.java @@ -28,7 +28,7 @@ public abstract class BaseFluidHandlerHolderRenderer { @SuppressWarnings("deprecation") - private static final RenderType FLUID_RENDER_TYPE = RenderType.create( + public static final RenderType FLUID_RENDER_TYPE = RenderType.create( AnvilCraft.of("fluid_tank").toString(), RenderSetup.builder(RenderPipelines.TRANSLUCENT_BLOCK) .withTexture("Sampler0", TextureAtlas.LOCATION_BLOCKS) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/state/AutoEnchantingTableBlockEntityRenderState.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/state/AutoEnchantingTableBlockEntityRenderState.java new file mode 100644 index 0000000000..cf5734e9b8 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/state/AutoEnchantingTableBlockEntityRenderState.java @@ -0,0 +1,20 @@ +package dev.dubhe.anvilcraft.client.renderer.blockentity.state; + +import dev.dubhe.anvilcraft.util.MutableValue; +import lombok.Getter; +import lombok.Setter; +import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState; +import net.minecraft.world.level.material.Fluid; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.transfer.fluid.FluidResource; + +@Getter +@Setter +public class AutoEnchantingTableBlockEntityRenderState extends BlockEntityRenderState { + public float time; + public float rotY; + public final MutableValue amount = new MutableValue<>(0); + public Fluid fluid; + public FluidStack fluidStack; + public FluidResource fluidResource; +} diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/FluidRenderHelper.java b/src/main/java/dev/dubhe/anvilcraft/client/support/FluidRenderHelper.java index 43776c36b6..79f659f60a 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/FluidRenderHelper.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/FluidRenderHelper.java @@ -37,6 +37,59 @@ public static FluidModel getModel(FluidStateModelSet set, Fluid fluid) { return accessor.getModelByFluid().getOrDefault(fluid, accessor.getMissingModel()); } + public void renderFluidBox( + Direction face, + TextureAtlasSprite sprite, + FluidResource fluid, + float minX, + float minY, + float minZ, + float maxX, + float maxY, + float maxZ, + int color, + VertexConsumer builder, + PoseStack.Pose pose, + int light, + boolean renderBottom, + boolean invertGasses + ) { + int blockLightIn = (light >> 4) & 0xF; + int luminosity = Math.max(blockLightIn, fluid.getFluidType().getLightLevel()); + light = (light & 0xF00000) | luminosity << 4; + + Vector3f center = new Vector3f(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2, minZ + (maxZ - minZ) / 2); + if (invertGasses && fluid.getFluidType().isLighterThanAir()) { + pose.translate(center.x, center.y, center.z); + pose.rotate(Axis.XP.rotationDegrees(180)); + pose.translate(-center.x, -center.y, -center.z); + } + + if (face == Direction.DOWN && !renderBottom) { + return; + } + boolean positive = face.getAxisDirection() == Direction.AxisDirection.POSITIVE; + if (face.getAxis() + .isHorizontal()) { + if (face.getAxis() == Direction.Axis.X) { + renderStillTiledFace( + face, minZ, minY, maxZ, maxY, positive ? maxX : minX, + builder, pose, light, color, sprite + ); + } else { + renderStillTiledFace( + face, minX, minY, maxX, maxY, positive ? maxZ : minZ, + builder, pose, light, color, sprite + ); + } + } else { + renderStillTiledFace( + face, minX, minZ, maxX, maxZ, positive ? maxY : minY, + builder, pose, light, color, sprite + ); + } + } + public void renderFluidBox( TextureAtlasSprite sprite, FluidResource fluid, diff --git a/src/main/java/dev/dubhe/anvilcraft/init/ModCapabilities.java b/src/main/java/dev/dubhe/anvilcraft/init/ModCapabilities.java index a2da0a66f3..e3fa3a54f9 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/ModCapabilities.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/ModCapabilities.java @@ -159,6 +159,18 @@ public static void registerCapabilities(final RegisterCapabilitiesEvent event) { ModBlockEntities.CELESTIAL_FORGING_ANVIL_FLUID_INTERFACE.get(), ModCapabilities::fluid ); + + event.registerBlockEntity( + Capabilities.Item.BLOCK, + ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), + (be, ignore) -> be + ); + + event.registerBlockEntity( + Capabilities.Fluid.BLOCK, + ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), + (be, ignore) -> be.getFluidHandler() + ); } /// 物品 diff --git a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java index a6706e08c0..5591219be4 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java @@ -5,6 +5,7 @@ import dev.dubhe.anvilcraft.block.entity.ActivatorSlidingRailBlockEntity; import dev.dubhe.anvilcraft.block.entity.ActiveSilencerBlockEntity; import dev.dubhe.anvilcraft.block.entity.AdvancedComparatorBlockEntity; +import dev.dubhe.anvilcraft.block.entity.AutoEnchantingTableBlockEntity; import dev.dubhe.anvilcraft.block.entity.BlackHoleBlockEntity; import dev.dubhe.anvilcraft.block.entity.BurningHeaterBlockEntity; import dev.dubhe.anvilcraft.block.entity.CelestialForgingAnvilBlockEntity; @@ -79,6 +80,7 @@ import dev.dubhe.anvilcraft.block.entity.storage.LargeCrateBlockEntity; import dev.dubhe.anvilcraft.block.entity.storage.ShulkerContainerBlockEntity; import dev.dubhe.anvilcraft.client.renderer.blockentity.AdvancedComparatorRenderer; +import dev.dubhe.anvilcraft.client.renderer.blockentity.AutoEnchantingTableBlockEntityRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.BatchCraftingRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.CFARenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.ChargeCollectorRenderer; @@ -524,6 +526,12 @@ public class ModBlockEntities { .validBlock(ModBlocks.SPACETIME_SUPERCOMPUTER) .register(); + public static final BlockEntityEntry AUTO_ENCHANTING_TABLE = REGISTRUM + .blockEntity("auto_enchanting_table", AutoEnchantingTableBlockEntity::new) + .validBlock(ModBlocks.AUTO_ENCHANTING_TABLE) + .renderer(() -> AutoEnchantingTableBlockEntityRenderer::new) + .register(); + public static void register() { } } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java index 41b8ca1c76..a500eea448 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java @@ -172,6 +172,7 @@ import dev.dubhe.anvilcraft.block.utility.redstone.BlockComparatorBlock; import dev.dubhe.anvilcraft.block.utility.redstone.ItemDetectorBlock; import dev.dubhe.anvilcraft.block.utility.redstone.PulseGeneratorBlock; +import dev.dubhe.anvilcraft.block.workstation.AutoEnchantingTableBlock; import dev.dubhe.anvilcraft.block.workstation.BurningHeaterBlock; import dev.dubhe.anvilcraft.block.workstation.ConfinementChamberBlock; import dev.dubhe.anvilcraft.block.workstation.CorruptedBeaconBlock; @@ -1114,6 +1115,15 @@ public void accept(DataGenContext ctx, RegistrumItemModelGenera .recipe(RegistrumBlockRecipeLoader::discharger) .register(); + public static final BlockEntry AUTO_ENCHANTING_TABLE = REGISTRUM + .block("auto_enchanting_table", AutoEnchantingTableBlock::new) + .initialProperties(() -> Blocks.ENCHANTING_TABLE) + .properties(BlockBehaviour.Properties::noOcclusion) + .tag(BlockTags.MINEABLE_WITH_PICKAXE) + .blockstate(DataGenUtil::noExtraModelOrState) + .simpleItem() + .register(); + public static final BlockEntry ACTIVE_SILENCER = REGISTRUM.block("active_silencer", ActiveSilencerBlock::new) .simpleItem() .initialProperties(() -> Blocks.IRON_BLOCK) diff --git a/src/main/java/dev/dubhe/anvilcraft/util/MutableValue.java b/src/main/java/dev/dubhe/anvilcraft/util/MutableValue.java new file mode 100644 index 0000000000..611dcb49cb --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/util/MutableValue.java @@ -0,0 +1,18 @@ +package dev.dubhe.anvilcraft.util; + +import lombok.Getter; + +@Getter +public class MutableValue { + private T value; + + public MutableValue(T value) { + this.value = value; + } + + public void setValue(T value) { + if (!this.value.equals(value)) { + this.value = value; + } + } +} diff --git a/src/main/resources/assets/anvilcraft/blockstates/auto_enchanting_table.json b/src/main/resources/assets/anvilcraft/blockstates/auto_enchanting_table.json new file mode 100644 index 0000000000..ee1a63d58e --- /dev/null +++ b/src/main/resources/assets/anvilcraft/blockstates/auto_enchanting_table.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "anvilcraft:block/auto_enchanting_table" + } + } +} \ No newline at end of file From b4f7dbd0735b73580ce288baf4b4b34b0b889f66 Mon Sep 17 00:00:00 2001 From: XeKr <45423407+XeKr@users.noreply.github.com> Date: Thu, 4 Jun 2026 19:02:19 +0800 Subject: [PATCH 2/7] Create auto_enchanting_table.png (#3628) --- .../machine/background/auto_enchanting_table.png | Bin 0 -> 1071 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/assets/anvilcraft/textures/gui/machine/background/auto_enchanting_table.png diff --git a/src/main/resources/assets/anvilcraft/textures/gui/machine/background/auto_enchanting_table.png b/src/main/resources/assets/anvilcraft/textures/gui/machine/background/auto_enchanting_table.png new file mode 100644 index 0000000000000000000000000000000000000000..e93b3db28c9ca91c2f225a29d89767de845cc30f GIT binary patch literal 1071 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911MRQ8&P5FjO4;u=vBoS#-wo>-L1;Fyx1 zl&avFo0y&&l$w}QS$Hzl2B^p{z$e7@?%liL;o%D=7A2SE9ys_gC@#UvKQb~a!$e=D zykdcknVYjyKy^VxVRP|;b)B8FYhr>OADo)Mc**IaqM{HV>tn}`b$56F|NkFo9uVBW z+o1!bq)LMPf`NkA0fUGV!z~5|=0Hyu$B>F!Z|?Nw-7*kqd$=U>QSXFHXN@oX-hZ*x zWNlads*O)2W`C{|axxV%@oy^hdt>ta)TdpAvp#QMrNsDQ^&+s9AcCi58_SQtP!@(O z^SOQ6^EwTey|13^^Ih)!`QD@7QqGYWl+;@r>W%=A|&M*)RR9?OK(k9)sVEg(VF~9pn=JKY1F-m*B;4WAXHz zt0T7GwG}$R(ooj$POO3b4fC$s4AbHnmrFYQvYsHH@QawbtJPgJMG}r_*>rB+`jZfuoG|7K-Hk@Ix#DV+I_A$80 zJqY6Z{Q2Isd(sC|7&e$~x_;g4_C4_fclZx zI~k^(dsmdH@hzZ`CE}@jL-8xdIZ+L%8!l_HMI5u4)z9!XzVY>?m;gQt(|&G&nTnTg zDDZPln4z$cO~KSz#i%t%y1{<>MuvvDMIaPs>9C&tz>_^jAqowQ3fc#j$Rw=dObFpK z&|)_@#n@BSaC+x~37<=LI5Z>*NVB&XyDVauXml|x^8O$020uOpt7u)O3F)(9m=?Td zTUq6h(|&}(&vFvejQ`9ln>u>cSTDysBRO7 zg6%Drq8KKya)f(Ky~YHRLjpie4_ohFidw-DD8Sg@Cx9dcW@ Date: Wed, 8 Jul 2026 21:26:45 +0800 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E9=99=84=E9=AD=94=E5=8F=B0=E7=9A=84=E5=8E=9F=E7=89=88?= =?UTF-8?q?=E9=99=84=E9=AD=94=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现自动附魔台的无引物、原版的附魔功能 - 修复创造板条箱渲染物品的z打架 --- .../AutoEnchantingTableBlockEntity.java | 515 +++++++++++++----- .../entity/CreativeCrateBlockEntity.java | 10 +- .../workstation/AutoEnchantingTableBlock.java | 39 +- .../gui/component/FluidDisplayWidget.java | 90 +++ .../gui/screen/AutoEnchantingTableScreen.java | 88 +++ ...utoEnchantingTableBlockEntityRenderer.java | 53 +- .../blockentity/CreativeCrateRenderer.java | 15 +- ...EnchantingTableBlockEntityRenderState.java | 8 + .../anvilcraft/init/ModCapabilities.java | 3 +- .../dubhe/anvilcraft/init/ModMenuTypes.java | 6 + .../inventory/AutoEnchantingTableMenu.java | 118 ++++ 11 files changed, 789 insertions(+), 156 deletions(-) create mode 100644 src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/client/gui/screen/AutoEnchantingTableScreen.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java index a77022dc6f..7455ac4c7f 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java @@ -2,76 +2,81 @@ import dev.dubhe.anvilcraft.api.power.IPowerConsumer; import dev.dubhe.anvilcraft.api.power.PowerGrid; +import dev.dubhe.anvilcraft.block.workstation.AutoEnchantingTableBlock; +import dev.dubhe.anvilcraft.init.ModMenuTypes; import dev.dubhe.anvilcraft.init.block.ModFluids; +import dev.dubhe.anvilcraft.init.enchantment.ModEnchantments; +import dev.dubhe.anvilcraft.init.item.ModItems; +import dev.dubhe.anvilcraft.inventory.AutoEnchantingTableMenu; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import lombok.Getter; import lombok.Setter; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; +import net.minecraft.core.HolderSet; +import net.minecraft.core.IdMap; +import net.minecraft.core.NonNullList; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; +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.resources.ResourceKey; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.tags.EnchantmentTags; +import net.minecraft.util.Mth; import net.minecraft.util.ProblemReporter; +import net.minecraft.util.RandomSource; +import net.minecraft.util.Util; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.WorldlyContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.EnchantmentInstance; +import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.EnchantingTableBlock; +import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.TagValueOutput; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import net.neoforged.neoforge.fluids.FluidStack; -import net.neoforged.neoforge.transfer.ResourceHandler; import net.neoforged.neoforge.transfer.fluid.FluidResource; import net.neoforged.neoforge.transfer.fluid.FluidStacksResourceHandler; -import net.neoforged.neoforge.transfer.item.ItemResource; -import net.neoforged.neoforge.transfer.transaction.SnapshotJournal; -import net.neoforged.neoforge.transfer.transaction.TransactionContext; +import net.neoforged.neoforge.transfer.transaction.Transaction; import org.jspecify.annotations.Nullable; -public class AutoEnchantingTableBlockEntity extends BlockEntity implements IPowerConsumer, ResourceHandler { +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class AutoEnchantingTableBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, IPowerConsumer { @Getter @Setter + @Nullable private PowerGrid grid; - private int power = 16; + /// 索引0:物品输入 1:物品输出 2:引物 + private NonNullList items = NonNullList.withSize(3, ItemStack.EMPTY); @Getter - private ItemStack inputStack = ItemStack.EMPTY; - @Getter - private ItemStack outputStack = ItemStack.EMPTY; - @Getter - @Setter - private ItemStack prologueStack = ItemStack.EMPTY; - - public int time; - public float rot; - public float oldRot; - public float targetRot; - - private final SnapshotJournal snapshotJournal = new AutoEnchantTableJournal(); - - private record AutoEnchantTableSnapshot(ItemStack input, ItemStack output) {} - - private class AutoEnchantTableJournal extends SnapshotJournal { - @Override - protected AutoEnchantTableSnapshot createSnapshot() { - return new AutoEnchantTableSnapshot( - AutoEnchantingTableBlockEntity.this.inputStack.copy(), - AutoEnchantingTableBlockEntity.this.outputStack.copy() - ); - } - - @Override - protected void revertToSnapshot(AutoEnchantTableSnapshot snapshot) { - AutoEnchantingTableBlockEntity.this.inputStack = snapshot.input; - AutoEnchantingTableBlockEntity.this.outputStack = snapshot.output; - } - - @Override - protected void onRootCommit(AutoEnchantTableSnapshot originalState) { - AutoEnchantingTableBlockEntity.this.onChange(); - } - } + private final Map>> enchantmentMap = new Object2ObjectOpenHashMap<>(); + private int enchantmentSeed = 0; + private final RandomSource random = RandomSource.create(); @Getter private final FluidStacksResourceHandler fluidHandler = new FluidStacksResourceHandler(1, 32_000) { @@ -86,36 +91,182 @@ protected void onContentsChanged(int index, FluidStack previousContents) { } }; - public AutoEnchantingTableBlockEntity(BlockEntityType type, BlockPos worldPosition, BlockState blockState) { - super(type, worldPosition, blockState); + private int cooldown = 0; + // region 控制书本的渲染 + public int time; + public float rot; + public float oldRot; + public float targetRot; + public float open; + public float oldOpen; + public float flip; + public float oldFlip; + public float flipT; + public float flipA; + // endregion + + public static Map>> getEnchantmentMap(Level level) { + Registry enchantments = level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT); + + // 蓝宝石护符 + ObjectArrayList> sapphireAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.BREACH).ifPresent((ref) -> sapphireAmulet.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.AQUA_AFFINITY).ifPresent((ref) -> sapphireAmulet.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.DEPTH_STRIDER).ifPresent((ref) -> sapphireAmulet.add(Objects.requireNonNull(ref.getKey()))); + + // 红宝石护符 + ObjectArrayList> rubyAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.FIRE_PROTECTION).ifPresent((ref) -> rubyAmulet.add(Objects.requireNonNull(ref.getKey()))); + + // 黄玉护符 + ObjectArrayList> topazAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.CHANNELING).ifPresent((ref) -> topazAmulet.add(Objects.requireNonNull(ref.getKey()))); + + // 绿宝石护符 + Iterable> tradeableEnchants = enchantments.getTagOrEmpty(EnchantmentTags.TRADEABLE); + ObjectArrayList> emeraldAmulet = new ObjectArrayList<>(); + tradeableEnchants.forEach((holder) -> { + ResourceKey key = Objects.requireNonNull(holder.getKey()); + if (!sapphireAmulet.contains(key) && !rubyAmulet.contains(key) && !topazAmulet.contains(key)) { + emeraldAmulet.add(key); + } + }); + + // 宝石护符 + ObjectArrayList> gemAmulet = new ObjectArrayList<>(); + gemAmulet.addAll(sapphireAmulet); + gemAmulet.addAll(rubyAmulet); + gemAmulet.addAll(topazAmulet); + gemAmulet.addAll(emeraldAmulet); + + // 羽毛护符 + ObjectArrayList> featherAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.FEATHER_FALLING).ifPresent((ref) -> featherAmulet.add(Objects.requireNonNull(ref.getKey()))); + + // 寂静护符 + ObjectArrayList> silenceAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.PROTECTION).ifPresent((ref) -> silenceAmulet.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.SWIFT_SNEAK).ifPresent((ref) -> silenceAmulet.add(Objects.requireNonNull(ref.getKey()))); + + // 猫护符 + ObjectArrayList> catAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.BLAST_PROTECTION).ifPresent((ref) -> catAmulet.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.THORNS).ifPresent((ref) -> catAmulet.add(Objects.requireNonNull(ref.getKey()))); + + // 狗护符 + ObjectArrayList> dogAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.PROJECTILE_PROTECTION).ifPresent((ref) -> dogAmulet.add(Objects.requireNonNull(ref.getKey()))); + + // 自然护符 + ObjectArrayList> natureAmulet = new ObjectArrayList<>(); + natureAmulet.addAll(featherAmulet); + natureAmulet.addAll(silenceAmulet); + natureAmulet.addAll(catAmulet); + natureAmulet.addAll(dogAmulet); + + // 紫水晶 + ObjectArrayList> amethystShard = new ObjectArrayList<>(); + enchantments.get(ModEnchantments.FELLING_KEY).ifPresent((ref) -> amethystShard.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(ModEnchantments.HARVEST_KEY).ifPresent((ref) -> amethystShard.add(Objects.requireNonNull(ref.getKey()))); + + // 皇家钢锭 + ObjectArrayList> royalSteelShard = new ObjectArrayList<>(); + enchantments.get(Enchantments.SILK_TOUCH).ifPresent((ref) -> royalSteelShard.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.UNBREAKING).ifPresent((ref) -> royalSteelShard.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.MENDING).ifPresent((ref) -> royalSteelShard.add(Objects.requireNonNull(ref.getKey()))); + + // 余烬金属锭 + ObjectArrayList> emberMetal = new ObjectArrayList<>(); + enchantments.get(ModEnchantments.SMELTING_KEY).ifPresent((ref) -> emberMetal.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.FIRE_ASPECT).ifPresent((ref) -> emberMetal.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.FLAME).ifPresent((ref) -> emberMetal.add(Objects.requireNonNull(ref.getKey()))); + + // 浮霜金属锭 + ObjectArrayList> frostMetal = new ObjectArrayList<>(); + enchantments.get(ModEnchantments.DISINTEGRATION_KEY).ifPresent((ref) -> frostMetal.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.FROST_WALKER).ifPresent((ref) -> frostMetal.add(Objects.requireNonNull(ref.getKey()))); + + // 超限合金锭 + ObjectArrayList> transcendium = new ObjectArrayList<>(); + enchantments.get(Enchantments.FORTUNE).ifPresent((ref) -> transcendium.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.LOOTING).ifPresent((ref) -> transcendium.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(ModEnchantments.BEHEADING_KEY).ifPresent((ref) -> transcendium.add(Objects.requireNonNull(ref.getKey()))); + enchantments.get(Enchantments.LUCK_OF_THE_SEA).ifPresent((ref) -> transcendium.add(Objects.requireNonNull(ref.getKey()))); + transcendium.addAll(amethystShard); + transcendium.addAll(royalSteelShard); + transcendium.addAll(emberMetal); + transcendium.addAll(frostMetal); + + return Map.copyOf(Util.make(new Object2ObjectOpenHashMap<>(), (map) -> { + map.put(ModItems.SAPPHIRE_AMULET.get(), List.copyOf(sapphireAmulet)); + map.put(ModItems.RUBY_AMULET.get(), List.copyOf(rubyAmulet)); + map.put(ModItems.TOPAZ.get(), List.copyOf(topazAmulet)); + map.put(ModItems.EMERALD_AMULET.get(), List.copyOf(emeraldAmulet)); + map.put(ModItems.GEM_AMULET.get(), List.copyOf(gemAmulet)); + map.put(ModItems.FEATHER_AMULET.get(), List.copyOf(featherAmulet)); + map.put(ModItems.SILENCE_AMULET.get(), List.copyOf(silenceAmulet)); + map.put(ModItems.CAT_AMULET.get(), List.copyOf(catAmulet)); + map.put(ModItems.DOG_AMULET.get(), List.copyOf(dogAmulet)); + map.put(ModItems.NATURE_AMULET.get(), List.copyOf(natureAmulet)); + map.put(Items.AMETHYST_SHARD, List.copyOf(amethystShard)); + map.put(ModItems.ROYAL_STEEL_INGOT.get(), List.copyOf(royalSteelShard)); + map.put(ModItems.EMBER_METAL_INGOT.get(), List.copyOf(emberMetal)); + map.put(ModItems.FROST_METAL_INGOT.get(), List.copyOf(frostMetal)); + map.put(ModItems.TRANSCENDIUM_INGOT.get(), List.copyOf(transcendium)); + })); + } + + public AutoEnchantingTableBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); } - public static void bookAnimationTick( + public ItemStack getDisplayInputItem() { + return this.items.getFirst(); + } + + public ItemStack getDisplayOutputItem() { + return this.items.get(1); + } + + public void bookAnimationTick( final Level level, final BlockPos worldPosition, - final BlockState state, - final AutoEnchantingTableBlockEntity entity + final BlockState state ) { - entity.oldRot = entity.rot; - entity.targetRot += 0.02F; + this.oldOpen = this.open; + this.oldRot = this.rot; + this.targetRot += 0.02F; + + if (!this.getItem(0).isEmpty()) { + this.open += 0.1f; + if (this.open < 0.5f || random.nextInt(40) == 0) { + float old = this.flipT; + + do { + this.flipT = this.flipT + (random.nextInt(4) - random.nextInt(4)); + } while (old == this.flipT); + } + } else { + this.open -= 0.1f; + } - while (entity.rot >= (float) Math.PI) { - entity.rot -= (float) (Math.PI * 2); + while (this.rot >= (float) Math.PI) { + this.rot -= (float) (Math.PI * 2); } - while (entity.rot < (float) -Math.PI) { - entity.rot += (float) (Math.PI * 2); + while (this.rot < (float) -Math.PI) { + this.rot += (float) (Math.PI * 2); } - while (entity.targetRot >= (float) Math.PI) { - entity.targetRot -= (float) (Math.PI * 2); + while (this.targetRot >= (float) Math.PI) { + this.targetRot -= (float) (Math.PI * 2); } - while (entity.targetRot < (float) -Math.PI) { - entity.targetRot += (float) (Math.PI * 2); + while (this.targetRot < (float) -Math.PI) { + this.targetRot += (float) (Math.PI * 2); } - float rotDir = entity.targetRot - entity.rot; + float rotDir = this.targetRot - this.rot; while (rotDir >= (float) Math.PI) { rotDir -= (float) (Math.PI * 2); @@ -125,38 +276,158 @@ public static void bookAnimationTick( rotDir += (float) (Math.PI * 2); } - entity.rot += rotDir * 0.4F; - entity.time++; + this.rot += rotDir * 0.4F; + this.open = Mth.clamp(this.open, 0, 1f); + this.time++; + this.oldFlip = this.flip; + float diff = (this.flipT - this.flip) * 0.4f; + diff = Mth.clamp(diff, -0.2f, 0.2f); + this.flipA = this.flipA + (diff - this.flipA) * 0.9f; + this.flip = this.flip + this.flipA; } - public static void serverTick( + public void serverTick( final Level level, - final BlockPos worldPosition, - final BlockState state, - final AutoEnchantingTableBlockEntity entity + final BlockPos pos, + final BlockState state ) { - if (!entity.inputStack.isEmpty() && entity.outputStack.isEmpty()) { - entity.outputStack = entity.inputStack.copy(); - entity.inputStack = ItemStack.EMPTY; + if (this.cooldown > 0) { + this.cooldown--; + } else if (this.cooldown == 0) { + this.cooldown = 80; + if (this.grid != null && !this.grid.isWorking()) { + return; + } + if (!this.getItem(1).isEmpty() || this.getItem(0).isEmpty()) { + return; + } + ItemStack enchantItem = this.getItem(0).copyWithCount(1); + if (!enchantItem.isEnchantable()) { + this.setItem(0, ItemStack.EMPTY); + this.setItem(1, enchantItem); + return; + } + if (this.getItem(2).isEmpty()) { + // 无引物模式 + int exp = 0; + for (BlockPos offset : AutoEnchantingTableBlock.BOOKSHELF_OFFSETS) { + if (EnchantingTableBlock.isValidBookShelf(level, pos, offset)) { + exp += 400; + if (exp >= 6000) { + exp = 6000; + break; + } + } + } + if (this.fluidHandler.getAmountAsInt(0) < exp) { + return; + } + int[][] costAndEnchant = this.getCostAndEnchant(enchantItem); + if (costAndEnchant.length == 0) { + return; + } + int index = level.getRandom().nextInt(0, 3); + int[] enchant = costAndEnchant[index]; + List enchantmentList = this.getEnchantmentList(level.registryAccess(), enchantItem, index, enchant[0]); + if (enchantItem.is(Items.BOOK)) { + enchantItem.transmuteCopy(Items.ENCHANTED_BOOK); + } + if (!enchantmentList.isEmpty()) { + for (EnchantmentInstance enchantmentInstance : enchantmentList) { + enchantItem.enchant(enchantmentInstance.enchantment(), enchantmentInstance.level()); + } + } + this.setItem(0, ItemStack.EMPTY); + this.setItem(1, enchantItem); + try (Transaction ts = Transaction.openRoot()) { + this.fluidHandler.extract(FluidResource.of(ModFluids.EXP_FLUID), exp, ts); + ts.commit(); + this.onChange(); + } + this.enchantmentSeed = level.getRandom().nextInt(); + level.playSound(null, this.getBlockPos(), SoundEvents.ENCHANTMENT_TABLE_USE, SoundSource.BLOCKS, 1.0F, level.getRandom().nextFloat() * 0.1F + 0.9F); + } else { + // TODO: 引物模式 + } } } + private int[][] getCostAndEnchant(ItemStack itemStack) { + int[] costs = new int[3]; + int[] enchantClue = new int[]{-1, -1, -1}; + int[] levelClue = new int[]{-1, -1, -1}; + if (itemStack.isEmpty() || !itemStack.isEnchantable()) { + return new int[][]{}; + } + if (this.level != null) { + IdMap> idMap = this.level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); + int bookcases = 0; + for (BlockPos offset : AutoEnchantingTableBlock.BOOKSHELF_OFFSETS) { + if (EnchantingTableBlock.isValidBookShelf(this.level, this.getBlockPos(), offset)) { + bookcases++; + } + } + if (this.enchantmentSeed == 0) { + this.enchantmentSeed = level.getRandom().nextInt(); + } + random.setSeed(this.enchantmentSeed); + for (int ixx = 0; ixx < 3; ixx++) { + costs[ixx] = EnchantmentHelper.getEnchantmentCost(random, ixx, bookcases, itemStack); + enchantClue[ixx] = -1; + levelClue[ixx] = -1; + if (costs[ixx] < ixx + 1) { + costs[ixx] = 0; + } + } + + for (int ix = 0; ix < 3; ix++) { + if (costs[ix] > 0) { + List list = this.getEnchantmentList(level.registryAccess(), itemStack, ix, costs[ix]); + if (!list.isEmpty()) { + EnchantmentInstance enchant = list.get(random.nextInt(list.size())); + enchantClue[ix] = idMap.getId(enchant.enchantment()); + levelClue[ix] = enchant.level(); + } + } + } + return new int[][]{ + new int[] { costs[0], enchantClue[0], levelClue[0] }, + new int[] { costs[1], enchantClue[1], levelClue[1] }, + new int[] { costs[2], enchantClue[2], levelClue[2] } + }; + } + return new int[][]{}; + } + + private List getEnchantmentList(final RegistryAccess access, final ItemStack itemStack, final int slot, final int enchantmentCost) { + random.setSeed(this.enchantmentSeed + slot); + Optional> tag = access.lookupOrThrow(Registries.ENCHANTMENT).get(EnchantmentTags.IN_ENCHANTING_TABLE); + if (tag.isEmpty()) { + return List.of(); + } + + List list = EnchantmentHelper.selectEnchantment(random, itemStack, enchantmentCost, tag.get().stream()); + if (itemStack.is(Items.BOOK) && list.size() > 1) { + list.remove(random.nextInt(list.size())); + } + + return list; + } + @Override protected void saveAdditional(ValueOutput output) { super.saveAdditional(output); - output.store("input", ItemStack.OPTIONAL_CODEC, this.inputStack); - output.store("output", ItemStack.OPTIONAL_CODEC, this.outputStack); - output.store("prologue", ItemStack.OPTIONAL_CODEC, this.prologueStack); + ContainerHelper.saveAllItems(output, this.items); this.fluidHandler.serialize(output); + output.putInt("cooldown", this.cooldown); } @Override protected void loadAdditional(ValueInput input) { super.loadAdditional(input); - input.read("input", ItemStack.OPTIONAL_CODEC).ifPresent((stack) -> this.inputStack = stack); - input.read("output", ItemStack.OPTIONAL_CODEC).ifPresent((stack) -> this.outputStack = stack); - input.read("prologue", ItemStack.OPTIONAL_CODEC).ifPresent((stack) -> this.prologueStack = stack); + ContainerHelper.loadAllItems(input, this.items); this.fluidHandler.deserialize(input); + input.getInt("cooldown").ifPresent((cooldown) -> this.cooldown = cooldown); } @Override @@ -171,7 +442,7 @@ public CompoundTag getUpdateTag(HolderLookup.Provider registries) { return output.buildResult(); } - private void onChange() { + public void onChange() { this.setChanged(); if (this.level != null) { this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 2); @@ -179,99 +450,73 @@ private void onChange() { } @Override - public @Nullable Level getCurrentLevel() { - return this.level; + public int getContainerSize() { + return this.items.size(); } @Override - public BlockPos getPos() { - return this.getBlockPos(); + protected Component getDefaultName() { + return Component.translatable("block.anvilcraft.auto_enchanting_table"); } @Override - public int getInputPower() { - return this.power; + protected NonNullList getItems() { + return this.items; } @Override - public int size() { - return 2; + protected void setItems(NonNullList items) { + this.items = items; } @Override - public ItemResource getResource(int index) { - if (index == 0) { - return ItemResource.of(this.inputStack); - } - if (index == 1) { - return ItemResource.of(this.outputStack); - } - return ItemResource.EMPTY; + public void setItem(int slot, ItemStack itemStack) { + this.items.set(slot, itemStack.copyWithCount(1)); + this.onChange(); } @Override - public long getAmountAsLong(int index) { - if (index == 0) { - return this.inputStack.isEmpty() ? 0 : this.inputStack.count(); - } - if (index == 1) { - return this.outputStack.isEmpty() ? 0 : this.outputStack.count(); - } - return 0; + public int getMaxStackSize() { + return 1; } @Override - public long getCapacityAsLong(int index, ItemResource resource) { + public int getMaxStackSize(ItemStack itemStack) { return 1; } @Override - public boolean isValid(int index, ItemResource resource) { - return true; + protected AbstractContainerMenu createMenu(int containerId, Inventory inventory) { + return new AutoEnchantingTableMenu(ModMenuTypes.AUTO_ENCHANTING_TABLE.get(), containerId, inventory, this); } @Override - public int insert(int index, ItemResource resource, int amount, TransactionContext transaction) { - if (resource.isEmpty() || amount <= 0) { - return 0; - } - if (index != 0) { - return 0; - } - if (!this.inputStack.isEmpty()) { - return 0; - } - this.snapshotJournal.updateSnapshots(transaction); - this.inputStack = resource.toStack(1); - return 1; + public int[] getSlotsForFace(Direction direction) { + return new int[] { 0, 1 }; } @Override - public int insert(ItemResource resource, int amount, TransactionContext transaction) { - return this.insert(0, resource, amount, transaction); + public boolean canPlaceItemThroughFace(int slot, ItemStack itemStack, @Nullable Direction direction) { + return slot == 0; } @Override - public int extract(int index, ItemResource resource, int amount, TransactionContext transaction) { - if (resource.isEmpty() || amount <= 0) { - return 0; - } - if (index != 1) { - return 0; - } - if (this.outputStack.isEmpty()) { - return 0; - } - if (!ItemResource.of(this.outputStack).equals(resource)) { - return 0; - } - this.snapshotJournal.updateSnapshots(transaction); - this.outputStack = ItemStack.EMPTY; - return 1; + public boolean canTakeItemThroughFace(int slot, ItemStack itemStack, Direction direction) { + return slot == 1; + } + + @Override + public @Nullable Level getCurrentLevel() { + return this.level; } @Override - public int extract(ItemResource resource, int amount, TransactionContext transaction) { - return this.extract(1, resource, amount, transaction); + public BlockPos getPos() { + return this.getBlockPos(); + } + + @Override + public int getInputPower() { + return this.items.get(2).isEmpty() ? 16 : 64; } } diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/CreativeCrateBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/CreativeCrateBlockEntity.java index bc615c211c..0fb543c486 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/CreativeCrateBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/CreativeCrateBlockEntity.java @@ -8,12 +8,14 @@ import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.util.ProblemReporter; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.TagValueOutput; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import net.neoforged.neoforge.transfer.ResourceHandler; @@ -44,11 +46,9 @@ public void loadAdditional(ValueInput input) { @Override public CompoundTag getUpdateTag(HolderLookup.Provider registries) { - CompoundTag tag = super.getUpdateTag(registries); - if (!this.itemHandler.isEmpty()) { - tag.store("item", ItemStack.CODEC, this.itemHandler.getStack()); - } - return tag; + TagValueOutput output = TagValueOutput.createWithContext(new ProblemReporter.Collector(this.problemPath()), registries); + this.saveAdditional(output); + return output.buildResult(); } @Override diff --git a/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java index a03579a3dd..320abcd328 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java @@ -3,11 +3,17 @@ import com.mojang.serialization.MapCodec; import dev.dubhe.anvilcraft.block.better.BetterBaseEntityBlock; import dev.dubhe.anvilcraft.block.entity.AutoEnchantingTableBlockEntity; +import dev.dubhe.anvilcraft.init.ModMenuTypes; import dev.dubhe.anvilcraft.init.block.ModBlockEntities; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.RandomSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; @@ -16,6 +22,8 @@ import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.pathfinder.PathComputationType; +import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import org.jspecify.annotations.Nullable; @@ -44,6 +52,28 @@ protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, return SHAPE; } + @Override + protected boolean useShapeForLightOcclusion(BlockState state) { + return true; + } + + @Override + public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (level.isClientSide()) { + return InteractionResult.SUCCESS; + } + BlockEntity blockEntity = level.getBlockEntity(pos); + if (blockEntity instanceof AutoEnchantingTableBlockEntity autoEnchantingTableBlockEntity) { + if (player instanceof ServerPlayer serverPlayer) { + if (serverPlayer.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) { + return InteractionResult.PASS; + } + ModMenuTypes.open(serverPlayer, autoEnchantingTableBlockEntity, pos); + } + } + return InteractionResult.SUCCESS; + } + @Override public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { super.animateTick(state, level, pos, random); @@ -72,7 +102,12 @@ public BlockEntity newBlockEntity(BlockPos worldPosition, BlockState blockState) @Override public @Nullable BlockEntityTicker getTicker(Level level, BlockState blockState, BlockEntityType type) { return level.isClientSide() - ? createTickerHelper(type, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), AutoEnchantingTableBlockEntity::bookAnimationTick) - : createTickerHelper(type, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), AutoEnchantingTableBlockEntity::serverTick); + ? createTickerHelper(type, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), (world, pos, state, be) -> be.bookAnimationTick(world, pos, state)) + : createTickerHelper(type, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), (world, pos, state, be) -> be.serverTick(world, pos, state)); + } + + @Override + protected boolean isPathfindable(BlockState state, PathComputationType type) { + return false; } } diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java new file mode 100644 index 0000000000..1d04fa9873 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java @@ -0,0 +1,90 @@ +package dev.dubhe.anvilcraft.client.gui.component; + +import dev.dubhe.anvilcraft.AnvilCraft; +import lombok.Getter; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.client.input.MouseButtonInfo; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.core.Holder; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.world.level.material.Fluid; +import net.neoforged.neoforge.transfer.fluid.FluidResource; +import net.neoforged.neoforge.transfer.fluid.FluidStacksResourceHandler; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + +public class FluidDisplayWidget extends AbstractWidget { + @Getter + private final FluidStacksResourceHandler fluidHandler; + + public FluidDisplayWidget( + int x, + int y, + int width, + int height, + FluidStacksResourceHandler fluidHandler, + Function message + ) { + super(x, y, width, height, message.apply(fluidHandler)); + this.fluidHandler = fluidHandler; + } + + @Override + protected void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + Identifier fluidTexture = getFluidTexture(); + if (fluidTexture != null) { + graphics.blit( + RenderPipelines.GUI_TEXTURED, + fluidTexture, + this.getX(), this.getY() + this.height - this.getCapacity(), + 0, 0, + this.width, this.getCapacity(), + 16, 256 + ); + } + } + + @Override + protected void updateWidgetNarration(NarrationElementOutput output) { + } + + @Override + protected boolean isValidClickButton(MouseButtonInfo buttonInfo) { + return false; + } + + @Nullable + private Identifier getFluidTexture() { + FluidResource resource = this.fluidHandler.getResource(0); + if (resource.isEmpty()) { + return null; + } + AtomicReference<@org.jspecify.annotations.Nullable Identifier> texture = new AtomicReference<>(); + ClientLevel level = Minecraft.getInstance().level; + if (level != null) { + Optional> holder = level.holder(Objects.requireNonNull(resource.typeHolder().getKey())); + holder.ifPresent((fluid) -> { + String registeredName = fluid.getRegisteredName().split(":")[1]; + texture.set(AnvilCraft.of("textures/block/" + registeredName + ".png")); + }); + } + return texture.get(); + } + + private int getCapacity() { + if (this.fluidHandler.getResource(0).isEmpty()) { + return 0; + } + final int stored = this.fluidHandler.getAmountAsInt(0); + return (stored * this.height / this.fluidHandler.getCapacityAsInt(0, FluidResource.EMPTY)); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/AutoEnchantingTableScreen.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/AutoEnchantingTableScreen.java new file mode 100644 index 0000000000..0e6621a0b9 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/AutoEnchantingTableScreen.java @@ -0,0 +1,88 @@ +package dev.dubhe.anvilcraft.client.gui.screen; + +import dev.dubhe.anvilcraft.client.gui.component.FluidDisplayWidget; +import dev.dubhe.anvilcraft.constant.SharedTextures; +import dev.dubhe.anvilcraft.inventory.AutoEnchantingTableMenu; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.client.gui.screens.inventory.tooltip.DefaultTooltipPositioner; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.world.entity.player.Inventory; +import net.neoforged.neoforge.transfer.fluid.FluidResource; + +import java.util.List; + +public class AutoEnchantingTableScreen extends AbstractContainerScreen { + private static final Identifier BACKGROUND = SharedTextures.bg("machine", "auto_enchanting_table"); + + public AutoEnchantingTableScreen(AutoEnchantingTableMenu menu, Inventory inventory, Component title) { + super(menu, inventory, title); + } + + @Override + protected void init() { + super.init(); + this.addRenderableWidget( + new FluidDisplayWidget( + this.leftPos + 151, this.topPos + 16, + 18, 56, + this.menu.getBlockEntity().getFluidHandler(), + (fluidHandler) -> Component.literal("Exp: ") + .append(String.valueOf(fluidHandler.getAmountAsInt(0))) + .append("/") + .append(String.valueOf(fluidHandler.getCapacityAsInt(0, FluidResource.EMPTY))) + ) + ); + } + + @Override + public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + super.extractBackground(graphics, mouseX, mouseY, a); + graphics.blit( + RenderPipelines.GUI_TEXTURED, + BACKGROUND, + this.leftPos, + this.topPos, + 0, + 0, + this.getImageWidth(), + this.getImageHeight(), + 256, + 256 + ); + } + + @Override + protected void extractLabels(GuiGraphicsExtractor graphics, int xm, int ym) { + int x = (this.imageWidth - this.font.width(this.title)) / 2; + graphics.text(this.font, this.title, x, 2, -12566464, false); + } + + @Override + protected void extractTooltip(GuiGraphicsExtractor graphics, int mouseX, int mouseY) { + super.extractTooltip(graphics, mouseX, mouseY); + if (this.isHovering(151, 16, 18, 56, mouseX, mouseY)) { + graphics.tooltip( + Minecraft.getInstance().font, + List.of( + ClientTooltipComponent.create( + Component.literal("Exp: ") + .append(String.valueOf(this.menu.getBlockEntity().getFluidHandler().getAmountAsInt(0))) + .append("/") + .append(String.valueOf(this.menu.getBlockEntity().getFluidHandler().getCapacityAsInt(0, FluidResource.EMPTY))) + .append("mB") + .getVisualOrderText() + ) + ), + mouseX, + mouseY, + DefaultTooltipPositioner.INSTANCE, + null + ); + } + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/AutoEnchantingTableBlockEntityRenderer.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/AutoEnchantingTableBlockEntityRenderer.java index 77cb454de3..afba9a7367 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/AutoEnchantingTableBlockEntityRenderer.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/AutoEnchantingTableBlockEntityRenderer.java @@ -2,9 +2,9 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Axis; -import dev.dubhe.anvilcraft.AnvilCraft; import dev.dubhe.anvilcraft.block.entity.AutoEnchantingTableBlockEntity; import dev.dubhe.anvilcraft.client.renderer.blockentity.state.AutoEnchantingTableBlockEntityRenderState; +import dev.dubhe.anvilcraft.client.support.FeatureRendererSupport; import dev.dubhe.anvilcraft.client.support.FluidRenderHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.model.geom.ModelLayers; @@ -14,7 +14,10 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.EnchantTableRenderer; +import net.minecraft.client.renderer.entity.state.ItemClusterRenderState; import net.minecraft.client.renderer.feature.ModelFeatureRenderer; +import net.minecraft.client.renderer.item.ItemModelResolver; +import net.minecraft.client.renderer.item.ItemStackRenderState; import net.minecraft.client.renderer.state.level.CameraRenderState; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.texture.TextureAtlasSprite; @@ -31,10 +34,12 @@ public class AutoEnchantingTableBlockEntityRenderer AutoEnchantingTableBlockEntityRenderState> { private final SpriteGetter sprites; private final BookModel bookModel; + private final ItemModelResolver resolver; public AutoEnchantingTableBlockEntityRenderer(BlockEntityRendererProvider.Context context) { this.sprites = context.sprites(); this.bookModel = new BookModel(context.bakeLayer(ModelLayers.BOOK)); + this.resolver = context.itemModelResolver(); } @Override @@ -51,6 +56,8 @@ public void extractRenderState( ModelFeatureRenderer.@Nullable CrumblingOverlay breakProgress ) { BlockEntityRenderer.super.extractRenderState(blockEntity, state, partialTicks, cameraPosition, breakProgress); + state.flip = Mth.lerp(partialTicks, blockEntity.oldFlip, blockEntity.flip); + state.open = Mth.lerp(partialTicks, blockEntity.oldOpen, blockEntity.open); state.time = blockEntity.time + partialTicks; float or = blockEntity.rot - blockEntity.oldRot; @@ -70,6 +77,10 @@ public void extractRenderState( state.fluid = resource.getFluid(); state.fluidStack = resource.toStack(1); state.fluidResource = resource; + state.displayInputItem = blockEntity.getDisplayInputItem(); + state.inputItemState = FeatureRendererSupport.initialize(state.displayInputItem, this.resolver); + state.displayOutputItem = blockEntity.getDisplayOutputItem(); + state.outputItemState = FeatureRendererSupport.initialize(state.displayOutputItem, this.resolver); } @Override @@ -79,13 +90,16 @@ public void submit( SubmitNodeCollector submitNodeCollector, CameraRenderState camera ) { + // region 渲染书本 poseStack.pushPose(); poseStack.translate(0.5F, 0.75F, 0.5F); + poseStack.translate(0.0F, 0.1F + Mth.sin(state.time * 0.1F) * 0.01F, 0.0F); float rotY = state.rotY; poseStack.mulPose(Axis.YP.rotation(-rotY)); poseStack.mulPose(Axis.ZP.rotationDegrees(80.0F)); - poseStack.translate(0.0F, 0.1F + Mth.sin(state.time * 0.1F) * 0.01F, 0.0F); - BookModel.State animationState = BookModel.State.forAnimation(state.time, 0, 0, 0); + float ff1 = Mth.frac(state.flip + 0.25f) * 1.6f - 0.3f; + float ff2 = Mth.frac(state.flip + 0.75f) * 1.6f - 0.3f; + BookModel.State animationState = BookModel.State.forAnimation(state.time, Mth.clamp(ff1, 0, 1f), Mth.clamp(ff2, 0, 1), state.open); submitNodeCollector.submitModel( this.bookModel, animationState, @@ -99,7 +113,35 @@ public void submit( state.breakProgress ); poseStack.popPose(); + // endregion + // region 渲染物品 + if (!state.displayInputItem.isEmpty()) { + poseStack.pushPose(); + ItemClusterRenderState itemState = state.getInputItemState(); + ItemStackRenderState item = itemState.item; + double x = 0.5; + double y = 1.15; + double z = 0.5; + poseStack.translate(x, y, z); + poseStack.mulPose(Axis.YP.rotation(rotY)); + item.submit(poseStack, submitNodeCollector, state.lightCoords, OverlayTexture.NO_OVERLAY, itemState.outlineColor); + poseStack.popPose(); + } else if (!state.displayOutputItem.isEmpty()) { + poseStack.pushPose(); + ItemClusterRenderState itemState = state.getOutputItemState(); + ItemStackRenderState item = itemState.item; + double x = 0.5; + double y = 1.35; + double z = 0.5; + poseStack.translate(x, y, z); + poseStack.mulPose(Axis.YP.rotation(rotY)); + item.submit(poseStack, submitNodeCollector, state.lightCoords, OverlayTexture.NO_OVERLAY, itemState.outlineColor); + poseStack.popPose(); + } + // endregion + + // region 渲染流体 if (state.amount.getValue() > 0) { poseStack.pushPose(); FluidModel model = FluidRenderHelper.getModel( @@ -111,10 +153,6 @@ public void submit( TextureAtlasSprite sprite = model.stillMaterial().sprite(); int tintColor = tintSource.colorAsStack(state.fluidStack); final int[] numbers = splitNumber(state.getAmount().getValue()); - AnvilCraft.LOGGER.debug("split: "); - for (int i : numbers) { - AnvilCraft.LOGGER.debug(" {}", i); - } Direction[] directions = { Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH }; for (int i = 0; i < numbers.length; i++) { renderFluid(directions[i], numbers[i], submitNodeCollector, poseStack, sprite, state, tintColor); @@ -122,6 +160,7 @@ public void submit( } poseStack.popPose(); } + // endregion } private static void renderFluid( diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/CreativeCrateRenderer.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/CreativeCrateRenderer.java index e4adeaa75b..e442f8e0eb 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/CreativeCrateRenderer.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/CreativeCrateRenderer.java @@ -62,27 +62,30 @@ public void submit( // 在六个面的近表面渲染物品,参考 1.21.1 的渲染位置 for (int side = 0; side < 6; side++) { poseStack.pushPose(); + poseStack.scale(0.5f, 0.5f, 0.5f); switch (side) { - case 0 -> poseStack.translate(0.5, 0.5, 0.9); + case 0 -> poseStack.translate(1f, 1f, 0.2f); case 1 -> { - poseStack.translate(0.5, 0.5, 0.1); + poseStack.translate(1f, 1f, 1.8f); poseStack.mulPose(Axis.YP.rotationDegrees(180)); } case 2 -> { - poseStack.translate(0.9, 0.5, 0.5); + poseStack.translate(0.2f, 1f, 1f); poseStack.mulPose(Axis.YP.rotationDegrees(90)); } case 3 -> { - poseStack.translate(0.1, 0.5, 0.5); + poseStack.translate(1.8f, 1f, 1f); poseStack.mulPose(Axis.YP.rotationDegrees(270)); } case 4 -> { - poseStack.translate(0.5, 0.9, 0.5); + poseStack.translate(1f, 0.2f, 1f); poseStack.mulPose(Axis.XP.rotationDegrees(90)); + poseStack.mulPose(Axis.ZP.rotationDegrees(270)); } case 5 -> { - poseStack.translate(0.5, 0.1, 0.5); + poseStack.translate(1f, 1.8f, 1f); poseStack.mulPose(Axis.XP.rotationDegrees(270)); + poseStack.mulPose(Axis.ZP.rotationDegrees(270)); } default -> { // This should never happen as side is always 0-5 diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/state/AutoEnchantingTableBlockEntityRenderState.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/state/AutoEnchantingTableBlockEntityRenderState.java index cf5734e9b8..32b91600b4 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/state/AutoEnchantingTableBlockEntityRenderState.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/state/AutoEnchantingTableBlockEntityRenderState.java @@ -4,6 +4,8 @@ import lombok.Getter; import lombok.Setter; import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState; +import net.minecraft.client.renderer.entity.state.ItemClusterRenderState; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.material.Fluid; import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.neoforge.transfer.fluid.FluidResource; @@ -13,8 +15,14 @@ public class AutoEnchantingTableBlockEntityRenderState extends BlockEntityRenderState { public float time; public float rotY; + public float flip; + public float open; public final MutableValue amount = new MutableValue<>(0); public Fluid fluid; public FluidStack fluidStack; public FluidResource fluidResource; + public ItemStack displayInputItem; + public ItemStack displayOutputItem; + public ItemClusterRenderState inputItemState; + public ItemClusterRenderState outputItemState; } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/ModCapabilities.java b/src/main/java/dev/dubhe/anvilcraft/init/ModCapabilities.java index e3fa3a54f9..583263a3c9 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/ModCapabilities.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/ModCapabilities.java @@ -41,6 +41,7 @@ import net.neoforged.neoforge.transfer.fluid.BucketResourceHandler; import net.neoforged.neoforge.transfer.fluid.FluidResource; import net.neoforged.neoforge.transfer.item.ItemResource; +import net.neoforged.neoforge.transfer.item.WorldlyContainerWrapper; import org.jspecify.annotations.Nullable; import java.util.Objects; @@ -163,7 +164,7 @@ public static void registerCapabilities(final RegisterCapabilitiesEvent event) { event.registerBlockEntity( Capabilities.Item.BLOCK, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), - (be, ignore) -> be + WorldlyContainerWrapper::new ); event.registerBlockEntity( diff --git a/src/main/java/dev/dubhe/anvilcraft/init/ModMenuTypes.java b/src/main/java/dev/dubhe/anvilcraft/init/ModMenuTypes.java index 4ddefefe36..253ef18a61 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/ModMenuTypes.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/ModMenuTypes.java @@ -3,6 +3,7 @@ import dev.anvilcraft.lib.v2.registrum.util.entry.MenuEntry; import dev.dubhe.anvilcraft.client.gui.screen.ActiveSilencerScreen; import dev.dubhe.anvilcraft.client.gui.screen.AdvancedComparatorScreen; +import dev.dubhe.anvilcraft.client.gui.screen.AutoEnchantingTableScreen; import dev.dubhe.anvilcraft.client.gui.screen.BatchCrafterScreen; import dev.dubhe.anvilcraft.client.gui.screen.BatchCutterScreen; import dev.dubhe.anvilcraft.client.gui.screen.CelestialForgingAnvilScreen; @@ -32,6 +33,7 @@ import dev.dubhe.anvilcraft.client.gui.screen.TranscendenceAnvilScreen; import dev.dubhe.anvilcraft.inventory.ActiveSilencerMenu; import dev.dubhe.anvilcraft.inventory.AdvancedComparatorMenu; +import dev.dubhe.anvilcraft.inventory.AutoEnchantingTableMenu; import dev.dubhe.anvilcraft.inventory.BatchCrafterMenu; import dev.dubhe.anvilcraft.inventory.BatchCutterMenu; import dev.dubhe.anvilcraft.inventory.CelestialForgingAnvilMenu; @@ -188,6 +190,10 @@ public class ModMenuTypes { .menu("storage", StorageMenu::new, () -> StorageScreen::new) .register(); + public static final MenuEntry AUTO_ENCHANTING_TABLE = REGISTRUM + .menu("auto_enchanting_table", AutoEnchantingTableMenu::new, () -> AutoEnchantingTableScreen::new) + .register(); + public static void register() { } diff --git a/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java b/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java new file mode 100644 index 0000000000..cbeedf3545 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java @@ -0,0 +1,118 @@ +package dev.dubhe.anvilcraft.inventory; + +import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.block.entity.AutoEnchantingTableBlockEntity; +import lombok.Getter; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.Container; +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.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.jspecify.annotations.Nullable; + +@Getter +public class AutoEnchantingTableMenu extends AbstractContainerMenu { + private final AutoEnchantingTableBlockEntity blockEntity; + private final Container container; + + private final Level level; + + public AutoEnchantingTableMenu( + @Nullable MenuType menuType, + int containerId, + Inventory inventory, + FriendlyByteBuf extraData + ) { + this(menuType, containerId, inventory, (AutoEnchantingTableBlockEntity) inventory.player.level().getBlockEntity(extraData.readBlockPos())); + } + + public AutoEnchantingTableMenu( + @Nullable MenuType menuType, + int containerId, + Inventory inventory, + AutoEnchantingTableBlockEntity blockEntity + ) { + super(menuType, containerId); + this.level = inventory.player.level(); + this.blockEntity = blockEntity; + this.container = blockEntity; + + this.addPlayerInventory(inventory); + this.addPlayerHotbar(inventory); + + this.addSlot(new Slot(this.container, 0, 7, 18) { + @Override + public boolean mayPlace(ItemStack itemStack) { + return false; + } + + @Override + public boolean mayPickup(Player player) { + return false; + } + }); + this.addSlot(new Slot(this.container, 1, 7, 52) { + @Override + public boolean mayPlace(ItemStack itemStack) { + return false; + } + + @Override + public boolean mayPickup(Player player) { + return false; + } + }); + this.addSlot(new Slot(this.container, 2, 27, 18)); + } + + private void addPlayerInventory(Inventory playerInventory) { + for (int i = 0; i < 3; ++i) { + for (int l = 0; l < 9; ++l) { + this.addSlot(new Slot(playerInventory, l + i * 9 + 9, 8 + l * 18, 84 + i * 18)); + } + } + } + + private void addPlayerHotbar(Inventory playerInventory) { + for (int i = 0; i < 9; ++i) { + this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 142)); + } + } + + @Override + public ItemStack quickMoveStack(Player player, int slotIndex) { + AnvilCraft.LOGGER.debug("index: {}", slotIndex); + if (slotIndex == 36 || slotIndex == 37) { + return ItemStack.EMPTY; + } + Slot slot = this.slots.get(slotIndex); + if (slotIndex == 38) { + ItemStack item = slot.getItem(); + if (!item.isEmpty()) { + if (!this.moveItemStackTo(item, + 1, 36, true)) { + return ItemStack.EMPTY; + } + } + } + if (slotIndex >= 0 && slotIndex <= 35) { + ItemStack item = slot.getItem(); + if (!item.isEmpty()) { + if (!this.moveItemStackTo(item, + 38, 39, true)) { + return ItemStack.EMPTY; + } + } + } + return ItemStack.EMPTY; + } + + @Override + public boolean stillValid(Player player) { + return this.container.stillValid(player); + } +} From ed386614ad89446a6f1960b9297016ba9312d999 Mon Sep 17 00:00:00 2001 From: burin Date: Wed, 8 Jul 2026 21:39:27 +0800 Subject: [PATCH 4/7] fix: fix style --- .../AutoEnchantingTableBlockEntity.java | 34 +++++++++++++------ .../workstation/AutoEnchantingTableBlock.java | 8 +++-- .../gui/component/FluidDisplayWidget.java | 2 +- .../inventory/AutoEnchantingTableMenu.java | 8 ++++- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java index 7455ac4c7f..cd1319b73b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java @@ -239,11 +239,11 @@ public void bookAnimationTick( if (!this.getItem(0).isEmpty()) { this.open += 0.1f; - if (this.open < 0.5f || random.nextInt(40) == 0) { + if (this.open < 0.5f || this.random.nextInt(40) == 0) { float old = this.flipT; do { - this.flipT = this.flipT + (random.nextInt(4) - random.nextInt(4)); + this.flipT = this.flipT + (this.random.nextInt(4) - this.random.nextInt(4)); } while (old == this.flipT); } } else { @@ -345,7 +345,14 @@ public void serverTick( this.onChange(); } this.enchantmentSeed = level.getRandom().nextInt(); - level.playSound(null, this.getBlockPos(), SoundEvents.ENCHANTMENT_TABLE_USE, SoundSource.BLOCKS, 1.0F, level.getRandom().nextFloat() * 0.1F + 0.9F); + level.playSound( + null, + this.getBlockPos(), + SoundEvents.ENCHANTMENT_TABLE_USE, + SoundSource.BLOCKS, + 1.0F, + level.getRandom().nextFloat() * 0.1F + 0.9F + ); } else { // TODO: 引物模式 } @@ -360,7 +367,7 @@ private int[][] getCostAndEnchant(ItemStack itemStack) { return new int[][]{}; } if (this.level != null) { - IdMap> idMap = this.level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); + final IdMap> idMap = this.level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); int bookcases = 0; for (BlockPos offset : AutoEnchantingTableBlock.BOOKSHELF_OFFSETS) { if (EnchantingTableBlock.isValidBookShelf(this.level, this.getBlockPos(), offset)) { @@ -370,9 +377,9 @@ private int[][] getCostAndEnchant(ItemStack itemStack) { if (this.enchantmentSeed == 0) { this.enchantmentSeed = level.getRandom().nextInt(); } - random.setSeed(this.enchantmentSeed); + this.random.setSeed(this.enchantmentSeed); for (int ixx = 0; ixx < 3; ixx++) { - costs[ixx] = EnchantmentHelper.getEnchantmentCost(random, ixx, bookcases, itemStack); + costs[ixx] = EnchantmentHelper.getEnchantmentCost(this.random, ixx, bookcases, itemStack); enchantClue[ixx] = -1; levelClue[ixx] = -1; if (costs[ixx] < ixx + 1) { @@ -384,7 +391,7 @@ private int[][] getCostAndEnchant(ItemStack itemStack) { if (costs[ix] > 0) { List list = this.getEnchantmentList(level.registryAccess(), itemStack, ix, costs[ix]); if (!list.isEmpty()) { - EnchantmentInstance enchant = list.get(random.nextInt(list.size())); + EnchantmentInstance enchant = list.get(this.random.nextInt(list.size())); enchantClue[ix] = idMap.getId(enchant.enchantment()); levelClue[ix] = enchant.level(); } @@ -399,16 +406,21 @@ private int[][] getCostAndEnchant(ItemStack itemStack) { return new int[][]{}; } - private List getEnchantmentList(final RegistryAccess access, final ItemStack itemStack, final int slot, final int enchantmentCost) { - random.setSeed(this.enchantmentSeed + slot); + private List getEnchantmentList( + final RegistryAccess access, + final ItemStack itemStack, + final int slot, + final int enchantmentCost + ) { + this.random.setSeed(this.enchantmentSeed + slot); Optional> tag = access.lookupOrThrow(Registries.ENCHANTMENT).get(EnchantmentTags.IN_ENCHANTING_TABLE); if (tag.isEmpty()) { return List.of(); } - List list = EnchantmentHelper.selectEnchantment(random, itemStack, enchantmentCost, tag.get().stream()); + List list = EnchantmentHelper.selectEnchantment(this.random, itemStack, enchantmentCost, tag.get().stream()); if (itemStack.is(Items.BOOK) && list.size() > 1) { - list.remove(random.nextInt(list.size())); + list.remove(this.random.nextInt(list.size())); } return list; diff --git a/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java index 320abcd328..39d7d2c806 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java @@ -102,8 +102,12 @@ public BlockEntity newBlockEntity(BlockPos worldPosition, BlockState blockState) @Override public @Nullable BlockEntityTicker getTicker(Level level, BlockState blockState, BlockEntityType type) { return level.isClientSide() - ? createTickerHelper(type, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), (world, pos, state, be) -> be.bookAnimationTick(world, pos, state)) - : createTickerHelper(type, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), (world, pos, state, be) -> be.serverTick(world, pos, state)); + ? createTickerHelper( + type, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), + (world, pos, state, be) -> be.bookAnimationTick(world, pos, state)) + : createTickerHelper( + type, ModBlockEntities.AUTO_ENCHANTING_TABLE.get(), ( + world, pos, state, be) -> be.serverTick(world, pos, state)); } @Override diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java index 1d04fa9873..0b546cc069 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java @@ -40,7 +40,7 @@ public FluidDisplayWidget( @Override protected void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { - Identifier fluidTexture = getFluidTexture(); + Identifier fluidTexture = this.getFluidTexture(); if (fluidTexture != null) { graphics.blit( RenderPipelines.GUI_TEXTURED, diff --git a/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java b/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java index cbeedf3545..d021a0f6aa 100644 --- a/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java +++ b/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java @@ -27,7 +27,12 @@ public AutoEnchantingTableMenu( Inventory inventory, FriendlyByteBuf extraData ) { - this(menuType, containerId, inventory, (AutoEnchantingTableBlockEntity) inventory.player.level().getBlockEntity(extraData.readBlockPos())); + this( + menuType, + containerId, + inventory, + (AutoEnchantingTableBlockEntity) inventory.player.level().getBlockEntity(extraData.readBlockPos()) + ); } public AutoEnchantingTableMenu( @@ -55,6 +60,7 @@ public boolean mayPickup(Player player) { return false; } }); + this.addSlot(new Slot(this.container, 1, 7, 52) { @Override public boolean mayPlace(ItemStack itemStack) { From 852eed178c46b8c1d7cbf04c985e09d7759bb838 Mon Sep 17 00:00:00 2001 From: burin Date: Wed, 15 Jul 2026 21:19:02 +0800 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E9=99=84=E9=AD=94=E5=8F=B0=E7=9A=84=E5=BC=95=E7=89=A9?= =?UTF-8?q?=E9=99=84=E9=AD=94=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dependencies.gradle | 3 - .../assets/anvilcraft/lang/en_ud.json | 2 + .../assets/anvilcraft/lang/en_us.json | 2 + .../recipes/misc/auto_enchanting_table.json | 65 +++ .../recipe/auto_enchanting_table.json | 17 + .../block/enchantment_power_provider.json | 5 + .../AutoEnchantingTableBlockEntity.java | 296 +++++++------ .../block/storage/TranscendiumBlock.java | 8 + .../workstation/AutoEnchantingTableBlock.java | 32 ++ .../component/AutoEnchantingTableButton.java | 63 +++ .../gui/component/FluidDisplayWidget.java | 2 +- .../client/gui/component/TriStateButton.java | 57 +-- .../gui/screen/AutoEnchantingTableScreen.java | 395 +++++++++++++++++- .../gui/screen/SmartBlockPlacerScreen.java | 4 + .../anvilcraft/data/lang/ScreenLang.java | 3 + .../data/recipe/ShapedRecipeLoader.java | 19 + .../anvilcraft/data/tags/BlockTagLoader.java | 3 + .../init/item/tabs/FunctionalBlocks.java | 1 + .../inventory/AutoEnchantingTableMenu.java | 195 ++++++++- .../AutoEnchantingTableContainer.java | 38 ++ .../AutoEnchantingTableSyncPacket.java | 47 +++ .../dubhe/anvilcraft/util/TickDebouncer.java | 32 ++ .../blockstates/auto_enchanting_table.json | 5 +- 23 files changed, 1088 insertions(+), 206 deletions(-) create mode 100644 src/generated/resources/data/anvilcraft/advancement/recipes/misc/auto_enchanting_table.json create mode 100644 src/generated/resources/data/anvilcraft/recipe/auto_enchanting_table.json create mode 100644 src/generated/resources/data/minecraft/tags/block/enchantment_power_provider.json create mode 100644 src/main/java/dev/dubhe/anvilcraft/client/gui/component/AutoEnchantingTableButton.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/inventory/container/AutoEnchantingTableContainer.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/network/AutoEnchantingTableSyncPacket.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/util/TickDebouncer.java diff --git a/dependencies.gradle b/dependencies.gradle index 2981f65c60..3638fc1767 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -26,7 +26,4 @@ dependencies { // Jade compileClasspath(libs.jade) runtimeClasspath(libs.jade) - - compileOnly("maven.modrinth:pipez:neoforge-1.2.30+26.1.2") - runtimeOnly("maven.modrinth:pipez:neoforge-1.2.30+26.1.2") } diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 8b674c4541..2587c08131 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -1422,6 +1422,8 @@ "screen.anvilcraft.active_silencer.search": "ɥɔɹɐǝs oʇ pɹoʍʎǝʞ ɹǝʇuǝ", "screen.anvilcraft.active_silencer.title": "ɹǝɔuǝꞁᴉS ǝʌᴉʇɔⱯ", "screen.anvilcraft.anvil_hammer.title": "ʞɔoꞁᗺ ᵷuᴉʎɟᴉpoW", + "screen.anvilcraft.auto_enchanting_table.fluid_display": "%s :dxƎ", + "screen.anvilcraft.auto_enchanting_table.out_of_limit": "ǝɯnꞁoʌ pᴉnꞁɟ ǝɔuǝᴉɹǝdxǝ ɹo ʎʇᴉꞁᴉqɐ ʇuǝɯʇuɐɥɔuǝ ǝɥʇ uɐɥʇ ɹǝʇɐǝɹᵷ sᴉ sꞁǝʌǝꞁ ʇuǝɯʇuɐɥɔuǝ ꞁꞁɐ ɟo ɯns ǝɥ⟘", "screen.anvilcraft.button.compare_mode_hysteresis": "sᴉsǝɹǝʇsʎH :ǝpoW", "screen.anvilcraft.button.compare_mode_window": "ʍopuᴉM :ǝpoW", "screen.anvilcraft.button.direction": "%s :uoᴉʇɔǝɹᴉᗡ ʇndʇnO", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index 85d36f833e..c2db0b46cd 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -1422,6 +1422,8 @@ "screen.anvilcraft.active_silencer.search": "enter keyword to search", "screen.anvilcraft.active_silencer.title": "Active Silencer", "screen.anvilcraft.anvil_hammer.title": "Modifying Block", + "screen.anvilcraft.auto_enchanting_table.fluid_display": "Exp: %s", + "screen.anvilcraft.auto_enchanting_table.out_of_limit": "The sum of all enchantment levels is greater than the enchantment ability or experience fluid volume", "screen.anvilcraft.button.compare_mode_hysteresis": "Mode: Hysteresis", "screen.anvilcraft.button.compare_mode_window": "Mode: Window", "screen.anvilcraft.button.direction": "Output Direction: %s", diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/misc/auto_enchanting_table.json b/src/generated/resources/data/anvilcraft/advancement/recipes/misc/auto_enchanting_table.json new file mode 100644 index 0000000000..dd11240d72 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/misc/auto_enchanting_table.json @@ -0,0 +1,65 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_enchanting_table": { + "conditions": { + "items": [ + { + "items": "minecraft:enchanting_table" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_fluid_tank": { + "conditions": { + "items": [ + { + "items": "anvilcraft:fluid_tank" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_magnetoelectric_core": { + "conditions": { + "items": [ + { + "items": "anvilcraft:magnetoelectric_core" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_royal_steel_ingot": { + "conditions": { + "items": [ + { + "items": "anvilcraft:royal_steel_ingot" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:auto_enchanting_table" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_fluid_tank", + "has_enchanting_table", + "has_royal_steel_ingot", + "has_magnetoelectric_core" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:auto_enchanting_table" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/auto_enchanting_table.json b/src/generated/resources/data/anvilcraft/recipe/auto_enchanting_table.json new file mode 100644 index 0000000000..8ed72e757e --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/auto_enchanting_table.json @@ -0,0 +1,17 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "E": "minecraft:enchanting_table", + "R": "anvilcraft:royal_steel_ingot", + "T": "anvilcraft:fluid_tank", + "e": "anvilcraft:magnetoelectric_core" + }, + "pattern": [ + "TET", + "ReR" + ], + "result": { + "id": "anvilcraft:auto_enchanting_table" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/enchantment_power_provider.json b/src/generated/resources/data/minecraft/tags/block/enchantment_power_provider.json new file mode 100644 index 0000000000..82f62fce6c --- /dev/null +++ b/src/generated/resources/data/minecraft/tags/block/enchantment_power_provider.json @@ -0,0 +1,5 @@ +{ + "values": [ + "anvilcraft:transcendium_block" + ] +} \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java index cd1319b73b..1683eaea6c 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java @@ -1,15 +1,14 @@ package dev.dubhe.anvilcraft.block.entity; +import com.mojang.serialization.Codec; import dev.dubhe.anvilcraft.api.power.IPowerConsumer; import dev.dubhe.anvilcraft.api.power.PowerGrid; import dev.dubhe.anvilcraft.block.workstation.AutoEnchantingTableBlock; import dev.dubhe.anvilcraft.init.ModMenuTypes; import dev.dubhe.anvilcraft.init.block.ModFluids; -import dev.dubhe.anvilcraft.init.enchantment.ModEnchantments; -import dev.dubhe.anvilcraft.init.item.ModItems; import dev.dubhe.anvilcraft.inventory.AutoEnchantingTableMenu; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.ints.IntArraySet; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import lombok.Getter; import lombok.Setter; import net.minecraft.core.BlockPos; @@ -19,7 +18,6 @@ import net.minecraft.core.HolderSet; import net.minecraft.core.IdMap; import net.minecraft.core.NonNullList; -import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; @@ -27,25 +25,25 @@ import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -import net.minecraft.resources.ResourceKey; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.EnchantmentTags; import net.minecraft.util.Mth; import net.minecraft.util.ProblemReporter; import net.minecraft.util.RandomSource; -import net.minecraft.util.Util; +import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; +import net.minecraft.world.InteractionHand; import net.minecraft.world.WorldlyContainer; import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.item.enchantment.EnchantmentInstance; -import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.item.enchantment.ItemEnchantments; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.EnchantingTableBlock; import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; @@ -57,13 +55,14 @@ import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.neoforge.transfer.fluid.FluidResource; import net.neoforged.neoforge.transfer.fluid.FluidStacksResourceHandler; +import net.neoforged.neoforge.transfer.fluid.FluidUtil; import net.neoforged.neoforge.transfer.transaction.Transaction; import org.jspecify.annotations.Nullable; import java.util.List; -import java.util.Map; -import java.util.Objects; import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; public class AutoEnchantingTableBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, IPowerConsumer { @Getter @@ -72,9 +71,19 @@ public class AutoEnchantingTableBlockEntity extends BaseContainerBlockEntity imp private PowerGrid grid; /// 索引0:物品输入 1:物品输出 2:引物 private NonNullList items = NonNullList.withSize(3, ItemStack.EMPTY); - @Getter - private final Map>> enchantmentMap = new Object2ObjectOpenHashMap<>(); + private final Set selectedEnchantmentSet = new IntArraySet(); + @Getter + @Setter + private ItemStack lastPrologueItem = ItemStack.EMPTY; + @Getter + private Runnable prologueSlotUpdateListener = () -> {}; + @Getter + @Setter + private Consumer slotChangedListener = (_) -> {}; + @Setter + private boolean isOpenMenu = false; + private int enchantmentSeed = 0; private final RandomSource random = RandomSource.create(); @@ -105,117 +114,6 @@ protected void onContentsChanged(int index, FluidStack previousContents) { public float flipA; // endregion - public static Map>> getEnchantmentMap(Level level) { - Registry enchantments = level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT); - - // 蓝宝石护符 - ObjectArrayList> sapphireAmulet = new ObjectArrayList<>(); - enchantments.get(Enchantments.BREACH).ifPresent((ref) -> sapphireAmulet.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.AQUA_AFFINITY).ifPresent((ref) -> sapphireAmulet.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.DEPTH_STRIDER).ifPresent((ref) -> sapphireAmulet.add(Objects.requireNonNull(ref.getKey()))); - - // 红宝石护符 - ObjectArrayList> rubyAmulet = new ObjectArrayList<>(); - enchantments.get(Enchantments.FIRE_PROTECTION).ifPresent((ref) -> rubyAmulet.add(Objects.requireNonNull(ref.getKey()))); - - // 黄玉护符 - ObjectArrayList> topazAmulet = new ObjectArrayList<>(); - enchantments.get(Enchantments.CHANNELING).ifPresent((ref) -> topazAmulet.add(Objects.requireNonNull(ref.getKey()))); - - // 绿宝石护符 - Iterable> tradeableEnchants = enchantments.getTagOrEmpty(EnchantmentTags.TRADEABLE); - ObjectArrayList> emeraldAmulet = new ObjectArrayList<>(); - tradeableEnchants.forEach((holder) -> { - ResourceKey key = Objects.requireNonNull(holder.getKey()); - if (!sapphireAmulet.contains(key) && !rubyAmulet.contains(key) && !topazAmulet.contains(key)) { - emeraldAmulet.add(key); - } - }); - - // 宝石护符 - ObjectArrayList> gemAmulet = new ObjectArrayList<>(); - gemAmulet.addAll(sapphireAmulet); - gemAmulet.addAll(rubyAmulet); - gemAmulet.addAll(topazAmulet); - gemAmulet.addAll(emeraldAmulet); - - // 羽毛护符 - ObjectArrayList> featherAmulet = new ObjectArrayList<>(); - enchantments.get(Enchantments.FEATHER_FALLING).ifPresent((ref) -> featherAmulet.add(Objects.requireNonNull(ref.getKey()))); - - // 寂静护符 - ObjectArrayList> silenceAmulet = new ObjectArrayList<>(); - enchantments.get(Enchantments.PROTECTION).ifPresent((ref) -> silenceAmulet.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.SWIFT_SNEAK).ifPresent((ref) -> silenceAmulet.add(Objects.requireNonNull(ref.getKey()))); - - // 猫护符 - ObjectArrayList> catAmulet = new ObjectArrayList<>(); - enchantments.get(Enchantments.BLAST_PROTECTION).ifPresent((ref) -> catAmulet.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.THORNS).ifPresent((ref) -> catAmulet.add(Objects.requireNonNull(ref.getKey()))); - - // 狗护符 - ObjectArrayList> dogAmulet = new ObjectArrayList<>(); - enchantments.get(Enchantments.PROJECTILE_PROTECTION).ifPresent((ref) -> dogAmulet.add(Objects.requireNonNull(ref.getKey()))); - - // 自然护符 - ObjectArrayList> natureAmulet = new ObjectArrayList<>(); - natureAmulet.addAll(featherAmulet); - natureAmulet.addAll(silenceAmulet); - natureAmulet.addAll(catAmulet); - natureAmulet.addAll(dogAmulet); - - // 紫水晶 - ObjectArrayList> amethystShard = new ObjectArrayList<>(); - enchantments.get(ModEnchantments.FELLING_KEY).ifPresent((ref) -> amethystShard.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(ModEnchantments.HARVEST_KEY).ifPresent((ref) -> amethystShard.add(Objects.requireNonNull(ref.getKey()))); - - // 皇家钢锭 - ObjectArrayList> royalSteelShard = new ObjectArrayList<>(); - enchantments.get(Enchantments.SILK_TOUCH).ifPresent((ref) -> royalSteelShard.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.UNBREAKING).ifPresent((ref) -> royalSteelShard.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.MENDING).ifPresent((ref) -> royalSteelShard.add(Objects.requireNonNull(ref.getKey()))); - - // 余烬金属锭 - ObjectArrayList> emberMetal = new ObjectArrayList<>(); - enchantments.get(ModEnchantments.SMELTING_KEY).ifPresent((ref) -> emberMetal.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.FIRE_ASPECT).ifPresent((ref) -> emberMetal.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.FLAME).ifPresent((ref) -> emberMetal.add(Objects.requireNonNull(ref.getKey()))); - - // 浮霜金属锭 - ObjectArrayList> frostMetal = new ObjectArrayList<>(); - enchantments.get(ModEnchantments.DISINTEGRATION_KEY).ifPresent((ref) -> frostMetal.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.FROST_WALKER).ifPresent((ref) -> frostMetal.add(Objects.requireNonNull(ref.getKey()))); - - // 超限合金锭 - ObjectArrayList> transcendium = new ObjectArrayList<>(); - enchantments.get(Enchantments.FORTUNE).ifPresent((ref) -> transcendium.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.LOOTING).ifPresent((ref) -> transcendium.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(ModEnchantments.BEHEADING_KEY).ifPresent((ref) -> transcendium.add(Objects.requireNonNull(ref.getKey()))); - enchantments.get(Enchantments.LUCK_OF_THE_SEA).ifPresent((ref) -> transcendium.add(Objects.requireNonNull(ref.getKey()))); - transcendium.addAll(amethystShard); - transcendium.addAll(royalSteelShard); - transcendium.addAll(emberMetal); - transcendium.addAll(frostMetal); - - return Map.copyOf(Util.make(new Object2ObjectOpenHashMap<>(), (map) -> { - map.put(ModItems.SAPPHIRE_AMULET.get(), List.copyOf(sapphireAmulet)); - map.put(ModItems.RUBY_AMULET.get(), List.copyOf(rubyAmulet)); - map.put(ModItems.TOPAZ.get(), List.copyOf(topazAmulet)); - map.put(ModItems.EMERALD_AMULET.get(), List.copyOf(emeraldAmulet)); - map.put(ModItems.GEM_AMULET.get(), List.copyOf(gemAmulet)); - map.put(ModItems.FEATHER_AMULET.get(), List.copyOf(featherAmulet)); - map.put(ModItems.SILENCE_AMULET.get(), List.copyOf(silenceAmulet)); - map.put(ModItems.CAT_AMULET.get(), List.copyOf(catAmulet)); - map.put(ModItems.DOG_AMULET.get(), List.copyOf(dogAmulet)); - map.put(ModItems.NATURE_AMULET.get(), List.copyOf(natureAmulet)); - map.put(Items.AMETHYST_SHARD, List.copyOf(amethystShard)); - map.put(ModItems.ROYAL_STEEL_INGOT.get(), List.copyOf(royalSteelShard)); - map.put(ModItems.EMBER_METAL_INGOT.get(), List.copyOf(emberMetal)); - map.put(ModItems.FROST_METAL_INGOT.get(), List.copyOf(frostMetal)); - map.put(ModItems.TRANSCENDIUM_INGOT.get(), List.copyOf(transcendium)); - })); - } - public AutoEnchantingTableBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); } @@ -291,13 +189,20 @@ public void serverTick( final BlockPos pos, final BlockState state ) { + if (this.grid == null) { + level.setBlockAndUpdate(this.getBlockPos(), this.getBlockState().setValue(AutoEnchantingTableBlock.OVERLOAD, true)); + return; + } + if (this.grid.isWorking() && this.getBlockState().getValue(AutoEnchantingTableBlock.OVERLOAD)) { + level.setBlockAndUpdate(this.getBlockPos(), this.getBlockState().setValue(AutoEnchantingTableBlock.OVERLOAD, false)); + } else if (!this.grid.isWorking() && !this.getBlockState().getValue(AutoEnchantingTableBlock.OVERLOAD)) { + level.setBlockAndUpdate(this.getBlockPos(), this.getBlockState().setValue(AutoEnchantingTableBlock.OVERLOAD, true)); + return; + } if (this.cooldown > 0) { this.cooldown--; } else if (this.cooldown == 0) { this.cooldown = 80; - if (this.grid != null && !this.grid.isWorking()) { - return; - } if (!this.getItem(1).isEmpty() || this.getItem(0).isEmpty()) { return; } @@ -307,30 +212,26 @@ public void serverTick( this.setItem(1, enchantItem); return; } - if (this.getItem(2).isEmpty()) { + ItemStack prologueItem = this.getItem(2); + if (prologueItem.isEmpty()) { + this.lastPrologueItem = ItemStack.EMPTY; // 无引物模式 - int exp = 0; - for (BlockPos offset : AutoEnchantingTableBlock.BOOKSHELF_OFFSETS) { - if (EnchantingTableBlock.isValidBookShelf(level, pos, offset)) { - exp += 400; - if (exp >= 6000) { - exp = 6000; - break; - } - } - } - if (this.fluidHandler.getAmountAsInt(0) < exp) { - return; - } + int exp = Mth.clamp(this.getBookShelf(level, pos) * 400, 0, 6000); int[][] costAndEnchant = this.getCostAndEnchant(enchantItem); if (costAndEnchant.length == 0) { return; } int index = level.getRandom().nextInt(0, 3); + if (exp <= 0) { + exp = (7 + 2 * index) * 20; + } + if (this.fluidHandler.getAmountAsInt(0) < exp) { + return; + } int[] enchant = costAndEnchant[index]; List enchantmentList = this.getEnchantmentList(level.registryAccess(), enchantItem, index, enchant[0]); if (enchantItem.is(Items.BOOK)) { - enchantItem.transmuteCopy(Items.ENCHANTED_BOOK); + enchantItem = enchantItem.transmuteCopy(Items.ENCHANTED_BOOK, 1); } if (!enchantmentList.isEmpty()) { for (EnchantmentInstance enchantmentInstance : enchantmentList) { @@ -342,7 +243,6 @@ public void serverTick( try (Transaction ts = Transaction.openRoot()) { this.fluidHandler.extract(FluidResource.of(ModFluids.EXP_FLUID), exp, ts); ts.commit(); - this.onChange(); } this.enchantmentSeed = level.getRandom().nextInt(); level.playSound( @@ -354,11 +254,82 @@ public void serverTick( level.getRandom().nextFloat() * 0.1F + 0.9F ); } else { - // TODO: 引物模式 + if (this.isOpenMenu) { + return; + } + if (this.selectedEnchantmentSet.isEmpty()) { + return; + } + IdMap> idMap = level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); + int totalLevel = 0; + for (int id : this.getSelectedEnchantmentSet()) { + Holder enchantmentHolder = idMap.byId(id); + if (enchantmentHolder != null) { + totalLevel += enchantmentHolder.value().getMaxLevel(); + } + } + if (totalLevel > this.getBookShelf(level, pos) + || totalLevel * 400 > this.fluidHandler.getAmountAsInt(0)) { + return; + } + ItemStack enchantedBook = Items.ENCHANTED_BOOK.getDefaultInstance(); + for (int id : this.selectedEnchantmentSet) { + Holder enchantmentHolder = idMap.byId(id); + if (enchantmentHolder != null) { + enchantedBook.enchant(enchantmentHolder, enchantmentHolder.value().getMaxLevel()); + } + } + if (enchantItem.is(Items.BOOK)) { + this.setItem(0, ItemStack.EMPTY); + this.setItem(1, enchantedBook); + } else { + applyEnchantment(enchantItem, enchantedBook); + this.setItem(0, ItemStack.EMPTY); + this.setItem(1, enchantItem); + } + try (Transaction ts = Transaction.openRoot()) { + this.fluidHandler.extract(FluidResource.of(ModFluids.EXP_FLUID), totalLevel * 400, ts); + ts.commit(); + } + level.playSound( + null, + this.getBlockPos(), + SoundEvents.ENCHANTMENT_TABLE_USE, + SoundSource.BLOCKS, + 1.0F, + level.getRandom().nextFloat() * 0.1F + 0.9F + ); } } } + public boolean onPlayerUse(Player player, InteractionHand hand) { + return FluidUtil.interactWithFluidHandler(player, hand, worldPosition, this.getFluidHandler()); + } + + public static void applyEnchantment(ItemStack item, ItemStack enchantedBook) { + ItemEnchantments enchantmentsOnRight = EnchantmentHelper.getEnchantmentsForCrafting(enchantedBook); + for (Object2IntMap.Entry> entry : enchantmentsOnRight.entrySet()) { + Holder holder = entry.getKey(); + int enchantmentsOnRightLevel = entry.getIntValue(); + boolean compatible = item.supportsEnchantment(holder); + if (!compatible) { + continue; + } + item.enchant(holder, enchantmentsOnRightLevel); + } + } + + private int getBookShelf(Level level, BlockPos pos) { + float bookcases = 0; + for (BlockPos offset : AutoEnchantingTableBlock.BOOKSHELF_OFFSETS) { + if (EnchantingTableBlock.isValidBookShelf(level, pos, offset)) { + bookcases += level.getBlockState(pos.offset(offset)).getEnchantPowerBonus(level, pos.offset(offset)); + } + } + return (int) bookcases; + } + private int[][] getCostAndEnchant(ItemStack itemStack) { int[] costs = new int[3]; int[] enchantClue = new int[]{-1, -1, -1}; @@ -368,18 +339,13 @@ private int[][] getCostAndEnchant(ItemStack itemStack) { } if (this.level != null) { final IdMap> idMap = this.level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); - int bookcases = 0; - for (BlockPos offset : AutoEnchantingTableBlock.BOOKSHELF_OFFSETS) { - if (EnchantingTableBlock.isValidBookShelf(this.level, this.getBlockPos(), offset)) { - bookcases++; - } - } + float bookcases = this.getBookShelf(level, getBlockPos()); if (this.enchantmentSeed == 0) { this.enchantmentSeed = level.getRandom().nextInt(); } this.random.setSeed(this.enchantmentSeed); for (int ixx = 0; ixx < 3; ixx++) { - costs[ixx] = EnchantmentHelper.getEnchantmentCost(this.random, ixx, bookcases, itemStack); + costs[ixx] = EnchantmentHelper.getEnchantmentCost(this.random, ixx, (int) bookcases, itemStack); enchantClue[ixx] = -1; levelClue[ixx] = -1; if (costs[ixx] < ixx + 1) { @@ -426,12 +392,28 @@ private List getEnchantmentList( return list; } + public void registerUpdateListener(final Runnable prologueSlotUpdateListener) { + this.prologueSlotUpdateListener = prologueSlotUpdateListener; + } + + @Override + public void setChanged() { + super.setChanged(); + this.slotChangedListener.accept(this); + } + @Override protected void saveAdditional(ValueOutput output) { super.saveAdditional(output); ContainerHelper.saveAllItems(output, this.items); this.fluidHandler.serialize(output); output.putInt("cooldown", this.cooldown); + output.store("LastPrologueItem", ItemStack.OPTIONAL_CODEC, this.lastPrologueItem); + ValueOutput.TypedOutputList selectedEnchantments = output.list("selectedEnchantments", Codec.INT); + for (Integer id : this.selectedEnchantmentSet) { + selectedEnchantments.add(id); + } + output.putBoolean("openMenu", this.isOpenMenu); } @Override @@ -440,6 +422,12 @@ protected void loadAdditional(ValueInput input) { ContainerHelper.loadAllItems(input, this.items); this.fluidHandler.deserialize(input); input.getInt("cooldown").ifPresent((cooldown) -> this.cooldown = cooldown); + input.read("LastPrologueItem", ItemStack.OPTIONAL_CODEC).ifPresent(this::setLastPrologueItem); + this.selectedEnchantmentSet.clear(); + for (Integer id : input.listOrEmpty("selectedEnchantments", Codec.INT)) { + this.selectedEnchantmentSet.add(id); + } + input.getBooleanOr("openMenu", false); } @Override @@ -472,7 +460,7 @@ protected Component getDefaultName() { } @Override - protected NonNullList getItems() { + public NonNullList getItems() { return this.items; } @@ -509,12 +497,12 @@ public int[] getSlotsForFace(Direction direction) { @Override public boolean canPlaceItemThroughFace(int slot, ItemStack itemStack, @Nullable Direction direction) { - return slot == 0; + return slot == 0 && this.items.getFirst().isEmpty(); } @Override public boolean canTakeItemThroughFace(int slot, ItemStack itemStack, Direction direction) { - return slot == 1; + return slot == 1 && !this.items.get(1).isEmpty(); } @Override diff --git a/src/main/java/dev/dubhe/anvilcraft/block/storage/TranscendiumBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/storage/TranscendiumBlock.java index 04bc9cc009..571478b98b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/storage/TranscendiumBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/storage/TranscendiumBlock.java @@ -1,10 +1,18 @@ package dev.dubhe.anvilcraft.block.storage; import dev.dubhe.anvilcraft.api.block.ITranscendiumBlock; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; public class TranscendiumBlock extends Block implements ITranscendiumBlock { public TranscendiumBlock(Properties properties) { super(properties); } + + @Override + public float getEnchantPowerBonus(BlockState state, BlockGetter level, BlockPos pos) { + return 10f; + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java index 39d7d2c806..493d3d0c58 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/workstation/AutoEnchantingTableBlock.java @@ -1,6 +1,7 @@ package dev.dubhe.anvilcraft.block.workstation; import com.mojang.serialization.MapCodec; +import dev.dubhe.anvilcraft.api.power.IPowerComponent; import dev.dubhe.anvilcraft.block.better.BetterBaseEntityBlock; import dev.dubhe.anvilcraft.block.entity.AutoEnchantingTableBlockEntity; import dev.dubhe.anvilcraft.init.ModMenuTypes; @@ -12,6 +13,7 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; @@ -22,6 +24,8 @@ import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; @@ -31,6 +35,8 @@ import java.util.List; public class AutoEnchantingTableBlock extends BetterBaseEntityBlock { + public static final BooleanProperty OVERLOAD = IPowerComponent.OVERLOAD; + private static final VoxelShape SHAPE = Block.column(16.0, 0.0, 12.0); public static final List BOOKSHELF_OFFSETS = BlockPos.betweenClosedStream(-2, 0, -2, 2, 1, 2) @@ -45,6 +51,8 @@ protected MapCodec codec() { public AutoEnchantingTableBlock(Properties properties) { super(properties); + this.registerDefaultState(this.getStateDefinition().any() + .setValue(OVERLOAD, true)); } @Override @@ -57,6 +65,24 @@ protected boolean useShapeForLightOcclusion(BlockState state) { return true; } + @Override + protected InteractionResult useItemOn( + ItemStack stack, + BlockState state, + Level level, + BlockPos pos, + Player player, + InteractionHand hand, + BlockHitResult hitResult + ) { + if (level.getBlockEntity(pos) instanceof AutoEnchantingTableBlockEntity autoEnchantingTableBlockEntity) { + if (autoEnchantingTableBlockEntity.onPlayerUse(player, hand)) { + return InteractionResult.SUCCESS; + } + } + return super.useItemOn(stack, state, level, pos, player, hand, hitResult); + } + @Override public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { if (level.isClientSide()) { @@ -69,6 +95,7 @@ public InteractionResult use(BlockState state, Level level, BlockPos pos, Player return InteractionResult.PASS; } ModMenuTypes.open(serverPlayer, autoEnchantingTableBlockEntity, pos); + autoEnchantingTableBlockEntity.setOpenMenu(true); } } return InteractionResult.SUCCESS; @@ -114,4 +141,9 @@ public BlockEntity newBlockEntity(BlockPos worldPosition, BlockState blockState) protected boolean isPathfindable(BlockState state, PathComputationType type) { return false; } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(OVERLOAD); + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/AutoEnchantingTableButton.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/AutoEnchantingTableButton.java new file mode 100644 index 0000000000..e6293ce8a2 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/AutoEnchantingTableButton.java @@ -0,0 +1,63 @@ +package dev.dubhe.anvilcraft.client.gui.component; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import lombok.Getter; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.input.InputWithModifiers; +import net.minecraft.core.Holder; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantment; + +import java.util.List; + +public class AutoEnchantingTableButton extends TriStateButton { + @Getter + private Holder holder; + private final OnPress onPress; + + public AutoEnchantingTableButton( + int x, + int y, + int width, + int height, + Identifier texture, + Holder enchantmentHolder, + int textureWidth, + int textureHeight, + int[] stateOffset, + OnPress onPress, + List tooltips + ) { + super(x, y, width, height, texture, textureWidth, textureHeight, stateOffset, (ignore) -> {}, List.of()); + this.onPress = onPress; + this.holder = enchantmentHolder; + List tooltip = new ObjectArrayList<>(); + tooltip.add(Enchantment.getFullname(this.holder, this.holder.value().getMaxLevel())); + tooltip.addAll(tooltips); + this.setTooltips(tooltip); + } + + @Override + protected void extractContents(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + super.extractContents(graphics, mouseX, mouseY, a); + ItemStack enchantedBookItem = Items.ENCHANTED_BOOK.getDefaultInstance(); + enchantedBookItem.enchant(this.holder, this.holder.value().getMaxLevel()); + int y = this.getY(); + if (this.selected) { + y++; + } + graphics.item(enchantedBookItem, this.getX() + 1, y); + } + + @Override + public void onPress(InputWithModifiers input) { + this.onPress.onPress(this); + } + + public interface OnPress { + void onPress(AutoEnchantingTableButton btn); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java index 0b546cc069..9921fa8e6b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/FluidDisplayWidget.java @@ -15,7 +15,7 @@ import net.minecraft.world.level.material.Fluid; import net.neoforged.neoforge.transfer.fluid.FluidResource; import net.neoforged.neoforge.transfer.fluid.FluidStacksResourceHandler; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.Nullable; import java.util.Objects; import java.util.Optional; diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/TriStateButton.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/TriStateButton.java index 61dcf3604a..4b28117dc4 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/TriStateButton.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/TriStateButton.java @@ -1,22 +1,31 @@ package dev.dubhe.anvilcraft.client.gui.component; +import lombok.Getter; +import lombok.Setter; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; +import net.minecraft.client.input.InputWithModifiers; import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; -import java.util.Collections; import java.util.List; public class TriStateButton extends Button { + private final OnPress onPress; + @Setter private Identifier texture; private final int textureWidth; private final int textureHeight; - private final int texYDiff; + @Getter + @Setter private List tooltips; - private boolean selected; + @Getter + @Setter + protected boolean selected; + private final int[] stateOffset; + // StateOffset: 0:未选中 1:按下 2: 悬停 public TriStateButton( int x, int y, @@ -25,46 +34,28 @@ public TriStateButton( Identifier texture, int textureWidth, int textureHeight, + int[] stateOffset, OnPress onPress, List tooltips ) { - super(x, y, width, height, Component.empty(), onPress, DEFAULT_NARRATION); + super(x, y, width, height, Component.empty(), (ignore) -> {}, DEFAULT_NARRATION); + this.onPress = onPress; + this.stateOffset = stateOffset; this.texture = texture; this.textureWidth = textureWidth; this.textureHeight = textureHeight; - this.texYDiff = textureHeight / 3; this.tooltips = tooltips; } - public void setSelected(boolean selected) { - this.selected = selected; - } - - public boolean isSelected() { - return this.selected; - } - - public void setTooltips(List tooltips) { - this.tooltips = tooltips; - } - - public List getTooltips() { - return this.tooltips; - } - - public void setTexture(Identifier texture) { - this.texture = texture; - } - @Override protected void extractContents(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { if (!this.visible) return; this.isHovered = this.isMouseOver(mouseX, mouseY); - int offsetV = 0; + int offsetV = this.stateOffset[0]; if (this.selected) { - offsetV = this.texYDiff * 2; + offsetV = this.stateOffset[1]; } else if (this.isHovered) { - offsetV = this.texYDiff; + offsetV = this.stateOffset[2]; } graphics.blit( RenderPipelines.GUI_TEXTURED, @@ -86,4 +77,14 @@ protected void extractContents(GuiGraphicsExtractor graphics, int mouseX, int mo ); } } + + @Override + public void onPress(InputWithModifiers input) { + this.selected = !this.selected; + this.onPress.onPress(this); + } + + public interface OnPress { + void onPress(TriStateButton btn); + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/AutoEnchantingTableScreen.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/AutoEnchantingTableScreen.java index 0e6621a0b9..ca7b52bb78 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/AutoEnchantingTableScreen.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/AutoEnchantingTableScreen.java @@ -1,42 +1,374 @@ package dev.dubhe.anvilcraft.client.gui.screen; +import dev.dubhe.anvilcraft.block.entity.AutoEnchantingTableBlockEntity; +import dev.dubhe.anvilcraft.block.workstation.AutoEnchantingTableBlock; +import dev.dubhe.anvilcraft.client.gui.component.AutoEnchantingTableButton; import dev.dubhe.anvilcraft.client.gui.component.FluidDisplayWidget; import dev.dubhe.anvilcraft.constant.SharedTextures; import dev.dubhe.anvilcraft.inventory.AutoEnchantingTableMenu; +import dev.dubhe.anvilcraft.network.AutoEnchantingTableSyncPacket; +import dev.dubhe.anvilcraft.util.TickDebouncer; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; import net.minecraft.client.gui.screens.inventory.tooltip.DefaultTooltipPositioner; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.IdMap; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.Identifier; +import net.minecraft.util.ARGB; +import net.minecraft.util.Mth; import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.EnchantingTableBlock; import net.neoforged.neoforge.transfer.fluid.FluidResource; +import org.jspecify.annotations.Nullable; import java.util.List; +import java.util.Set; public class AutoEnchantingTableScreen extends AbstractContainerScreen { private static final Identifier BACKGROUND = SharedTextures.bg("machine", "auto_enchanting_table"); + @Nullable + private EditBox editBox; + @Nullable + private TickDebouncer editBoxTickDebouncer; + @Nullable + private final AutoEnchantingTableButton[] buttons = new AutoEnchantingTableButton[10]; + private List> enchantmentList = new ObjectArrayList<>(); + private int currentIndex = 0; + private int scrollOffset = 0; + private boolean draggedArea = false; + private int errorCooldown = 0; + private final Set> selectedEnchantments = new ObjectOpenHashSet<>(); + private ItemStack finishItem = ItemStack.EMPTY; + public AutoEnchantingTableScreen(AutoEnchantingTableMenu menu, Inventory inventory, Component title) { super(menu, inventory, title); + menu.getBlockEntity().registerUpdateListener(() -> { + this.addWidget(0); + this.selectedEnchantments.clear(); + this.scrollOffset = 0; + }); + ClientLevel level = Minecraft.getInstance().level; + if (level != null) { + IdMap> idMap = level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); + this.selectedEnchantments.clear(); + for (int id : this.menu.getBlockEntity().getSelectedEnchantmentSet()) { + Holder enchantmentHolder = idMap.byId(id); + if (enchantmentHolder != null) { + this.selectedEnchantments.add(enchantmentHolder); + } + } + } + ItemStack itemStack = this.menu.getBlockEntity().getItems().getFirst().copyWithCount(1); + if (!itemStack.isEmpty()) { + ItemStack enchantedBook = Items.ENCHANTED_BOOK.getDefaultInstance(); + for (Holder selectedEnchantment : this.selectedEnchantments) { + enchantedBook.enchant(selectedEnchantment, selectedEnchantment.value().getMaxLevel()); + } + AutoEnchantingTableBlockEntity.applyEnchantment(itemStack, enchantedBook); + this.finishItem = itemStack.copyWithCount(1); + } } @Override protected void init() { super.init(); + this.clearWidgets(); this.addRenderableWidget( new FluidDisplayWidget( this.leftPos + 151, this.topPos + 16, 18, 56, this.menu.getBlockEntity().getFluidHandler(), - (fluidHandler) -> Component.literal("Exp: ") - .append(String.valueOf(fluidHandler.getAmountAsInt(0))) - .append("/") - .append(String.valueOf(fluidHandler.getCapacityAsInt(0, FluidResource.EMPTY))) + (fluidHandler) -> Component.translatable( + "screen.anvilcraft.auto_enchanting_table.fluid_display", + fluidHandler.getAmountAsInt(0) + "/" + fluidHandler.getCapacityAsInt(0, FluidResource.EMPTY) + ) ) ); + this.editBox = this.addRenderableWidget( + new EditBox(this.font, this.leftPos + 46, this.topPos + 17, 99, 12, Component.empty()) + ); + this.editBoxTickDebouncer = new TickDebouncer(20, () -> this.addWidget(0)); + this.editBox.setResponder((_) -> this.editBoxTickDebouncer.trigger()); + + this.addWidget(0); + } + + private void addWidget(int startIndex) { + if (startIndex < 0) { + return; + } + for (int i = 0; i < this.buttons.length; i++) { + AutoEnchantingTableButton button = this.buttons[i]; + if (button != null) { + this.removeWidget(button); + } + this.buttons[i] = null; + } + this.currentIndex = startIndex; + this.enchantmentList = this.menu.getEnchantmentList().stream().filter((holder) -> { + if (this.editBox != null) { + String enchantmentName = this.editBox.getValue(); + if (enchantmentName.isBlank()) { + return true; + } + return holder.value().description().getString().contains(enchantmentName); + } + return true; + }).toList(); + if (startIndex < this.enchantmentList.size()) { + int index = 0; + for (int i = startIndex; i < this.enchantmentList.size() && i < startIndex + 10; i++) { + Holder holder = this.enchantmentList.get(i); + AutoEnchantingTableButton button = this.getAutoEnchantingTableButton(index, holder); + this.buttons[index] = button; + this.addRenderableWidget(button); + index++; + } + } + } + + private AutoEnchantingTableButton getAutoEnchantingTableButton(int index, Holder enchantment) { + AutoEnchantingTableButton button = new AutoEnchantingTableButton( + this.leftPos + 47 + 18 * (index % 5), this.topPos + 32 + 18 * (index / 5), + 18, 18, + SharedTextures.SWITCH_TABLE_BUTTON, + enchantment, + 18, 54, + new int[]{0, 18, 36}, + (btn) -> { + if (btn.isSelected()) { + this.selectedEnchantments.remove(btn.getHolder()); + ItemStack itemStack = this.menu.getBlockEntity().getItems().getFirst().copyWithCount(1); + if (!itemStack.isEmpty()) { + ItemStack enchantedBook = Items.ENCHANTED_BOOK.getDefaultInstance().copyWithCount(1); + for (Holder selectedEnchantment : this.selectedEnchantments) { + enchantedBook.enchant(selectedEnchantment, selectedEnchantment.value().getMaxLevel()); + } + AutoEnchantingTableBlockEntity.applyEnchantment(itemStack, enchantedBook); + this.finishItem = itemStack.copyWithCount(1); + } + btn.setSelected(!btn.isSelected()); + return; + } + int totalLevel = this.selectedEnchantments.stream() + .mapToInt((holder) -> holder.value().getMaxLevel()) + .reduce(Integer::sum) + .orElse(0) + btn.getHolder().value().getMaxLevel(); + ClientLevel level = Minecraft.getInstance().level; + if (level != null) { + if (totalLevel <= this.getBookShelf(level, this.menu.getBlockEntity().getBlockPos()) + && totalLevel * 400 <= this.menu.getBlockEntity().getFluidHandler().getCapacityAsInt(0, FluidResource.EMPTY)) { + if (!btn.isSelected()) { + ItemStack itemStack = this.menu.getBlockEntity().getItems().getFirst().copyWithCount(1); + if (!itemStack.isEmpty()) { + ItemStack enchantedBook = Items.ENCHANTED_BOOK.getDefaultInstance().copyWithCount(1); + for (Holder selectedEnchantment : this.selectedEnchantments) { + enchantedBook.enchant(selectedEnchantment, selectedEnchantment.value().getMaxLevel()); + } + enchantedBook.enchant(btn.getHolder(), btn.getHolder().value().getMaxLevel()); + AutoEnchantingTableBlockEntity.applyEnchantment(itemStack, enchantedBook); + this.finishItem = itemStack.copyWithCount(1); + } + this.selectedEnchantments.add(btn.getHolder()); + } + btn.setSelected(!btn.isSelected()); + } else { + this.errorCooldown = 80; + } + } + }, + List.of() + ); + if (Minecraft.getInstance().level != null) { + if (this.selectedEnchantments.contains(button.getHolder())) { + button.setSelected(true); + } + } + return button; + } + + private int getBookShelf(Level level, BlockPos pos) { + float bookcases = 0; + for (BlockPos offset : AutoEnchantingTableBlock.BOOKSHELF_OFFSETS) { + if (EnchantingTableBlock.isValidBookShelf(level, pos, offset)) { + bookcases += level.getBlockState(pos.offset(offset)).getEnchantPowerBonus(level, pos.offset(offset)); + } + } + return (int) bookcases; + } + + private boolean mouseInListArea(double mouseX, double mouseY) { + return mouseX >= this.leftPos + 47 + && mouseX <= this.leftPos + 144 + && mouseY >= this.topPos + 32 + && mouseY <= this.topPos + 68; + } + + private boolean mouseInScrollBarArea(double mouseX, double mouseY) { + return mouseX >= this.leftPos + 140 + && mouseX <= this.leftPos + 144 + && mouseY >= this.topPos + 32 + && mouseY <= this.topPos + 68; + } + + @Override + protected List getTooltipFromContainerItem(ItemStack itemStack) { + + return super.getTooltipFromContainerItem(itemStack); + } + + @Override + public boolean mouseScrolled(double x, double y, double scrollX, double scrollY) { + if (this.mouseInListArea(x, y)) { + if (this.errorCooldown > 0) { + return true; + } + if (this.currentIndex + 10 >= this.enchantmentList.size() && scrollY < 0) { + return true; + } + if (this.enchantmentList.size() > 10) { + int newIndex = Mth.clamp(this.currentIndex + (int) -scrollY * 10, 0, this.enchantmentList.size() - 1); + this.addWidget(newIndex); + this.scrollOffset = newIndex; + return true; + } + } + return super.mouseScrolled(x, y, scrollX, scrollY); + } + + @Override + public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { + this.draggedArea = false; + if (event.button() == 0) { + if (this.mouseInScrollBarArea(event.x(), event.y())) { + if (this.errorCooldown <= 0) { + this.draggedArea = true; + } + } + } else if (event.button() == 1) { + if (this.editBox != null && this.editBox.isMouseOver(event.x(), event.y())) { + this.editBox.setValue(""); + this.addWidget(0); + } + } + return super.mouseClicked(event, doubleClick); + } + + @Override + public boolean mouseDragged(MouseButtonEvent event, double dx, double dy) { + int trackY = this.topPos + 32; + int trackHeight = 36 - 12; + if (this.draggedArea) { + int size = this.enchantmentList.size(); + int maxIndex = size > 10 ? (size - 1) / 10 * 10 : 0; + if (maxIndex > 0) { + int continuousIndex = Mth.clamp((int) ((event.y() - trackY) * (double) maxIndex / trackHeight), 0, maxIndex); + this.scrollOffset = continuousIndex; + int newIndex = Mth.clamp((int) (Math.round(continuousIndex / 10.0) * 10), 0, maxIndex); + this.addWidget(newIndex); + return true; + } + } + return super.mouseDragged(event, dx, dy); + } + + @Override + public boolean mouseReleased(MouseButtonEvent event) { + this.draggedArea = false; + return super.mouseReleased(event); + } + + @Override + protected void containerTick() { + if (this.editBoxTickDebouncer != null) { + this.editBoxTickDebouncer.tick(); + } + if (this.errorCooldown > 0) { + this.errorCooldown--; + } + } + + @Override + public boolean shouldCloseOnEsc() { + AutoEnchantingTableBlockEntity blockEntity = this.menu.getBlockEntity(); + ClientPacketListener connection = Minecraft.getInstance().getConnection(); + if (Minecraft.getInstance().level != null) { + IdMap> idMap = Minecraft.getInstance().level.registryAccess() + .lookupOrThrow(Registries.ENCHANTMENT) + .asHolderIdMap(); + if (connection != null) { + connection.send(new AutoEnchantingTableSyncPacket( + blockEntity.getBlockPos(), + this.selectedEnchantments.stream().map(idMap::getId).toList() + )); + } + } + return true; + } + + @Override + public void onClose() { + super.onClose(); + AutoEnchantingTableBlockEntity blockEntity = this.menu.getBlockEntity(); + ClientPacketListener connection = Minecraft.getInstance().getConnection(); + if (Minecraft.getInstance().level != null) { + IdMap> idMap = Minecraft.getInstance().level.registryAccess() + .lookupOrThrow(Registries.ENCHANTMENT) + .asHolderIdMap(); + if (connection != null) { + connection.send(new AutoEnchantingTableSyncPacket( + blockEntity.getBlockPos(), + this.selectedEnchantments.stream().map(idMap::getId).toList() + )); + } + } + } + + @Override + public void extractContents(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) { + super.extractContents(graphics, mouseX, mouseY, a); + if (this.errorCooldown > 0) { + graphics.fill(this.leftPos + 47, this.topPos + 32, this.leftPos + 137, this.topPos + 68, ARGB.color(128, 255, 0, 0)); + } + int size = this.enchantmentList.size(); + if (size > 10) { + int maxY = this.topPos + 32 + 36 - 12; + int trackHeight = 36 - 12; + int maxIndex = (size - 1) / 10 * 10; + int scrollY = Mth.clamp(this.topPos + 32 + (int) ((float) this.scrollOffset * trackHeight / maxIndex), this.topPos + 32, maxY); + graphics.blit( + RenderPipelines.GUI_TEXTURED, + SharedTextures.SWITCH_TABLE_SLIDER, + this.leftPos + 140, + scrollY, + 0, 0, + 4, 12, + 8, 12 + ); + } + if (this.menu.getBlockEntity().getItems().get(1).isEmpty() && !this.finishItem.isEmpty()) { + graphics.item(this.finishItem, this.leftPos + 7, this.topPos + 52); + graphics.fill(this.leftPos + 7, this.topPos + 52, this.leftPos + 23, this.topPos + 68, 0x99777777); + } } @Override @@ -60,22 +392,63 @@ public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mou protected void extractLabels(GuiGraphicsExtractor graphics, int xm, int ym) { int x = (this.imageWidth - this.font.width(this.title)) / 2; graphics.text(this.font, this.title, x, 2, -12566464, false); + if (this.errorCooldown > 0) { + MutableComponent text = Component.translatable("screen.anvilcraft.auto_enchanting_table.out_of_limit"); + x = (this.imageWidth - this.font.width(text)) / 2; + graphics.text(this.font, text, x, -15, ARGB.color(255, 0, 0), false); + } } @Override protected void extractTooltip(GuiGraphicsExtractor graphics, int mouseX, int mouseY) { - super.extractTooltip(graphics, mouseX, mouseY); + if (this.hoveredSlot != null && this.hoveredSlot.hasItem()) { + ItemStack item = this.hoveredSlot.getItem(); + if ( + this.menu.getCarried().isEmpty() + || item.getTooltipImage() + .map(ClientTooltipComponent::create) + .map(ClientTooltipComponent::showTooltipWithItemInHand) + .orElse(false) + ) { + if (this.hoveredSlot.index != 37) { + graphics.setTooltipForNextFrame( + this.font, + this.getTooltipFromContainerItem(item), + item.getTooltipImage(), + item, + mouseX, + mouseY, + item.get(DataComponents.TOOLTIP_STYLE) + ); + } + } + } + if (this.isHovering(7, 52, 16, 16, mouseX, mouseY)) { + if (this.menu.getBlockEntity().getItems().get(1).isEmpty()) { + if (!this.finishItem.isEmpty()) { + graphics.setTooltipForNextFrame( + this.font, + this.getTooltipFromContainerItem(this.finishItem), + this.finishItem.getTooltipImage(), + this.finishItem, + mouseX, + mouseY, + this.finishItem.get(DataComponents.TOOLTIP_STYLE) + ); + } + } + } if (this.isHovering(151, 16, 18, 56, mouseX, mouseY)) { graphics.tooltip( Minecraft.getInstance().font, List.of( ClientTooltipComponent.create( - Component.literal("Exp: ") - .append(String.valueOf(this.menu.getBlockEntity().getFluidHandler().getAmountAsInt(0))) - .append("/") - .append(String.valueOf(this.menu.getBlockEntity().getFluidHandler().getCapacityAsInt(0, FluidResource.EMPTY))) - .append("mB") - .getVisualOrderText() + Component.translatable( + "screen.anvilcraft.auto_enchanting_table.fluid_display", + this.menu.getBlockEntity().getFluidHandler().getAmountAsInt(0) + + "/" + + this.menu.getBlockEntity().getFluidHandler().getCapacityAsInt(0, FluidResource.EMPTY) + ).getVisualOrderText() ) ), mouseX, diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/SmartBlockPlacerScreen.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/SmartBlockPlacerScreen.java index 3492046785..84addf2e7e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/SmartBlockPlacerScreen.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/SmartBlockPlacerScreen.java @@ -169,6 +169,7 @@ private void initLayerButtons() { LAYER_DEFAULT[i], 16, 48, + new int[] {0, 32, 16}, _ -> this.onLayerButtonClick(index), List.of(Component.translatable("screen.anvilcraft.smart_block_placer.layer." + (i + 1))) ); @@ -263,6 +264,7 @@ private void initMissingModeButton() { SKIP_MISSING, 16, 48, + new int[] {0, 32, 16}, _ -> this.onSkipMissingButtonClick(), List.of(Component.translatable("screen.anvilcraft.smart_block_placer.missing_mode.skip")) ); @@ -277,6 +279,7 @@ private void initMissingModeButton() { STOP_MISSING, 16, 48, + new int[] {0, 32, 16}, _ -> this.onStopMissingButtonClick(), List.of(Component.translatable("screen.anvilcraft.smart_block_placer.missing_mode.stop")) ); @@ -381,6 +384,7 @@ private TriStateButton createPositionButton(int row, int col, int positionIndex, TriStateButton button = new TriStateButton( xpos, ypos, 16, 16, POSITION_SELECT, 16, 48, + new int[] {0, 32, 16}, (_) -> this.onPositionButtonClick(row, col, positionIndex, tooltipSelected, tooltipUnselected), selected ? tooltipSelected : tooltipUnselected ); diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java index 13f3ff957f..70aa4aa71b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java @@ -456,6 +456,9 @@ public static void init(RegistrumLangProvider provider) { provider.add("screen.anvilcraft.storage.category.add", "Left click when holding Filter to add custom category"); provider.add("screen.anvilcraft.storage.category.alternate.removable", "Left click to select, right click to delete this category"); provider.add("screen.anvilcraft.storage.category.alternate.unremovable", "Left click to select"); + + provider.add("screen.anvilcraft.auto_enchanting_table.out_of_limit", "The sum of all enchantment levels is greater than the enchantment ability or experience fluid volume"); + provider.add("screen.anvilcraft.auto_enchanting_table.fluid_display", "Exp: %s"); } // 启用行长警告""" } diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/ShapedRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/ShapedRecipeLoader.java index d80d98dbee..71c51b8d1f 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/ShapedRecipeLoader.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/ShapedRecipeLoader.java @@ -4,16 +4,35 @@ import dev.dubhe.anvilcraft.data.AnvilCraftDatagen; import dev.dubhe.anvilcraft.init.block.ModBlocks; import dev.dubhe.anvilcraft.init.item.ModItemTags; +import dev.dubhe.anvilcraft.init.item.ModItems; import net.minecraft.core.HolderGetter; import net.minecraft.data.recipes.RecipeCategory; import net.minecraft.data.recipes.ShapedRecipeBuilder; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Blocks; public class ShapedRecipeLoader { public ShapedRecipeLoader(RegistrumRecipeProvider provider) { this.nineToOne(provider); + + ShapedRecipeBuilder.shaped(provider.getItems(), RecipeCategory.MISC, ModBlocks.AUTO_ENCHANTING_TABLE) + .pattern("TET") + .pattern("ReR") + .define('T', ModBlocks.FLUID_TANK) + .define('E', Blocks.ENCHANTING_TABLE) + .define('R', ModItems.ROYAL_STEEL_INGOT) + .define('e', ModBlocks.MAGNETO_ELECTRIC_CORE_BLOCK) + .unlockedBy(AnvilCraftDatagen.hasItem(ModBlocks.FLUID_TANK), + AnvilCraftDatagen.has(provider.getItems(), ModBlocks.FLUID_TANK)) + .unlockedBy(AnvilCraftDatagen.hasItem(Blocks.ENCHANTING_TABLE), + AnvilCraftDatagen.has(provider.getItems(), Blocks.ENCHANTING_TABLE)) + .unlockedBy(AnvilCraftDatagen.hasItem(ModItems.ROYAL_STEEL_INGOT), + AnvilCraftDatagen.has(provider.getItems(), ModItems.ROYAL_STEEL_INGOT)) + .unlockedBy(AnvilCraftDatagen.hasItem(ModBlocks.MAGNETO_ELECTRIC_CORE_BLOCK), + AnvilCraftDatagen.has(provider.getItems(), ModBlocks.MAGNETO_ELECTRIC_CORE_BLOCK)) + .save(provider); } public void nineToOne(RegistrumRecipeProvider provider) { diff --git a/src/main/java/dev/dubhe/anvilcraft/data/tags/BlockTagLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/tags/BlockTagLoader.java index d84a6a96f4..3ea99acc36 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/tags/BlockTagLoader.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/tags/BlockTagLoader.java @@ -380,5 +380,8 @@ public static void init(RegistrumTagsProvider provider) { .addElement(ModBlocks.CELESTIAL_FORGING_ANVIL_LASER_INTERFACE.getId()) .addElement(ModBlocks.CELESTIAL_FORGING_ANVIL_FLUID_INTERFACE.getId()) .addElement(ModBlocks.CELESTIAL_FORGING_ANVIL_PORTAL.getId()); + + provider.rawBuilder(BlockTags.ENCHANTMENT_POWER_PROVIDER) + .addElement(ModBlocks.TRANSCENDIUM_BLOCK.getId()); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/FunctionalBlocks.java b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/FunctionalBlocks.java index d5d78e2fc7..5216eb6b4c 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/FunctionalBlocks.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/FunctionalBlocks.java @@ -62,6 +62,7 @@ public void accept() { this.plain(ModBlocks.ITEM_COLLECTOR); // 物品收集器 this.plain(ModBlocks.CHARGER); // 充电器 this.plain(ModBlocks.DISCHARGER); // 放电器 + this.plain(ModBlocks.AUTO_ENCHANTING_TABLE); // 自动附魔台 this.plain(ModBlocks.BLOCK_PLACER); // 方块放置器 this.plain(ModBlocks.BLOCK_DEVOURER); // 方块吞噬器 this.plain(ModBlocks.STRUCTURE_SCANNER); // 结构扫描仪 diff --git a/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java b/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java index d021a0f6aa..7adef005f8 100644 --- a/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java +++ b/src/main/java/dev/dubhe/anvilcraft/inventory/AutoEnchantingTableMenu.java @@ -1,9 +1,16 @@ package dev.dubhe.anvilcraft.inventory; -import dev.dubhe.anvilcraft.AnvilCraft; import dev.dubhe.anvilcraft.block.entity.AutoEnchantingTableBlockEntity; +import dev.dubhe.anvilcraft.init.enchantment.ModEnchantments; +import dev.dubhe.anvilcraft.init.item.ModItems; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; import lombok.Getter; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.tags.EnchantmentTags; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -11,15 +18,25 @@ import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.Level; import org.jspecify.annotations.Nullable; +import java.util.List; +import java.util.stream.StreamSupport; + @Getter public class AutoEnchantingTableMenu extends AbstractContainerMenu { private final AutoEnchantingTableBlockEntity blockEntity; private final Container container; private final Level level; + @Getter + private List> enchantmentList = new ObjectArrayList<>(); + + final Slot prologueSlot; public AutoEnchantingTableMenu( @Nullable MenuType menuType, @@ -45,6 +62,7 @@ public AutoEnchantingTableMenu( this.level = inventory.player.level(); this.blockEntity = blockEntity; this.container = blockEntity; + this.blockEntity.setSlotChangedListener(this::slotsChanged); this.addPlayerInventory(inventory); this.addPlayerHotbar(inventory); @@ -59,8 +77,17 @@ public boolean mayPlace(ItemStack itemStack) { public boolean mayPickup(Player player) { return false; } - }); + @Override + public int getMaxStackSize() { + return 1; + } + + @Override + public int getMaxStackSize(ItemStack itemStack) { + return 1; + } + }); this.addSlot(new Slot(this.container, 1, 7, 52) { @Override public boolean mayPlace(ItemStack itemStack) { @@ -71,8 +98,21 @@ public boolean mayPlace(ItemStack itemStack) { public boolean mayPickup(Player player) { return false; } + + @Override + public int getMaxStackSize() { + return 1; + } + + @Override + public int getMaxStackSize(ItemStack itemStack) { + return 1; + } }); - this.addSlot(new Slot(this.container, 2, 27, 18)); + this.prologueSlot = this.addSlot(new Slot(this.container, 2, 27, 18)); + if (!this.prologueSlot.getItem().isEmpty()) { + this.enchantmentList = this.getEnchantmentList(this.level, this.prologueSlot.getItem()); + } } private void addPlayerInventory(Inventory playerInventory) { @@ -89,9 +129,19 @@ private void addPlayerHotbar(Inventory playerInventory) { } } + @Override + public void slotsChanged(Container container) { + ItemStack prologueItem = this.prologueSlot.getItem(); + if (!prologueItem.is(this.blockEntity.getLastPrologueItem().getItem())) { + this.blockEntity.setLastPrologueItem(prologueItem); + this.enchantmentList = this.getEnchantmentList(this.level, prologueItem); + this.blockEntity.getSelectedEnchantmentSet().clear(); + this.blockEntity.getPrologueSlotUpdateListener().run(); + } + } + @Override public ItemStack quickMoveStack(Player player, int slotIndex) { - AnvilCraft.LOGGER.debug("index: {}", slotIndex); if (slotIndex == 36 || slotIndex == 37) { return ItemStack.EMPTY; } @@ -99,8 +149,7 @@ public ItemStack quickMoveStack(Player player, int slotIndex) { if (slotIndex == 38) { ItemStack item = slot.getItem(); if (!item.isEmpty()) { - if (!this.moveItemStackTo(item, - 1, 36, true)) { + if (!this.moveItemStackTo(item, 0, 36, false)) { return ItemStack.EMPTY; } } @@ -108,8 +157,7 @@ public ItemStack quickMoveStack(Player player, int slotIndex) { if (slotIndex >= 0 && slotIndex <= 35) { ItemStack item = slot.getItem(); if (!item.isEmpty()) { - if (!this.moveItemStackTo(item, - 38, 39, true)) { + if (!this.moveItemStackTo(item, 38, 39, true)) { return ItemStack.EMPTY; } } @@ -121,4 +169,135 @@ public ItemStack quickMoveStack(Player player, int slotIndex) { public boolean stillValid(Player player) { return this.container.stillValid(player); } + + private List> getEnchantmentList(Level level, ItemStack prologueItem) { + Registry enchantments = level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT); + + // 蓝宝石护符 + ObjectArrayList> sapphireAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.RESPIRATION).ifPresent(sapphireAmulet::add); + enchantments.get(Enchantments.AQUA_AFFINITY).ifPresent(sapphireAmulet::add); + enchantments.get(Enchantments.DEPTH_STRIDER).ifPresent(sapphireAmulet::add); + + // 红宝石护符 + ObjectArrayList> rubyAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.FIRE_PROTECTION).ifPresent(rubyAmulet::add); + + // 黄玉护符 + ObjectArrayList> topazAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.CHANNELING).ifPresent(topazAmulet::add); + + // 绿宝石护符 + ObjectArrayList> emeraldAmulet = StreamSupport + .stream(enchantments.getTagOrEmpty(EnchantmentTags.TRADEABLE).spliterator(), false) + .filter((instance) -> !sapphireAmulet.contains(instance) && !rubyAmulet.contains(instance) && !topazAmulet.contains(instance)) + .collect( + ObjectArrayList::new, + ObjectArrayList::add, + ObjectList::addAll + ); + + // 宝石护符 + ObjectArrayList> gemAmulet = new ObjectArrayList<>(); + gemAmulet.addAll(sapphireAmulet); + gemAmulet.addAll(rubyAmulet); + gemAmulet.addAll(topazAmulet); + gemAmulet.addAll(emeraldAmulet); + + if (prologueItem.is(ModItems.SAPPHIRE_AMULET)) { + return List.copyOf(sapphireAmulet); + } else if (prologueItem.is(ModItems.RUBY_AMULET)) { + return List.copyOf(rubyAmulet); + } else if (prologueItem.is(ModItems.TOPAZ_AMULET)) { + return List.copyOf(topazAmulet); + } else if (prologueItem.is(ModItems.EMERALD_AMULET)) { + return List.copyOf(emeraldAmulet); + } else if (prologueItem.is(ModItems.GEM_AMULET)) { + return List.copyOf(gemAmulet); + } + + // 羽毛护符 + ObjectArrayList> featherAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.FEATHER_FALLING).ifPresent(featherAmulet::add); + + // 寂静护符 + ObjectArrayList> silenceAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.PROTECTION).ifPresent(silenceAmulet::add); + enchantments.get(Enchantments.SWIFT_SNEAK).ifPresent(silenceAmulet::add); + + // 猫护符 + ObjectArrayList> catAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.BLAST_PROTECTION).ifPresent(catAmulet::add); + enchantments.get(Enchantments.THORNS).ifPresent(catAmulet::add); + + // 狗护符 + ObjectArrayList> dogAmulet = new ObjectArrayList<>(); + enchantments.get(Enchantments.PROJECTILE_PROTECTION).ifPresent(dogAmulet::add); + + // 自然护符 + ObjectArrayList> natureAmulet = new ObjectArrayList<>(); + natureAmulet.addAll(featherAmulet); + natureAmulet.addAll(silenceAmulet); + natureAmulet.addAll(catAmulet); + natureAmulet.addAll(dogAmulet); + + if (prologueItem.is(ModItems.FEATHER_AMULET)) { + return List.copyOf(featherAmulet); + } else if (prologueItem.is(ModItems.SILENCE_AMULET)) { + return List.copyOf(silenceAmulet); + } else if (prologueItem.is(ModItems.CAT_AMULET)) { + return List.copyOf(catAmulet); + } else if (prologueItem.is(ModItems.DOG_AMULET)) { + return List.copyOf(dogAmulet); + } else if (prologueItem.is(ModItems.NATURE_AMULET)) { + return List.copyOf(natureAmulet); + } + + // 紫水晶 + ObjectArrayList> amethystShard = new ObjectArrayList<>(); + enchantments.get(ModEnchantments.FELLING_KEY).ifPresent(amethystShard::add); + enchantments.get(ModEnchantments.HARVEST_KEY).ifPresent(amethystShard::add); + + // 皇家钢锭 + ObjectArrayList> royalSteel = new ObjectArrayList<>(); + enchantments.get(Enchantments.SILK_TOUCH).ifPresent(royalSteel::add); + enchantments.get(Enchantments.UNBREAKING).ifPresent(royalSteel::add); + enchantments.get(Enchantments.MENDING).ifPresent(royalSteel::add); + + // 余烬金属锭 + ObjectArrayList> emberMetal = new ObjectArrayList<>(); + enchantments.get(ModEnchantments.SMELTING_KEY).ifPresent(emberMetal::add); + enchantments.get(Enchantments.FIRE_ASPECT).ifPresent(emberMetal::add); + enchantments.get(Enchantments.FLAME).ifPresent(emberMetal::add); + + // 浮霜金属锭 + ObjectArrayList> frostMetal = new ObjectArrayList<>(); + enchantments.get(ModEnchantments.DISINTEGRATION_KEY).ifPresent(frostMetal::add); + enchantments.get(Enchantments.FROST_WALKER).ifPresent(frostMetal::add); + + // 超限合金锭 + ObjectArrayList> transcendium = new ObjectArrayList<>(); + enchantments.get(Enchantments.FORTUNE).ifPresent(transcendium::add); + enchantments.get(Enchantments.LOOTING).ifPresent(transcendium::add); + enchantments.get(ModEnchantments.BEHEADING_KEY).ifPresent(transcendium::add); + enchantments.get(Enchantments.LUCK_OF_THE_SEA).ifPresent(transcendium::add); + transcendium.addAll(amethystShard); + transcendium.addAll(royalSteel); + transcendium.addAll(emberMetal); + transcendium.addAll(frostMetal); + + if (prologueItem.is(Items.AMETHYST_SHARD)) { + return List.copyOf(amethystShard); + } else if (prologueItem.is(ModItems.ROYAL_STEEL_INGOT)) { + return List.copyOf(royalSteel); + } else if (prologueItem.is(ModItems.EMBER_METAL_INGOT)) { + return List.copyOf(emberMetal); + } else if (prologueItem.is(ModItems.FROST_METAL_INGOT)) { + return List.copyOf(frostMetal); + } else if (prologueItem.is(ModItems.TRANSCENDIUM_INGOT)) { + return List.copyOf(transcendium); + } + + return List.of(); + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/inventory/container/AutoEnchantingTableContainer.java b/src/main/java/dev/dubhe/anvilcraft/inventory/container/AutoEnchantingTableContainer.java new file mode 100644 index 0000000000..aa13e5047f --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/inventory/container/AutoEnchantingTableContainer.java @@ -0,0 +1,38 @@ +package dev.dubhe.anvilcraft.inventory.container; + +import net.minecraft.core.Direction; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.WorldlyContainer; +import net.minecraft.world.item.ItemStack; +import org.jspecify.annotations.Nullable; + +public class AutoEnchantingTableContainer extends SimpleContainer implements WorldlyContainer { + public AutoEnchantingTableContainer(ItemStack... items) { + super(items); + } + + @Override + public int getMaxStackSize() { + return 1; + } + + @Override + public int getMaxStackSize(ItemStack itemStack) { + return 1; + } + + @Override + public int[] getSlotsForFace(Direction direction) { + return new int[] { 0, 1 }; + } + + @Override + public boolean canPlaceItemThroughFace(int slot, ItemStack itemStack, @Nullable Direction direction) { + return slot == 0 && this.getItems().getFirst().isEmpty(); + } + + @Override + public boolean canTakeItemThroughFace(int slot, ItemStack itemStack, Direction direction) { + return slot == 1 && !this.getItems().get(1).isEmpty(); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/network/AutoEnchantingTableSyncPacket.java b/src/main/java/dev/dubhe/anvilcraft/network/AutoEnchantingTableSyncPacket.java new file mode 100644 index 0000000000..e03b29b039 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/network/AutoEnchantingTableSyncPacket.java @@ -0,0 +1,47 @@ +package dev.dubhe.anvilcraft.network; + +import dev.anvilcraft.lib.v2.network.packet.IPacket; +import dev.anvilcraft.lib.v2.network.packet.IServerboundPacket; +import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.block.entity.AutoEnchantingTableBlockEntity; +import io.netty.buffer.ByteBuf; +import net.minecraft.core.BlockPos; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +import java.util.List; +import java.util.Set; + +public record AutoEnchantingTableSyncPacket(BlockPos pos, List ids) implements IServerboundPacket { + private static final Type TYPE = + IPacket.type(AnvilCraft.of("auto_enchanting_table")); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, + AutoEnchantingTableSyncPacket::pos, + ByteBufCodecs.INT.apply(ByteBufCodecs.list()), + AutoEnchantingTableSyncPacket::ids, + AutoEnchantingTableSyncPacket::new + ); + + @Override + public Type type() { + return TYPE; + } + + @Override + public void handleOnServer(Player player) { + Level level = player.level(); + BlockEntity blockEntity = level.getBlockEntity(this.pos); + if (blockEntity instanceof AutoEnchantingTableBlockEntity autoEnchantingTableBlockEntity) { + Set selectedEnchantmentList = autoEnchantingTableBlockEntity.getSelectedEnchantmentSet(); + selectedEnchantmentList.clear(); + selectedEnchantmentList.addAll(this.ids); + autoEnchantingTableBlockEntity.setOpenMenu(false); + autoEnchantingTableBlockEntity.onChange(); + } + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/util/TickDebouncer.java b/src/main/java/dev/dubhe/anvilcraft/util/TickDebouncer.java new file mode 100644 index 0000000000..35cb051871 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/util/TickDebouncer.java @@ -0,0 +1,32 @@ +package dev.dubhe.anvilcraft.util; + +public class TickDebouncer { + private final Runnable action; + private final int delayTicks; + private int remainingTicks = -1; + + public TickDebouncer(int delayTicks, Runnable action) { + this.delayTicks = delayTicks; + this.action = action; + } + + public void trigger() { + this.remainingTicks = this.delayTicks; + } + + public void tick() { + if (this.remainingTicks > 0) { + this.remainingTicks--; + if (this.remainingTicks == 0) { + this.action.run(); + } + } + } + + public void run() { + if (this.remainingTicks > 0) { + this.remainingTicks = 0; + this.action.run(); + } + } +} diff --git a/src/main/resources/assets/anvilcraft/blockstates/auto_enchanting_table.json b/src/main/resources/assets/anvilcraft/blockstates/auto_enchanting_table.json index ee1a63d58e..8e306c4a46 100644 --- a/src/main/resources/assets/anvilcraft/blockstates/auto_enchanting_table.json +++ b/src/main/resources/assets/anvilcraft/blockstates/auto_enchanting_table.json @@ -1,6 +1,9 @@ { "variants": { - "": { + "overload=true": { + "model": "anvilcraft:block/auto_enchanting_table_overload" + }, + "overload=false": { "model": "anvilcraft:block/auto_enchanting_table" } } From c67890fb034f9169ac5ff6b1919118c46462f3b3 Mon Sep 17 00:00:00 2001 From: burin Date: Wed, 15 Jul 2026 21:30:25 +0800 Subject: [PATCH 6/7] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/AutoEnchantingTableBlockEntity.java | 6 +++++- .../blockentity/CreativeCrateRenderer.java | 15 ++++++--------- .../anvilcraft/init/block/ModBlockEntities.java | 7 +++++++ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java index 1683eaea6c..bdb50717a5 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/AutoEnchantingTableBlockEntity.java @@ -304,7 +304,11 @@ public void serverTick( } public boolean onPlayerUse(Player player, InteractionHand hand) { - return FluidUtil.interactWithFluidHandler(player, hand, worldPosition, this.getFluidHandler()); + try (Transaction transaction = Transaction.openRoot()) { + boolean success = FluidUtil.interactWithFluidHandler(player, hand, this.getBlockPos(), this.getFluidHandler(), transaction); + if (success) transaction.commit(); + return success; + } } public static void applyEnchantment(ItemStack item, ItemStack enchantedBook) { diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/CreativeCrateRenderer.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/CreativeCrateRenderer.java index ac891613c7..b04ecb7698 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/CreativeCrateRenderer.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/CreativeCrateRenderer.java @@ -62,30 +62,27 @@ public void submit( // 在六个面的近表面渲染物品,参考 1.21.1 的渲染位置 for (int side = 0; side < 6; side++) { poseStack.pushPose(); - poseStack.scale(0.5f, 0.5f, 0.5f); switch (side) { - case 0 -> poseStack.translate(1f, 1f, 0.2f); + case 0 -> poseStack.translate(0.5, 0.5, 0.9); case 1 -> { - poseStack.translate(1f, 1f, 1.8f); + poseStack.translate(0.5, 0.5, 0.1); poseStack.mulPose(Axis.YP.rotationDegrees(180)); } case 2 -> { - poseStack.translate(0.2f, 1f, 1f); + poseStack.translate(0.9, 0.5, 0.5); poseStack.mulPose(Axis.YP.rotationDegrees(90)); } case 3 -> { - poseStack.translate(1.8f, 1f, 1f); + poseStack.translate(0.1, 0.5, 0.5); poseStack.mulPose(Axis.YP.rotationDegrees(270)); } case 4 -> { - poseStack.translate(1f, 0.2f, 1f); + poseStack.translate(0.5, 0.9, 0.5); poseStack.mulPose(Axis.XP.rotationDegrees(90)); - poseStack.mulPose(Axis.ZP.rotationDegrees(270)); } case 5 -> { - poseStack.translate(1f, 1.8f, 1f); + poseStack.translate(0.5, 0.1, 0.5); poseStack.mulPose(Axis.XP.rotationDegrees(270)); - poseStack.mulPose(Axis.ZP.rotationDegrees(270)); } default -> { // This should never happen as side is always 0-5 diff --git a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java index 792a4988c7..68ef58f125 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlockEntities.java @@ -53,6 +53,7 @@ import dev.dubhe.anvilcraft.block.entity.RubyLaserBlockEntity; import dev.dubhe.anvilcraft.block.entity.RubyPrismBlockEntity; import dev.dubhe.anvilcraft.block.entity.SimpleChuteBlockEntity; +import dev.dubhe.anvilcraft.block.entity.SimpleMagneticChuteBlockEntity; import dev.dubhe.anvilcraft.block.entity.SmartBlockPlacerBlockEntity; import dev.dubhe.anvilcraft.block.entity.SpaceOvercompressorBlockEntity; import dev.dubhe.anvilcraft.block.entity.SpacetimeSupercomputerBlockEntity; @@ -61,8 +62,11 @@ import dev.dubhe.anvilcraft.block.entity.TransmissionPoleBlockEntity; import dev.dubhe.anvilcraft.block.entity.VoidEnergyCollectorBlockEntity; import dev.dubhe.anvilcraft.block.entity.WhiteHoleBlockEntity; +import dev.dubhe.anvilcraft.block.entity.WipBlockEntity; import dev.dubhe.anvilcraft.block.entity.batch.BatchCrafterBlockEntity; import dev.dubhe.anvilcraft.block.entity.batch.BatchCutterBlockEntity; +import dev.dubhe.anvilcraft.block.entity.fluid.ControlValveBlockEntity; +import dev.dubhe.anvilcraft.block.entity.fluid.DrainBlockEntity; import dev.dubhe.anvilcraft.block.entity.fluid.PipeBlockEntity; import dev.dubhe.anvilcraft.block.entity.fluid.PipeNodeBlockEntity; import dev.dubhe.anvilcraft.block.entity.fluid.PumpBlockEntity; @@ -86,6 +90,7 @@ import dev.dubhe.anvilcraft.client.renderer.blockentity.ChargeCollectorRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.ChargerRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.ConfinementChamberRenderer; +import dev.dubhe.anvilcraft.client.renderer.blockentity.ControlValveBlockEntityRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.CorruptedBeaconRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.CreativeCrateRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.CreativeFluidTankRenderer; @@ -101,10 +106,12 @@ import dev.dubhe.anvilcraft.client.renderer.blockentity.InfiniteCollectorRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.LargeFluidTankRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.LaserBlockEntityRenderer; +import dev.dubhe.anvilcraft.client.renderer.blockentity.PipeCheckValveBERenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.PumpBlockEntityRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.SmartBlockPlacerRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.TeslaTowerRenderer; import dev.dubhe.anvilcraft.client.renderer.blockentity.VoidEnergyCollectorRenderer; +import dev.dubhe.anvilcraft.client.renderer.blockentity.WipBlockEntityRenderer; import static dev.dubhe.anvilcraft.AnvilCraft.REGISTRUM; From 520633131555756600cd635a379e48e0e604f67c Mon Sep 17 00:00:00 2001 From: burin Date: Wed, 15 Jul 2026 21:36:46 +0800 Subject: [PATCH 7/7] =?UTF-8?q?fix:=20=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AutoEnchantingTableContainer.java | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 src/main/java/dev/dubhe/anvilcraft/inventory/container/AutoEnchantingTableContainer.java diff --git a/src/main/java/dev/dubhe/anvilcraft/inventory/container/AutoEnchantingTableContainer.java b/src/main/java/dev/dubhe/anvilcraft/inventory/container/AutoEnchantingTableContainer.java deleted file mode 100644 index aa13e5047f..0000000000 --- a/src/main/java/dev/dubhe/anvilcraft/inventory/container/AutoEnchantingTableContainer.java +++ /dev/null @@ -1,38 +0,0 @@ -package dev.dubhe.anvilcraft.inventory.container; - -import net.minecraft.core.Direction; -import net.minecraft.world.SimpleContainer; -import net.minecraft.world.WorldlyContainer; -import net.minecraft.world.item.ItemStack; -import org.jspecify.annotations.Nullable; - -public class AutoEnchantingTableContainer extends SimpleContainer implements WorldlyContainer { - public AutoEnchantingTableContainer(ItemStack... items) { - super(items); - } - - @Override - public int getMaxStackSize() { - return 1; - } - - @Override - public int getMaxStackSize(ItemStack itemStack) { - return 1; - } - - @Override - public int[] getSlotsForFace(Direction direction) { - return new int[] { 0, 1 }; - } - - @Override - public boolean canPlaceItemThroughFace(int slot, ItemStack itemStack, @Nullable Direction direction) { - return slot == 0 && this.getItems().getFirst().isEmpty(); - } - - @Override - public boolean canTakeItemThroughFace(int slot, ItemStack itemStack, Direction direction) { - return slot == 1 && !this.getItems().get(1).isEmpty(); - } -}