Skip to content

Commit a978de2

Browse files
Merge branch '1.21' into Blacksmith-an-stuff3
2 parents 3ebe2bf + 97a1923 commit a978de2

3 files changed

Lines changed: 298 additions & 3 deletions

File tree

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
package com.tcm.MineTale.block;
2+
3+
import com.mojang.serialization.MapCodec;
4+
import com.tcm.MineTale.util.CoopPart;
5+
6+
import net.minecraft.core.BlockPos;
7+
import net.minecraft.core.Direction;
8+
import net.minecraft.util.RandomSource;
9+
import net.minecraft.world.entity.LivingEntity;
10+
import net.minecraft.world.entity.player.Player;
11+
import net.minecraft.world.item.ItemStack;
12+
import net.minecraft.world.item.context.BlockPlaceContext;
13+
import net.minecraft.world.level.Level;
14+
import net.minecraft.world.level.LevelReader;
15+
import net.minecraft.world.level.ScheduledTickAccess;
16+
import net.minecraft.world.level.block.Block;
17+
import net.minecraft.world.level.block.Blocks;
18+
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
19+
import net.minecraft.world.level.block.entity.BlockEntity;
20+
import net.minecraft.world.level.block.state.BlockState;
21+
import net.minecraft.world.level.block.state.StateDefinition;
22+
import net.minecraft.world.level.block.state.properties.EnumProperty;
23+
import org.jetbrains.annotations.Nullable;
24+
25+
public class ChickenCoopBlock extends HorizontalDirectionalBlock {
26+
public static final EnumProperty<CoopPart> PART = EnumProperty.create("part", CoopPart.class);
27+
28+
public static final MapCodec<ChickenCoopBlock> CODEC = simpleCodec(ChickenCoopBlock::new);
29+
30+
public ChickenCoopBlock(Properties properties) {
31+
super(properties);
32+
// Default to the origin part (Bottom Front Left) facing North
33+
this.registerDefaultState(this.stateDefinition.any()
34+
.setValue(FACING, Direction.NORTH)
35+
.setValue(PART, CoopPart.BOTTOM_FRONT_LEFT));
36+
}
37+
38+
@Override
39+
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
40+
builder.add(FACING, PART);
41+
}
42+
43+
@Nullable
44+
@Override
45+
public BlockState getStateForPlacement(BlockPlaceContext context) {
46+
BlockPos clickedPos = context.getClickedPos();
47+
Level level = context.getLevel();
48+
Direction facing = context.getHorizontalDirection();
49+
50+
// Verification loop to ensure space is clear
51+
for (int x = 0; x < 3; x++) {
52+
for (int z = 0; z < 2; z++) {
53+
for (int y = 0; y < 3; y++) {
54+
BlockPos targetPos = calculateOffset(clickedPos, facing, x, z, y);
55+
if (!level.getBlockState(targetPos).canBeReplaced(context)) {
56+
return null;
57+
}
58+
}
59+
}
60+
}
61+
// Set the initial block to the Center-Front part
62+
return this.defaultBlockState()
63+
.setValue(FACING, facing)
64+
.setValue(PART, CoopPart.BOTTOM_FRONT_CENTER);
65+
}
66+
67+
@Override
68+
public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
69+
if (!level.isClientSide()) {
70+
Direction facing = state.getValue(FACING);
71+
72+
for (int x = 0; x < 3; x++) {
73+
for (int z = 0; z < 2; z++) { // z=0 is front, z=1 is back (away)
74+
for (int y = 0; y < 3; y++) {
75+
// Skip the block actually placed by the item (Bottom Front Center)
76+
if (x == 1 && z == 0 && y == 0) continue;
77+
78+
BlockPos targetPos = calculateOffset(pos, facing, x, z, y);
79+
CoopPart part = CoopPart.getPartFromCoords(x, z, y);
80+
81+
level.setBlock(targetPos, state.setValue(PART, part), 3);
82+
}
83+
}
84+
}
85+
}
86+
}
87+
88+
@Override
89+
protected BlockState updateShape(
90+
BlockState state,
91+
LevelReader levelReader,
92+
ScheduledTickAccess scheduledTickAccess,
93+
BlockPos pos,
94+
Direction direction,
95+
BlockPos neighborPos,
96+
BlockState neighborState,
97+
RandomSource randomSource
98+
) {
99+
// If a neighbor block that is supposed to be part of this coop is now AIR,
100+
// we return AIR to destroy this part of the coop as well.
101+
if (!neighborState.is(this) && isNeighborPartOfCoop(state, direction)) {
102+
return Blocks.AIR.defaultBlockState();
103+
}
104+
105+
return super.updateShape(state, levelReader, scheduledTickAccess, pos, direction, neighborPos, neighborState, randomSource);
106+
}
107+
108+
/**
109+
* Helper to check if the block in a specific direction is technically "connected"
110+
* to this specific part of the 3x3x2 grid.
111+
*/
112+
private boolean isNeighborPartOfCoop(BlockState state, Direction dir) {
113+
CoopPart part = state.getValue(PART);
114+
Direction facing = state.getValue(HorizontalDirectionalBlock.FACING);
115+
116+
// 1. Get the local offset of the neighbor block relative to this part
117+
// We convert the world Direction into a local x, y, z change
118+
int dx = dir.getStepX();
119+
int dy = dir.getStepY();
120+
int dz = dir.getStepZ();
121+
122+
// 2. Adjust for rotation (Facing)
123+
// This ensures that "Front" always matches your Enum's Z-axis logic
124+
// Note: This math varies slightly depending on how your placement logic
125+
// maps "Front" to the world. Below is a standard mapping:
126+
int localDx, localDz;
127+
switch (facing) {
128+
case NORTH -> { localDx = dx; localDz = dz; }
129+
case SOUTH -> { localDx = -dx; localDz = -dz; }
130+
case WEST -> { localDx = dz; localDz = -dx; }
131+
case EAST -> { localDx = -dz; localDz = dx; }
132+
default -> { localDx = dx; localDz = dz; }
133+
}
134+
135+
// 3. Calculate the neighbor's hypothetical grid position
136+
int neighborX = part.getXOffset() + localDx;
137+
int neighborZ = part.getZOffset() + localDz;
138+
int neighborY = part.getYOffset() + dy;
139+
140+
// 4. Check if these coordinates are within the 3x2x3 bounds
141+
// Width: 0-2 (X), Depth: 0-1 (Z), Height: 0-2 (Y)
142+
return neighborX >= 0 && neighborX < 3 &&
143+
neighborZ >= 0 && neighborZ < 2 &&
144+
neighborY >= 0 && neighborY < 3;
145+
}
146+
147+
@Override
148+
public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
149+
if (!level.isClientSide()) {
150+
Direction facing = state.getValue(FACING);
151+
CoopPart currentPart = state.getValue(PART);
152+
153+
// Calculate origin based on the piece being broken
154+
BlockPos origin = pos.subtract(calculateOffset(BlockPos.ZERO, facing,
155+
currentPart.getXOffset(), currentPart.getZOffset(), currentPart.getYOffset()));
156+
157+
// Use a flag to prevent re-entry if isNeighborPartOfCoop triggers
158+
for (int x = 0; x < 3; x++) {
159+
for (int z = 0; z < 2; z++) {
160+
for (int y = 0; y < 3; y++) {
161+
BlockPos targetPos = calculateOffset(origin, facing, x, z, y);
162+
BlockState targetState = level.getBlockState(targetPos);
163+
164+
if (targetState.is(this)) {
165+
// 1. Handle Drops: This checks the loot table (JSON) and drops items
166+
if (!player.isCreative()) {
167+
BlockEntity blockEntity = targetState.hasBlockEntity() ? level.getBlockEntity(targetPos) : null;
168+
Block.dropResources(targetState, level, targetPos, blockEntity, player, player.getMainHandItem());
169+
}
170+
171+
// 2. Set to AIR with flag 3 (Update neighbors + Send to clients)
172+
// Using destroyBlock with 'false' for drops since we handled it above
173+
// for better control, or just setBlock to AIR.
174+
level.setBlock(targetPos, Blocks.AIR.defaultBlockState(), 3);
175+
176+
// 3. Play break effects
177+
level.levelEvent(2001, targetPos, Block.getId(targetState));
178+
}
179+
}
180+
}
181+
}
182+
}
183+
184+
return super.playerWillDestroy(level, pos, state, player);
185+
}
186+
187+
/**
188+
* Rotates the 3x3x2 grid logic based on which way the player is facing.
189+
*/
190+
private BlockPos calculateOffset(BlockPos origin, Direction facing, int x, int z, int y) {
191+
// x-1 centers the 3-wide structure (0=left, 1=center, 2=right)
192+
int xAdjusted = x - 1;
193+
194+
// We use the 'facing' direction for depth (z).
195+
// This ensures z=1 is always "further away" from the player.
196+
return origin.relative(facing, z)
197+
.relative(facing.getClockWise(), xAdjusted)
198+
.above(y);
199+
}
200+
201+
@Override
202+
protected MapCodec<? extends HorizontalDirectionalBlock> codec() {
203+
return CODEC;
204+
}
205+
}

