Skip to content

Commit 9df4330

Browse files
Merge pull request #70 from CodeMonkeysMods/feat/chicken-coop
feat: add roost logic
2 parents 8c1c970 + 5aeca54 commit 9df4330

File tree

14 files changed

+998
-27
lines changed

14 files changed

+998
-27
lines changed

src/client/java/com/tcm/MineTale/MineTaleClient.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.tcm.MineTale;
22

3-
import com.tcm.MineTale.block.workbenches.menu.BlacksmithsWorkbenchMenu;
43
import com.tcm.MineTale.block.workbenches.screen.*;
54
import com.tcm.MineTale.network.ClientboundNearbyInventorySyncPacket;
65

src/client/java/com/tcm/MineTale/block/workbenches/screen/FurnitureWorkbenchScreen.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.tcm.MineTale.MineTale;
44
import com.tcm.MineTale.block.workbenches.menu.AbstractWorkbenchContainerMenu;
5-
import com.tcm.MineTale.block.workbenches.menu.BlacksmithsWorkbenchMenu;
65
import com.tcm.MineTale.block.workbenches.menu.FurnitureWorkbenchMenu;
76
import com.tcm.MineTale.mixin.client.ClientRecipeBookAccessor;
87
import com.tcm.MineTale.network.CraftRequestPayload;

src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
package com.tcm.MineTale.datagen;
22

33
import com.tcm.MineTale.registry.ModBlocks;
4-
import com.tcm.MineTale.util.ModTags;
54
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
65
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
76
import net.minecraft.core.HolderLookup;
87
import net.minecraft.tags.BlockTags;
9-
import net.minecraft.world.level.block.Block;
10-
import net.minecraft.world.level.block.Blocks;
118

129
import java.util.concurrent.CompletableFuture;
1310

src/client/java/com/tcm/MineTale/datagen/ModItemTagProvider.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import net.minecraft.core.HolderLookup;
88
import net.minecraft.world.item.Item;
99
import net.minecraft.world.item.Items;
10-
import net.minecraft.world.level.block.Blocks;
1110

1211
import java.util.concurrent.CompletableFuture;
1312

src/client/java/com/tcm/MineTale/datagen/ModModelProvider.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import net.minecraft.client.renderer.block.model.VariantMutator;
1515
import net.minecraft.core.Direction;
1616
import net.minecraft.resources.Identifier;
17-
import net.minecraft.world.item.Items;
1817
import net.minecraft.world.level.block.Block;
1918
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
2019
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;

src/client/java/com/tcm/MineTale/datagen/recipes/AlchemistRecipes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.tcm.MineTale.registry.ModItems;
66
import com.tcm.MineTale.registry.ModRecipeDisplay;
77
import com.tcm.MineTale.registry.ModRecipes;
8+
89
import net.minecraft.core.HolderLookup;
910
import net.minecraft.data.recipes.RecipeOutput;
1011
import net.minecraft.data.recipes.RecipeProvider;

src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.tcm.MineTale.datagen.recipes;
22

3-
import com.tcm.MineTale.datagen.ModItemTagProvider;
43
import com.tcm.MineTale.datagen.builders.WorkbenchRecipeBuilder;
54
import com.tcm.MineTale.registry.ModBlocks;
65
import com.tcm.MineTale.registry.ModItems;

src/main/java/com/tcm/MineTale/block/ChickenCoopBlock.java

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,50 @@
11
package com.tcm.MineTale.block;
22

33
import com.mojang.serialization.MapCodec;
4+
import com.tcm.MineTale.block.entity.ChickenCoopEntity;
5+
import com.tcm.MineTale.registry.ModBlockEntities;
46
import com.tcm.MineTale.util.CoopPart;
57

