mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-12-01 00:37:59 +01:00
eeded18c36
Unicorns: - have a general even preference of foods - Improved benefits from cooking their food before eating it - Can still eat raw and rotten but at a reduced yield Earth Ponies: - Are vegans - They get the most from foraging - Pastries are their passion - If they must eat meat, they have to cook it and not let it spoil. - They have a sweet tooth and prefer candy, desserts, and rocks - Candy and rocks gives them a massive saturation boost. Maybe too much? Pegasus - prefer fish over other food sources - Cannot eat love, or raw/rotten meat - Can eat raw and rotten fish but still prefers if they are cooked - Can safely eat fresh and cooked fish with no ill effects - Is less affected when eating rotten fish Bat Ponies: - prefer cooked foods over raw, and meat/insects over fish - Doesn't like baked goods but really likes meats, fish, and insects - Gets food poisoning from eating rotten and raw meat - Can eat cooked meat and insects without negative effects - Becomes hyper when eating mangoes Kirins: - Much like Earth Ponies, Kirins must cook their meat before they eat it - Cannot eat love, or raw/rotten meats and fish - Can eat cooked meat and insect without negative effects - Can eat blinding, prickly, strengthening, and glowing foraged foods without negative effects Changelings: - like meat and fish but really prefer feasting on ponies' love directly from the tap - Doesn't like baked goods but really likes meats, fish, and insects - Can eat fish, meat, insects, and love without negative effects - Gets sick when eating foraged plants and vegetables Hippogriffs: - like fish, nuts, and seeds - Can eat fish and prickly foods without negative effect - Gains more health from pinecones Seaponies: - can eat seaweed, kelp, shells, and other undersea foods - Can eat fish without negative effect - Gains more health from pinecones
222 lines
9.4 KiB
Java
222 lines
9.4 KiB
Java
package com.minelittlepony.unicopia.diet;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Predicate;
|
|
import java.util.stream.Collectors;
|
|
|
|
import org.jetbrains.annotations.Nullable;
|
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
|
import com.minelittlepony.unicopia.item.ItemDuck;
|
|
import com.mojang.datafixers.util.Pair;
|
|
import com.mojang.serialization.Codec;
|
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|
|
|
import net.minecraft.client.item.TooltipContext;
|
|
import net.minecraft.entity.player.PlayerEntity;
|
|
import net.minecraft.item.FoodComponent;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.network.PacketByteBuf;
|
|
import net.minecraft.text.Text;
|
|
import net.minecraft.util.Formatting;
|
|
import net.minecraft.util.Identifier;
|
|
import net.minecraft.util.UseAction;
|
|
|
|
public record DietProfile(
|
|
float defaultMultiplier,
|
|
float foragingMultiplier,
|
|
List<Multiplier> multipliers,
|
|
List<FoodGroupEffects> effects,
|
|
Optional<FoodGroupEffects> defaultEffect
|
|
) {
|
|
public static final DietProfile EMPTY = new DietProfile(1, 1, List.of(), List.of(), Optional.empty());
|
|
public static final Codec<DietProfile> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
|
Codec.FLOAT.fieldOf("default_multiplier").forGetter(DietProfile::defaultMultiplier),
|
|
Codec.FLOAT.fieldOf("foraging_multiplier").forGetter(DietProfile::foragingMultiplier),
|
|
Codec.list(Multiplier.CODEC).fieldOf("multipliers").forGetter(DietProfile::multipliers),
|
|
Codec.list(FoodGroupEffects.CODEC).fieldOf("effects").forGetter(DietProfile::effects),
|
|
FoodGroupEffects.CODEC.optionalFieldOf("default_effect").forGetter(DietProfile::defaultEffect)
|
|
).apply(instance, DietProfile::new));
|
|
|
|
public DietProfile(PacketByteBuf buffer) {
|
|
this(buffer.readFloat(), buffer.readFloat(),
|
|
buffer.readList(Multiplier::new),
|
|
buffer.readList(b -> new FoodGroupEffects(b, FoodGroupKey.LOOKUP)),
|
|
buffer.readOptional(b -> new FoodGroupEffects(b, FoodGroupKey.LOOKUP))
|
|
);
|
|
}
|
|
|
|
public void validate(Consumer<String> issues, Predicate<Identifier> foodGroupExists) {
|
|
multipliers.stream().flatMap(i -> i.tags().stream()).forEach(key -> {
|
|
if (!foodGroupExists.test(key.id())) {
|
|
issues.accept("Multiplier referenced unknown food group: " + key.id());
|
|
}
|
|
});
|
|
effects.stream().flatMap(i -> i.tags().stream()).forEach(key -> {
|
|
if (!foodGroupExists.test(key.id())) {
|
|
issues.accept("Override defined for unknown food group: " + key.id());
|
|
}
|
|
});
|
|
defaultEffect.stream().flatMap(i -> i.tags().stream()).forEach(key -> {
|
|
if (!foodGroupExists.test(key.id())) {
|
|
issues.accept("Default override defined for unknown food group: " + key.id());
|
|
}
|
|
});
|
|
}
|
|
|
|
public void toBuffer(PacketByteBuf buffer) {
|
|
buffer.writeFloat(defaultMultiplier);
|
|
buffer.writeFloat(foragingMultiplier);
|
|
buffer.writeCollection(multipliers, (b, t) -> t.toBuffer(b));
|
|
buffer.writeCollection(effects, (b, t) -> t.toBuffer(b));
|
|
buffer.writeOptional(defaultEffect, (b, t) -> t.toBuffer(b));
|
|
}
|
|
|
|
public Optional<Multiplier> findMultiplier(ItemStack stack) {
|
|
return multipliers.stream().filter(m -> m.test(stack)).findFirst();
|
|
}
|
|
|
|
public Optional<Effect> findEffect(ItemStack stack) {
|
|
return effects.stream().filter(m -> m.test(stack)).findFirst().or(this::defaultEffect).map(Effect.class::cast);
|
|
}
|
|
|
|
static boolean isForaged(ItemStack stack) {
|
|
return ((ItemDuck)stack.getItem()).getOriginalFoodComponent().isEmpty();
|
|
}
|
|
|
|
@Nullable
|
|
public FoodComponent getAdjustedFoodComponent(ItemStack stack) {
|
|
var food = stack.getItem().getFoodComponent();
|
|
if (this == EMPTY) {
|
|
return food;
|
|
}
|
|
|
|
var ratios = getRatios(stack);
|
|
if (isInedible(ratios)) {
|
|
return null;
|
|
}
|
|
|
|
float hunger = food.getHunger() * ratios.getFirst();
|
|
int baseline = (int)hunger;
|
|
|
|
return FoodAttributes.copy(food)
|
|
.hunger(Math.max(1, (hunger - baseline) >= 0.5F ? baseline + 1 : baseline))
|
|
.saturationModifier(food.getSaturationModifier() * ratios.getSecond())
|
|
.build();
|
|
}
|
|
|
|
public boolean isInedible(ItemStack stack) {
|
|
return isInedible(getRatios(stack));
|
|
}
|
|
|
|
public boolean isInedible(Pair<Float, Float> ratios) {
|
|
return ratios.getFirst() <= 0.01F && ratios.getSecond() <= 0.01F;
|
|
}
|
|
|
|
public Pair<Float, Float> getRatios(ItemStack stack) {
|
|
Optional<Multiplier> multiplier = findMultiplier(stack);
|
|
|
|
float baseMultiplier = (isForaged(stack) ? foragingMultiplier() : defaultMultiplier());
|
|
float hungerMultiplier = multiplier.map(Multiplier::hunger).orElse(baseMultiplier);
|
|
float saturationMultiplier = multiplier.map(Multiplier::saturation).orElse(baseMultiplier);
|
|
return Pair.of(hungerMultiplier, saturationMultiplier);
|
|
}
|
|
|
|
public void appendTooltip(ItemStack stack, @Nullable PlayerEntity user, List<Text> tooltip, TooltipContext context) {
|
|
var food = stack.getItem().getFoodComponent();
|
|
|
|
var ratios = getRatios(stack);
|
|
if (food == null || isInedible(ratios)) {
|
|
if (stack.getUseAction() != UseAction.DRINK) {
|
|
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.not_edible")).formatted(Formatting.DARK_GRAY));
|
|
}
|
|
return;
|
|
}
|
|
|
|
float baseMultiplier = (isForaged(stack) ? foragingMultiplier() : defaultMultiplier());
|
|
|
|
if (context.isAdvanced()) {
|
|
var nonAdjustedFood = getNonAdjustedFoodComponent(stack, user).orElse(food);
|
|
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.base_multiplier", baseMultiplier).formatted(Formatting.DARK_GRAY)));
|
|
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.hunger.detailed", food.getHunger(), nonAdjustedFood.getHunger(), (int)(ratios.getFirst() * 100))).formatted(Formatting.DARK_GRAY));
|
|
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.saturation.detailed", food.getSaturationModifier(), nonAdjustedFood.getSaturationModifier(), (int)(ratios.getSecond() * 100))).formatted(Formatting.DARK_GRAY));
|
|
} else {
|
|
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.hunger", (int)(ratios.getFirst() * 100))).formatted(Formatting.DARK_GRAY));
|
|
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.saturation", (int)(ratios.getSecond() * 100))).formatted(Formatting.DARK_GRAY));
|
|
}
|
|
}
|
|
|
|
private Optional<FoodComponent> getNonAdjustedFoodComponent(ItemStack stack, @Nullable PlayerEntity user) {
|
|
@Nullable
|
|
Pony pony = Pony.of(user);
|
|
Optional<FoodComponent> food = ((ItemDuck)stack.getItem()).getOriginalFoodComponent();
|
|
|
|
if (food.isEmpty() && pony.getObservedSpecies().hasIronGut()) {
|
|
return findEffect(stack)
|
|
.flatMap(Effect::foodComponent)
|
|
.or(() -> PonyDiets.getInstance().getEffects(stack).foodComponent());
|
|
}
|
|
|
|
return food;
|
|
}
|
|
|
|
public record Multiplier(
|
|
Set<FoodGroupKey> tags,
|
|
float hunger,
|
|
float saturation
|
|
) implements Predicate<ItemStack> {
|
|
public static final Codec<Set<FoodGroupKey>> TAGS_CODEC = FoodGroupKey.CODEC.listOf().xmap(
|
|
l -> l.stream().distinct().collect(Collectors.toSet()),
|
|
set -> new ArrayList<>(set)
|
|
);
|
|
public static final Codec<Multiplier> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
|
TAGS_CODEC.fieldOf("tags").forGetter(Multiplier::tags),
|
|
Codec.FLOAT.fieldOf("hunger").forGetter(Multiplier::hunger),
|
|
Codec.FLOAT.fieldOf("saturation").forGetter(Multiplier::saturation)
|
|
).apply(instance, Multiplier::new));
|
|
|
|
public Multiplier(PacketByteBuf buffer) {
|
|
this(buffer.readCollection(HashSet::new, p -> FoodGroupKey.LOOKUP.apply(p.readIdentifier())), buffer.readFloat(), buffer.readFloat());
|
|
}
|
|
|
|
@Override
|
|
public boolean test(ItemStack stack) {
|
|
return tags.stream().anyMatch(tag -> tag.contains(stack));
|
|
}
|
|
|
|
public void toBuffer(PacketByteBuf buffer) {
|
|
buffer.writeCollection(tags, (p, t) -> p.writeIdentifier(t.id()));
|
|
buffer.writeFloat(hunger);
|
|
buffer.writeFloat(saturation);
|
|
}
|
|
|
|
public static final class Builder {
|
|
private Set<FoodGroupKey> tags = new HashSet<>();
|
|
private float hunger = 1;
|
|
private float saturation = 1;
|
|
|
|
public Builder tag(Identifier tag) {
|
|
tags.add(FoodGroupKey.LOOKUP.apply(tag));
|
|
return this;
|
|
}
|
|
|
|
public Builder hunger(float hunger) {
|
|
this.hunger = hunger;
|
|
return this;
|
|
}
|
|
|
|
public Builder saturation(float saturation) {
|
|
this.saturation = saturation;
|
|
return this;
|
|
}
|
|
|
|
public Multiplier build() {
|
|
return new Multiplier(tags, hunger, saturation);
|
|
}
|
|
}
|
|
}
|
|
}
|