src/main/java/com/tcm/MineTale/registry/ModBlocks.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
import java.util.ArrayList;
44
import java.util.List;
5-
import java.util.Map;
65
import java.util.function.Function;
76

87
import com.tcm.MineTale.MineTale;
98
import com.tcm.MineTale.block.workbenches.*;
9+
import com.tcm.MineTale.block.ChickenCoopBlock;
1010
import com.tcm.MineTale.item.ModCreativeTab;
1111

1212
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
@@ -18,13 +18,11 @@
1818
import net.minecraft.resources.ResourceKey;
1919
import net.minecraft.sounds.SoundEvents;
2020
import net.minecraft.world.item.BlockItem;
21-
import net.minecraft.world.item.DyeColor;
2221
import net.minecraft.world.item.Item;
2322
import net.minecraft.world.item.Items;
2423
import net.minecraft.world.level.block.*;
2524
import net.minecraft.world.level.block.entity.BlockEntityType;
2625
import net.minecraft.world.level.block.state.BlockBehaviour;
27-
import net.minecraft.world.level.block.state.properties.BedPart;
2826
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
2927
import net.minecraft.world.level.material.MapColor;
3028
import net.minecraft.world.level.material.PushReaction;
@@ -373,6 +371,22 @@ public class ModBlocks {
373371
// public static final Block THORIUM_ORE_STONE = registerOreBlock("thorium_ore_stone", Block::new, BlockBehaviour.Properties.of().strength(2).requiresCorrectToolForDrops(), Blocks.STONE, Items.COPPER_ORE, 1);
374372
// public static final Block THORIUM_ORE_SANDSTONE = registerOreBlock("thorium_ore_sandstone", Block::new, BlockBehaviour.Properties.of().strength(2).requiresCorrectToolForDrops(), Blocks.SANDSTONE, Items.COPPER_ORE, 1);
375373

374+
public static final Block CHICKEN_COOP = register(
375+
"chicken_coop",
376+
ChickenCoopBlock::new,
377+
BlockBehaviour.Properties.of()
378+
.mapColor(MapColor.WOOD)
379+
.instrument(NoteBlockInstrument.BASS)
380+
.strength(2.0f, 3.0f)
381+
.sound(SoundType.WOOD)
382+
// This is important for multi-blocks:
383+
.noOcclusion()
384+
// 1.21.1 requires manual ignition/burning logic if you want it flammable,
385+
// but standard wood properties are a good start.
386+
.ignitedByLava(),
387+
true // We want a BlockItem so we can place it!
388+
);
389+
376390
/**
377391
* Adds all mod-registered blocks to the MineTale creative tab and logs the action.
378392
*
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.tcm.MineTale.util;
2+
3+
import net.minecraft.util.StringRepresentable;
4+
import org.jetbrains.annotations.NotNull;
5+
6+
public enum CoopPart implements StringRepresentable {
7+
// 18 Parts: 3 Wide (x) x 2 Deep (z) x 3 High (y)
8+
9+
// --- BOTTOM LAYER (y=0) ---
10+
BOTTOM_FRONT_LEFT("bottom_front_left", 0, 0, 0),
11+
BOTTOM_FRONT_CENTER("bottom_front_center", 1, 0, 0),
12+
BOTTOM_FRONT_RIGHT("bottom_front_right", 2, 0, 0),
13+
BOTTOM_BACK_LEFT("bottom_back_left", 0, 1, 0),
14+
BOTTOM_BACK_CENTER("bottom_back_center", 1, 1, 0),
15+
BOTTOM_BACK_RIGHT("bottom_back_right", 2, 1, 0),
16+
17+
// --- MIDDLE LAYER (y=1) ---
18+
MIDDLE_FRONT_LEFT("middle_front_left", 0, 0, 1),
19+
MIDDLE_FRONT_CENTER("middle_front_center", 1, 0, 1),
20+
MIDDLE_FRONT_RIGHT("middle_front_right", 2, 0, 1),
21+
MIDDLE_BACK_LEFT("middle_back_left", 0, 1, 1),
22+
MIDDLE_BACK_CENTER("middle_back_center", 1, 1, 1),
23+
MIDDLE_BACK_RIGHT("middle_back_right", 2, 1, 1),
24+
25+
// --- TOP LAYER (y=2) ---
26+
TOP_FRONT_LEFT("top_front_left", 0, 0, 2),
27+
TOP_FRONT_CENTER("top_front_center", 1, 0, 2),
28+
TOP_FRONT_RIGHT("top_front_right", 2, 0, 2),
29+
TOP_BACK_LEFT("top_back_left", 0, 1, 2),
30+
TOP_BACK_CENTER("top_back_center", 1, 1, 2),
31+
TOP_BACK_RIGHT("top_back_right", 2, 1, 2);
32+
33+
private final String name;
34+
private final int xOffset;
35+
private final int zOffset;
36+
private final int yOffset;
37+
38+
CoopPart(String name, int x, int z, int y) {
39+
this.name = name;
40+
this.xOffset = x;
41+
this.zOffset = z;
42+
this.yOffset = y;
43+
}
44+
45+
@Override
46+
public @NotNull String getSerializedName() {
47+
return this.name;
48+
}
49+
50+
public int getXOffset() { return xOffset; }
51+
public int getYOffset() { return yOffset; }
52+
public int getZOffset() { return zOffset; }
53+
54+
/**
55+
* Retrieves the CoopPart corresponding to the given grid offsets.
56+
* The grid is structured as 3x2x3 (width x depth x height),
57+
* corresponding to the x, z, and y axes respectively.
58+
*
59+
* @param x The width offset
60+
* @param z The depth offset
61+
* @param y The height offset
62+
* @return The matching CoopPart
63+
* @throws IllegalArgumentException if no part exists at the specified coordinates
64+
*/
65+
public static CoopPart getPartFromCoords(int x, int z, int y) {
66+
for (CoopPart part : values()) {
67+
if (part.xOffset == x && part.zOffset == z && part.yOffset == y) {
68+
return part;
69+
}
70+
}
71+
72+
throw new IllegalArgumentException(
73+
String.format("No CoopPart found at coordinates: x=%d, z=%d, y=%d", x, z, y)
74+
);
75+
}
76+
}

0 commit comments

Comments
 (0)