68
import net.minecraft.core.BlockPos;
79
import net.minecraft.core.Direction;
10+
import net.minecraft.sounds.SoundEvents;
11+
import net.minecraft.sounds.SoundSource;
812
import net.minecraft.util.RandomSource;
13+
import net.minecraft.world.InteractionResult;
914
import net.minecraft.world.entity.LivingEntity;
1015
import net.minecraft.world.entity.player.Player;
1116
import net.minecraft.world.item.ItemStack;
17+
import net.minecraft.world.item.Items;
1218
import net.minecraft.world.item.context.BlockPlaceContext;
1319
import net.minecraft.world.level.Level;
1420
import net.minecraft.world.level.LevelReader;
1521
import net.minecraft.world.level.ScheduledTickAccess;
1622
import net.minecraft.world.level.block.Block;
1723
import net.minecraft.world.level.block.Blocks;
24+
import net.minecraft.world.level.block.EntityBlock;
1825
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
1926
import net.minecraft.world.level.block.entity.BlockEntity;
27+
import net.minecraft.world.level.block.entity.BlockEntityTicker;
28+
import net.minecraft.world.level.block.entity.BlockEntityType;
2029
import net.minecraft.world.level.block.state.BlockState;
2130
import net.minecraft.world.level.block.state.StateDefinition;
2231
import net.minecraft.world.level.block.state.properties.EnumProperty;
32+
import net.minecraft.world.phys.BlockHitResult;
33+
2334
import org.jetbrains.annotations.Nullable;
2435

25-
public class ChickenCoopBlock extends HorizontalDirectionalBlock {
36+
public class ChickenCoopBlock extends HorizontalDirectionalBlock implements EntityBlock {
2637
public static final EnumProperty<CoopPart> PART = EnumProperty.create("part", CoopPart.class);
2738

2839
public static final MapCodec<ChickenCoopBlock> CODEC = simpleCodec(ChickenCoopBlock::new);
2940

41+
/**
42+
* Create a ChickenCoopBlock configured with the provided block properties and a default state.
43+
*
44+
* The default state sets FACING to NORTH and PART to CoopPart.BOTTOM_FRONT_LEFT.
45+
*
46+
* @param properties block properties used to configure this block's behaviour and characteristics
47+
*/
3048
public ChickenCoopBlock(Properties properties) {
3149
super(properties);
3250
// Default to the origin part (Bottom Front Left) facing North
@@ -35,6 +53,57 @@ public ChickenCoopBlock(Properties properties) {
3553
.setValue(PART, CoopPart.BOTTOM_FRONT_LEFT));
3654
}
3755

56+
/**
57+
* Provides a BlockEntityTicker for the coop's centre-front part on the server.
58+
*
59+
* Returns a ticker that delegates to ChickenCoopEntity.tick when the call is on the logical server,
60+
* the block state's PART is BOTTOM_FRONT_CENTER and the requested BlockEntityType equals ModBlockEntities.CHICKEN_COOP_BE.
61+
*
62+
* @param <T> the block entity type
63+
* @param level the level containing the block
64+
* @param state the block state for which a ticker is requested
65+
* @param type the requested block entity type
66+
* @return a ticker delegating to ChickenCoopEntity.tick when applicable, `null` otherwise
67+
*/
68+
@Nullable
69+
@Override
70+
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
71+
// 1. Only tick on server side
72+
if (level.isClientSide()) return null;
73+
74+
// 2. Only tick if this is the correct part of the coop
75+
if (state.getValue(PART) != CoopPart.BOTTOM_FRONT_CENTER) return null;
76+
77+
// 3. Link to the static tick method in your Entity class
78+
return type == ModBlockEntities.CHICKEN_COOP_BE
79+
? (lvl, pos, st, be) -> ChickenCoopEntity.tick(lvl, pos, st, (ChickenCoopEntity) be)
80+
: null;
81+
}
82+
83+
/**
84+
* Creates the block entity for the coop when this block represents the centre-front (brain) part.
85+
*
86+
* @param pos the block position
87+
* @param state the current block state
88+
* @return {@code ChickenCoopEntity} for the centre-front part, {@code null} otherwise
89+
*/
90+
@Nullable
91+
@Override
92+
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
93+
// Only the center-front part gets the "brain"
94+
if (state.getValue(PART) == CoopPart.BOTTOM_FRONT_CENTER) {
95+
return new ChickenCoopEntity(pos, state);
96+
}
97+
return null;
98+
}
99+
100+
/**
101+
* Registers this block's state properties.
102+
*
103+
* Adds the horizontal facing and coop part properties so block states can represent orientation and segment.
104+
*
105+
* @param builder the state definition builder to register properties with
106+
*/
38107
@Override
39108
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
40109
builder.add(FACING, PART);
@@ -185,7 +254,14 @@ public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state,
185254
}
186255

