Skip to content

Commit 71b32c3

Browse files
feat: load more abstractions
1 parent 9d46031 commit 71b32c3

11 files changed

Lines changed: 451 additions & 263 deletions

File tree

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
package com.tcm.MineTale;
22

33
import com.tcm.MineTale.block.workbenches.screen.FurnaceWorkbenchScreen;
4+
import com.tcm.MineTale.block.workbenches.screen.CampfireWorkbenchScreen;
45
import com.tcm.MineTale.registry.ModMenuTypes;
56

67
import net.fabricmc.api.ClientModInitializer;
78
import net.minecraft.client.gui.screens.MenuScreens;
89

910
public class MineTaleClient implements ClientModInitializer {
10-
/**
11-
* Registers the screen factory for the furnace workbench menu so the client can create FurnaceWorkbenchScreen instances for that menu type.
12-
*/
1311
@Override
1412
public void onInitializeClient() {
1513
MenuScreens.register(ModMenuTypes.FURNACE_WORKBENCH_MENU, FurnaceWorkbenchScreen::new);
14+
MenuScreens.register(ModMenuTypes.CAMPFIRE_WORKBENCH_MENU, CampfireWorkbenchScreen::new);
1615
}
1716
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.tcm.MineTale.block.workbenches.screen;
2+
3+
import com.tcm.MineTale.block.workbenches.menu.CampfireWorkbenchMenu;
4+
5+
import net.minecraft.client.gui.GuiGraphics;
6+
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
7+
import net.minecraft.client.renderer.RenderPipelines;
8+
import net.minecraft.resources.Identifier;
9+
import net.minecraft.world.entity.player.Inventory;
10+
import net.minecraft.network.chat.Component;
11+
12+
public class CampfireWorkbenchScreen extends AbstractContainerScreen<CampfireWorkbenchMenu> {
13+
private static final Identifier TEXTURE =
14+
// Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "textures/gui/container/furnace_workbench.png");
15+
Identifier.withDefaultNamespace("textures/gui/container/furnace.png");
16+
17+
/**
18+
* Creates a new furnace workbench screen for the given menu, player inventory, and title.
19+
*
20+
* @param menu the container menu that provides slots and syncs state for this screen
21+
* @param inventory the player's inventory to display and interact with
22+
* @param title the title component shown at the top of the screen
23+
*/
24+
public CampfireWorkbenchScreen(CampfireWorkbenchMenu menu, Inventory inventory, Component title) {
25+
super(menu, inventory, title);
26+
}
27+
28+
/**
29+
* Initializes the screen and centers the title horizontally by setting {@code titleLabelX}.
30+
*/
31+
@Override
32+
protected void init() {
33+
super.init();
34+
this.titleLabelX = (this.imageWidth - this.font.width(this.title)) / 2;
35+
}
36+
37+
/**
38+
* Draws the furnace workbench background texture onto the screen.
39+
*
40+
* @param guiGraphics the graphics context used for drawing
41+
* @param f partial tick time used for interpolation
42+
* @param i current mouse x position
43+
* @param j current mouse y position
44+
*/
45+
protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
46+
int k = this.leftPos;
47+
int l = this.topPos;
48+
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, k, l, 0.0F, 0.0F, this.imageWidth, this.imageHeight, 256, 256);
49+
}
50+
51+
/**
52+
* Renders the furnace workbench screen, drawing its background, contents, and tooltips.
53+
*
54+
* @param graphics the graphics context used for rendering
55+
* @param mouseX the current mouse X coordinate
56+
* @param mouseY the current mouse Y coordinate
57+
* @param delta the frame time delta (partial tick) used for animated rendering
58+
*/
59+
@Override
60+
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
61+
renderBackground(graphics, mouseX, mouseY, delta);
62+
super.render(graphics, mouseX, mouseY, delta);
63+
renderTooltip(graphics, mouseX, mouseY);
64+
}
65+
}

src/main/java/com/tcm/MineTale/block/workbenches/CampfireWorkbench.java

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,14 @@
1010

1111
import net.minecraft.core.BlockPos;
1212
import net.minecraft.core.Direction;
13-
import net.minecraft.world.InteractionResult;
14-
import net.minecraft.world.entity.player.Player;
1513
import net.minecraft.world.level.BlockGetter;
16-
import net.minecraft.world.level.Level;
1714
import net.minecraft.world.level.block.Block;
1815
import net.minecraft.world.level.block.RenderShape;
1916
import net.minecraft.world.level.block.entity.BlockEntity;
2017
import net.minecraft.world.level.block.entity.BlockEntityType;
2118
import net.minecraft.world.level.block.state.BlockState;
2219
import net.minecraft.world.level.block.state.properties.ChestType;
2320
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
24-
import net.minecraft.world.phys.BlockHitResult;
2521
import net.minecraft.world.phys.shapes.CollisionContext;
2622
import net.minecraft.world.phys.shapes.VoxelShape;
2723

@@ -84,49 +80,16 @@ public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, Co
8480
}
8581

