33import net .minecraft .core .BlockPos ;
44import net .minecraft .core .Direction ;
55import net .minecraft .util .RandomSource ;
6+ import net .minecraft .world .InteractionResult ;
7+ import net .minecraft .world .MenuProvider ;
68import net .minecraft .world .entity .LivingEntity ;
9+ import net .minecraft .world .entity .player .Player ;
710import net .minecraft .world .item .ItemStack ;
811import net .minecraft .world .item .context .BlockPlaceContext ;
912import net .minecraft .world .level .Level ;
1821import net .minecraft .world .level .block .state .BlockState ;
1922import net .minecraft .world .level .block .state .StateDefinition ;
2023import net .minecraft .world .level .block .state .properties .*;
24+ import net .minecraft .world .phys .BlockHitResult ;
25+
2126import org .jetbrains .annotations .Nullable ;
2227
28+ import com .tcm .MineTale .block .workbenches .entity .AbstractWorkbenchEntity ;
29+
2330import java .util .function .Supplier ;
2431
25- public abstract class AbstractWorkbench <E extends BlockEntity > extends BaseEntityBlock {
32+ public abstract class AbstractWorkbench <E extends AbstractWorkbenchEntity > extends BaseEntityBlock {
2633 public static final EnumProperty <Direction > FACING = HorizontalDirectionalBlock .FACING ;
2734 public static final EnumProperty <DoubleBlockHalf > HALF = BlockStateProperties .DOUBLE_BLOCK_HALF ;
2835 public static final EnumProperty <ChestType > TYPE = BlockStateProperties .CHEST_TYPE ;
@@ -127,8 +134,98 @@ private boolean isCompatiblePart(BlockState current, BlockState neighbor) {
127134 return true ;
128135 }
129136
137+ /**
138+ * Registers the block state properties used by this block.
139+ *
140+ * @param builder the state builder to populate with this block's properties (`FACING`, `HALF`, `TYPE`)
141+ */
130142 @ Override
131143 protected void createBlockStateDefinition (StateDefinition .Builder <Block , BlockState > builder ) {
132144 builder .add (FACING , HALF , TYPE );
133145 }
146+
147+ /**
148+ * Create the block entity for the primary anchor of a multipart workbench.
149+ *
150+ * @param pos the position where the block entity would be created
151+ * @param state the block state at the position
152+ * @return the new block entity when this block is the primary anchor (lower half with TYPE `LEFT` or `SINGLE`), or `null` if this block is a secondary part
153+ */
154+ @ Nullable
155+ @ Override
156+ public BlockEntity newBlockEntity (BlockPos pos , BlockState state ) {
157+ // We only want a Block Entity at the 'primary' anchor point of the structure.
158+ // For 1x1: HALF=LOWER, TYPE=SINGLE
159+ // For 2x1: HALF=LOWER, TYPE=LEFT
160+ // For 2x2: HALF=LOWER, TYPE=LEFT
161+
162+ boolean isLower = state .getValue (HALF ) == DoubleBlockHalf .LOWER ;
163+ ChestType type = state .getValue (TYPE );
164+
165+ // If it's the RIGHT side of a wide block, or the UPPER half of a tall block, return null.
166+ if (isLower && (type == ChestType .LEFT || type == ChestType .SINGLE )) {
167+ return this .blockEntityType .get ().create (pos , state );
168+ }
169+
170+ return null ;
171+ }
172+
173+ /**
174+ * Open the workbench menu for the block's master part when a player interacts without an item.
175+ *
176+ * @param state the block state at the clicked position
177+ * @param level the level in which the interaction occurs
178+ * @param pos the position of the block that was interacted with
179+ * @param player the player performing the interaction
180+ * @param hitResult details about the hit (hit position and face)
181+ * @return {@code InteractionResult.CONSUME} if a menu was opened on the server, {@code InteractionResult.SUCCESS} on the client, {@code InteractionResult.PASS} otherwise
182+ */
183+ @ Override
184+ protected InteractionResult useWithoutItem (BlockState state , Level level , BlockPos pos , Player player , BlockHitResult hitResult ) {
185+ if (level .isClientSide ()) {
186+ return InteractionResult .SUCCESS ;
187+ }
188+
189+ // 1. Find the Master Position (Bottom-Left)
190+ BlockPos masterPos = getMasterPos (state , pos );
191+ BlockEntity blockEntity = level .getBlockEntity (masterPos );
192+
193+ // 2. Check if the Master block has the MenuProvider trait
194+ if (blockEntity instanceof MenuProvider menuProvider ) {
195+ // 3. Open the Screen (this triggers the ScreenHandler/Menu)
196+ player .openMenu (menuProvider );
197+ return InteractionResult .CONSUME ;
198+ }
199+
200+ return InteractionResult .PASS ;
201+ }
202+
203+ /**
204+ * Compute the master (bottom-left) anchor position for this workbench block.
205+ *
206+ * The master is the block that serves as the primary anchor for multi-block
207+ * behavior and block-entity placement: if this block is the upper half the
208+ * master is one block below; if this block is the right-side part the master
209+ * is one block to the left relative to the block's facing.
210+ *
211+ * @param state the block state used to determine HALF, TYPE, and FACING
212+ * @param pos the current block position
213+ * @return the position of the master (bottom-left) block for this workbench
214+ */
215+ public BlockPos getMasterPos (BlockState state , BlockPos pos ) {
216+ BlockPos master = pos ;
217+ Direction facing = state .getValue (FACING );
218+
219+ // Move down if we are the upper half
220+ if (state .getValue (HALF ) == DoubleBlockHalf .UPPER ) {
221+ master = master .below ();
222+ }
223+
224+ // Move left if we are the right side (relative to facing)
225+ if (state .getValue (TYPE ) == ChestType .RIGHT ) {
226+ master = master .relative (facing .getCounterClockWise ());
227+ }
228+
229+ return master ;
230+ }
134231}
0 commit comments