187256
/**
188-
* Rotates the 3x3x2 grid logic based on which way the player is facing.
257+
* Compute the world block position for a local coordinate inside the coop's 3×2×3 grid, taking block facing into account.
258+
*
259+
* @param origin the reference origin position (the block considered as the grid origin)
260+
* @param facing the horizontal direction the coop is facing; used to convert local depth into world direction
261+
* @param x local x index in the 3-wide grid (0 = left, 1 = centre, 2 = right)
262+
* @param z local depth index along the facing direction (0..2); larger values are further away from the player
263+
* @param y local vertical index (0..2) measured as blocks above the origin
264+
* @return the computed BlockPos in world coordinates for the given local grid coordinate
189265
*/
190266
private BlockPos calculateOffset(BlockPos origin, Direction facing, int x, int z, int y) {
191267
// x-1 centers the 3-wide structure (0=left, 1=center, 2=right)
@@ -198,8 +274,67 @@ private BlockPos calculateOffset(BlockPos origin, Direction facing, int x, int z
198274
.above(y);
199275
}
200276

201-
@Override
277+
/**
278+
* Provide the block's MapCodec used by the game's codec system for (de)serialisation.
279+
*
280+
* @return the MapCodec instance for this block's state
281+
*/
282+
@Override
202283
protected MapCodec<? extends HorizontalDirectionalBlock> codec() {
203284
return CODEC;
204285
}
205-
}
286+
287+
/**
288+
* Handle interaction with the coop when used without an item, dispensing any collected eggs to the player.
289+
*
290+
* On the client this returns `InteractionResult.SUCCESS`. On the server this locates the coop's brain
291+
* block entity (the bottom-front-center part); if that entity has eggs they are transferred to the player
292+
* (or dropped at the player's feet if their inventory is full) and a chicken-egg sound is played.
293+
*
294+
* @param state the current block state
295+
* @param level the level where the block is located
296+
* @param pos the position of the interacted block
297+
* @param player the player performing the interaction
298+
* @param hitResult hit information for the interaction
299+
* @return `InteractionResult.SUCCESS` if eggs were given or on the client, `InteractionResult.PASS` otherwise.
300+
*/
301+
@Override
302+
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
303+
if (level.isClientSide()) return InteractionResult.SUCCESS;
304+
305+
// 1. Find the "brain" position (the Bottom Front Center)
306+
Direction facing = state.getValue(FACING);
307+
CoopPart currentPart = state.getValue(PART);
308+
309+
// Calculate the origin (Bottom Front Center is our origin in calculateOffset logic)
310+
// Based on your calculateOffset, the BFC is at x=1, z=0, y=0.
311+
BlockPos brainPos = pos.subtract(calculateOffset(BlockPos.ZERO, facing,
312+
currentPart.getXOffset(), currentPart.getZOffset(), currentPart.getYOffset()))
313+
.relative(facing, 0) // already at z=0
314+
.relative(facing.getClockWise(), 0); // x=1 is center, so we shift back to it
315+
316+
// Easier way: Since you know the brain is always at BOTTOM_FRONT_CENTER:
317+
// We just need to find where that specific part is relative to the current block.
318+
// However, your 'calculateOffset' is already the source of truth.
319+
320+
if (level.getBlockEntity(brainPos) instanceof ChickenCoopEntity coopBe) {
321+
int eggsToGive = coopBe.takeAllEggs();
322+
323+
if (eggsToGive > 0) {
324+
// Give the player an egg
325+
ItemStack eggStack = new ItemStack(Items.EGG, eggsToGive);
326+
if (!player.getInventory().add(eggStack)) {
327+
// If inventory full, drop at player's feet
328+
player.drop(eggStack, false);
329+
}
330+
331+
// Play a sound to give feedback
332+
level.playSound(null, pos, SoundEvents.CHICKEN_EGG, SoundSource.PLAYERS, 1.0f, 1.0f);
333+
return InteractionResult.SUCCESS;
334+
}
335+
}
336+
337+
return InteractionResult.PASS;
338+
}
339+
340+
}

0 commit comments

Comments
 (0)