Skip to content

Commit d0b26c3

Browse files
feat: campfire can cook single item recipes
1 parent 71b32c3 commit d0b26c3

16 files changed

Lines changed: 792 additions & 20 deletions

build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ loom {
3030

3131
}
3232

33+
fabricApi {
34+
configureDataGeneration() {
35+
client = true
36+
}
37+
}
38+
3339
dependencies {
3440
// To change the versions see the gradle.properties file
3541
minecraft "com.mojang:minecraft:${project.minecraft_version}"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// 1.21.11 -999999999-01-01T00:00:00 Example mod/minetaleModRecipeProvider
2+
dcc5dac31dc51d16b53e14666f87d47297089c0d data/minetale/advancement/recipes/campfire_pork_cooking.json
3+
96af174cae154bddc108902a40e90c9bd971c7cf data/minetale/recipe/campfire_pork_cooking.json
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"parent": "minecraft:recipes/root",
3+
"criteria": {
4+
"has_the_recipe": {
5+
"conditions": {
6+
"recipe": "minetale:campfire_pork_cooking"
7+
},
8+
"trigger": "minecraft:recipe_unlocked"
9+
}
10+
},
11+
"requirements": [
12+
[
13+
"has_the_recipe"
14+
]
15+
],
16+
"rewards": {
17+
"recipes": [
18+
"minetale:campfire_pork_cooking"
19+
]
20+
}
21+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"type": "minetale:campfire_alloying",
3+
"cookTime": 600,
4+
"ingredients": [
5+
"minecraft:porkchop"
6+
],
7+
"results": [
8+
{
9+
"count": 1,
10+
"id": "minecraft:cooked_porkchop"
11+
}
12+
]
13+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.tcm.MineTale.registry.ModEntityDataSerializers;
1212
import com.tcm.MineTale.registry.ModItems;
1313
import com.tcm.MineTale.registry.ModMenuTypes;
14+
import com.tcm.MineTale.registry.ModRecipes;
1415

1516
public class MineTale implements ModInitializer {
1617
public static final String MOD_ID = "minetale";
@@ -29,6 +30,7 @@ public class MineTale implements ModInitializer {
2930
@Override
3031
public void onInitialize() {
3132
ModBlocks.initialize();
33+
ModRecipes.initialize();
3234
ModBlockEntities.initialize();
3335
ModMenuTypes.initialize();
3436
ModEntities.initialize();

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55
import org.jetbrains.annotations.Nullable;
66

77
import com.mojang.serialization.MapCodec;
8+
import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
89
import com.tcm.MineTale.block.workbenches.entity.CampfireWorkbenchEntity;
910
import com.tcm.MineTale.registry.ModBlockEntities;
1011

1112
import net.minecraft.core.BlockPos;
1213
import net.minecraft.core.Direction;
1314
import net.minecraft.world.level.BlockGetter;
15+
import net.minecraft.world.level.Level;
1416
import net.minecraft.world.level.block.Block;
1517
import net.minecraft.world.level.block.RenderShape;
1618
import net.minecraft.world.level.block.entity.BlockEntity;
19+
import net.minecraft.world.level.block.entity.BlockEntityTicker;
1720
import net.minecraft.world.level.block.entity.BlockEntityType;
1821
import net.minecraft.world.level.block.state.BlockState;
1922
import net.minecraft.world.level.block.state.properties.ChestType;
@@ -50,6 +53,14 @@ public CampfireWorkbench(Properties properties, Supplier<BlockEntityType<? exten
5053
super(properties, supplier, IS_WIDE, IS_TALL);
5154
}
5255

56+
// In your CampfireWorkbenchBlock.java (The Block class)
57+
@Nullable
58+
@Override
59+
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
60+
// This connects the Level's ticking system to your static tick method
61+
return createTickerHelper(type, ModBlockEntities.CAMPFIRE_WORKBENCH_BE, AbstractWorkbenchEntity::tick);
62+
}
63+
5364
@Override
5465
protected MapCodec<? extends CampfireWorkbench> codec() {
5566
return CODEC;

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

Lines changed: 184 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22

33
import java.util.ArrayList;
44
import java.util.List;
5+
import java.util.Optional;
56

67
import org.jspecify.annotations.Nullable;
78

9+
import com.tcm.MineTale.recipe.WorkbenchRecipe;
10+
import com.tcm.MineTale.recipe.WorkbenchRecipeInput;
11+
import com.tcm.MineTale.util.Constants;
12+
813
import net.minecraft.core.BlockPos;
914
import net.minecraft.network.chat.Component;
1015
import net.minecraft.world.Container;
@@ -14,6 +19,9 @@
1419
import net.minecraft.world.entity.player.Player;
1520
import net.minecraft.world.inventory.AbstractContainerMenu;
1621
import net.minecraft.world.item.ItemStack;
22+
import net.minecraft.world.item.crafting.RecipeHolder;
23+
import net.minecraft.world.item.crafting.RecipeType;
24+
import net.minecraft.world.level.Level;
1725
import net.minecraft.world.level.block.entity.BlockEntity;
1826
import net.minecraft.world.level.block.entity.BlockEntityType;
1927
import net.minecraft.world.level.block.state.BlockState;
@@ -22,6 +30,11 @@ public abstract class AbstractWorkbenchEntity extends BlockEntity implements Men
2230
protected int tier = 1;
2331
protected double scanRadius = 5.0;
2432

33+
// Slot Mapping: 0-1 Inputs, 2 Fuel, 3-6 Outputs
34+
protected final SimpleContainer inventory = new SimpleContainer(7);
35+
protected int progress = 0;
36+
protected int maxProgress = 200;
37+
2538
/**
2639
* Creates a new workbench block entity instance.
2740
*
@@ -34,17 +47,178 @@ public AbstractWorkbenchEntity(BlockEntityType<?> type, BlockPos pos, BlockState
3447
}
3548

3649
/**
37-
* Gets the workstation's current tier.
38-
*
39-
* @return the current tier value
40-
*/
41-
public int getTier() { return tier; }
50+
* Subclasses (Campfire/Furnace) must define which recipe type they look for.
51+
*/
52+
public abstract RecipeType<WorkbenchRecipe> getWorkbenchRecipeType();
53+
54+
public static void tick(Level level, BlockPos pos, BlockState state, AbstractWorkbenchEntity entity) {
55+
// 1. Create the input wrapper using the internal SimpleContainer
56+
// Slot 1 = Input A, Slot 2 = Input B
57+
WorkbenchRecipeInput input = new WorkbenchRecipeInput(
58+
entity.inventory.getItem(Constants.INPUT_1),
59+
entity.inventory.getItem(Constants.INPUT_2)
60+
);
61+
62+
// DEBUG 1: Is the machine even seeing the pork?
63+
if (!entity.inventory.getItem(Constants.INPUT_1).isEmpty()) {
64+
System.out.println("Slot 1 (Input) contains: " + entity.inventory.getItem(Constants.INPUT_1).getItem().toString());
65+
}
66+
67+
if (!entity.inventory.getItem(Constants.FUEL_SLOT).isEmpty()) {
68+
System.out.println("Slot 0 (Fuel) contains: " + entity.inventory.getItem(Constants.FUEL_SLOT).getItem().toString());
69+
}
70+
71+
// 2. Fetch the RecipeManager from the server
72+
if (level.getServer() == null) return;
73+
var recipeManager = level.getServer().getRecipeManager();
74+
75+
// 3. Lookup the recipe using our explicit generic types
76+
Optional<RecipeHolder<WorkbenchRecipe>> recipeHolder = recipeManager
77+
.getRecipeFor(entity.getWorkbenchRecipeType(), input, level);
78+
79+
if (recipeHolder.isPresent()) {
80+
WorkbenchRecipe recipe = recipeHolder.get().value();
81+
entity.maxProgress = recipe.cookTime();
82+
83+
if (!entity.hasFuel()) {
84+
System.out.println("DEBUG: Failed because hasFuel() is false.");
85+
}
86+
if (!entity.canFitOutputs(recipeHolder.get().value().results())) {
87+
System.out.println("DEBUG: Failed because outputs are full.");
88+
}
89+
90+
boolean hasFuel = entity.hasFuel();
91+
boolean canFit = entity.canFitOutputs(recipe.results());
92+
93+
if (hasFuel && canFit) {
94+
entity.progress++;
95+
// Only print every 20 ticks (1 second) to avoid console spam
96+
if (entity.progress % 20 == 0) {
97+
System.out.println("DEBUG: Cooking... Progress is now " + entity.progress);
98+
}
99+
100+
setChanged(level, pos, state);
101+
102+
if (entity.progress >= entity.maxProgress) {
103+
System.out.println("DEBUG: Progress complete! Triggering craft().");
104+
entity.craft(recipe);
105+
entity.progress = 0;
106+
}
107+
} else {
108+
// This tells us exactly WHY it stopped
109+
if (!hasFuel) {
110+
System.out.println("DEBUG: Cooking stalled - NO FUEL (Check LIT state or Fuel Slot)");
111+
}
112+
if (!canFit) {
113+
System.out.println("DEBUG: Cooking stalled - NO SPACE in output slots (3-6)");
114+
}
115+
}
116+
} else {
117+
if (!input.getItem(0).isEmpty()) {
118+
System.out.println("DEBUG: No recipe found for this input type: " + entity.getWorkbenchRecipeType().toString());
119+
}
120+
// Reset progress if ingredients are removed
121+
if (entity.progress > 0) {
122+
entity.progress = 0;
123+
setChanged(level, pos, state);
124+
}
125+
}
126+
}
127+
128+
public ItemStack getItem(int slot) { return this.inventory.getItem(slot); }
129+
130+
public boolean canFitOutputs(List<ItemStack> results) {
131+
for (ItemStack result : results) {
132+
// If we can't find a home for even one of the results, return false
133+
if (findOutputSlot(result, Constants.OUTPUT_START, Constants.OUTPUT_END) == -1) {
134+
return false;
135+
}
136+
}
137+
return true;
138+
}
139+
140+
public int findOutputSlot(ItemStack result, int start, int end) {
141+
for (int i = start; i <= end; i++) {
142+
ItemStack stack = getItem(i);
143+
if (stack.isEmpty()) return i;
144+
145+
// 1.21.1 Check: Same item + same components + space for more
146+
if (ItemStack.isSameItemSameComponents(stack, result) &&
147+
stack.getCount() + result.getCount() <= stack.getMaxStackSize()) {
148+
return i;
149+
}
150+
}
151+
return -1;
152+
}
153+
154+
public int getContainerSize() {
155+
return this.inventory.getContainerSize();
156+
}
157+
158+
public boolean isEmpty() {
159+
return this.inventory.isEmpty();
160+
}
161+
162+
protected void craft(WorkbenchRecipe recipe) {
163+
// 1. Consume 1 from each ingredient slot (Slots 1 and 2)
164+
this.removeItem(Constants.INPUT_1, 1);
165+
this.removeItem(Constants.INPUT_2, 1);
166+
167+
// 2. Distribute results from the recipe
168+
for (ItemStack result : recipe.results()) {
169+
if (result.isEmpty()) continue;
170+
171+
// Use the constants for the output range (3 to 6)
172+
int slot = findOutputSlot(result, Constants.OUTPUT_START, Constants.OUTPUT_END);
173+
174+
if (slot != -1) {
175+
ItemStack existing = getItem(slot);
176+
if (existing.isEmpty()) {
177+
setItem(slot, result.copy());
178+
} else {
179+
existing.grow(result.getCount());
180+
// Crucial: SimpleContainer needs to know the stack changed
181+
this.setChanged();
182+
}
183+
}
184+
}
185+
}
186+
187+
public ItemStack removeItem(int slot, int amount) {
188+
// SimpleContainer has its own removeItem logic built-in
189+
ItemStack result = this.inventory.removeItem(slot, amount);
190+
if (!result.isEmpty()) {
191+
this.setChanged();
192+
}
193+
return result;
194+
}
195+
196+
public void setItem(int slot, ItemStack stack) {
197+
// Use setItem(), not set()
198+
this.inventory.setItem(slot, stack);
199+
200+
// Check max stack size
201+
if (!stack.isEmpty() && stack.getCount() > stack.getMaxStackSize()) {
202+
stack.setCount(stack.getMaxStackSize());
203+
}
204+
this.setChanged();
205+
}
206+
207+
// Subclasses handle fuel logic (Campfires might return true always, Furnaces check slot 2)
208+
protected abstract boolean hasFuel();
209+
42210
/**
43-
* Sets the workstation's tier and marks the block entity as changed.
44-
*
45-
* @param tier the new tier value for this workstation
46-
*/
47-
public void setTier(int tier) { this.tier = tier; setChanged(); }
211+
* Gets the workstation's current tier.
212+
*
213+
* @return the current tier value
214+
*/
215+
public int getTier() { return tier; }
216+
/**
217+
* Sets the workstation's tier and marks the block entity as changed.
218+
*
219+
* @param tier the new tier value for this workstation
220+
*/
221+
public void setTier(int tier) { this.tier = tier; setChanged(); }
48222

49223
/**
50224
* Collects nearby inventory-containing block entities within the configured scan radius and vertical range.

0 commit comments

Comments
 (0)