8682
/**
87-
* Creates and returns the block entity for this block only when the block represents the master
88-
* position (the lower half and not of type RIGHT).
83+
* Create the block entity for this block; only the master block of the multi-block workbench receives an entity.
8984
*
90-
* @return the created BlockEntity when this block is the master (HALF == LOWER and TYPE != RIGHT), or `null` otherwise
85+
* @return the created {@link BlockEntity} for the master block, or `null` if this position does not host an entity
9186
*/
9287
@Nullable
9388
@Override
9489
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
95-
// Only spawn the entity at the "Master" position (LOWER + LEFT or LOWER + SINGLE)
96-
if (state.getValue(HALF) == DoubleBlockHalf.LOWER && state.getValue(TYPE) != ChestType.RIGHT) {
97-
return blockEntityType.get().create(pos, state);
98-
}
99-
return null;
100-
}
101-
102-
/**
103-
* Handles a player's interaction with the workbench when no item is used.
104-
*
105-
* <p>On the client this acknowledges the interaction. On the server this method
106-
* is a hook for workbench-specific handling; if the workbench processes the
107-
* interaction it will consume it, otherwise the interaction is passed to other handlers.</p>
108-
*
109-
* @param state the block state of the workbench
110-
* @param level the world in which the interaction occurs
111-
* @param pos the position of the interacted block
112-
* @param player the player performing the interaction
113-
* @param hit the hit result describing the interaction point
114-
* @return {@code InteractionResult.SUCCESS} on client, {@code InteractionResult.CONSUME} if handled by the workbench, or {@code InteractionResult.PASS} otherwise
115-
*/
116-
@Override
117-
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hit) {
118-
if (level.isClientSide()) return InteractionResult.SUCCESS;
119-
120-
// BlockPos masterPos = getMasterPos(state, pos);
121-
// BlockEntity be = level.getBlockEntity(masterPos);
122-
123-
// if (be instanceof AbstractWorkbenchEntity) {
124-
// // Open UI or handle Recycling logic here
125-
// // Example: if player is holding a tool, try to recycle it
126-
// return InteractionResult.CONSUME;
127-
// }
128-
129-
return InteractionResult.PASS;
90+
// AbstractWorkbench logic ensures only the Master block gets the entity.
91+
// We override it here to point specifically to our Furnace entity.
92+
return super.newBlockEntity(pos, state);
13093
}
13194

