locales, more requirements, cycle behavior

This commit is contained in:
2025-03-16 00:57:20 +01:00
parent 8a5293eae3
commit fafa7410a6
14 changed files with 208 additions and 250 deletions

View File

@@ -14,7 +14,7 @@ parchment_version = 2024.11.17
mod_id=mechanical_lemon_lib
mod_name=MechanicalLemonLib
mod_license=LGPL3
mod_version=0.1.32
mod_version=0.1.49
mod_group_id=com.oierbravo
mod_author=oierbravo
mod_description=Mod Utility Library

View File

@@ -10,7 +10,7 @@ import net.minecraft.world.level.Level;
public class CycleBehavior extends BlockEntityBehaviour {
private final int cycleTime;
private int cycleTime;
private boolean actuateHalfcycle;
public static final BehaviourType<CycleBehavior> TYPE = new BehaviourType<>();
public CycleBehaviourSpecifics specifics;
@@ -24,19 +24,21 @@ public class CycleBehavior extends BlockEntityBehaviour {
public interface CycleBehaviourSpecifics {
public void onCycleCompleted();
public void onOperationCompletd();
public float getKineticSpeed();
public boolean tryProcess(boolean simulate);
public void playSound();
void onCycleCompleted();
void onOperationCompletd();
float getKineticSpeed();
boolean tryProcess(boolean simulate);
void playSound();
int getCycles();
}
public <T extends SmartBlockEntity & CycleBehaviourSpecifics> CycleBehavior(T te, int pCycle, boolean pActuateHalfCycle, int pNumCycles) {
public <T extends SmartBlockEntity & CycleBehaviourSpecifics> CycleBehavior(T te, int pCycle, boolean pActuateHalfCycle) {
super(te);
this.specifics = te;
cycleTime = pCycle;
actuateHalfcycle = pActuateHalfCycle;
numCycles = pNumCycles;
numCycles = 0;
cycleDivider = (actuateHalfcycle) ? 2 : 1;
currentCycle = 0;
}
@@ -47,6 +49,8 @@ public class CycleBehavior extends BlockEntityBehaviour {
finished = compound.getBoolean("Finished");
prevRunningTicks = runningTicks = compound.getInt("Ticks");
currentCycle = compound.getInt("CurrentCycle");
cycleTime = compound.getInt("CycleTime");
numCycles = compound.getInt("NumCycles");
super.read(compound,registries, clientPacket);
}
@@ -56,6 +60,8 @@ public class CycleBehavior extends BlockEntityBehaviour {
compound.putBoolean("Finished", finished);
compound.putInt("Ticks", runningTicks);
compound.putInt("CurrentCycle", currentCycle);
compound.putInt("CycleTime", cycleTime);
compound.putInt("NumCycles", numCycles);
super.write(compound, registries, clientPacket);
}
@@ -64,7 +70,9 @@ public class CycleBehavior extends BlockEntityBehaviour {
prevRunningTicks = 0;
runningTicks = 0;
currentCycle = 0;
numCycles = specifics.getCycles();
blockEntity.sendData();
}
@Override
@@ -102,7 +110,7 @@ public class CycleBehavior extends BlockEntityBehaviour {
blockEntity.sendData();
}
if (!level.isClientSide && runningTicks > cycleTime / cycleDivider) {
if (!level.isClientSide && runningTicks > cycleTime) {
specifics.onCycleCompleted();
currentCycle++;
if(currentCycle == numCycles){
@@ -152,7 +160,23 @@ public class CycleBehavior extends BlockEntityBehaviour {
public boolean isRunning(){
return running;
}
public int getProgressPercent() {
public int getTotalProgressPercent() {
return Mth.clamp(runningTicks * 100 / (cycleTime /cycleDivider) * numCycles, 0,100);
}
public int getCycleProgressPercent() {
return Mth.clamp(runningTicks * 100 / (cycleTime /cycleDivider), 0,100);
}
public int getCycleTime(){
return cycleTime;
}
public int getCurrentCycle(){
return currentCycle;
}
public int getPrevRunningTicks() {
return prevRunningTicks;
}
public int getRunningTicks() {
return runningTicks;
}
}

View File

@@ -78,17 +78,11 @@ public class RecipeRequirementsBehaviour<R extends IRecipeWithRequirements> exte
boolean result = true;
for( IRecipeRequirement requirement : pRecipe.getRecipeRequirements()){
if(!checkRequirement(requirement, pLevel, (BlockEntity) pSpecifics)){
missingRequirements.add(requirement.getType().toString());
missingRequirements.add(requirement.getIdString());
result = false;
}
}
/*for (Map.Entry<RecipeRequirementType<?>, IRecipeRequirement> entry : pRecipe.getRecipeRequirements().entrySet()) {
if(!checkRequirement(entry.getValue(), pLevel, (BlockEntity) pSpecifics)){
missingRequirements.add(entry.toString());
result = false;
}
}*/
return result;
}
private boolean checkRequirement(IRecipeRequirement value, Level pLevel, BlockEntity pSpecifics){
@@ -99,7 +93,7 @@ public class RecipeRequirementsBehaviour<R extends IRecipeWithRequirements> exte
return false;
for(String requirementId : missingRequirements){
LibLang.translate("mechanical_lemon_lib.ui.recipe_requirement." + requirementId + ".missing").style(ChatFormatting.RED).forGoggles(tooltip,1);
LibLang.translate("ui.recipe_requirement." + requirementId + ".missing").style(ChatFormatting.RED).forGoggles(tooltip,1);
added = true;
}
return added;

View File

@@ -25,24 +25,19 @@ public interface IRecipeRequirement {
.dispatch(IRecipeRequirement::getType, RecipeRequirementType::streamCodec);
StreamCodec<RegistryFriendlyByteBuf, List<IRecipeRequirement>> LIST_STREAM_CODEC =
//StreamCodec<RegistryFriendlyByteBuf, Map<RecipeRequirementType<?>, IRecipeRequirement>> LIST_STREAM_CODEC =
/* ByteBufCodecs.map(
HashMap::new, // Constructs a map with the specified capacity
RecipeRequirementType::streamCodec,
IRecipeRequirement
);*/
STREAM_CODEC.apply(ByteBufCodecs.list(256));
boolean test(Level pLevel, BlockEntity pBlockEntity);
boolean isPresent();
/*boolean isPresent();*/
String getIdString();
String toString();
RecipeRequirementType<?> getType();
default Component toTooltipComponent(){
return LibLang.translate("ui.recipe_requirement." + getType().toString() + ".tooltip", toString()).component();
return LibLang.translate("ui.recipe_requirement." + getIdString() + ".tooltip", toString()).component();
};
default Component toMissingComponent(){
return LibLang.translate("ui.recipe_requirement." + getType().toString() + ".missing", toString()).component();
return LibLang.translate("ui.recipe_requirement." + getIdString() + ".missing").component();
}
}

View File

@@ -15,7 +15,7 @@ public interface IRecipeWithRequirements {
default Optional<IRecipeRequirement> getRequirement(RecipeRequirementType<?> type) {
return getRecipeRequirements().stream().filter(iRecipeRequirement -> iRecipeRequirement == type).findFirst();
return getRecipeRequirements().stream().filter(iRecipeRequirement -> iRecipeRequirement.getType() == type).findFirst();
}
boolean checkRequirements(Level pLevel, BlockEntity pBlockEntity);

View File

@@ -1,97 +0,0 @@
package com.oierbravo.mechanical_lemon_lib.foundation.recipe.requirements;
import com.google.gson.JsonObject;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.RecipeRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.RecipeRequirementType;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
public class MaxHeightRequirement extends RecipeRequirement {
public static final RecipeRequirementType<?> TYPE = new MaxHeightRequirementType();
public static final MaxHeightRequirement EMPTY = new MaxHeightRequirement();
private Integer value;
public MaxHeightRequirement() {}
public MaxHeightRequirement(int pMaxHeight) {
value = pMaxHeight;
}
public static MaxHeightRequirement of(int pMaxHeight) {
return new MaxHeightRequirement(pMaxHeight);
}
public boolean test(Level pLevel, BlockEntity pBlockEntity) {
if(value == null)
return true;
BlockPos pos = pBlockEntity.getBlockPos();
return pos.getCenter().y <= value;
}
@Override
public RecipeRequirementType<?> getType() {
return TYPE;
}
public boolean isPresent(){
return value != null;
}
public String toString(){
if(value == null)
return null;
return value.toString();
}
public int getValue(){
return value;
}
private static class MaxHeightRequirementType extends RecipeRequirementType<MaxHeightRequirement> {
public MaxHeightRequirementType() {
super("max_height");
}
@Override
public MaxHeightRequirement fromJson(JsonObject pJson) {
if (GsonHelper.isValidNode(pJson, this.getId())) {
return of(pJson.get(this.getId()).getAsInt());
}
return EMPTY;
}
@Override
public JsonObject toJson(JsonObject pJson, RecipeRequirement pRecipeRequirement) {
if(!pRecipeRequirement.isPresent())
return pJson;
pJson.addProperty(this.getId(), pRecipeRequirement.toString());
return pJson;
}
@Override
public MaxHeightRequirement fromNetwork(FriendlyByteBuf buffer) {
boolean hasRequirement = buffer.readBoolean();
if(hasRequirement) {
return of(buffer.readInt());
}
return MaxHeightRequirement.EMPTY;
}
@Override
public void toNetwork(FriendlyByteBuf buffer, RecipeRequirement pRecipeRequirement) {
if(pRecipeRequirement == null)
pRecipeRequirement = new MaxHeightRequirement();
if(pRecipeRequirement instanceof MaxHeightRequirement){
buffer.writeBoolean(pRecipeRequirement.isPresent());
if(pRecipeRequirement.isPresent())
buffer.writeInt(((MaxHeightRequirement) pRecipeRequirement).getValue());
}
}
}
}

View File

@@ -0,0 +1,55 @@
package com.oierbravo.mechanical_lemon_lib.foundation.recipe.requirements;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.IRecipeRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.RecipeRequirementType;
import com.oierbravo.mechanical_lemon_lib.register.MechanicalLemonRecipeRequirementTypes;
import com.simibubi.create.content.kinetics.base.KineticBlockEntity;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
public record MaxSpeedRequirement(Float speed) implements IRecipeRequirement {
public static String ID = "max_speed";
public static MapCodec<MaxSpeedRequirement> CODEC = RecordCodecBuilder.mapCodec((builder) -> builder.group(Codec.FLOAT.optionalFieldOf("value", null).forGetter(MaxSpeedRequirement::speed)).apply(builder, MaxSpeedRequirement::new));
public static final StreamCodec<RegistryFriendlyByteBuf, MaxSpeedRequirement> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.FLOAT, MaxSpeedRequirement::speed,
MaxSpeedRequirement::new
);
public static MaxSpeedRequirement of(Float speed){
return new MaxSpeedRequirement(speed);
}
@Override
public boolean test(Level pLevel, BlockEntity pBlockEntity) {
if(pBlockEntity instanceof KineticBlockEntity){
return ((KineticBlockEntity) pBlockEntity).getSpeed() <= speed;
}
return false;
}
/* @Override
public boolean isPresent() {
return false;
}*/
@Override
public RecipeRequirementType<?> getType() {
return MechanicalLemonRecipeRequirementTypes.MIN_SPEED.get();
}
@Override
public String getIdString() {
return ID;
}
@Override
public String toString() {
return speed.toString();
}
}

View File

@@ -0,0 +1,61 @@
package com.oierbravo.mechanical_lemon_lib.foundation.recipe.requirements;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.IRecipeRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.RecipeRequirementType;
import com.oierbravo.mechanical_lemon_lib.register.MechanicalLemonRecipeRequirementTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
public record MaxYRequirement(Integer maxY) implements IRecipeRequirement{
public static String ID = "max_y";
public static MapCodec<MaxYRequirement> CODEC = RecordCodecBuilder
.mapCodec((builder)
-> builder
.group(Codec.INT.optionalFieldOf("value", null)
.forGetter(MaxYRequirement::maxY)).apply(builder, MaxYRequirement::new));
public static final StreamCodec<RegistryFriendlyByteBuf, MaxYRequirement> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.INT, MaxYRequirement::maxY,
MaxYRequirement::new
);
public static MaxYRequirement of(Integer maxY){
return new MaxYRequirement(maxY);
}
@Override
public boolean test(Level pLevel, BlockEntity pBlockEntity) {
if(maxY == null)
return true;
BlockPos pos = pBlockEntity.getBlockPos();
return pos.getCenter().y <= maxY;
}
/* @Override
public boolean isPresent() {
return false;
}*/
@Override
public RecipeRequirementType<?> getType() {
return MechanicalLemonRecipeRequirementTypes.MAX_Y.get();
}
@Override
public String getIdString() {
return ID;
}
@Override
public String toString() {
return maxY.toString();
}
}

View File

@@ -1,100 +0,0 @@
package com.oierbravo.mechanical_lemon_lib.foundation.recipe.requirements;
import com.google.gson.JsonObject;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.RecipeRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.RecipeRequirementType;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
public class MinHeightRequirement extends RecipeRequirement {
public static final RecipeRequirementType<?> TYPE = new MinHeightRequirementType();
public static final MinHeightRequirement EMPTY = new MinHeightRequirement();
private Integer value;
public MinHeightRequirement() {
}
public MinHeightRequirement(int pValue) {
value = pValue;
}
public static MinHeightRequirement of(int pValue) {
return new MinHeightRequirement(pValue);
}
public boolean test(Level pLevel, BlockEntity pBlockEntity) {
if(value == null)
return true;
BlockPos pos = pBlockEntity.getBlockPos();
return pos.getCenter().y >= value;
}
public boolean isPresent(){
return value != null;
}
public String toString(){
if(value == null)
return null;
return value.toString();
}
public int getValue(){
return value;
}
@Override
public RecipeRequirementType<?> getType() {
return TYPE;
}
private static class MinHeightRequirementType extends RecipeRequirementType<MinHeightRequirement> {
public MinHeightRequirementType() {
super("min_height");
}
@Override
public MinHeightRequirement fromJson(JsonObject pJson) {
if (GsonHelper.isValidNode(pJson, this.getId())) {
return of(pJson.get(this.getId()).getAsInt());
}
return EMPTY;
}
@Override
public JsonObject toJson(JsonObject pJson, RecipeRequirement pRecipeRequirement) {
if(!pRecipeRequirement.isPresent())
return pJson;
pJson.addProperty(this.getId(), pRecipeRequirement.toString());
return pJson;
}
@Override
public MinHeightRequirement fromNetwork(FriendlyByteBuf buffer) {
boolean hasRequirement = buffer.readBoolean();
if(hasRequirement) {
return of(buffer.readInt());
}
return MinHeightRequirement.EMPTY;
}
@Override
public void toNetwork(FriendlyByteBuf buffer, RecipeRequirement pRecipeRequirement) {
if(pRecipeRequirement == null)
pRecipeRequirement = new MinHeightRequirement();
if(pRecipeRequirement instanceof MinHeightRequirement){
buffer.writeBoolean(pRecipeRequirement.isPresent());
if(pRecipeRequirement.isPresent())
buffer.writeInt(((MinHeightRequirement) pRecipeRequirement).getValue());
}
}
}
}

View File

@@ -6,41 +6,50 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.IRecipeRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.RecipeRequirementType;
import com.oierbravo.mechanical_lemon_lib.register.MechanicalLemonRecipeRequirementTypes;
import com.simibubi.create.content.kinetics.base.KineticBlockEntity;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
public record SpeedRequirement(Float speed) implements IRecipeRequirement {
public record MinSpeedRequirement(Float speed) implements IRecipeRequirement {
public static String ID = "min_speed";
public static MapCodec<SpeedRequirement> CODEC = RecordCodecBuilder.mapCodec((builder) -> builder.group(Codec.FLOAT.optionalFieldOf("value", null).forGetter(SpeedRequirement::speed)).apply(builder,SpeedRequirement::new));
public static MapCodec<MinSpeedRequirement> CODEC = RecordCodecBuilder.mapCodec((builder) -> builder.group(Codec.FLOAT.optionalFieldOf("value", null).forGetter(MinSpeedRequirement::speed)).apply(builder, MinSpeedRequirement::new));
public static final StreamCodec<RegistryFriendlyByteBuf, SpeedRequirement> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.FLOAT, SpeedRequirement::speed,
SpeedRequirement::new
public static final StreamCodec<RegistryFriendlyByteBuf, MinSpeedRequirement> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.FLOAT, MinSpeedRequirement::speed,
MinSpeedRequirement::new
);
public static SpeedRequirement of(Float speed){
return new SpeedRequirement(speed);
public static MinSpeedRequirement of(Float speed){
return new MinSpeedRequirement(speed);
}
@Override
public boolean test(Level pLevel, BlockEntity pBlockEntity) {
if(pBlockEntity instanceof KineticBlockEntity){
return ((KineticBlockEntity) pBlockEntity).getSpeed() >= speed;
}
return false;
}
@Override
/* @Override
public boolean isPresent() {
return false;
}
}*/
@Override
public RecipeRequirementType<?> getType() {
return MechanicalLemonRecipeRequirementTypes.SPEED.get();
return MechanicalLemonRecipeRequirementTypes.MIN_SPEED.get();
}
@Override
public String getIdString() {
return ID;
}
@Override
public String toString() {
return ID;
return speed.toString();
}
}

View File

@@ -6,6 +6,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.IRecipeRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.RecipeRequirementType;
import com.oierbravo.mechanical_lemon_lib.register.MechanicalLemonRecipeRequirementTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
@@ -31,13 +32,17 @@ public record MinYRequirement(Integer minY) implements IRecipeRequirement{
@Override
public boolean test(Level pLevel, BlockEntity pBlockEntity) {
return false;
if(minY == null)
return true;
BlockPos pos = pBlockEntity.getBlockPos();
return pos.getCenter().y >= minY;
}
@Override
/*@Override
public boolean isPresent() {
return false;
}
}*/
@Override
public RecipeRequirementType<?> getType() {
@@ -45,7 +50,12 @@ public record MinYRequirement(Integer minY) implements IRecipeRequirement{
}
@Override
public String toString() {
public String getIdString() {
return ID;
}
@Override
public String toString() {
return minY.toString();
}
}

View File

@@ -4,8 +4,10 @@ import com.mojang.serialization.MapCodec;
import com.oierbravo.mechanical_lemon_lib.MechanicalLemonLib;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.IRecipeRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.RecipeRequirementType;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.requirements.MaxSpeedRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.requirements.MaxYRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.requirements.MinYRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.requirements.SpeedRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.requirements.MinSpeedRequirement;
import net.minecraft.network.codec.StreamCodec;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredRegister;
@@ -18,12 +20,17 @@ public class MechanicalLemonRecipeRequirementTypes {
public static final DeferredRegister<RecipeRequirementType<?>> RECIPE_REQUIREMENT_TYPES =
DeferredRegister.create(MechanicalLemonRegistries.Keys.RECIPE_REQUIREMENT, MechanicalLemonLib.MODID);
public static final Supplier<RecipeRequirementType<SpeedRequirement>> SPEED =
register(SpeedRequirement.ID, SpeedRequirement.CODEC, SpeedRequirement.STREAM_CODEC);
public static final Supplier<RecipeRequirementType<MinSpeedRequirement>> MIN_SPEED =
register(MinSpeedRequirement.ID, MinSpeedRequirement.CODEC, MinSpeedRequirement.STREAM_CODEC);
public static final Supplier<RecipeRequirementType<MaxSpeedRequirement>> MAX_SPEED =
register(MaxSpeedRequirement.ID, MaxSpeedRequirement.CODEC, MaxSpeedRequirement.STREAM_CODEC);
public static final Supplier<RecipeRequirementType<MinYRequirement>> MIN_Y =
register(MinYRequirement.ID, MinYRequirement.CODEC, MinYRequirement.STREAM_CODEC);
public static final Supplier<RecipeRequirementType<MaxYRequirement>> MAX_Y =
register(MaxYRequirement.ID, MaxYRequirement.CODEC, MaxYRequirement.STREAM_CODEC);
public static void init(IEventBus modEventBus) {
RECIPE_REQUIREMENT_TYPES.register(modEventBus);
}

View File

@@ -1,7 +1,6 @@
package com.oierbravo.mechanical_lemon_lib.register;
import com.oierbravo.mechanical_lemon_lib.MechanicalLemonLib;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.IRecipeRequirement;
import com.oierbravo.mechanical_lemon_lib.foundation.recipe.RecipeRequirementType;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
@@ -10,6 +9,7 @@ import net.neoforged.neoforge.registries.RegistryBuilder;
public class MechanicalLemonRegistries {
public static Registry<RecipeRequirementType<?>> RECIPE_REQUIREMENT_TYPE;// = (new RegistryBuilder(Keys.RECIPE_REQUIREMENT_TYPES)).create();
public MechanicalLemonRegistries() {
}

View File

@@ -3,12 +3,12 @@
"itemGroup.mechanical_lemon_lib": "Mechanicals",
"mechanical_lemon_lib.ui.recipe_requirement.none.tooltip": "No specific requirement",
"mechanical_lemon_lib.ui.recipe_requirement.biome.tooltip": "Biome: %s",
"mechanical_lemon_lib.ui.recipe_requirement.min_height.tooltip": "Min Y: %s",
"mechanical_lemon_lib.ui.recipe_requirement.max_height.tooltip": "Max Y: %s",
"mechanical_lemon_lib.ui.recipe_requirement.min_y.tooltip": "Min Y: %s",
"mechanical_lemon_lib.ui.recipe_requirement.max_y.tooltip": "Max Y: %s",
"mechanical_lemon_lib.ui.recipe_requirement.min_speed.tooltip": "Min Speed: %s",
"mechanical_lemon_lib.ui.recipe_requirement.biome.missing": "Incorrect biome",
"mechanical_lemon_lib.ui.recipe_requirement.max_height.missing": "Too high",
"mechanical_lemon_lib.ui.recipe_requirement.min_height.missing": "Too low",
"mechanical_lemon_lib.ui.recipe_requirement.max_y.missing": "Too high",
"mechanical_lemon_lib.ui.recipe_requirement.min_y.missing": "Too low",
"mechanical_lemon_lib.ui.recipe_requirement.min_speed.missing": "Not enough speed",
"mechanical_lemon_lib.ui.recipe_requirement.output.missing": "Output full or incompatible",
"mechanical_lemon_lib.ui.recipe_requirement.ingredients.missing": "Missing ingredients"