package net.minecraft.world.food; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.List; import java.util.Optional; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.util.ExtraCodecs; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.ItemLike; public record FoodProperties( int nutrition, float saturation, boolean canAlwaysEat, float eatSeconds, Optional usingConvertsTo, List effects ) { private static final float DEFAULT_EAT_SECONDS = 1.6F; public static final Codec DIRECT_CODEC = RecordCodecBuilder.create( instance -> instance.group( ExtraCodecs.NON_NEGATIVE_INT.fieldOf("nutrition").forGetter(FoodProperties::nutrition), Codec.FLOAT.fieldOf("saturation").forGetter(FoodProperties::saturation), Codec.BOOL.optionalFieldOf("can_always_eat", false).forGetter(FoodProperties::canAlwaysEat), ExtraCodecs.POSITIVE_FLOAT.optionalFieldOf("eat_seconds", 1.6F).forGetter(FoodProperties::eatSeconds), ItemStack.SINGLE_ITEM_CODEC.optionalFieldOf("using_converts_to").forGetter(FoodProperties::usingConvertsTo), FoodProperties.PossibleEffect.CODEC.listOf().optionalFieldOf("effects", List.of()).forGetter(FoodProperties::effects) ) .apply(instance, FoodProperties::new) ); public static final StreamCodec DIRECT_STREAM_CODEC = StreamCodec.composite( ByteBufCodecs.VAR_INT, FoodProperties::nutrition, ByteBufCodecs.FLOAT, FoodProperties::saturation, ByteBufCodecs.BOOL, FoodProperties::canAlwaysEat, ByteBufCodecs.FLOAT, FoodProperties::eatSeconds, ItemStack.STREAM_CODEC.apply(ByteBufCodecs::optional), FoodProperties::usingConvertsTo, FoodProperties.PossibleEffect.STREAM_CODEC.apply(ByteBufCodecs.list()), FoodProperties::effects, FoodProperties::new ); public int eatDurationTicks() { return (int)(this.eatSeconds * 20.0F); } public static class Builder { private int nutrition; private float saturationModifier; private boolean canAlwaysEat; private float eatSeconds = 1.6F; private Optional usingConvertsTo = Optional.empty(); private final ImmutableList.Builder effects = ImmutableList.builder(); public FoodProperties.Builder nutrition(int nutrition) { this.nutrition = nutrition; return this; } public FoodProperties.Builder saturationModifier(float saturationModifier) { this.saturationModifier = saturationModifier; return this; } public FoodProperties.Builder alwaysEdible() { this.canAlwaysEat = true; return this; } public FoodProperties.Builder fast() { this.eatSeconds = 0.8F; return this; } public FoodProperties.Builder effect(MobEffectInstance effect, float probability) { this.effects.add(new FoodProperties.PossibleEffect(effect, probability)); return this; } public FoodProperties.Builder usingConvertsTo(ItemLike item) { this.usingConvertsTo = Optional.of(new ItemStack(item)); return this; } public FoodProperties build() { float f = FoodConstants.saturationByModifier(this.nutrition, this.saturationModifier); return new FoodProperties(this.nutrition, f, this.canAlwaysEat, this.eatSeconds, this.usingConvertsTo, this.effects.build()); } } public record PossibleEffect(MobEffectInstance effect, float probability) { public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( MobEffectInstance.CODEC.fieldOf("effect").forGetter(FoodProperties.PossibleEffect::effect), Codec.floatRange(0.0F, 1.0F).optionalFieldOf("probability", 1.0F).forGetter(FoodProperties.PossibleEffect::probability) ) .apply(instance, FoodProperties.PossibleEffect::new) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( MobEffectInstance.STREAM_CODEC, FoodProperties.PossibleEffect::effect, ByteBufCodecs.FLOAT, FoodProperties.PossibleEffect::probability, FoodProperties.PossibleEffect::new ); public MobEffectInstance effect() { return new MobEffectInstance(this.effect); } } }