13295
/**

src/main/java/com/tcm/MineTale/block/workbenches/entity/AbstractWorkbenchEntity.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import net.minecraft.network.chat.Component;
1010
import net.minecraft.world.Container;
1111
import net.minecraft.world.MenuProvider;
12+
import net.minecraft.world.SimpleContainer;
1213
import net.minecraft.world.entity.player.Inventory;
1314
import net.minecraft.world.entity.player.Player;
1415
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -78,6 +79,23 @@ public List<Container> getNearbyInventories() {
7879
return inventories;
7980
}
8081

82+
/**
83+
* Finds the first output slot that can accept the given result.
84+
*
85+
* @param result the item stack to place into an output slot
86+
* @return the index of the first suitable output slot between OUTPUT_START and OUTPUT_END, or -1 if none is available
87+
*/
88+
public int findOutputSlot(ItemStack result, SimpleContainer inventory, int start, int end) {
89+
for (int i = start; i <= end; i++) {
90+
ItemStack out = inventory.getItem(i);
91+
if (out.isEmpty() || (ItemStack.isSameItem(out, result)
92+
&& out.getCount() + result.getCount() <= out.getMaxStackSize())) {
93+
return i;
94+
}
95+
}
96+
return -1;
97+
}
98+
8199
/**
82100
* Attempt to recycle an ItemStack into component items and distribute those components to nearby inventories or drop them at the workbench location.
83101
*

src/main/java/com/tcm/MineTale/block/workbenches/entity/CampfireWorkbenchEntity.java

Lines changed: 116 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,79 @@
22

33
import org.jspecify.annotations.Nullable;
44

5+
import com.mojang.serialization.Codec;
6+
import com.tcm.MineTale.block.workbenches.menu.CampfireWorkbenchMenu;
57
import com.tcm.MineTale.registry.ModBlockEntities;
68

79
import net.minecraft.core.BlockPos;
10+
import net.minecraft.world.SimpleContainer;
811
import net.minecraft.world.entity.player.Inventory;
912
import net.minecraft.world.entity.player.Player;
1013
import net.minecraft.world.inventory.AbstractContainerMenu;
14+
import net.minecraft.world.inventory.ContainerData;
15+
import net.minecraft.world.level.Level;
1116
import net.minecraft.world.level.block.state.BlockState;
17+
import net.minecraft.world.level.storage.ValueInput;
18+
import net.minecraft.world.level.storage.ValueOutput;
1219

1320
public class CampfireWorkbenchEntity extends AbstractWorkbenchEntity {
21+
private final SimpleContainer inventory = new SimpleContainer(7);
22+
23+
private int cookTime;
24+
private int cookTimeTotal = 200;
25+
private int fuelTime;
26+
27+
protected final ContainerData data = new ContainerData() {
28+
/**
29+
* Retrieves an internal data value by index for UI synchronization.
30+
*
31+
* @param index the data index: 0 = remaining fuel time, 1 = fuel total (constant 100),
32+
* 2 = current cook progress, 3 = total cook time
33+
* @return the value associated with {@code index}, or 0 for any other index
34+
*/
35+
@Override
36+
public int get(int index) {
37+
return switch (index) {
38+
case 0 -> fuelTime;
39+
case 1 -> 100; // Fuel total
40+
case 2 -> cookTime;
41+
case 3 -> cookTimeTotal;
42+
default -> 0;
43+
};
44+
}
45+
46+
/**
47+
* Sets an internal workbench data field identified by index.
48+
*
49+
* Supported indices:
50+
* <ul>
51+
* <li>0 — sets {@code fuelTime}</li>
52+
* <li>2 — sets {@code cookTime}</li>
53+
* </ul>
54+
* Other indices are ignored.
55+
*
56+
* @param index the data index to set
57+
* @param value the value to assign to the indexed field
58+
*/
59+
@Override
60+
public void set(int index, int value) {
61+
switch (index) {
62+
case 0 -> fuelTime = value;
63+
case 2 -> cookTime = value;
64+
}
65+
}
66+
67+
/**
68+
* The number of data values exposed by this ContainerData.
69+
*
70+
* @return the number of data entries (4)
71+
*/
72+
@Override
73+
public int getCount() {
74+
return 4;
75+
}
76+
};
77+
1478
/**
1579
* Creates a CampfireWorkbenchEntity at the given position with the provided block state.
1680
*
@@ -22,21 +86,65 @@ public class CampfireWorkbenchEntity extends AbstractWorkbenchEntity {
2286
public CampfireWorkbenchEntity(BlockPos blockPos, BlockState blockState) {
2387
super(ModBlockEntities.CAMPFIRE_WORKBENCH_BE, blockPos, blockState);
2488

25-
this.scanRadius = 6.0;
89+
this.scanRadius = 0.0;
2690
this.tier = 1;
2791
}
2892

93+
public void tick(Level level, BlockPos pos, BlockState state) {
94+
if (level.isClientSide()) return;
95+
96+
// boolean changed = false;
97+
// ItemStack fuel = inventory.getItem(FUEL_SLOT);
98+
// List<ItemStack> inputs = List.of(inventory.getItem(INPUT_1), inventory.getItem(INPUT_2));
99+
}
100+
101+
/**
102+
* Determines whether the provided item stack is a supported food.
103+
*
104+
* @param stack the item stack to test
105+
*/
106+
// private boolean isCookable(List<ItemStack> stacks) { return stack.is(ItemTags.MEAT); }
107+
108+
/**
109+
* Determines whether the given item stack represents a fuel item.
110+
*
111+
* @param stack the item stack to inspect
112+
*/
113+
// private boolean isFuel(ItemStack stack) { return stack.is(ItemTags.LOGS_THAT_BURN) || stack.is(Items.STICK); }
114+
115+
/**
116+
* Persist entity-specific state into the provided ValueOutput.
117+
*
118+
* Stores the workbench's tier as "WorkbenchTier" and its scan radius as "ScanRadius"
119+
* using type-safe Codecs.
120+
*
121+
* @param valueOutput the output writer used to serialize this entity's fields
122+
*/
123+
@Override
124+
protected void saveAdditional(ValueOutput valueOutput) {
125+
super.saveAdditional(valueOutput);
126+
// store() uses Codecs for type safety
127+
valueOutput.store("WorkbenchTier", Codec.INT, this.tier);
128+
valueOutput.store("ScanRadius", Codec.DOUBLE, this.scanRadius);
129+
}
130+
29131
/**
30-
* Creates the server-side container menu for this workbench for the given player and window ID.
132+
* Restores workbench-specific state from persistent storage and applies defaults when keys are absent.
31133
*
32-
* @param syncId the window synchronization ID provided by the client
33-
* @param playerInventory the player's inventory
34-
* @param player the player opening the menu
35-
* @return the created {@link AbstractContainerMenu}, or `null` if no menu should be opened
134+
* Delegates to the superclass load logic, then reads:
135+
* - "WorkbenchTier" (int) into {@code tier}, defaulting to {@code 1} if missing.
136+
* - "ScanRadius" (double) into {@code scanRadius}, defaulting to {@code 5.0} if missing.
36137
*/
138+
@Override
139+
protected void loadAdditional(ValueInput valueInput) {
140+
super.loadAdditional(valueInput);
141+
// read() returns an Optional
142+
this.tier = valueInput.read("WorkbenchTier", Codec.INT).orElse(1);
143+
this.scanRadius = valueInput.read("ScanRadius", Codec.DOUBLE).orElse(0.0);
144+
}
145+
37146
@Override
38147
public @Nullable AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) {
39-
// TODO Auto-generated method stub
40-
throw new UnsupportedOperationException("Unimplemented method 'createMenu'");
148+
return new CampfireWorkbenchMenu(syncId, playerInventory, this.inventory, this.data);
41149
}
42150
}

0 commit comments

Comments
 (0)