summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorGravatar Uko Kokņevičs2024-01-10 02:05:34 +0100
committerGravatar Uko Kokņevičs2024-01-10 02:05:34 +0100
commitc27088bb3ba8e83d9fd4b5bd0d944be54c1482b2 (patch)
treed257f91576f8f4bc08c87a67ba44036b4d86cee1 /src/main
parentSome refactoring (diff)
downloadmc-eris-alchemy-c27088bb3ba8e83d9fd4b5bd0d944be54c1482b2.tar.gz
mc-eris-alchemy-c27088bb3ba8e83d9fd4b5bd0d944be54c1482b2.tar.xz
mc-eris-alchemy-c27088bb3ba8e83d9fd4b5bd0d944be54c1482b2.zip
Move out common chest logic to separate files
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/lv/enes/mc/eris_alchemy/block/AlchemicalChestBlock.java196
-rw-r--r--src/main/java/lv/enes/mc/eris_alchemy/block/ChestLikeBlock.java213
-rw-r--r--src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestBlockEntity.java174
-rw-r--r--src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeBlockEntity.java200
-rw-r--r--src/main/java/lv/enes/mc/eris_alchemy/menu/AlchemicalChestMenu.java58
-rw-r--r--src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeBlockMenu.java67
6 files changed, 500 insertions, 408 deletions
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/AlchemicalChestBlock.java b/src/main/java/lv/enes/mc/eris_alchemy/block/AlchemicalChestBlock.java
index 1d8698a..d50ee77 100644
--- a/src/main/java/lv/enes/mc/eris_alchemy/block/AlchemicalChestBlock.java
+++ b/src/main/java/lv/enes/mc/eris_alchemy/block/AlchemicalChestBlock.java
@@ -4,144 +4,23 @@ import jakarta.annotation.Nonnull;
4import jakarta.annotation.Nullable; 4import jakarta.annotation.Nullable;
5import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry; 5import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry;
6import lv.enes.mc.eris_alchemy.block.entity.AlchemicalChestBlockEntity; 6import lv.enes.mc.eris_alchemy.block.entity.AlchemicalChestBlockEntity;
7import lv.enes.mc.eris_alchemy.menu.AlchemicalChestMenu;
8import net.minecraft.core.BlockPos; 7import net.minecraft.core.BlockPos;
9import net.minecraft.core.Direction;
10import net.minecraft.network.chat.Component; 8import net.minecraft.network.chat.Component;
11import net.minecraft.world.*;
12import net.minecraft.world.entity.monster.piglin.PiglinAi;
13import net.minecraft.world.entity.player.Player;
14import net.minecraft.world.item.context.BlockPlaceContext;
15import net.minecraft.world.level.BlockGetter;
16import net.minecraft.world.level.Level;
17import net.minecraft.world.level.LevelAccessor;
18import net.minecraft.world.level.block.*;
19import net.minecraft.world.level.block.entity.BlockEntity; 9import net.minecraft.world.level.block.entity.BlockEntity;
20import net.minecraft.world.level.block.entity.BlockEntityTicker;
21import net.minecraft.world.level.block.entity.BlockEntityType;
22import net.minecraft.world.level.block.entity.ChestBlockEntity;
23import net.minecraft.world.level.block.state.BlockState; 10import net.minecraft.world.level.block.state.BlockState;
24import net.minecraft.world.level.block.state.StateDefinition;
25import net.minecraft.world.level.block.state.properties.BlockStateProperties;
26import net.minecraft.world.level.block.state.properties.BooleanProperty;
27import net.minecraft.world.level.block.state.properties.DirectionProperty;
28import net.minecraft.world.level.material.FluidState;
29import net.minecraft.world.level.material.Fluids;
30import net.minecraft.world.level.pathfinder.PathComputationType;
31import net.minecraft.world.phys.BlockHitResult;
32import net.minecraft.world.phys.shapes.CollisionContext;
33import net.minecraft.world.phys.shapes.VoxelShape;
34 11
35public class AlchemicalChestBlock 12public class AlchemicalChestBlock extends ChestLikeBlock<AlchemicalChestBlockEntity>
36 extends AbstractChestBlock<AlchemicalChestBlockEntity>
37 implements SimpleWaterloggedBlock
38{ 13{
39 public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
40 public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
41
42 public static final Component CONTAINER_TITLE = Component.translatable("container.eris_alchemy.alchemical_chest"); 14 public static final Component CONTAINER_TITLE = Component.translatable("container.eris_alchemy.alchemical_chest");
43 public static final VoxelShape SHAPE = Block.box(
44 1.0, 0.0, 1.0,
45 15.0, 14.0, 15.0
46 );
47
48 public static Container getContainer(Level world, BlockPos pos) {
49 if (world.getBlockEntity(pos) instanceof AlchemicalChestBlockEntity container) {
50 return container;
51 }
52 return null;
53 }
54 15
55 public AlchemicalChestBlock(Properties properties) { 16 public AlchemicalChestBlock(Properties properties) {
56 super(properties, () -> ErisAlchemyRegistry.BlockEntities.ALCHEMICAL_CHEST); 17 super(properties, () -> ErisAlchemyRegistry.BlockEntities.ALCHEMICAL_CHEST);
57 registerDefaultState(getStateDefinition().any()
58 .setValue(FACING, Direction.NORTH)
59 .setValue(WATERLOGGED, false)
60 );
61 }
62
63 @Nonnull
64 @Override
65 public DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> combine(
66 BlockState state,
67 Level world,
68 BlockPos pos,
69 boolean ignoreBlocked
70 ) {
71 return DoubleBlockCombiner.Combiner::acceptNone;
72 }
73
74 @SuppressWarnings("deprecation")
75 @Override
76 public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) {
77 return AlchemicalChestMenu.getRedstoneSignalFromContainer(getContainer(world, pos));
78 }
79
80 @SuppressWarnings("deprecation")
81 @Nonnull
82 @Override
83 public FluidState getFluidState(BlockState state) {
84 return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
85 }
86
87 @Override
88 public MenuProvider getMenuProvider(BlockState state, Level world, BlockPos pos) {
89 if (world.getBlockEntity(pos) instanceof AlchemicalChestBlockEntity entity) {
90 return new SimpleMenuProvider(entity, CONTAINER_TITLE);
91 }
92 return null;
93 } 18 }
94 19
95 @Nonnull
96 @Override 20 @Override
97 public RenderShape getRenderShape(BlockState state) {
98 return RenderShape.ENTITYBLOCK_ANIMATED;
99 }
100
101 @SuppressWarnings("deprecation")
102 @Nonnull 21 @Nonnull
103 @Override 22 protected Component getContainerTitle() {
104 public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext ctx) { 23 return CONTAINER_TITLE;
105 return SHAPE;
106 }
107
108 @Override
109 public BlockState getStateForPlacement(BlockPlaceContext ctx) {
110 return defaultBlockState()
111 .setValue(FACING, ctx.getHorizontalDirection().getOpposite())
112 .setValue(WATERLOGGED, ctx.getLevel().getFluidState(ctx.getClickedPos()).is(Fluids.WATER));
113 }
114
115 @Override
116 public <T extends BlockEntity> BlockEntityTicker<T> getTicker(
117 Level ignoredWorld,
118 BlockState ignoredState,
119 BlockEntityType<T> type
120 ) {
121 return createTickerHelper(
122 type,
123 blockEntityType.get(),
124 (world, pos, state, entity) -> entity.tick(world, pos, state)
125 );
126 }
127
128 @SuppressWarnings("deprecation")
129 @Override
130 public boolean hasAnalogOutputSignal(BlockState state) {
131 return true;
132 }
133
134 @SuppressWarnings("deprecation")
135 @Override
136 public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) {
137 return false;
138 }
139
140 @SuppressWarnings("deprecation")
141 @Nonnull
142 @Override
143 public BlockState mirror(BlockState state, Mirror mirror) {
144 return state.rotate(mirror.getRotation(state.getValue(FACING)));
145 } 24 }
146 25
147 @Nullable 26 @Nullable
@@ -149,73 +28,4 @@ public class AlchemicalChestBlock
149 public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { 28 public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
150 return new AlchemicalChestBlockEntity(pos, state); 29 return new AlchemicalChestBlockEntity(pos, state);
151 } 30 }
152
153 @SuppressWarnings("deprecation")
154 @Override
155 public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean moved) {
156 if (!state.is(newState.getBlock())) {
157 var entity = world.getBlockEntity(pos);
158 if (entity instanceof Container container) {
159 Containers.dropContents(world, pos, container);
160 world.updateNeighbourForOutputSignal(pos, this);
161 }
162
163 super.onRemove(state, world, pos, newState, moved);
164 }
165 }
166
167 @SuppressWarnings("deprecation")
168 @Nonnull
169 @Override
170 public BlockState rotate(BlockState state, Rotation rotation) {
171 return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
172 }
173
174 @SuppressWarnings("deprecation")
175 @Nonnull
176 @Override
177 public BlockState updateShape(
178 BlockState state,
179 Direction direction,
180 BlockState neighborState,
181 LevelAccessor world,
182 BlockPos pos,
183 BlockPos neighborPos
184 ) {
185 if (state.getValue(WATERLOGGED)) {
186 world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
187 }
188
189 return super.updateShape(state, direction, neighborState, world, pos, neighborPos);
190 }
191
192 @SuppressWarnings("deprecation")
193 @Nonnull
194 @Override
195 public InteractionResult use(
196 BlockState state,
197 Level world,
198 BlockPos pos,
199 Player player,
200 InteractionHand hand,
201 BlockHitResult hit
202 ) {
203 if (world.isClientSide) {
204 return InteractionResult.SUCCESS;
205 }
206
207 var provider = getMenuProvider(state, world, pos);
208 if (provider != null) {
209 player.openMenu(provider);
210 // TODO: player.awardStat(getOpenChestStat);
211 PiglinAi.angerNearbyPiglins(player, true);
212 }
213
214 return InteractionResult.CONSUME;
215 }
216
217 @Override
218 protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
219 builder.add(FACING, WATERLOGGED);
220 }
221} 31}
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/ChestLikeBlock.java b/src/main/java/lv/enes/mc/eris_alchemy/block/ChestLikeBlock.java
new file mode 100644
index 0000000..e50563a
--- /dev/null
+++ b/src/main/java/lv/enes/mc/eris_alchemy/block/ChestLikeBlock.java
@@ -0,0 +1,213 @@
1package lv.enes.mc.eris_alchemy.block;
2
3import jakarta.annotation.Nonnull;
4import lv.enes.mc.eris_alchemy.block.entity.ChestLikeBlockEntity;
5import net.minecraft.core.BlockPos;
6import net.minecraft.core.Direction;
7import net.minecraft.network.chat.Component;
8import net.minecraft.world.*;
9import net.minecraft.world.entity.monster.piglin.PiglinAi;
10import net.minecraft.world.entity.player.Player;
11import net.minecraft.world.inventory.AbstractContainerMenu;
12import net.minecraft.world.item.context.BlockPlaceContext;
13import net.minecraft.world.level.BlockGetter;
14import net.minecraft.world.level.Level;
15import net.minecraft.world.level.LevelAccessor;
16import net.minecraft.world.level.block.*;
17import net.minecraft.world.level.block.DoubleBlockCombiner.NeighborCombineResult;
18import net.minecraft.world.level.block.entity.BlockEntity;
19import net.minecraft.world.level.block.entity.BlockEntityTicker;
20import net.minecraft.world.level.block.entity.BlockEntityType;
21import net.minecraft.world.level.block.entity.ChestBlockEntity;
22import net.minecraft.world.level.block.state.BlockState;
23import net.minecraft.world.level.block.state.StateDefinition;
24import net.minecraft.world.level.block.state.properties.BlockStateProperties;
25import net.minecraft.world.level.block.state.properties.BooleanProperty;
26import net.minecraft.world.level.block.state.properties.DirectionProperty;
27import net.minecraft.world.level.material.FluidState;
28import net.minecraft.world.level.material.Fluids;
29import net.minecraft.world.level.pathfinder.PathComputationType;
30import net.minecraft.world.phys.BlockHitResult;
31import net.minecraft.world.phys.shapes.CollisionContext;
32import net.minecraft.world.phys.shapes.VoxelShape;
33
34import java.util.function.Supplier;
35
36public abstract class ChestLikeBlock<E extends ChestLikeBlockEntity>
37 extends AbstractChestBlock<E>
38 implements SimpleWaterloggedBlock
39{
40 public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
41 public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
42
43 public static final VoxelShape SHAPE = Block.box(
44 1.0, 0.0, 1.0,
45 15.0, 14.0, 15.0
46 );
47
48 protected ChestLikeBlock(Properties settings, Supplier<BlockEntityType<? extends E>> blockEntityTypeSupplier) {
49 super(settings, blockEntityTypeSupplier);
50 registerDefaultState(getStateDefinition().any()
51 .setValue(FACING, Direction.NORTH)
52 .setValue(WATERLOGGED, false)
53 );
54 }
55
56 @Nonnull
57 protected abstract Component getContainerTitle();
58
59 @Nonnull
60 @Override
61 public NeighborCombineResult<? extends ChestBlockEntity> combine(
62 BlockState state,
63 Level world,
64 BlockPos pos,
65 boolean ignoreBlocked
66 ) {
67 return DoubleBlockCombiner.Combiner::acceptNone;
68 }
69
70 @SuppressWarnings("deprecation")
71 @Override
72 public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) {
73 if (world.getBlockEntity(pos) instanceof Container container) {
74 return AbstractContainerMenu.getRedstoneSignalFromContainer(container);
75 }
76 return 0;
77 }
78
79 @SuppressWarnings("deprecation")
80 @Nonnull
81 @Override
82 public FluidState getFluidState(BlockState state) {
83 return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
84 }
85
86 @Override
87 public MenuProvider getMenuProvider(BlockState state, Level world, BlockPos pos) {
88 if (world.getBlockEntity(pos) instanceof ChestLikeBlockEntity entity) {
89 return new SimpleMenuProvider(entity, getContainerTitle());
90 }
91 return null;
92 }
93
94 @Nonnull
95 @Override
96 public RenderShape getRenderShape(BlockState state) {
97 return RenderShape.ENTITYBLOCK_ANIMATED;
98 }
99
100 @SuppressWarnings("deprecation")
101 @Nonnull
102 @Override
103 public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext ctx) {
104 return SHAPE;
105 }
106
107 @Override
108 public BlockState getStateForPlacement(BlockPlaceContext ctx) {
109 return defaultBlockState()
110 .setValue(FACING, ctx.getHorizontalDirection().getOpposite())
111 .setValue(WATERLOGGED, ctx.getLevel().getFluidState(ctx.getClickedPos()).is(Fluids.WATER));
112 }
113
114 @Override
115 public <T extends BlockEntity> BlockEntityTicker<T> getTicker(
116 Level ignoredWorld,
117 BlockState ignoredState,
118 BlockEntityType<T> type
119 ) {
120 return createTickerHelper(
121 type,
122 blockEntityType.get(),
123 (world, pos, state, entity) -> entity.tick(world, pos, state)
124 );
125 }
126
127 @SuppressWarnings("deprecation")
128 @Override
129 public boolean hasAnalogOutputSignal(BlockState state) {
130 return true;
131 }
132
133 @SuppressWarnings("deprecation")
134 @Override
135 public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) {
136 return false;
137 }
138
139 @SuppressWarnings("deprecation")
140 @Nonnull
141 @Override
142 public BlockState mirror(BlockState state, Mirror mirror) {
143 return state.rotate(mirror.getRotation(state.getValue(FACING)));
144 }
145
146 @SuppressWarnings("deprecation")
147 @Override
148 public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean moved) {
149 if (!state.is(newState.getBlock())) {
150 if (world.getBlockEntity(pos) instanceof Container container) {
151 Containers.dropContents(world, pos, container);
152 world.updateNeighbourForOutputSignal(pos, this);
153 }
154
155 super.onRemove(state, world, pos, newState, moved);
156 }
157 }
158
159 @SuppressWarnings("deprecation")
160 @Nonnull
161 @Override
162 public BlockState rotate(BlockState state, Rotation rotation) {
163 return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
164 }
165
166 @SuppressWarnings("deprecation")
167 @Nonnull
168 @Override
169 public BlockState updateShape(
170 BlockState state,
171 Direction direction,
172 BlockState neighborState,
173 LevelAccessor world,
174 BlockPos pos,
175 BlockPos neighborPos
176 ) {
177 if (state.getValue(WATERLOGGED)) {
178 world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
179 }
180
181 return super.updateShape(state, direction, neighborState, world, pos, neighborPos);
182 }
183
184 @SuppressWarnings("deprecation")
185 @Nonnull
186 @Override
187 public InteractionResult use(
188 BlockState state,
189 Level world,
190 BlockPos pos,
191 Player player,
192 InteractionHand hand,
193 BlockHitResult hit
194 ) {
195 if (world.isClientSide) {
196 return InteractionResult.SUCCESS;
197 }
198
199 var provider = getMenuProvider(state, world, pos);
200 if (provider != null) {
201 player.openMenu(provider);
202 // TODO: player.awardStat(getOpenChestStat);
203 PiglinAi.angerNearbyPiglins(player, true);
204 }
205
206 return InteractionResult.CONSUME;
207 }
208
209 @Override
210 protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
211 builder.add(FACING, WATERLOGGED);
212 }
213}
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestBlockEntity.java b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestBlockEntity.java
index 1e881f8..bac49b6 100644
--- a/src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestBlockEntity.java
+++ b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/AlchemicalChestBlockEntity.java
@@ -3,202 +3,44 @@ package lv.enes.mc.eris_alchemy.block.entity;
3import jakarta.annotation.Nonnull; 3import jakarta.annotation.Nonnull;
4import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry; 4import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry;
5import lv.enes.mc.eris_alchemy.menu.AlchemicalChestMenu; 5import lv.enes.mc.eris_alchemy.menu.AlchemicalChestMenu;
6import lv.enes.mc.eris_alchemy.menu.ChestLikeBlockMenu;
6import net.minecraft.core.BlockPos; 7import net.minecraft.core.BlockPos;
7import net.minecraft.core.NonNullList; 8import net.minecraft.core.NonNullList;
8import net.minecraft.nbt.CompoundTag;
9import net.minecraft.network.chat.Component; 9import net.minecraft.network.chat.Component;
10import net.minecraft.sounds.SoundEvents;
11import net.minecraft.sounds.SoundSource;
12import net.minecraft.world.Container;
13import net.minecraft.world.ContainerHelper;
14import net.minecraft.world.entity.player.Inventory; 10import net.minecraft.world.entity.player.Inventory;
15import net.minecraft.world.entity.player.Player;
16import net.minecraft.world.inventory.AbstractContainerMenu;
17import net.minecraft.world.item.ItemStack; 11import net.minecraft.world.item.ItemStack;
18import net.minecraft.world.level.Level; 12import net.minecraft.world.level.block.Block;
19import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
20import net.minecraft.world.level.block.entity.ChestLidController;
21import net.minecraft.world.level.block.entity.ContainerOpenersCounter;
22import net.minecraft.world.level.block.entity.LidBlockEntity;
23import net.minecraft.world.level.block.state.BlockState; 13import net.minecraft.world.level.block.state.BlockState;
24 14
25public class AlchemicalChestBlockEntity extends BaseContainerBlockEntity implements LidBlockEntity { 15public class AlchemicalChestBlockEntity extends ChestLikeBlockEntity {
26 private final static int WIDTH = 13; 16 private final static int WIDTH = 13;
27 private final static int HEIGHT = 8; 17 private final static int HEIGHT = 8;
28 18
29 private final static int EVENT = 1;
30
31 private final NonNullList<ItemStack> items = NonNullList.withSize(WIDTH * HEIGHT, ItemStack.EMPTY); 19 private final NonNullList<ItemStack> items = NonNullList.withSize(WIDTH * HEIGHT, ItemStack.EMPTY);
32 private final ChestLidController lidController = new ChestLidController();
33 private final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() {
34 @Override
35 protected void onOpen(Level world, BlockPos pos, BlockState state) {
36 // TODO: Sound effects?
37 world.playSound(
38 null,
39 pos.getX() + 0.5,
40 pos.getY() + 0.5,
41 pos.getZ() + 0.5,
42 SoundEvents.ENDER_CHEST_OPEN,
43 SoundSource.BLOCKS,
44 0.5f,
45 world.random.nextFloat() * 0.1f + 0.9f
46 );
47 }
48
49 @Override
50 protected void onClose(Level world, BlockPos pos, BlockState state) {
51 // TODO: Sound effects?
52 world.playSound(
53 null,
54 pos.getX() + 0.5,
55 pos.getY() + 0.5,
56 pos.getZ() + 0.5,
57 SoundEvents.ENDER_CHEST_CLOSE,
58 SoundSource.BLOCKS,
59 0.5f,
60 world.random.nextFloat() * 0.1f + 0.9f
61 );
62 }
63
64 @Override
65 protected void openerCountChanged(
66 Level world,
67 BlockPos pos,
68 BlockState state,
69 int oldViewerCount,
70 int newViewerCount
71 ) {
72 world.blockEvent(worldPosition, ErisAlchemyRegistry.Blocks.ALCHEMICAL_CHEST, EVENT, newViewerCount);
73 }
74
75 @Override
76 protected boolean isOwnContainer(Player player) {
77 if (player.containerMenu instanceof AlchemicalChestMenu menu) {
78 return menu.getContainer() == AlchemicalChestBlockEntity.this;
79 }
80 return false;
81 }
82 };
83 20
84 public AlchemicalChestBlockEntity(BlockPos pos, BlockState state) { 21 public AlchemicalChestBlockEntity(BlockPos pos, BlockState state) {
85 super(ErisAlchemyRegistry.BlockEntities.ALCHEMICAL_CHEST, pos, state); 22 super(ErisAlchemyRegistry.BlockEntities.ALCHEMICAL_CHEST, pos, state);
86 } 23 }
87 24
88 @Override
89 public void clearContent() {
90 items.clear();
91 }
92
93 @Nonnull 25 @Nonnull
94 @Override 26 @Override
95 protected AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) { 27 protected ChestLikeBlockMenu createMenu(int syncId, Inventory playerInventory) {
96 return new AlchemicalChestMenu(syncId, playerInventory, this); 28 return new AlchemicalChestMenu(syncId, playerInventory, this);
97 } 29 }
98 30
99 @Override
100 public int getContainerSize() {
101 return items.size();
102 }
103
104 @Nonnull 31 @Nonnull
105 @Override 32 @Override
106 protected Component getDefaultName() { 33 protected Component getDefaultName() {
107 return Component.translatable("container.eris_alchemy.alchemical_chest"); 34 return Component.translatable("container.eris_alchemy.alchemical_chest");
108 } 35 }
109 36
110 @Nonnull
111 @Override
112 public ItemStack getItem(int slot) {
113 return items.get(slot);
114 }
115
116 @Override
117 public float getOpenNess(float tickDelta) {
118 return lidController.getOpenness(tickDelta);
119 }
120
121 @Override
122 public boolean isEmpty() {
123 return items.stream().allMatch(ItemStack::isEmpty);
124 }
125
126 @Override
127 public void load(CompoundTag nbt) {
128 super.load(nbt);
129 ContainerHelper.loadAllItems(nbt, items);
130 }
131
132 private void recheckOpen() {
133 if (!remove) {
134 openersCounter.recheckOpeners(getLevel(), getBlockPos(), getBlockState());
135 }
136 }
137
138 @Nonnull
139 @Override
140 public ItemStack removeItem(int slot, int amount) {
141 var stack = ContainerHelper.removeItem(items, slot, amount);
142 if (!stack.isEmpty()) {
143 this.setChanged();
144 }
145 return stack;
146 }
147
148 @Nonnull
149 @Override
150 public ItemStack removeItemNoUpdate(int slot) {
151 return ContainerHelper.takeItem(items, slot);
152 }
153
154 @Override
155 protected void saveAdditional(CompoundTag nbt) {
156 super.saveAdditional(nbt);
157 ContainerHelper.saveAllItems(nbt, items);
158 }
159
160 @Override 37 @Override
161 public void setItem(int slot, ItemStack stack) { 38 protected NonNullList<ItemStack> getItems() {
162 items.set(slot, stack); 39 return items;
163 if (stack.getCount() > getMaxStackSize()) {
164 stack.setCount(getMaxStackSize());
165 }
166 this.setChanged();
167 } 40 }
168 41
169 @Override 42 @Override
170 public void startOpen(Player player) { 43 protected Block getParent() {
171 if (!remove && !player.isSpectator()) { 44 return ErisAlchemyRegistry.Blocks.ALCHEMICAL_CHEST;
172 openersCounter.incrementOpeners(player, getLevel(), getBlockPos(), getBlockState());
173 }
174 }
175
176 @Override
177 public boolean stillValid(Player player) {
178 return Container.stillValidBlockEntity(this, player);
179 }
180
181 @Override
182 public void stopOpen(Player player) {
183 if (!remove && !player.isSpectator()) {
184 openersCounter.decrementOpeners(player, getLevel(), getBlockPos(), getBlockState());
185 }
186 }
187
188 public void tick(Level world, BlockPos ignoredPos, BlockState ignoredState) {
189 if (world.isClientSide) {
190 lidController.tickLid();
191 }
192 recheckOpen();
193 }
194
195 @Override
196 public boolean triggerEvent(int type, int data) {
197 if (type == EVENT) {
198 lidController.shouldBeOpen(data > 0);
199 return true;
200 }
201
202 return super.triggerEvent(type, data);
203 } 45 }
204} 46}
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeBlockEntity.java b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeBlockEntity.java
new file mode 100644
index 0000000..48b3ad7
--- /dev/null
+++ b/src/main/java/lv/enes/mc/eris_alchemy/block/entity/ChestLikeBlockEntity.java
@@ -0,0 +1,200 @@
1package lv.enes.mc.eris_alchemy.block.entity;
2
3import jakarta.annotation.Nonnull;
4import jakarta.annotation.Nullable;
5import lv.enes.mc.eris_alchemy.menu.ChestLikeBlockMenu;
6import net.minecraft.core.BlockPos;
7import net.minecraft.core.NonNullList;
8import net.minecraft.nbt.CompoundTag;
9import net.minecraft.sounds.SoundEvents;
10import net.minecraft.sounds.SoundSource;
11import net.minecraft.world.Container;
12import net.minecraft.world.ContainerHelper;
13import net.minecraft.world.entity.player.Player;
14import net.minecraft.world.item.ItemStack;
15import net.minecraft.world.level.Level;
16import net.minecraft.world.level.block.Block;
17import net.minecraft.world.level.block.entity.*;
18import net.minecraft.world.level.block.state.BlockState;
19
20public abstract class ChestLikeBlockEntity extends BaseContainerBlockEntity implements LidBlockEntity {
21 protected static final int EVENT_INTERACTED = 1;
22
23 protected final ChestLidController lidController = new ChestLidController();
24 protected final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() {
25 @Override
26 protected void onOpen(Level world, BlockPos pos, BlockState state) {
27 ChestLikeBlockEntity.this.onOpen(world, pos);
28 }
29
30 @Override
31 protected void onClose(Level world, BlockPos pos, BlockState state) {
32 ChestLikeBlockEntity.this.onClose(world, pos);
33 }
34
35 @Override
36 protected void openerCountChanged(Level world, BlockPos pos, BlockState state, int oldViewerCount, int newViewerCount) {
37 world.blockEvent(worldPosition, getParent(), EVENT_INTERACTED, newViewerCount);
38 }
39
40 @Override
41 protected boolean isOwnContainer(Player player) {
42 if (player.containerMenu instanceof ChestLikeBlockMenu menu) {
43 return menu.getContainer() == ChestLikeBlockEntity.this;
44 }
45 return false;
46 }
47 };
48
49 public ChestLikeBlockEntity(
50 BlockEntityType<? extends ChestLikeBlockEntity> type,
51 BlockPos pos,
52 BlockState state
53 ) {
54 super(type, pos, state);
55 }
56
57 protected abstract Block getParent();
58 protected abstract NonNullList<ItemStack> getItems();
59
60 @Override
61 public void clearContent() {
62 getItems().clear();
63 }
64
65 @Override
66 public int getContainerSize() {
67 return getItems().size();
68 }
69
70 @Nonnull
71 @Override
72 public ItemStack getItem(int slot) {
73 return getItems().get(slot);
74 }
75
76 @Override
77 public float getOpenNess(float tickDelta) {
78 return lidController.getOpenness(tickDelta);
79 }
80
81 @Override
82 public boolean isEmpty() {
83 return getItems().stream().allMatch(ItemStack::isEmpty);
84 }
85
86 @Override
87 public void load(CompoundTag nbt) {
88 super.load(nbt);
89 ContainerHelper.loadAllItems(nbt, getItems());
90 }
91
92 protected void onClose(@Nullable Level world, BlockPos pos) {
93 if (world == null) {
94 return;
95 }
96
97 world.playSound(
98 null,
99 pos.getX() + 0.5,
100 pos.getY() + 0.5,
101 pos.getZ() + 0.5,
102 SoundEvents.CHEST_OPEN,
103 SoundSource.BLOCKS,
104 0.5f,
105 world.random.nextFloat() * 0.1f + 0.9f
106 );
107 }
108
109 protected void onOpen(@Nullable Level world, BlockPos pos) {
110 if (world == null) {
111 return;
112 }
113
114 world.playSound(
115 null,
116 pos.getX() + 0.5,
117 pos.getY() + 0.5,
118 pos.getZ() + 0.5,
119 SoundEvents.CHEST_CLOSE,
120 SoundSource.BLOCKS,
121 0.5f,
122 world.random.nextFloat() * 0.1f + 0.9f
123 );
124 }
125
126 private void recheckOpen() {
127 if (!remove) {
128 openersCounter.recheckOpeners(getLevel(), getBlockPos(), getBlockState());
129 }
130 }
131
132 @Nonnull
133 @Override
134 public ItemStack removeItem(int slot, int amount) {
135 var stack = ContainerHelper.removeItem(getItems(), slot, amount);
136 if (!stack.isEmpty()) {
137 this.setChanged();
138 }
139 return stack;
140 }
141
142 @Nonnull
143 @Override
144 public ItemStack removeItemNoUpdate(int slot) {
145 return ContainerHelper.takeItem(getItems(), slot);
146 }
147
148 @Override
149 protected void saveAdditional(CompoundTag nbt) {
150 super.saveAdditional(nbt);
151 ContainerHelper.saveAllItems(nbt, getItems());
152 }
153
154 @Override
155 public void setItem(int slot, ItemStack stack) {
156 getItems().set(slot, stack);
157 if (stack.getCount() > getMaxStackSize()) {
158 stack.setCount(getMaxStackSize());
159 }
160 this.setChanged();
161 }
162
163 @Override
164 public void startOpen(Player player) {
165 if (!remove && !player.isSpectator()) {
166 openersCounter.incrementOpeners(player, getLevel(), getBlockPos(), getBlockState());
167 }
168 }
169
170 @Override
171 public boolean stillValid(Player player) {
172 return Container.stillValidBlockEntity(this, player);
173 }
174
175 @Override
176 public void stopOpen(Player player) {
177 if (!remove && !player.isSpectator()) {
178 openersCounter.decrementOpeners(player, getLevel(), getBlockPos(), getBlockState());
179 }
180 }
181
182 public void tick(Level world, BlockPos ignoredPos, BlockState ignoredState) {
183 if (world.isClientSide) {
184 lidController.tickLid();
185 }
186 recheckOpen();
187 }
188
189
190 @Override
191 public boolean triggerEvent(int type, int data) {
192 if (type == EVENT_INTERACTED) {
193 lidController.shouldBeOpen(data > 0);
194 return true;
195 }
196
197 return super.triggerEvent(type, data);
198 }
199}
200
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/menu/AlchemicalChestMenu.java b/src/main/java/lv/enes/mc/eris_alchemy/menu/AlchemicalChestMenu.java
index a464f3f..1dc3364 100644
--- a/src/main/java/lv/enes/mc/eris_alchemy/menu/AlchemicalChestMenu.java
+++ b/src/main/java/lv/enes/mc/eris_alchemy/menu/AlchemicalChestMenu.java
@@ -1,38 +1,31 @@
1package lv.enes.mc.eris_alchemy.menu; 1package lv.enes.mc.eris_alchemy.menu;
2 2
3import jakarta.annotation.Nonnull;
4import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry; 3import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry;
5import net.minecraft.world.Container; 4import net.minecraft.world.Container;
6import net.minecraft.world.SimpleContainer; 5import net.minecraft.world.SimpleContainer;
7import net.minecraft.world.entity.player.Inventory; 6import net.minecraft.world.entity.player.Inventory;
8import net.minecraft.world.entity.player.Player;
9import net.minecraft.world.inventory.AbstractContainerMenu;
10import net.minecraft.world.inventory.Slot; 7import net.minecraft.world.inventory.Slot;
11import net.minecraft.world.item.ItemStack;
12 8
13public class AlchemicalChestMenu extends AbstractContainerMenu { 9public class AlchemicalChestMenu extends ChestLikeBlockMenu {
14 private static final int WIDTH = 13; 10 private static final int WIDTH = 13;
15 private static final int HEIGHT = 8; 11 private static final int HEIGHT = 8;
16 12
17 private final Container container;
18
19 public AlchemicalChestMenu(int syncId, Inventory playerInventory) { 13 public AlchemicalChestMenu(int syncId, Inventory playerInventory) {
20 this(syncId, playerInventory, new SimpleContainer(WIDTH * HEIGHT)); 14 this(syncId, playerInventory, new SimpleContainer(WIDTH * HEIGHT));
21 } 15 }
22 16
23 public AlchemicalChestMenu(int syncId, Inventory playerInventory, Container inventory) { 17 public AlchemicalChestMenu(int syncId, Inventory playerInventory, Container container) {
24 super(ErisAlchemyRegistry.Menus.ALCHEMICAL_CHEST, syncId); 18 super(ErisAlchemyRegistry.Menus.ALCHEMICAL_CHEST, syncId, playerInventory, container);
25 checkContainerSize(inventory, WIDTH * HEIGHT); 19 }
26
27 this.container = inventory;
28 inventory.startOpen(playerInventory.player);
29 20
21 @Override
22 protected void addSlots(Inventory playerInventory) {
30 var x_off = 8; 23 var x_off = 8;
31 var y_off = 8; 24 var y_off = 8;
32 25
33 for (var y = 0; y < HEIGHT; y++) { 26 for (var y = 0; y < HEIGHT; y++) {
34 for (var x = 0; x < WIDTH; x++ ) { 27 for (var x = 0; x < WIDTH; x++ ) {
35 addSlot(new Slot(inventory, y * WIDTH + x, x_off + x * 18, y_off + y * 18)); 28 addSlot(new Slot(container, y * WIDTH + x, x_off + x * 18, y_off + y * 18));
36 } 29 }
37 } 30 }
38 31
@@ -52,41 +45,8 @@ public class AlchemicalChestMenu extends AbstractContainerMenu {
52 } 45 }
53 } 46 }
54 47
55 public Container getContainer() {
56 return container;
57 }
58
59 @Nonnull
60 @Override
61 public ItemStack quickMoveStack(Player player, int fromIndex) {
62 var newStack = ItemStack.EMPTY;
63 var slot = slots.get(fromIndex);
64 if (!slot.hasItem()) {
65 return newStack;
66 }
67
68 var originalStack = slot.getItem();
69 newStack = originalStack.copy();
70
71 if (fromIndex < container.getContainerSize()) {
72 if (!moveItemStackTo(originalStack, container.getContainerSize(), slots.size(), true)) {
73 return ItemStack.EMPTY;
74 }
75 } else if (!moveItemStackTo(originalStack, 0, container.getContainerSize(), false)) {
76 return ItemStack.EMPTY;
77 }
78
79 if (originalStack.isEmpty()) {
80 slot.setByPlayer(ItemStack.EMPTY);
81 } else {
82 slot.setChanged();
83 }
84
85 return newStack;
86 }
87
88 @Override 48 @Override
89 public boolean stillValid(Player player) { 49 protected int getRequiredSize() {
90 return container.stillValid(player); 50 return WIDTH * HEIGHT;
91 } 51 }
92} 52}
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeBlockMenu.java b/src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeBlockMenu.java
new file mode 100644
index 0000000..897abe9
--- /dev/null
+++ b/src/main/java/lv/enes/mc/eris_alchemy/menu/ChestLikeBlockMenu.java
@@ -0,0 +1,67 @@
1package lv.enes.mc.eris_alchemy.menu;
2
3import jakarta.annotation.Nonnull;
4import net.minecraft.world.Container;
5import net.minecraft.world.entity.player.Inventory;
6import net.minecraft.world.entity.player.Player;
7import net.minecraft.world.inventory.AbstractContainerMenu;
8import net.minecraft.world.inventory.MenuType;
9import net.minecraft.world.item.ItemStack;
10
11public abstract class ChestLikeBlockMenu extends AbstractContainerMenu {
12 protected final Container container;
13
14 public ChestLikeBlockMenu(
15 MenuType<? extends ChestLikeBlockMenu> type,
16 int syncId,
17 Inventory playerInventory,
18 Container container
19 ) {
20 super(type, syncId);
21 checkContainerSize(container, getRequiredSize());
22 this.container = container;
23 container.startOpen(playerInventory.player);
24 addSlots(playerInventory);
25 }
26
27 protected abstract void addSlots(Inventory playerInventory);
28 protected abstract int getRequiredSize();
29
30 public Container getContainer() {
31 return container;
32 }
33
34 @Nonnull
35 @Override
36 public ItemStack quickMoveStack(Player player, int fromIndex) {
37 var newStack = ItemStack.EMPTY;
38 var slot = slots.get(fromIndex);
39 if (!slot.hasItem()) {
40 return newStack;
41 }
42
43 var originalStack = slot.getItem();
44 newStack = originalStack.copy();
45
46 if (fromIndex < container.getContainerSize()) {
47 if (!moveItemStackTo(originalStack, container.getContainerSize(), slots.size(), true)) {
48 return ItemStack.EMPTY;
49 }
50 } else if (!moveItemStackTo(originalStack, 0, container.getContainerSize(), false)) {
51 return ItemStack.EMPTY;
52 }
53
54 if (originalStack.isEmpty()) {
55 slot.setByPlayer(ItemStack.EMPTY);
56 } else {
57 slot.setChanged();
58 }
59
60 return newStack;
61 }
62
63 @Override
64 public boolean stillValid(Player player) {
65 return container.stillValid(player);
66 }
67}