Skip to content

Commit 403a90f

Browse files
Phoenixvine32908Ghostipediajurrejelle
authored
Updating Recipe conditions documentation. (#4106)
Co-authored-by: Ghostipedia / Caitlynn <46772882+Ghostipedia@users.noreply.github.com> Co-authored-by: Jurre Groenendijk <jurre@jilles.com>
1 parent 3992f2d commit 403a90f

File tree

2 files changed

+193
-142
lines changed

2 files changed

+193
-142
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
---
2+
title: Custom Recipe Condition
3+
---
4+
5+
!!! Warning
6+
Custom recipe conditions are only supported in Java. Therefore, this page will only contain Java examples.
7+
8+
Recipe conditions are custom conditions for your recipe, like biome, machine tier, or anything else you can think of.
9+
10+
!!! Note
11+
The condition is run after recipe matching and before recipe execution. If the recipe condition doesn't match, the machine will be suspended and won't be updated again until something in the inputs/outputs changes.
12+
13+
They are registered using
14+
```java
15+
@Mod(ExampleMod.MOD_ID)
16+
public class ExampleMod {
17+
18+
public ExampleMod(FMLJavaModLoadingContext context) {
19+
var bus = context.getModEventBus();
20+
bus.addGenericListener(RecipeConditionType.class, this::registerConditions);
21+
}
22+
23+
public static RecipeConditionType<ExampleCondition> EXAMPLE_CONDITION;
24+
25+
public void registerConditions(GTCEuAPI.RegisterEvent<String, RecipeConditionType<?>> event) {
26+
EXAMPLE_CONDITION = GTRegistries.RECIPE_CONDITIONS.register("example_condition",
27+
new RecipeConditionType<>(
28+
ExampleCondition::new,
29+
ExampleCondition.CODEC
30+
)
31+
);
32+
}
33+
}
34+
```
35+
36+
We will set up a condition that requires that the power buffer of the machine is above a certain Y level.
37+
```java
38+
public class ExampleCondition extends RecipeCondition {
39+
40+
public int height;
41+
42+
public static final Codec<ExampleCondition> CODEC = RecordCodecBuilder
43+
.create(instance -> RecipeCondition.isReverse(instance)
44+
.and(Codec.INT.fieldOf("height").forGetter(val -> val.height))
45+
.apply(instance, ExampleCondition::new));
46+
47+
48+
public ExampleCondition(boolean isReverse, int height) {
49+
this.isReverse = isReverse;
50+
this.height = height;
51+
}
52+
53+
public ExampleCondition(int height) {
54+
this(false, height);
55+
}
56+
57+
public ExampleCondition() {
58+
this(false, 0);
59+
}
60+
61+
@Override
62+
public RecipeConditionType<?> getType() {
63+
return ExampleMod.EXAMPLE_CONDITION;
64+
}
65+
66+
@Override
67+
public Component getTooltips() {
68+
return Component.literal(String.format("Should be ran at least at height %d", height));
69+
}
70+
71+
@Override
72+
protected boolean testCondition(@NotNull GTRecipe recipe, @NotNull RecipeLogic recipeLogic) {
73+
return recipeLogic.getMachine().getHolder().getCurrentPos().getY() >= height;
74+
}
75+
76+
@Override
77+
public RecipeCondition createTemplate() {
78+
return new ExampleCondition(0);
79+
}
80+
}
81+
```
82+
83+
Lets step through this example. This will not be in order as it is in the file, but rather in the order that makes most sense.
84+
85+
Starting with:
86+
```java
87+
@Override
88+
public RecipeConditionType<?> getType() {
89+
return ExampleMod.EXAMPLE_CONDITION;
90+
}
91+
92+
@Override
93+
public Component getTooltips() {
94+
return Component.literal(String.format("Should be ran at least at height %d", height));
95+
}
96+
```
97+
This part is quite simple, and just returns the type and tooltip for the condition. The tooltip is what gets added in the recipe viewer's screen if this condition is present.
98+
99+
```java
100+
public ExampleCondition(boolean isReverse, int height) {
101+
this.isReverse = isReverse;
102+
this.height = height;
103+
}
104+
105+
public ExampleCondition(int height) {
106+
this(false, height);
107+
}
108+
109+
public ExampleCondition() {
110+
this(false, 0);
111+
}
112+
```
113+
These are the constructors. We need the `isReverse`, as it is part of the overarching `RecipeCondition` type. `isReverse` means that if the condition is met, your recipe won't be run. Furthermore, a no-arg constructor is required for (de)serialization.
114+
115+
```java
116+
@Override
117+
public RecipeCondition createTemplate() {
118+
return new ExampleCondition(0);
119+
}
120+
```
121+
122+
This creates the basic "template" that might be used for serialization. This should return a default version of your condition.
123+
124+
```java
125+
@Override
126+
protected boolean testCondition(@NotNull GTRecipe recipe, @NotNull RecipeLogic recipeLogic) {
127+
return recipeLogic.getMachine().getHolder().getCurrentPos().getY() >= height;
128+
}
129+
```
130+
131+
This is the actual condition.
132+
133+
```java
134+
public int height;
135+
136+
public static final Codec<ExampleCondition> CODEC = RecordCodecBuilder
137+
.create(instance -> RecipeCondition.isReverse(instance)
138+
.and(Codec.INT.fieldOf("height").forGetter(val -> val.height))
139+
.apply(instance, ExampleCondition::new));
140+
```
141+
142+
The CODEC is how java knows how to serialize/deserialize your condition. This is needed for syncing between client/server, and storing it to json to load when the world loads.
143+
It consists of a few parts:
144+
145+
- `RecordCodecBuilder.create(instance -> ` means we will start a RecordCodecBuilder, or a builder that only consists of simple types.
146+
- `RecipeCondition.isReverse(instance)` is a helper codec that serializes the isReverse boolean of your codec.
147+
- `.and(` means this is the next field in the record.
148+
- `Codec.INT.fieldOf("height").forGetter(val -> val.height)` means we want to serialize an INT, we want to call it "height" in the json, and to get the value you call `.height`.
149+
- `.apply(instance, ExampleCondition::new)` means when deserializing back to an object, you apply these steps to get the values (in this case `bool isReverse, int height`) and call the constructor with those arguments.
150+
In this case, this would call our `new ExampleCondition(isReverse, height)` constructor we have defined earlier.
151+
152+
With this, you should have everything you need to make a custom RecipeCondition.
153+
154+
To apply it to a recipeCondition, you would add to the Recipe Builder: `.condition(new ExampleCondition(70))` for a required height of 70

docs/content/Modpacks/Recipes/Recipe-Conditions.md

Lines changed: 39 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -2,151 +2,48 @@
22
title: Recipe Conditions
33
---
44

5-
!!! Warning
6-
Custom recipe conditions are only supported in Java. Therefore, this page will only contain Java examples.
5+
Recipe Conditions are recipe properties that can prevent a recipe from starting based on certain criteria, like for example Biome, Weather, Quest Completions, or self-made custom Conditions.
76

8-
Recipe conditions are custom conditions for your recipe, like biome, machine tier, or anything else you can think of.
7+
These conditions can be used in both java and kubejs recipes. However, custom conditons can only be done in java. If you want to see how to make these, check out the [Custom Recipe Condition](../Examples/Custom-Recipe-Condition.md) example page.
98

109
!!! Note
1110
The condition is run after recipe matching and before recipe execution. If the recipe condition doesn't match, the machine will be suspended and won't be updated again until something in the inputs/outputs changes.
1211

13-
They are registered using
14-
```java
15-
@Mod(ExampleMod.MOD_ID)
16-
public class ExampleMod {
17-
18-
public ExampleMod(FMLJavaModLoadingContext context) {
19-
var bus = context.getModEventBus();
20-
bus.addGenericListener(RecipeConditionType.class, this::registerConditions);
21-
}
12+
### Base Conditons
13+
14+
- Biome: `.biome("namespace:biome_id")`
15+
- Locks a recipe behind being inside a certain biome, works with any biome a pack has loaded. For example, you could use `minecraft:plains`.
16+
- Dimension: `.dimension("namespace:dimension_id")`
17+
- Locks a recipe being behind a certain dimension, the gas collector is a good example of this. For example, you could use `minecraft:the_end`
18+
- Position_Y: `.posY(int min, int max)`
19+
- Locks a recipe behind a certain y level in-world. For example, you could use `.posY(120, 130)` to have a recipe require a machine to be in between y 120 and y 130.
20+
- Rain: `.rain(float level)`
21+
- Locks a recipe behind a certain level of rain. For example, you could use `.rain(1.0)` to make a recipe need full rain.
22+
- Adjacent_Fluids: `adjacentFluids("minecraft:water","minecraft:lava")`
23+
- You can pass through any amount of fluids into the array. Moreover, any fluid passed into the array will make the recipe require a full source block touching the machine. We also have `adjacentFluidTag("forge:water", "forge:lava")`.
24+
- Adjacent_Blocks: `adjacentBlocks("minecraft:stone", "minecraft:iron_block")`
25+
- Much like the fluid condition, you can pass blocks into the array that lock the recipe behind needing the machine to touch these blocks. We also have `adjacentBlockTag("forge:stone", "forge:storage_blocks/iron")`.
26+
- Thunder: `.thunder(float level)`
27+
- Locks a recipe behind a certain level of rain. For example, you could use `.thunder(1.0)` to make a recipe need a strong thunderstorm.
28+
- Vent: This condition is auto added to any steam single block, it blocks recipes from running if the vent is obstructed.
29+
- Cleanroom: `.cleanroom(CleanroomType.CLEANROOM)`
30+
- Locks a recipe to being inside a cleanroom. You can also use STERILE_CLEANROOM as well as your own custom cleanroom type.
31+
- Fusion_Start_EU: `.fusionStartEU(long eu)`
32+
- Locks a recipe behind the amount of stored power in a fusion machine. To use this, the machine must use the FusionReactorMachine class. For example, you could use `.fusionStartEU(600000)`
33+
- Station_Research: `.stationResearch(b => b.researchStack("namespace:item_id").EUt(long eu).CWUt(int minCWUPerTick, int TotalCWU))`
34+
- Locks a recipe behind having a certain research stack. For this condition to be properly seen, you will either need a base machine recipe type with the research ui component, or make your own. For example, you could do `.stationResearch(b => b.researchStack("gtceu:lv_motor").EUt(131000).CWUt(24, 12000))` which would lock a recipe behind needing a data orb with the lv motor research. It will also generate you a research station recipe.
35+
- Scanner_Research: `.scannerResearch(b => b.researchStack("namespace:item_id").EUt(long eu))`
36+
- Much like station research, this condition locks a recipe behind needing a research stack. However, in this case it will default to a data stick. For example, you could do `.scannerResearch(b => b.researchStack("gtceu:lv_motor").EUt(8192))`, which would make the recipe need a data stick with the lv motor research, and generates a scanner recipe.
37+
- Enviromental_Hazard: `.environmentalHazard(GTMedicalConditions.CARBON_MONOXIDE_POISONING)`
38+
- Locks a recipe into needing a certain environmental hazard to run. For now, carbon monoxide is the only one. An example of a machine using this condition is the air scrubber.
39+
- Daytime: `.daytime(boolean notNight)`
40+
- Locks recipe behind whether it is day or night. For example, you could do `.daytime(true)`, to make the recipe need it to be daytime.
41+
42+
### Mod Dependent Conditions
43+
- Ftb_Quests: `.ftbQuest(quest_id)`
44+
- Locks a recipe behind the owner of a machine completing a ftb quest. An example can't be easily given since every quest book is different.
45+
- Gamestage: `.gameStage(gameStage_id)`
46+
- Locks a recipe behind a certain game stage.
47+
- Heracles_Quests: `.heraclesQuest(quest_id)`
48+
- Locks a recipe behind the owner of a machine completing a heracles quest. An example can't be easily given since every quest book is different.
2249

23-
public static RecipeConditionType<ExampleCondition> EXAMPLE_CONDITION;
24-
25-
public void registerConditions(GTCEuAPI.RegisterEvent<String, RecipeConditionType<?>> event) {
26-
EXAMPLE_CONDITION = GTRegistries.RECIPE_CONDITIONS.register("example_condition",
27-
new RecipeConditionType<>(
28-
ExampleCondition::new,
29-
ExampleCondition.CODEC
30-
)
31-
);
32-
}
33-
}
34-
```
35-
36-
We will set up a condition that requires that the power buffer of the machine is above a certain Y level.
37-
```java
38-
public class ExampleCondition extends RecipeCondition {
39-
40-
public int height;
41-
42-
public static final Codec<ExampleCondition> CODEC = RecordCodecBuilder
43-
.create(instance -> RecipeCondition.isReverse(instance)
44-
.and(Codec.INT.fieldOf("height").forGetter(val -> val.height))
45-
.apply(instance, ExampleCondition::new));
46-
47-
48-
public ExampleCondition(boolean isReverse, int height) {
49-
this.isReverse = isReverse;
50-
this.height = height;
51-
}
52-
53-
public ExampleCondition(int height) {
54-
this(false, height);
55-
}
56-
57-
public ExampleCondition() {
58-
this(false, 0);
59-
}
60-
61-
@Override
62-
public RecipeConditionType<?> getType() {
63-
return ExampleMod.EXAMPLE_CONDITION;
64-
}
65-
66-
@Override
67-
public Component getTooltips() {
68-
return Component.literal(String.format("Should be ran at least at height %d", height));
69-
}
70-
71-
@Override
72-
protected boolean testCondition(@NotNull GTRecipe recipe, @NotNull RecipeLogic recipeLogic) {
73-
return recipeLogic.getMachine().getHolder().getCurrentPos().getY() >= height;
74-
}
75-
76-
@Override
77-
public RecipeCondition createTemplate() {
78-
return new ExampleCondition(0);
79-
}
80-
}
81-
```
82-
83-
Lets step through this example. This will not be in order as it is in the file, but rather in the order that makes most sense.
84-
85-
Starting with:
86-
```java
87-
@Override
88-
public RecipeConditionType<?> getType() {
89-
return ExampleMod.EXAMPLE_CONDITION;
90-
}
91-
92-
@Override
93-
public Component getTooltips() {
94-
return Component.literal(String.format("Should be ran at least at height %d", height));
95-
}
96-
```
97-
This part is quite simple, and just returns the type and tooltip for the condition. The tooltip is what gets added in the recipe viewer's screen if this condition is present.
98-
99-
```java
100-
public ExampleCondition(boolean isReverse, int height) {
101-
this.isReverse = isReverse;
102-
this.height = height;
103-
}
104-
105-
public ExampleCondition(int height) {
106-
this(false, height);
107-
}
108-
109-
public ExampleCondition() {
110-
this(false, 0);
111-
}
112-
```
113-
These are the constructors. We need the `isReverse`, as it is part of the overarching `RecipeCondition` type. `isReverse` means that if the condition is met, your recipe won't be run. Furthermore, a no-arg constructor is required for (de)serialization.
114-
115-
```java
116-
@Override
117-
public RecipeCondition createTemplate() {
118-
return new ExampleCondition(0);
119-
}
120-
```
121-
122-
This creates the basic "template" that might be used for serialization. This should return a default version of your condition.
123-
124-
```java
125-
@Override
126-
protected boolean testCondition(@NotNull GTRecipe recipe, @NotNull RecipeLogic recipeLogic) {
127-
return recipeLogic.getMachine().getHolder().getCurrentPos().getY() >= height;
128-
}
129-
```
130-
131-
This is the actual condition.
132-
133-
```java
134-
public int height;
135-
136-
public static final Codec<ExampleCondition> CODEC = RecordCodecBuilder
137-
.create(instance -> RecipeCondition.isReverse(instance)
138-
.and(Codec.INT.fieldOf("height").forGetter(val -> val.height))
139-
.apply(instance, ExampleCondition::new));
140-
```
141-
142-
The CODEC is how java knows how to serialize/deserialize your condition. This is needed for syncing between client/server, and storing it to json to load when the world loads.
143-
It consists of a few parts:
144-
145-
- `RecordCodecBuilder.create(instance -> ` means we will start a RecordCodecBuilder, or a builder that only consists of simple types.
146-
- `RecipeCondition.isReverse(instance)` is a helper codec that serializes the isReverse boolean of your codec.
147-
- `.and(` means this is the next field in the record.
148-
- `Codec.INT.fieldOf("height").forGetter(val -> val.height)` means we want to serialize an INT, we want to call it "height" in the json, and to get the value you call `.height`.
149-
- `.apply(instance, ExampleCondition::new)` means when deserializing back to an object, you apply these steps to get the values (in this case `bool isReverse, int height`) and call the constructor with those arguments.
150-
In this case, this would call our `new ExampleCondition(isReverse, height)` constructor we have defined earlier.
151-
152-
With this, you should have everything you need to make a custom RecipeCondition.

0 commit comments

Comments
 (0)