New food system:

- Foods fill for different amounts for different races
- Certain foods can no longer be eaten by certain races
- Added food categories for candy, rocks, desserts
- Moved everything to datapacks
This commit is contained in:
Sollace 2023-12-03 02:39:55 +00:00
parent 16a7b96f81
commit ad7a7d84c0
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
74 changed files with 723 additions and 891 deletions

View file

@ -1,7 +1,5 @@
package com.minelittlepony.unicopia;
import com.minelittlepony.unicopia.item.toxin.Toxics;
import net.minecraft.block.Block;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.damage.DamageType;
@ -79,8 +77,4 @@ public interface UTags {
static TagKey<DimensionType> dimension(String name) {
return TagKey.of(RegistryKeys.DIMENSION_TYPE, new Identifier("c", name));
}
static void bootstrap() {
Toxics.bootstrap();
}
}

View file

@ -22,8 +22,8 @@ import com.minelittlepony.unicopia.command.Commands;
import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate;
import com.minelittlepony.unicopia.container.SpellbookChapterLoader;
import com.minelittlepony.unicopia.container.UScreenHandlers;
import com.minelittlepony.unicopia.diet.AfflictionType;
import com.minelittlepony.unicopia.diet.DietsLoader;
import com.minelittlepony.unicopia.diet.affliction.AfflictionType;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.effect.UPotions;
import com.minelittlepony.unicopia.entity.mob.UEntities;
@ -66,7 +66,6 @@ public class Unicopia implements ModInitializer {
@Override
public void onInitialize() {
Channel.bootstrap();
UTags.bootstrap();
UCriteria.bootstrap();
UEntities.bootstrap();
Commands.bootstrap();

View file

@ -1,23 +1,22 @@
package com.minelittlepony.unicopia.diet;
import com.minelittlepony.unicopia.item.toxin.Toxicity;
import com.minelittlepony.unicopia.diet.affliction.Affliction;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.network.PacketByteBuf;
public record Ailment(Toxicity toxicity, Affliction effects) {
public record Ailment(Affliction effects) {
public static final Ailment EMPTY = new Ailment(Affliction.EMPTY);
public static final Codec<Ailment> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Toxicity.CODEC.fieldOf("toxicity").forGetter(Ailment::toxicity),
Affliction.CODEC.fieldOf("effects").forGetter(Ailment::effects)
).apply(instance, Ailment::new));
public Ailment(PacketByteBuf buffer) {
this(Toxicity.byName(buffer.readString()), Affliction.read(buffer));
this(Affliction.read(buffer));
}
public void toBuffer(PacketByteBuf buffer) {
buffer.writeString(toxicity.name());
Affliction.write(buffer, effects);
}
}

View file

@ -8,30 +8,43 @@ import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
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.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.UseAction;
public record DietProfile(
float defaultMultiplier,
float foragingMultiplier,
List<Multiplier> multipliers,
List<Effect> effects
List<Effect> effects,
Optional<Effect> 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(Effect.CODEC).fieldOf("effects").forGetter(DietProfile::effects)
Codec.list(Effect.CODEC).fieldOf("effects").forGetter(DietProfile::effects),
Effect.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(Effect::new));
this(buffer.readFloat(), buffer.readFloat(), buffer.readList(Multiplier::new), buffer.readList(Effect::new), buffer.readOptional(Effect::new));
}
public void toBuffer(PacketByteBuf buffer) {
@ -39,6 +52,7 @@ public record DietProfile(
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) {
@ -46,7 +60,68 @@ public record DietProfile(
}
public Optional<Effect> findEffect(ItemStack stack) {
return effects.stream().filter(m -> m.test(stack)).findFirst();
return effects.stream().filter(m -> m.test(stack)).findFirst().or(this::defaultEffect);
}
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;
}
return FoodAttributes.copy(food)
.hunger(Math.max(1, (int)(food.getHunger() * ratios.getFirst())))
.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()) {
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", Math.max(1, (int)(ratios.getFirst() * food.getHunger())), food.getHunger(), (int)(ratios.getFirst() * 100))).formatted(Formatting.DARK_GRAY));
tooltip.add(Text.literal(" ").append(Text.translatable("unicopia.diet.saturation.detailed", String.format("%.2f", ratios.getSecond() * food.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));
}
}
public record Multiplier(

View file

@ -0,0 +1,27 @@
package com.minelittlepony.unicopia.diet;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.world.World;
public interface DietView {
TypedActionResult<ItemStack> startUsing(ItemStack stack, World world, PlayerEntity user, Hand hand);
void finishUsing(ItemStack stack, World world, LivingEntity entity);
void appendTooltip(ItemStack stack, @Nullable PlayerEntity user, List<Text> tooltip, TooltipContext context);
interface Holder {
default DietView getDiets(ItemStack stack) {
return PonyDiets.getInstance();
}
}
}

View file

@ -40,23 +40,34 @@ public class DietsLoader implements IdentifiableResourceReloadListener {
Map<Race, DietProfile> profiles = new HashMap<>();
for (var entry : data.entrySet()) {
Identifier id = entry.getKey();
Race.REGISTRY.getOrEmpty(id).ifPresentOrElse(race -> DietProfile.CODEC.parse(JsonOps.INSTANCE, entry.getValue())
.resultOrPartial(LOGGER::error)
.ifPresent(profile -> profiles.put(race, profile)), () -> LOGGER.warn("Skipped diet for unknown race: " + id));
try {
Race.REGISTRY.getOrEmpty(id).ifPresentOrElse(race -> {
DietProfile.CODEC.parse(JsonOps.INSTANCE, entry.getValue())
.resultOrPartial(error -> LOGGER.error("Could not load diet profile {}: {}", id, error))
.ifPresent(profile -> profiles.put(race, profile));
}, () -> LOGGER.warn("Skipped diet for unknown race: " + id));
} catch (Throwable t) {
LOGGER.error("Could not load diet profile {}", id, t);
}
}
return profiles;
}, applyExecutor);
}, prepareExecutor);
var effectsLoadTask = loadData(manager, prepareExecutor, "diets/food_effects").thenApplyAsync(data -> data.values().stream()
.map(value -> Effect.CODEC.parse(JsonOps.INSTANCE, value)
.resultOrPartial(LOGGER::error))
var effectsLoadTask = loadData(manager, prepareExecutor, "diets/food_effects").thenApplyAsync(data -> data.entrySet().stream()
.map(entry -> {
try {
return Effect.CODEC.parse(JsonOps.INSTANCE, entry.getValue())
.resultOrPartial(error -> LOGGER.error("Could not load food effect {}: {}", entry.getKey(), error));
} catch (Throwable t) {
LOGGER.error("Could not load food effects {}", entry.getKey(), t);
}
return Optional.<Effect>empty();
})
.filter(Optional::isPresent)
.map(Optional::get)
.toList());
.toList(), prepareExecutor);
var future = CompletableFuture.allOf(dietsLoadTask, effectsLoadTask);
sync.getClass();
return future.thenRunAsync(() -> {
return CompletableFuture.allOf(dietsLoadTask, effectsLoadTask).thenCompose(sync::whenPrepared).thenRunAsync(() -> {
PonyDiets.load(new PonyDiets(
dietsLoadTask.getNow(Map.of()),
effectsLoadTask.getNow(List.of())

View file

@ -1,56 +1,76 @@
package com.minelittlepony.unicopia.diet;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.UseAction;
import net.minecraft.util.Util;
public record Effect(
TagKey<Item> tag,
List<TagKey<Item>> tags,
Optional<FoodComponent> foodComponent,
Ailment ailment
) implements Predicate<ItemStack> {
public static final Effect EMPTY = new Effect(List.of(), Optional.empty(), Ailment.EMPTY);
public static final Codec<Effect> CODEC = RecordCodecBuilder.create(instance -> instance.group(
TagKey.unprefixedCodec(RegistryKeys.ITEM).fieldOf("tag").forGetter(Effect::tag),
FoodComponent.CODEC.optionalFieldOf("food_component").forGetter(Effect::foodComponent),
TagKey.unprefixedCodec(RegistryKeys.ITEM).listOf().fieldOf("tags").forGetter(Effect::tags),
FoodAttributes.CODEC.optionalFieldOf("food_component").forGetter(Effect::foodComponent),
Ailment.CODEC.fieldOf("ailment").forGetter(Effect::ailment)
).apply(instance, Effect::new));
public Effect(PacketByteBuf buffer) {
this(TagKey.of(RegistryKeys.ITEM, buffer.readIdentifier()), buffer.readOptional(FoodComponent::new), new Ailment(buffer));
this(buffer.readList(b -> TagKey.of(RegistryKeys.ITEM, b.readIdentifier())), buffer.readOptional(FoodAttributes::read), new Ailment(buffer));
}
public void afflict(Pony pony, ItemStack stack) {
ailment().effects().afflict(pony.asEntity(), stack);
}
public void appendTooltip(ItemStack stack, List<Text> tooltip, TooltipContext context) {
int size = tooltip.size();
tags.forEach(tag -> {
if (stack.isIn(tag)) {
tooltip.add(Text.literal(" ").append(Text.translatable(Util.createTranslationKey("tag", tag.id()))).formatted(Formatting.GRAY));
}
});
if (tooltip.size() == size) {
if (stack.isFood()) {
tooltip.add(Text.literal(" ").append(Text.translatable("tag.unicopia.food_types.fruits_and_vegetables")).formatted(Formatting.GRAY));
} else if (stack.getUseAction() == UseAction.DRINK) {
tooltip.add(Text.literal(" ").append(Text.translatable("tag.unicopia.food_types.drinks")).formatted(Formatting.GRAY));
}
}
if (context.isAdvanced() && stack.isFood()) {
if (!ailment().effects().isEmpty()) {
tooltip.add(Text.translatable("unicopia.diet.side_effects").formatted(Formatting.DARK_PURPLE));
ailment().effects().appendTooltip(tooltip);
}
}
}
public void toBuffer(PacketByteBuf buffer) {
buffer.writeIdentifier(tag.id());
buffer.writeOptional(foodComponent, (b, f) -> f.toBuffer(b));
buffer.writeCollection(tags, (b, t) -> b.writeIdentifier(t.id()));
buffer.writeOptional(foodComponent, FoodAttributes::write);
ailment.toBuffer(buffer);
}
@Override
public boolean test(ItemStack stack) {
return stack.isIn(tag);
}
public record FoodComponent (float hunger, float saturation) {
public static final Codec<FoodComponent> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.FLOAT.fieldOf("hunger").forGetter(FoodComponent::hunger),
Codec.FLOAT.fieldOf("saturation").forGetter(FoodComponent::saturation)
).apply(instance, FoodComponent::new));
public FoodComponent(PacketByteBuf buffer) {
this(buffer.readFloat(), buffer.readFloat());
}
public void toBuffer(PacketByteBuf buffer) {
buffer.writeFloat(hunger);
buffer.writeFloat(saturation);
}
return tags.stream().anyMatch(stack::isIn);
}
}

View file

@ -0,0 +1,60 @@
package com.minelittlepony.unicopia.diet;
import java.util.List;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.item.FoodComponent;
import net.minecraft.network.PacketByteBuf;
final class FoodAttributes {
static final Codec<FoodComponent> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.INT.fieldOf("hunger").forGetter(FoodComponent::getHunger),
Codec.FLOAT.fieldOf("saturation").forGetter(FoodComponent::getSaturationModifier),
Codec.BOOL.optionalFieldOf("petFood", false).forGetter(FoodComponent::isMeat),
Codec.BOOL.optionalFieldOf("fastFood", false).forGetter(FoodComponent::isAlwaysEdible),
Codec.BOOL.optionalFieldOf("eatenQuickly", false).forGetter(FoodComponent::isSnack)
).apply(instance, FoodAttributes::create));
static FoodComponent create(int hunger, float saturation, boolean petFood, boolean fastFood, boolean eatenQuickly) {
return create(hunger, saturation, petFood, fastFood, eatenQuickly, List.of()).build();
}
static FoodComponent.Builder create(int hunger, float saturation, boolean petFood, boolean fastFood, boolean eatenQuickly, List<Pair<StatusEffectInstance, Float>> effects) {
var builder = new FoodComponent.Builder()
.hunger(hunger)
.saturationModifier(saturation);
if (petFood) {
builder.meat();
}
if (fastFood) {
builder.alwaysEdible();
}
if (eatenQuickly) {
builder.snack();
}
for (var effect : effects) {
builder.statusEffect(effect.getFirst(), effect.getSecond());
}
return builder;
}
static FoodComponent.Builder copy(FoodComponent food) {
return create(food.getHunger(), food.getSaturationModifier(), food.isMeat(), food.isAlwaysEdible(), food.isSnack(), food.getStatusEffects());
}
static FoodComponent read(PacketByteBuf buffer) {
return create(buffer.readInt(), buffer.readFloat(), buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean());
}
static void write(PacketByteBuf buffer, FoodComponent food) {
buffer.writeInt(food.getHunger());
buffer.writeFloat(food.getSaturationModifier());
buffer.writeBoolean(food.isMeat());
buffer.writeBoolean(food.isAlwaysEdible());
buffer.writeBoolean(food.isSnack());
}
}

View file

@ -1,43 +0,0 @@
package com.minelittlepony.unicopia.diet;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
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;
public record MultiplyHungerAffliction(float multiplier) implements Affliction {
public static final Codec<MultiplyHungerAffliction> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.FLOAT.fieldOf("multiplier").forGetter(MultiplyHungerAffliction::multiplier)
).apply(instance, MultiplyHungerAffliction::new));
public MultiplyHungerAffliction(PacketByteBuf buffer) {
this(buffer.readFloat());
}
@Override
public void toBuffer(PacketByteBuf buffer) {
buffer.writeFloat(multiplier);
}
@Override
public AfflictionType<?> getType() {
return AfflictionType.MULTIPLY_HUNGER;
}
@Override
public void afflict(PlayerEntity player, ItemStack stack) {
FoodComponent food = stack.getItem().getFoodComponent();
player.getHungerManager().setFoodLevel((int)(food.getHunger() * multiplier));
player.getHungerManager().setSaturationLevel(food.getSaturationModifier() * multiplier);
}
@Override
public Text getName() {
return Text.translatable("Lose %s%% hunger", multiplier * 100);
}
}

View file

@ -4,20 +4,30 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.diet.DietProfile.Multiplier;
import com.minelittlepony.unicopia.entity.player.Pony;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.entity.effect.FoodPoisoningStatusEffect;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.ItemDuck;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.world.World;
public class PonyDiets {
public class PonyDiets implements DietView {
private final Map<Race, DietProfile> diets;
private final List<Effect> effects;
static PonyDiets INSTANCE = new PonyDiets(Map.of(), List.of());
private static PonyDiets INSTANCE = new PonyDiets(Map.of(), List.of());
public static PonyDiets getinstance() {
public static PonyDiets getInstance() {
return INSTANCE;
}
@ -39,19 +49,61 @@ public class PonyDiets {
buffer.writeCollection(effects, (b, e) -> e.toBuffer(b));
}
public Optional<DietProfile> getDiet(Race race) {
return Optional.ofNullable(diets.get(race));
private DietProfile getDiet(Pony pony) {
return Optional.ofNullable(diets.get(pony.getObservedSpecies())).orElse(DietProfile.EMPTY);
}
public Optional<Effect> getEffects(ItemStack stack) {
return effects.stream().filter(effect -> effect.test(stack)).findFirst();
private Effect getEffects(ItemStack stack) {
return effects.stream().filter(effect -> effect.test(stack)).findFirst().orElse(Effect.EMPTY);
}
public Optional<Effect> getEffects(ItemStack stack, Pony pony) {
return getDiet(pony.getObservedSpecies()).flatMap(diet -> diet.findEffect(stack)).or(() -> getEffects(stack));
private Effect getEffects(ItemStack stack, Pony pony) {
return getDiet(pony).findEffect(stack).orElseGet(() -> getEffects(stack));
}
public Optional<Multiplier> getMultiplier(ItemStack stack, Pony pony) {
return getDiet(pony.getObservedSpecies()).flatMap(diet -> diet.findMultiplier(stack));
@Override
public TypedActionResult<ItemStack> startUsing(ItemStack stack, World world, PlayerEntity user, Hand hand) {
return initEdibility(stack, user)
? FoodPoisoningStatusEffect.apply(stack, user)
: TypedActionResult.fail(stack);
}
@Override
public void finishUsing(ItemStack stack, World world, LivingEntity entity) {
if (initEdibility(stack, entity)) {
Pony.of(entity).ifPresent(pony -> getEffects(stack, pony).afflict(pony, stack));
}
}
@Override
public void appendTooltip(ItemStack stack, @Nullable PlayerEntity user, List<Text> tooltip, TooltipContext context) {
if (initEdibility(stack, user)) {
Pony pony = Pony.of(user);
tooltip.add(Text.translatable("unicopia.diet.information").formatted(Formatting.DARK_PURPLE));
getEffects(stack, pony).appendTooltip(stack, tooltip, context);
getDiet(pony).appendTooltip(stack, user, tooltip, context);
}
}
private boolean initEdibility(ItemStack stack, LivingEntity user) {
ItemDuck item = (ItemDuck)stack.getItem();
item.resetFoodComponent();
return Pony.of(user).filter(pony -> {
DietProfile diet = getDiet(pony);
if (!stack.isFood() && pony.getObservedSpecies().hasIronGut()) {
diet.findEffect(stack)
.flatMap(Effect::foodComponent)
.or(() -> getEffects(stack).foodComponent())
.ifPresent(item::setFoodComponent);
}
if (stack.isFood()) {
item.setFoodComponent(diet.getAdjustedFoodComponent(stack));
}
return true;
}).isPresent();
}
}

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.diet;
package com.minelittlepony.unicopia.diet.affliction;
import java.util.List;
@ -9,19 +9,14 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.dynamic.Codecs;
public interface Affliction {
Text NO_EFFECT_TEXT = Text.of("No Effect");
Affliction EMPTY = new Affliction() {
@Override
public void afflict(PlayerEntity player, ItemStack stack) { }
@Override
public Text getName() {
return NO_EFFECT_TEXT;
}
@Override
public AfflictionType<?> getType() {
return AfflictionType.EMPTY;
@ -30,11 +25,9 @@ public interface Affliction {
@Override
public void toBuffer(PacketByteBuf buffer) { }
};
Codec<Affliction> CODEC = Codecs.xor(Codec.list(AfflictionType.CODEC)
.mapResult(null)
.xmap(
Codec<Affliction> CODEC = Codecs.xor(AfflictionType.CODEC, Codec.list(AfflictionType.CODEC).xmap(
afflictions -> {
afflictions.removeIf(f -> f.getType() == AfflictionType.EMPTY);
afflictions = afflictions.stream().filter(f -> !f.isEmpty()).toList();
return switch (afflictions.size()) {
case 0 -> EMPTY;
case 1 -> afflictions.get(0);
@ -42,18 +35,24 @@ public interface Affliction {
};
},
affliction -> ((CompoundAffliction)affliction).afflictions
), AfflictionType.CODEC).xmap(
)).xmap(
either -> either.left().or(either::right).get(),
affliction -> affliction instanceof CompoundAffliction ? Either.left(affliction) : Either.right(affliction)
);
void afflict(PlayerEntity player, ItemStack stack);
default void appendTooltip(List<Text> tooltip) {
tooltip.add(getName());
default boolean isEmpty() {
return getType() == AfflictionType.EMPTY;
}
Text getName();
default void appendTooltip(List<Text> tooltip) {
tooltip.add(Text.literal(" ").append(getName()).formatted(Formatting.DARK_GRAY));
}
default Text getName() {
return Text.translatable(getType().getTranslationKey());
}
AfflictionType<?> getType();

View file

@ -1,14 +1,16 @@
package com.minelittlepony.unicopia.diet;
package com.minelittlepony.unicopia.diet.affliction;
import com.google.gson.JsonObject;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.util.RegistryUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
import net.minecraft.network.PacketByteBuf.PacketReader;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.Util;
import net.minecraft.util.dynamic.Codecs;
public record AfflictionType<T extends Affliction>(Codec<T> codec, Identifier id, PacketReader<T> reader) {
@ -16,10 +18,11 @@ public record AfflictionType<T extends Affliction>(Codec<T> codec, Identifier id
public static final Registry<AfflictionType<?>> REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("affliction_type"), DEFAULT_ID);
@SuppressWarnings("unchecked")
public static final Codec<Affliction> CODEC = Codecs.JSON_ELEMENT.<Affliction>flatXmap(json -> {
JsonObject obj = json.getAsJsonObject();
return Identifier.validate(obj.has("type") ? obj.get("type").getAsString() : AfflictionType.DEFAULT_ID).flatMap(type -> {
return AfflictionType.REGISTRY.get(type).codec().parse(JsonOps.INSTANCE, json);
});
if (!json.isJsonObject()) {
return DataResult.error(() -> "Not a JSON object");
}
return Identifier.validate(JsonHelper.getString(JsonHelper.asObject(json, "affliction"), "type", AfflictionType.DEFAULT_ID))
.flatMap(type -> AfflictionType.REGISTRY.get(type).codec().parse(JsonOps.INSTANCE, json));
}, thing -> {
AfflictionType<?> type = thing.getType();
return ((Codec<Affliction>)type.codec()).encodeStart(JsonOps.INSTANCE, thing).map(json -> {
@ -33,12 +36,17 @@ public record AfflictionType<T extends Affliction>(Codec<T> codec, Identifier id
public static final AfflictionType<Affliction> EMPTY = register("empty", Codec.unit(Affliction.EMPTY), buffer -> Affliction.EMPTY);
public static final AfflictionType<Affliction> MANY = register("many", CompoundAffliction.CODEC, CompoundAffliction::new);
public static final AfflictionType<StatusEffectAffliction> APPLY_STATUS_EFFECT = register("apply_status_effect", StatusEffectAffliction.CODEC, StatusEffectAffliction::new);
public static final AfflictionType<MultiplyHungerAffliction> MULTIPLY_HUNGER = register("multiply_hunger", MultiplyHungerAffliction.CODEC, MultiplyHungerAffliction::new);
public static final AfflictionType<ClearLoveSicknessAffliction> CLEAR_LOVE_SICKNESS = register("clear_love_sickness", ClearLoveSicknessAffliction.CODEC, buffer -> ClearLoveSicknessAffliction.INSTANCE);
public static final AfflictionType<LoseHungerAffliction> LOSE_HUNGER = register("lose_hunger", LoseHungerAffliction.CODEC, LoseHungerAffliction::new);
public static final AfflictionType<HealingAffliction> HEALING = register("healing", HealingAffliction.CODEC, HealingAffliction::new);
public static final AfflictionType<ClearLoveSicknessAffliction> CURE_LOVE_SICKNESS = register("cure_love_sickness", ClearLoveSicknessAffliction.CODEC, buffer -> ClearLoveSicknessAffliction.INSTANCE);
static <T extends Affliction> AfflictionType<T> register(String name, Codec<T> codec, PacketReader<T> reader) {
return Registry.register(REGISTRY, Unicopia.id(name), new AfflictionType<>(codec, Unicopia.id(name), reader));
}
public String getTranslationKey() {
return Util.createTranslationKey("affliction", id());
}
public static void bootstrap() { }
}

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.diet;
package com.minelittlepony.unicopia.diet.affliction;
import com.minelittlepony.unicopia.entity.effect.UEffects;
import com.mojang.serialization.Codec;
@ -6,15 +6,14 @@ import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.Text;
public final class ClearLoveSicknessAffliction implements Affliction {
final class ClearLoveSicknessAffliction implements Affliction {
public static final ClearLoveSicknessAffliction INSTANCE = new ClearLoveSicknessAffliction();
public static final Codec<ClearLoveSicknessAffliction> CODEC = Codec.unit(INSTANCE);
@Override
public AfflictionType<?> getType() {
return AfflictionType.CLEAR_LOVE_SICKNESS;
return AfflictionType.CURE_LOVE_SICKNESS;
}
@Override
@ -24,11 +23,6 @@ public final class ClearLoveSicknessAffliction implements Affliction {
player.removeStatusEffect(UEffects.FOOD_POISONING);
}
@Override
public Text getName() {
return Text.literal("Love");
}
@Override
public void toBuffer(PacketByteBuf buffer) {
}

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.diet;
package com.minelittlepony.unicopia.diet.affliction;
import java.util.List;
@ -32,6 +32,11 @@ class CompoundAffliction implements Affliction {
return AfflictionType.MANY;
}
@Override
public boolean isEmpty() {
return afflictions.isEmpty();
}
@Override
public void appendTooltip(List<Text> tooltip) {
afflictions.forEach(i -> i.appendTooltip(tooltip));

View file

@ -0,0 +1,39 @@
package com.minelittlepony.unicopia.diet.affliction;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.Text;
record HealingAffliction(float health) implements Affliction {
public static final Codec<HealingAffliction> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.FLOAT.fieldOf("health").forGetter(HealingAffliction::health)
).apply(instance, HealingAffliction::new));
public HealingAffliction(PacketByteBuf buffer) {
this(buffer.readFloat());
}
@Override
public void toBuffer(PacketByteBuf buffer) {
buffer.writeFloat(health);
}
@Override
public AfflictionType<?> getType() {
return AfflictionType.HEALING;
}
@Override
public void afflict(PlayerEntity player, ItemStack stack) {
player.heal(health());
}
@Override
public Text getName() {
return Text.translatable(getType().getTranslationKey(), health());
}
}

View file

@ -0,0 +1,41 @@
package com.minelittlepony.unicopia.diet.affliction;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.Text;
public record LoseHungerAffliction(float multiplier) implements Affliction {
public static final Codec<LoseHungerAffliction> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.FLOAT.fieldOf("multiplier").forGetter(LoseHungerAffliction::multiplier)
).apply(instance, LoseHungerAffliction::new));
public LoseHungerAffliction(PacketByteBuf buffer) {
this(buffer.readFloat());
}
@Override
public void toBuffer(PacketByteBuf buffer) {
buffer.writeFloat(multiplier);
}
@Override
public AfflictionType<?> getType() {
return AfflictionType.LOSE_HUNGER;
}
@Override
public void afflict(PlayerEntity player, ItemStack stack) {
var hunger = player.getHungerManager();
hunger.setFoodLevel((int)(hunger.getFoodLevel() * multiplier));
hunger.setSaturationLevel(hunger.getSaturationLevel() * multiplier);
}
@Override
public Text getName() {
return Text.translatable(getType().getTranslationKey(), multiplier * 100);
}
}

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.diet;
package com.minelittlepony.unicopia.diet.affliction;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
@ -7,7 +7,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.dynamic.Codecs;
public record Range(int min, int max) {
record Range(int min, int max) {
public static final Codec<Range> CODEC = Codecs.xor(
Codec.INT.xmap(value -> Range.of(value, -1), range -> range.min()),
RecordCodecBuilder.create(instance -> instance.group(
@ -29,11 +29,11 @@ public record Range(int min, int max) {
buffer.writeInt(max);
}
public int getTicks(int currentTicks) {
return clamp((min * 20) + currentTicks);
public int getClamped(int currentTicks, int multiplier) {
return clamp((min * multiplier) + currentTicks, multiplier);
}
public int clamp(int value) {
return max > 0 ? Math.min(value, max * 20) : value;
public int clamp(int value, int multiplier) {
return max > 0 ? Math.min(value, max * multiplier) : value;
}
}

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.diet;
package com.minelittlepony.unicopia.diet.affliction;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
@ -15,7 +15,7 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.StringHelper;
import net.minecraft.util.math.MathHelper;
public record StatusEffectAffliction(Identifier effect, Range seconds, Range amplifier, int chance) implements Affliction {
record StatusEffectAffliction(Identifier effect, Range seconds, Range amplifier, int chance) implements Affliction {
public static final Codec<StatusEffectAffliction> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Identifier.CODEC.fieldOf("effect").forGetter(StatusEffectAffliction::effect),
Range.CODEC.fieldOf("seconds").forGetter(StatusEffectAffliction::seconds),
@ -27,6 +27,7 @@ public record StatusEffectAffliction(Identifier effect, Range seconds, Range amp
this(buffer.readIdentifier(), Range.of(buffer), Range.of(buffer), buffer.readInt());
}
@Override
public void toBuffer(PacketByteBuf buffer) {
buffer.writeIdentifier(effect);
seconds.toBuffer(buffer);
@ -48,8 +49,8 @@ public record StatusEffectAffliction(Identifier effect, Range seconds, Range amp
float health = player.getHealth();
StatusEffectInstance current = player.getStatusEffect(effect);
player.addStatusEffect(new StatusEffectInstance(effect,
seconds.getTicks(current == null ? 0 : current.getDuration()),
amplifier.getTicks(current == null ? 0 : current.getAmplifier())
seconds.getClamped(current == null ? 0 : current.getDuration(), 20),
amplifier.getClamped(current == null ? 0 : current.getAmplifier(), 1)
));
// keep original health
if (effect.getAttributeModifiers().containsKey(EntityAttributes.GENERIC_MAX_HEALTH)) {
@ -64,7 +65,7 @@ public record StatusEffectAffliction(Identifier effect, Range seconds, Range amp
MutableText text = effect.getName().copy();
if (amplifier.min() > 0) {
text = Text.translatable("potion.withAmplifier", text, Text.translatable("potion.potency." + (amplifier.min() * 20)));
text = Text.translatable("potion.withAmplifier", text, Text.translatable("potion.potency." + (amplifier.min())));
}
text = Text.translatable("potion.withDuration", text, StringHelper.formatTicks(seconds.min() * 20));
@ -73,6 +74,6 @@ public record StatusEffectAffliction(Identifier effect, Range seconds, Range amp
text = Text.translatable("potion.withChance", chance, text);
}
return (Text)text;
}).orElse(Text.of("No Effect"));
}).orElse(EMPTY.getName());
}
}

View file

@ -0,0 +1,16 @@
package com.minelittlepony.unicopia.item;
import java.util.Optional;
import com.minelittlepony.unicopia.entity.ItemImpl;
import net.minecraft.item.*;
public interface ItemDuck extends ItemConvertible, ItemImpl.TickableItem {
void setFoodComponent(FoodComponent food);
Optional<FoodComponent> getOriginalFoodComponent();
default void resetFoodComponent() {
setFoodComponent(getOriginalFoodComponent().orElse(null));
}
}

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.item.toxin;
package com.minelittlepony.unicopia.item;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;

View file

@ -12,7 +12,6 @@ import com.minelittlepony.unicopia.item.cloud.CloudBedItem;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.item.group.ItemGroupRegistry;
import com.minelittlepony.unicopia.item.group.UItemGroups;
import com.minelittlepony.unicopia.item.toxin.UFoodComponents;
import com.terraformersmc.terraform.boat.api.TerraformBoatType;
import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry;
import com.terraformersmc.terraform.boat.api.item.TerraformBoatItemHelper;

View file

@ -9,7 +9,6 @@ import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.group.MultiItem;
import com.minelittlepony.unicopia.item.toxin.*;
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.util.TraceHelper;
@ -36,7 +35,7 @@ import net.minecraft.registry.Registries;
import net.minecraft.world.World;
import net.minecraft.world.event.GameEvent;
public class ZapAppleItem extends Item implements ChameleonItem, ToxicHolder, MultiItem {
public class ZapAppleItem extends Item implements ChameleonItem, MultiItem {
public ZapAppleItem(Settings settings) {
super(settings);
}
@ -117,12 +116,6 @@ public class ZapAppleItem extends Item implements ChameleonItem, ToxicHolder, Mu
return hasAppearance(stack) ? getAppearanceStack(stack).getName() : super.getName(stack);
}
@SuppressWarnings("deprecation")
@Override
public Toxic getToxic(ItemStack stack, LivingEntity entity) {
return hasAppearance(stack) ? Toxic.SEVERE_INNERT : Toxics.FORAGE_EDIBLE;
}
@Override
public Rarity getRarity(ItemStack stack) {
if (hasAppearance(stack)) {

View file

@ -1,12 +0,0 @@
package com.minelittlepony.unicopia.item.toxin;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
public interface Affliction {
void afflict(PlayerEntity player, ItemStack stack);
interface Predicate {
boolean test(PlayerEntity player, ItemStack stack);
}
}

View file

@ -1,47 +0,0 @@
package com.minelittlepony.unicopia.item.toxin;
import java.util.*;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.text.Text;
public record Ailment (
Toxicity toxicity,
Toxin effect
) {
public static final Ailment INNERT = of(Toxicity.SAFE, Toxin.INNERT);
@Deprecated
public static Ailment of(Toxicity toxicity, Toxin effect) {
return new Ailment(toxicity, effect);
}
public void appendTooltip(List<Text> tooltip, TooltipContext context) {
tooltip.add(toxicity().getTooltip());
if (context.isAdvanced()) {
effect().appendTooltip(tooltip);
}
}
public interface Set {
Set EMPTY = e -> Optional.empty();
Optional<Ailment> get(LivingEntity entity);
static Ailment.Set of(Ailment def, Map<Race, Ailment> map) {
if (map.isEmpty()) {
return of(def);
}
return entity -> Optional.of(entity instanceof PlayerEntity player ? map.getOrDefault(Pony.of(player).getObservedSpecies(), def) : def);
}
static Ailment.Set of(Ailment ailment) {
final Optional<Ailment> value = Optional.of(ailment);
return entity -> value;
}
}
}

View file

@ -1,9 +0,0 @@
package com.minelittlepony.unicopia.item.toxin;
import com.minelittlepony.unicopia.entity.ItemImpl;
import net.minecraft.item.*;
public interface ItemDuck extends ItemConvertible, ToxicHolder, ItemImpl.TickableItem {
void setFoodComponent(FoodComponent food);
}

View file

@ -1,108 +0,0 @@
package com.minelittlepony.unicopia.item.toxin;
import static com.minelittlepony.unicopia.item.toxin.Toxicity.FAIR;
import static com.minelittlepony.unicopia.item.toxin.Toxicity.SEVERE;
import java.util.*;
import java.util.function.Function;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.entity.effect.FoodPoisoningStatusEffect;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.UseAction;
import net.minecraft.world.World;
public record Toxic (
Optional<UseAction> useAction,
Function<LivingEntity, Optional<FoodComponent>> food,
Ailment.Set ailment
) {
public static final Toxic EMPTY = new Toxic(Optional.empty(), entity -> Optional.empty(), Ailment.Set.EMPTY);
/**
* Default for all food that doesn't have a mapping
*/
@Deprecated
public static final Toxic DEFAULT = new Toxic.Builder(Ailment.INNERT)
.with(Race.CHANGELING, new Ailment(FAIR, Toxin.LOVE_SICKNESS))
.with(Race.SEAPONY, new Ailment(FAIR, Toxin.FOOD_POISONING))
.build();
public static final Toxic SEVERE_INNERT = new Builder(new Ailment(SEVERE, Toxin.INNERT)).build();
public void appendTooltip(PlayerEntity player, List<Text> tooltip, TooltipContext context) {
ailment.get(player).ifPresent(ailment -> ailment.appendTooltip(tooltip, context));
}
public TypedActionResult<ItemStack> startUsing(ItemStack stack, World world, PlayerEntity user, Hand hand) {
if (stack.getItem() instanceof BlockItem && ailment().get(user).isPresent() && !Pony.of(user).getObservedSpecies().hasIronGut()) {
return TypedActionResult.fail(stack);
}
return FoodPoisoningStatusEffect.apply(stack, user);
}
public void finishUsing(ItemStack stack, World world, LivingEntity entity) {
if (entity instanceof PlayerEntity player) {
ailment.get(entity).ifPresent(ailment -> ailment.effect().afflict(player, stack));
}
if (stack.isFood() || stack.getUseAction() == UseAction.DRINK) {
Pony.of(entity).ifPresent(pony -> pony.onEat(stack));
}
}
@Deprecated
public static class Builder {
private final Ailment def;
private final Map<Race, Ailment> overrides = new HashMap<>();
private Optional<UseAction> action = Optional.of(UseAction.EAT);
private final Map<Race, FoodComponent> components = new HashMap<>();
private Optional<FoodComponent> component = Optional.empty();
public Builder(Ailment def) {
this.def = def;
}
public Builder action(UseAction action) {
this.action = Optional.of(action);
return this;
}
public Builder food(FoodComponent food) {
component = Optional.ofNullable(food);
return this;
}
public Builder food(Race race, FoodComponent food) {
components.put(race, food);
return this;
}
public Builder with(Race race, Ailment ailment) {
overrides.put(race, ailment);
return this;
}
public Builder with(Ailment ailment, Race... races) {
for (Race race : races) {
overrides.put(race, ailment);
}
return this;
}
public Toxic build() {
return new Toxic(action, entity -> {
if (entity instanceof PlayerEntity player) {
return Optional.ofNullable(components.get(Pony.of(player).getObservedSpecies())).or(() -> component);
}
return component;
}, Ailment.Set.of(def, overrides));
}
}
}

View file

@ -1,10 +0,0 @@
package com.minelittlepony.unicopia.item.toxin;
import org.jetbrains.annotations.Nullable;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.*;
public interface ToxicHolder {
Toxic getToxic(ItemStack stack, @Nullable LivingEntity entity);
}

View file

@ -1,15 +0,0 @@
package com.minelittlepony.unicopia.item.toxin;
import net.minecraft.item.Item;
import net.minecraft.registry.tag.TagKey;
@Deprecated
public record ToxicRegistryEntry (
Toxic value,
TagKey<Item> tag
) {
public boolean matches(Item item) {
return item.getRegistryEntry().isIn(tag);
}
}

View file

@ -1,47 +0,0 @@
package com.minelittlepony.unicopia.item.toxin;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.StringIdentifiable;
public enum Toxicity implements StringIdentifiable {
SAFE(Formatting.GRAY),
MILD(Formatting.DARK_AQUA),
FAIR(Formatting.DARK_BLUE),
SEVERE(Formatting.DARK_PURPLE),
LETHAL(Formatting.RED);
private static final Map<String, Toxicity> REGISTRY = Arrays.stream(values()).collect(Collectors.toMap(Toxicity::name, Function.identity()));
@SuppressWarnings("deprecation")
public static final Codec<Toxicity> CODEC = StringIdentifiable.createCodec(Toxicity::values);
private final Formatting color;
private final String name = name().toLowerCase(Locale.ROOT);
Toxicity(Formatting color) {
this.color = color;
}
public String getTranslationKey() {
return String.format("toxicity.%s.name", name().toLowerCase());
}
public Text getTooltip() {
return Text.translatable(getTranslationKey()).formatted(color);
}
public static Toxicity byName(String name) {
return REGISTRY.get(name.toUpperCase());
}
@Override
public String asString() {
return name;
}
}

View file

@ -1,65 +0,0 @@
package com.minelittlepony.unicopia.item.toxin;
import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.util.RegistryUtils;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.FoodComponent;
import net.minecraft.registry.Registry;
import static com.minelittlepony.unicopia.item.toxin.Toxicity.*;
import static com.minelittlepony.unicopia.item.toxin.Ailment.*;
import static com.minelittlepony.unicopia.item.toxin.Toxin.*;
import org.jetbrains.annotations.Nullable;
@Deprecated
public interface Toxics {
Registry<ToxicRegistryEntry> REGISTRY = RegistryUtils.createSimple(Unicopia.id("toxic"));
Toxic FORAGE_EDIBLE = register("forage_edible", new Toxic.Builder(Ailment.INNERT).food(UFoodComponents.RANDOM_FOLIAGE).with(Race.HUMAN, of(LETHAL, FOOD_POISONING)).with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS)).with(Race.SEAPONY, of(FAIR, FOOD_POISONING)));
static void bootstrap() {
register("forage_edible_filling", new Toxic.Builder(Ailment.INNERT).food(UFoodComponents.RANDOM_FOLIAGE_FILLING).with(Race.CHANGELING, of(FAIR, LOVE_SICKNESS)).with(Race.SEAPONY, of(FAIR, FOOD_POISONING)));
register("forage_risky", new Toxic.Builder(of(FAIR, FOOD_POISONING.withChance(20))).food(UFoodComponents.RANDOM_FOLIAGE));
register("forage_moderate", new Toxic.Builder(of(MILD, FOOD_POISONING)).food(UFoodComponents.RANDOM_FOLIAGE));
register("forage_dangerous", new Toxic.Builder(of(SEVERE, FOOD_POISONING)).food(UFoodComponents.RANDOM_FOLIAGE));
register("forage_nauseating", new Toxic.Builder(of(SAFE, FOOD_POISONING.and(WEAKNESS.withChance(30)))).food(UFoodComponents.RANDOM_FOLIAGE));
register("forage_radioactive", new Toxic.Builder(of(SAFE, FOOD_POISONING.and(GLOWING.withChance(30)))).food(UFoodComponents.RANDOM_FOLIAGE));
register("forage_prickly", new Toxic.Builder(of(SAFE, INSTANT_DAMAGE.withChance(30))).food(UFoodComponents.RANDOM_FOLIAGE).with(Ailment.INNERT, Race.HIPPOGRIFF, Race.KIRIN));
register("forage_strengthening", new Toxic.Builder(of(SEVERE, STRENGTH.and(FOOD_POISONING))).food(UFoodComponents.RANDOM_FOLIAGE).with(Race.KIRIN, Ailment.INNERT));
register("forage_severely_nauseating", new Toxic.Builder(of(SEVERE, FOOD_POISONING.and(WEAKNESS))).food(UFoodComponents.RANDOM_FOLIAGE));
register("forage_blinding", new Toxic.Builder(of(SEVERE, BLINDNESS.and(FOOD_POISONING))).food(UFoodComponents.RANDOM_FOLIAGE).with(Race.KIRIN, Ailment.INNERT));
register("forage_severely_prickly", new Toxic.Builder(of(SEVERE, FOOD_POISONING.and(INSTANT_DAMAGE))).food(UFoodComponents.RANDOM_FOLIAGE).with(Race.KIRIN, Ailment.INNERT));
register("raw_meat", new Toxic.Builder(of(SEVERE, FOOD_POISONING.withChance(5).and(CHANCE_OF_POISON))).with(Ailment.INNERT, Race.HUMAN, Race.CHANGELING, Race.KIRIN).with(of(MILD, FOOD_POISONING), Race.BAT));
register("rotten_meat", new Toxic.Builder(of(SEVERE, STRONG_FOOD_POISONING)).with(Ailment.INNERT, Race.HUMAN, Race.CHANGELING).with(of(MILD, FOOD_POISONING), Race.BAT));
register("cooked_meat", new Toxic.Builder(of(FAIR, FOOD_POISONING)).with(Ailment.INNERT, Race.HUMAN, Race.CHANGELING, Race.BAT, Race.KIRIN).with(of(MILD, FOOD_POISONING), Race.HIPPOGRIFF));
register("raw_fish", new Toxic.Builder(of(FAIR, FOOD_POISONING.and(CHANCE_OF_POISON))).with(Ailment.INNERT, Race.HUMAN, Race.HIPPOGRIFF, Race.SEAPONY, Race.ALICORN).with(of(MILD, FOOD_POISONING), Race.PEGASUS).with(of(FAIR, LOVE_SICKNESS), Race.CHANGELING));
register("cooked_fish", new Toxic.Builder(of(MILD, FOOD_POISONING)).with(Ailment.INNERT, Race.HUMAN, Race.PEGASUS, Race.HIPPOGRIFF, Race.SEAPONY, Race.ALICORN).with(of(FAIR, LOVE_SICKNESS), Race.CHANGELING));
register("raw_insect", new Toxic.Builder(of(LETHAL, FOOD_POISONING)).food(UFoodComponents.INSECTS).with(Ailment.INNERT, Race.CHANGELING).with(of(MILD, WEAK_FOOD_POISONING), Race.BAT));
register("cooked_insect", new Toxic.Builder(of(LETHAL, FOOD_POISONING)).food(UFoodComponents.INSECTS).with(Ailment.INNERT, Race.CHANGELING, Race.KIRIN, Race.BAT));
register("love", new Toxic.Builder(Ailment.INNERT).with(of(Toxicity.SAFE, Toxin.LOVE_CONSUMPTION), Race.CHANGELING));
register("bat_ponys_delight", new Toxic.Builder(Ailment.INNERT).with(of(Toxicity.SAFE, Toxin.BAT_PONY_INTOXICATION), Race.BAT));
register("raw_sea_vegitable", new Toxic.Builder(Ailment.INNERT).food(Race.SEAPONY, UFoodComponents.RANDOM_FOLIAGE));
register("cooked_sea_vegitable", new Toxic.Builder(Ailment.INNERT).food(Race.SEAPONY, UFoodComponents.RANDOM_FOLIAGE_FILLING));
register("shells", new Toxic.Builder(Ailment.INNERT).food(Race.SEAPONY, UFoodComponents.SHELL));
register("shelly", new Toxic.Builder(Ailment.INNERT).food(Race.SEAPONY, UFoodComponents.SHELLY));
register("pinecone", new Toxic.Builder(of(Toxicity.SAFE, Toxin.healing(1))).with(Ailment.INNERT, Race.HUMAN).with(of(Toxicity.SAFE, Toxin.healing(3)), Race.HIPPOGRIFF));
}
static Toxic register(String name, Toxic.Builder builder) {
return Registry.register(REGISTRY, Unicopia.id(name), new ToxicRegistryEntry(builder.build(), UTags.item("food_types/" + name))).value();
}
static Toxic lookup(ItemDuck item, @Nullable LivingEntity entity) {
@Nullable FoodComponent food = item.asItem().getFoodComponent();
return REGISTRY.stream()
.filter(i -> i.matches(item.asItem()))
.map(ToxicRegistryEntry::value)
.map(t -> {
if (food == null) {
t.food().apply(entity).ifPresent(item::setFoodComponent);
}
return t;
}).findFirst().orElse(food == null ? Toxic.EMPTY : Toxic.DEFAULT);
}
}

View file

@ -1,180 +0,0 @@
package com.minelittlepony.unicopia.item.toxin;
import java.util.List;
import com.minelittlepony.unicopia.entity.effect.UEffects;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.ItemStack;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.StringHelper;
import net.minecraft.util.math.MathHelper;
public interface Toxin extends Affliction {
Toxin INNERT = of(Text.of("No Effect"), (player, stack) -> {});
@Deprecated
Toxin INSTANT_DAMAGE = of(StatusEffects.INSTANT_DAMAGE, 1, 0);
@Deprecated
Toxin GLOWING = of(StatusEffects.GLOWING, 15, 0);
@Deprecated
Toxin WEAKNESS = of(StatusEffects.WEAKNESS, 200, 1);
@Deprecated
Toxin STRENGTH = of(StatusEffects.STRENGTH, 30, 0);
@Deprecated
Toxin BLINDNESS = of(StatusEffects.BLINDNESS, 30, 0);
@Deprecated
Toxin CHANCE_OF_POISON = of(StatusEffects.POISON, 45, 2).withChance(80);
@Deprecated
Toxin FOOD_POISONING = of(UEffects.FOOD_POISONING, 100, 2);
@Deprecated
Toxin WEAK_FOOD_POISONING = of(UEffects.FOOD_POISONING, 50, 1);
@Deprecated
Toxin STRONG_FOOD_POISONING = of(UEffects.FOOD_POISONING, 400, 3);
@Deprecated
Toxin LOVE_SICKNESS = of(Text.of("Love Sickness "), (player, stack) -> {
FoodComponent food = stack.getItem().getFoodComponent();
player.getHungerManager().add(-food.getHunger()/2, -food.getSaturationModifier()/2);
}).and(FOOD_POISONING).and(WEAKNESS);
@Deprecated
Toxin LOVE_CONSUMPTION = of(Text.literal("Love"), (player, stack) -> {
player.heal(stack.isFood() ? stack.getItem().getFoodComponent().getHunger() : 1);
player.removeStatusEffect(StatusEffects.NAUSEA);
player.removeStatusEffect(UEffects.FOOD_POISONING);
});
@Deprecated
Toxin BAT_PONY_INTOXICATION = Toxin.of(StatusEffects.HEALTH_BOOST, 30, 60, 2, 6)
.and(Toxin.of(StatusEffects.JUMP_BOOST, 30, 60, 1, 6))
.and(Toxin.of(StatusEffects.SPEED, 30, 30, 1, 6))
.and(Toxin.of(StatusEffects.REGENERATION, 3, 30, 3, 6));
@Deprecated
static Toxin healing(int hearts) {
return of(Text.literal("Healing " + hearts + " Hearts"), (player, stack) -> player.heal(hearts));
}
default void appendTooltip(List<Text> tooltip) {
tooltip.add(getName());
}
@Deprecated
default Toxin withChance(int max) {
return Predicate.of(Text.of("1 in " + max + " chance of "), (player, stack) -> player.getWorld().random.nextInt(max) == 0).then(this);
}
Text getName();
@Deprecated
default Toxin and(Toxin other) {
Toxin self = this;
return new Toxin() {
@Override
public void afflict(PlayerEntity player, ItemStack stack) {
self.afflict(player, stack);
other.afflict(player, stack);
}
@Override
public void appendTooltip(List<Text> tooltip) {
self.appendTooltip(tooltip);
other.appendTooltip(tooltip);
}
@Override
public Text getName() {
return self.getName().copy().append(" + ").append(other.getName());
}
};
}
static Toxin of(Text name, Affliction affliction) {
return new Toxin() {
@Override
public void afflict(PlayerEntity player, ItemStack stack) {
affliction.afflict(player, stack);
}
@Override
public Text getName() {
return name;
}
};
}
@Deprecated
static Toxin of(StatusEffect effect, int seconds, int amplifier) {
return of(effect, seconds, -1, amplifier, -1);
}
static Toxin of(StatusEffect effect, int seconds, int maxSeconds, int amplifier, int maxAmplifier) {
final int ticks = seconds * 20;
final int maxTicks = maxSeconds * 20;
MutableText text = effect.getName().copy();
if (amplifier > 0) {
text = Text.translatable("potion.withAmplifier", text, Text.translatable("potion.potency." + amplifier));
}
text = Text.translatable("potion.withDuration", text, StringHelper.formatTicks(ticks));
return of(text, (player, stack) -> {
float health = player.getHealth();
StatusEffectInstance current = player.getStatusEffect(effect);
int t = applyLimit(ticks + (current == null ? 0 : current.getDuration()), maxTicks);
int a = applyLimit(amplifier + (current == null ? 0 : current.getAmplifier()), maxAmplifier);
player.addStatusEffect(new StatusEffectInstance(effect, t, a));
// keep original health
if (effect.getAttributeModifiers().containsKey(EntityAttributes.GENERIC_MAX_HEALTH)) {
player.setHealth(MathHelper.clamp(health, 0, player.getMaxHealth()));
}
});
}
private static int applyLimit(int value, int max) {
return max > 0 ? Math.min(value, max) : value;
}
interface Predicate {
static Predicate of(Text name, Affliction.Predicate predicate) {
return new Predicate() {
@Override
public boolean test(PlayerEntity player, ItemStack stack) {
return predicate.test(player, stack);
}
@Override
public Text getName() {
return name;
}
};
}
boolean test(PlayerEntity player, ItemStack stack);
Text getName();
default Toxin then(Toxin toxin) {
return new Toxin() {
@Override
public void afflict(PlayerEntity player, ItemStack stack) {
if (test(player, stack)) {
toxin.afflict(player, stack);
}
}
@Override
public Text getName() {
return Predicate.this.getName().copy().append(toxin.getName());
}
};
}
}
}

View file

@ -5,24 +5,21 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.item.toxin.ToxicHolder;
import com.minelittlepony.unicopia.server.world.WaterLoggingManager;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.util.UseAction;
@Mixin(BlockItem.class)
abstract class MixinBlockItem extends Item implements ToxicHolder {
abstract class MixinBlockItem extends Item {
MixinBlockItem() {super(null); }
@Override
/*@Override
public UseAction getUseAction(ItemStack stack) {
return getToxic(stack, null).useAction().orElseGet(() -> super.getUseAction(stack));
}
return PonyDiets.getinstance().getUseAction(stack).orElseGet(() -> super.getUseAction(stack));
}*/
@Inject(method = "getPlacementState", at = @At("RETURN"), cancellable = true)
private void onGetPlacementState(ItemPlacementContext context, CallbackInfoReturnable<BlockState> info) {

View file

@ -2,26 +2,26 @@ package com.minelittlepony.unicopia.mixin;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
import com.google.common.base.Suppliers;
import com.minelittlepony.unicopia.diet.DietView;
import com.minelittlepony.unicopia.entity.ItemImpl;
import com.minelittlepony.unicopia.entity.ItemImpl.GroundTickCallback;
import com.minelittlepony.unicopia.item.toxin.*;
import net.minecraft.entity.LivingEntity;
import com.minelittlepony.unicopia.item.ItemDuck;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@Mixin(Item.class)
abstract class MixinItem implements ItemDuck {
abstract class MixinItem implements ItemDuck, DietView.Holder {
private final List<ItemImpl.GroundTickCallback> tickCallbacks = new ArrayList<>();
private final Supplier<FoodComponent> originalFoodComponent = Suppliers.memoize(((Item)(Object)this)::getFoodComponent);
private final Supplier<Optional<FoodComponent>> originalFoodComponent = Suppliers.memoize(() -> {
return Optional.ofNullable(((Item)(Object)this).getFoodComponent());
});
@Override
public List<GroundTickCallback> getCallbacks() {
@ -34,10 +34,7 @@ abstract class MixinItem implements ItemDuck {
public abstract void setFoodComponent(FoodComponent food);
@Override
public Toxic getToxic(ItemStack stack, @Nullable LivingEntity entity) {
if (entity != null) {
setFoodComponent(originalFoodComponent.get());
}
return Toxics.lookup(this, entity);
public Optional<FoodComponent> getOriginalFoodComponent() {
return originalFoodComponent.get();
}
}

View file

@ -5,8 +5,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.item.toxin.ToxicHolder;
import com.minelittlepony.unicopia.diet.DietView;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
@ -20,7 +19,7 @@ abstract class MixinItemStack {
@Inject(method = "use", at = @At("HEAD"), cancellable = true)
private void onUse(World world, PlayerEntity user, Hand hand, CallbackInfoReturnable<TypedActionResult<ItemStack>> info) {
ItemStack self = (ItemStack)(Object)this;
TypedActionResult<ItemStack> result = ((ToxicHolder)self.getItem()).getToxic(self, user).startUsing(self, world, user, hand);
TypedActionResult<ItemStack> result = ((DietView.Holder)self.getItem()).getDiets(self).startUsing(self, world, user, hand);
if (result.getResult() != ActionResult.PASS) {
info.setReturnValue(result);
}
@ -29,6 +28,6 @@ abstract class MixinItemStack {
@Inject(method = "finishUsing", at = @At("HEAD"))
private void onFinishUsing(World world, LivingEntity user, CallbackInfoReturnable<ItemStack> info) {
ItemStack self = (ItemStack)(Object)this;
((ToxicHolder)self.getItem()).getToxic(self, user).finishUsing(self, world, user);
((DietView.Holder)self.getItem()).getDiets(self).finishUsing(self, world, user);
}
}

View file

@ -1,26 +0,0 @@
package com.minelittlepony.unicopia.mixin.client;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.minelittlepony.unicopia.item.toxin.ToxicHolder;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.world.World;
@Mixin(Item.class)
abstract class MixinItem implements ToxicHolder {
@Inject(method = "appendTooltip", at = @At("RETURN"))
private void onAppendTooltip(ItemStack stack, @Nullable World world, List<Text> tooltip, TooltipContext context, CallbackInfo into) {
getToxic(stack, MinecraftClient.getInstance().player).appendTooltip(MinecraftClient.getInstance().player, tooltip, context);
}
}

View file

@ -0,0 +1,24 @@
package com.minelittlepony.unicopia.mixin.client;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.diet.DietView;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
@Mixin(ItemStack.class)
abstract class MixinItemStack {
@Inject(method = "getTooltip", at = @At("RETURN"))
private void onGetTooltip(@Nullable PlayerEntity player, TooltipContext context, CallbackInfoReturnable<List<Text>> info) {
ItemStack self = (ItemStack)(Object)this;
((DietView.Holder)self.getItem()).getDiets(self).appendTooltip(self, player, info.getReturnValue(), context);
}
}

View file

@ -24,7 +24,7 @@ public record MsgServerResources (
SpellTraits.all(),
SpellbookChapterLoader.INSTANCE.getChapters(),
TreeTypeLoader.INSTANCE.getEntries(),
PonyDiets.getinstance()
PonyDiets.getInstance()
);
}

View file

@ -469,11 +469,57 @@
"trait.unicopia.poison.name": "Poison",
"trait.unicopia.poison.description": "A deadly dart kill a beast.",
"unicopia.diet.information": "Diet Information:",
"unicopia.diet.side_effects": "Side-Effects:",
"unicopia.diet.not_edible": "Item is not edible",
"unicopia.diet.base_multiplier": "Base Multiplier: %s%%",
"unicopia.diet.hunger.detailed": "Hunger gained: %s of %s (%s%%)",
"unicopia.diet.saturation.detailed": "Saturation gained: %s (%s%%)",
"unicopia.diet.hunger": "Hunger Ratio: %s%%",
"unicopia.diet.saturation": "Saturation Ratio: %s%%",
"tag.unicopia.food_types.rotten_meat": "Rotting Meat",
"tag.unicopia.food_types.raw_meat": "Fresh Meat",
"tag.unicopia.food_types.cooked_meat": "Prepared Meat",
"tag.unicopia.food_types.raw_fish": "Fresh Fish",
"tag.unicopia.food_types.cooked_fish": "Prepared Fish",
"tag.unicopia.food_types.raw_insect": "Bugs & Insects",
"tag.unicopia.food_types.cooked_insect": "Cooked Bugs & Insects",
"tag.unicopia.food_types.love": "Love",
"tag.unicopia.food_types.rocks": "Rocks",
"tag.unicopia.food_types.pinecone": "Nuts & Seeds",
"tag.unicopia.food_types.bat_ponys_delight": "Bat Pony Treats",
"tag.unicopia.food_types.cooked_sea_vegitables": "Prepared Fish Food",
"tag.unicopia.food_types.raw_sea_vegitables": "Fresh Fish Food",
"tag.unicopia.food_types.shells": "Sea Shells",
"tag.unicopia.food_types.shelly": "Sea Shells",
"tag.unicopia.food_types.candy": "Candy",
"tag.unicopia.food_types.desserts": "Desserts",
"tag.unicopia.food_types.fruits_and_vegetables": "Fruits & Vegetables",
"tag.unicopia.food_types.drinks": "Drinks",
"tag.unicopia.food_types.forage_edible_filling": "Bulky Plant Matter",
"tag.unicopia.food_types.forage_edible": "Plant Matter",
"tag.unicopia.food_types.forage_nauseating": "Nauseating",
"tag.unicopia.food_types.forage_prickly": "Prickly",
"tag.unicopia.food_types.forage_risky": "Unsafe",
"tag.unicopia.food_types.forage_strengthening": "Strength Enhancing",
"tag.unicopia.food_types.forage_severely_prickly": "Very Prickly",
"tag.unicopia.food_types.forage_severely_nauseating": "Sickening",
"tag.unicopia.food_types.forage_radioactive": "Glowy",
"tag.unicopia.food_types.forage_dangerous": "Dangerous",
"tag.unicopia.food_types.forage_blinding": "Toxic",
"toxicity.safe.name": "Safe",
"toxicity.mild.name": "Mildly Toxic",
"toxicity.fair.name": "Fairly Toxic",
"toxicity.severe.name": "Toxic",
"toxicity.lethal.name": "Lethal",
"affliction.unicopia.empty": "No Effect",
"affliction.unicopia.healing": "Gain %s%% health",
"affliction.unicopia.cure_love_sickness": "Cure Love Sickness",
"affliction.unicopia.lose_hunger": "Lose %s%% hunger",
"ability.unicopia.shoot": "Shoot Magic",
"ability.unicopia.shoot.with_spell": "Shoot %s",
@ -1068,7 +1114,7 @@
"advancements.unicopia.apple_route.description": "Start your journey towards the apple of legend",
"advancements.unicopia.juice.title": "Refreshing",
"advancements.unicopia.juice.description": "Finally a use for all these apples",
"advancements.unicopia.toast.title": "He Crispy and delicious!",
"advancements.unicopia.toast.title": "He's Crispy and delicious!",
"advancements.unicopia.toast.description": "Make a toasted companion",
"advancements.unicopia.burn_toast.title": "Toasty NOOOOOOO!",
"advancements.unicopia.burn_toast.description": "Burn Toasty",

View file

@ -1,7 +1,10 @@
{
"tag": "unicopia:food_types/cooked_fish",
"tags": [ "unicopia:food_types/cooked_fish" ],
"food_component": {
"hunger": 1,
"saturation": 0.1
},
"ailment": {
"toxicity": "fair",
"effects": [
{
"effect": "unicopia:food_poisoning",

View file

@ -1,7 +1,10 @@
{
"tag": "unicopia:food_types/raw_fish",
"tags": [ "unicopia:food_types/raw_fish" ],
"food_component": {
"hunger": 1,
"saturation": 0.1
},
"ailment": {
"toxicity": "risky",
"effects": [
{
"effect": "minecraft:poison",

View file

@ -1,7 +1,10 @@
{
"tag": "unicopia:food_types/rotten_fish",
"tags": [ "unicopia:food_types/rotten_fish" ],
"food_component": {
"hunger": 1,
"saturation": 0.1
},
"ailment": {
"toxicity": "severe",
"effects": [
{
"effect": "minecraft:poison",

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_blinding",
"tags": [ "unicopia:food_types/forage_blinding" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "severe",
"effects": [
{
"effect": "minecraft:blindness",

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_dangerous",
"tags": [ "unicopia:food_types/forage_dangerous" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "mild",
"effects": [
{
"effect": "unicopia:food_poisoning",

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_edible",
"tags": [ "unicopia:food_types/forage_edible" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "safe",
"effects": []
}
}

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_edible_filling",
"tags": [ "unicopia:food_types/forage_edible_filling" ],
"food_component": {
"hunger": 18,
"saturation": 9
},
"ailment": {
"toxicity": "safe",
"effects": []
}
}

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_moderate",
"tags": [ "unicopia:food_types/forage_moderate" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "mild",
"effects": [
{
"effect": "unicopia:food_poisoning",

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_nauseating",
"tags": [ "unicopia:food_types/forage_nauseating" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "severe",
"effects": [
{
"effect": "minecraft:weakness",

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_prickly",
"tags": [ "unicopia:food_types/forage_prickly" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "safe",
"effects": [
{
"effect": "minecraft:instant_damage",

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_radioactive",
"tags": [ "unicopia:food_types/forage_radioactive" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "severe",
"effects": [
{
"effect": "minecraft:glowing",

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_risky",
"tags": [ "unicopia:food_types/forage_risky" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "fair",
"effects": [
{
"effect": "unicopia:food_poisoning",

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_severely_nauseating",
"tags": [ "unicopia:food_types/forage_severely_nauseating" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "severe",
"effects": [
{
"effect": "minecraft:weakness",

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_severely_prickly",
"tags": [ "unicopia:food_types/forage_severely_prickly" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "severe",
"effects": [
{
"effect": "minecraft:instant_damage",

View file

@ -1,11 +1,10 @@
{
"tag": "unicopia:food_types/forage_strengthening",
"tags": [ "unicopia:food_types/forage_strengthening" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "safe",
"effects": [
{
"effect": "minecraft:strength",

View file

@ -1,7 +1,10 @@
{
"tag": "unicopia:food_types/cooked_insect",
"tags": [ "unicopia:food_types/cooked_insect" ],
"food_component": {
"hunger": 1,
"saturation": 0.1
},
"ailment": {
"toxicity": "severe",
"effects": [
{
"effect": "unicopia:food_poisoning",

View file

@ -1,7 +1,10 @@
{
"tag": "unicopia:food_types/raw_insect",
"tags": [ "unicopia:food_types/raw_insect" ],
"food_component": {
"hunger": 1,
"saturation": 0.1
},
"ailment": {
"toxicity": "lethal",
"effects": [
{
"effect": "unicopia:food_poisoning",

View file

@ -1,7 +1,10 @@
{
"tag": "unicopia:food_types/cooked_meat",
"tags": [ "unicopia:food_types/cooked_meat" ],
"food_component": {
"hunger": 12,
"saturation": 1.2
},
"ailment": {
"toxicity": "fair",
"effects": [
{
"effect": "unicopia:food_poisoning",

View file

@ -1,7 +1,10 @@
{
"tag": "unicopia:food_types/raw_meat",
"tags": [ "unicopia:food_types/raw_meat" ],
"food_component": {
"hunger": 1,
"saturation": 1
},
"ailment": {
"toxicity": "risky",
"effects": [
{
"effect": "minecraft:poison",

View file

@ -1,7 +1,10 @@
{
"tag": "unicopia:food_types/rotten_meat",
"tags": [ "unicopia:food_types/rotten_meat" ],
"food_component": {
"hunger": 1,
"saturation": 1
},
"ailment": {
"toxicity": "severe",
"effects": [
{
"effect": "minecraft:poison",

View file

@ -1,7 +1,10 @@
{
"tag": "unicopia:food_types/pinecone",
"tags": [ "unicopia:food_types/pinecone" ],
"food_component": {
"hunger": 1,
"saturation": 0.1
},
"ailment": {
"toxicity": "safe",
"effects": [
{
"type": "unicopia:healing",

View file

@ -0,0 +1,10 @@
{
"tags": [ "unicopia:food_types/rocks" ],
"food_component": {
"hunger": 1,
"saturation": 0.1
},
"ailment": {
"effects": []
}
}

View file

@ -3,16 +3,12 @@
"foraging_multiplier": 1,
"multipliers": [
{
"tags": [
"unicopia:food_types/cooked_fish"
],
"tags": [ "unicopia:food_types/cooked_fish" ],
"hunger": 1.5,
"saturation": 1.5
},
{
"tags": [
"unicopia:food_types/raw_fish"
],
"tags": [ "unicopia:food_types/raw_fish" ],
"hunger": 0.5,
"saturation": 0.6
},
@ -35,29 +31,25 @@
"saturation": 0
},
{
"tags": [
"unicopia:food_types/pinecone"
],
"tags": [ "unicopia:food_types/pinecone" ],
"hunger": 0.9,
"saturation": 0.9
}
],
"effects": [
{
"tag": "unicopia:food_types/cooked_fish",
"tags": [ "unicopia:food_types/cooked_fish" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tag": "unicopia:food_types/raw_fish",
"tags": [ "unicopia:food_types/raw_fish" ],
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
}

View file

@ -3,74 +3,55 @@
"foraging_multiplier": 0.9,
"multipliers": [
{
"tags": [
"unicopia:food_types/cooked_fish"
],
"tags": [ "unicopia:food_types/cooked_fish" ],
"hunger": 0.75,
"saturation": 0.75
},
{
"tags": [
"unicopia:food_types/raw_fish"
],
"tags": [ "unicopia:food_types/raw_fish" ],
"hunger": 0.5,
"saturation": 0.6
},
{
"tags": [
"unicopia:food_types/cooked_insect"
],
"tags": [ "unicopia:food_types/cooked_insect" ],
"hunger": 1.75,
"saturation": 1.75
},
{
"tags": [
"unicopia:food_types/cooked_meat"
],
"tags": [ "unicopia:food_types/cooked_meat" ],
"hunger": 1.15,
"saturation": 1.15
},
{
"tags": [
"unicopia:food_types/raw_insect"
],
"tags": [ "unicopia:food_types/raw_insect" ],
"hunger": 1,
"saturation": 1
},
{
"tags": [
"unicopia:food_types/raw_meat"
],
"tags": [ "unicopia:food_types/raw_meat" ],
"hunger": 0.25,
"saturation": 0.25
},
{
"tags": [
"unicopia:food_types/rotten_meat"
],
"tags": [ "unicopia:food_types/rotten_meat" ],
"hunger": 0.2,
"saturation": 0.2
},
{
"tags": [
"unicopia:food_types/love"
],
"tags": [ "unicopia:food_types/love" ],
"hunger": 0,
"saturation": 0
},
{
"tags": [
"unicopia:food_types/pinecone"
],
"tags": [ "unicopia:food_types/pinecone" ],
"hunger": 0.9,
"saturation": 0.9
}
],
"effects": [
{
"tag": "unicopia:food_types/rotten_fish",
"tags": [ "unicopia:food_types/rotten_fish" ],
"ailment": {
"toxicity": "mild",
"effects": [
{
"effect": "unicopia:food_poisoning",
@ -87,14 +68,12 @@
"unicopia:food_types/cooked_meat"
],
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tag": "unicopia:food_types/raw_insect",
"tags": [ "unicopia:food_types/raw_insect" ],
"ailment": {
"toxicity": "mild",
"effects": [
{
"effect": "unicopia:food_poisoning",
@ -110,7 +89,6 @@
"unicopia:food_types/rotten_meat"
],
"ailment": {
"toxicity": "risky",
"effects": [
{
"effect": "unicopia:food_poisoning",
@ -122,9 +100,8 @@
}
},
{
"tag": "unicopia:food_types/bat_ponys_delight",
"tags": [ "unicopia:food_types/bat_ponys_delight" ],
"ailment": {
"toxicity": "safe",
"effects": [
{
"effect": "minecraft:health_boost",

View file

@ -3,50 +3,37 @@
"foraging_multiplier": 0,
"multipliers": [
{
"tags": [
"unicopia:food_types/cooked_insect"
],
"tags": [ "unicopia:food_types/cooked_insect" ],
"hunger": 0.3,
"saturation": 0.3
},
{
"tags": [
"unicopia:food_types/cooked_meat"
],
"tags": [ "unicopia:food_types/cooked_meat" ],
"hunger": 0.1,
"saturation": 0.1
},
{
"tags": [
"unicopia:food_types/raw_insect"
],
"tags": [ "unicopia:food_types/raw_insect" ],
"hunger": 1,
"saturation": 1
},
{
"tags": [
"unicopia:food_types/raw_meat"
],
"tags": [ "unicopia:food_types/raw_meat" ],
"hunger": 0.25,
"saturation": 0.25
},
{
"tags": [
"unicopia:food_types/rotten_meat"
],
"tags": [ "unicopia:food_types/rotten_meat" ],
"hunger": 0.6,
"saturation": 0.6
},
{
"tags": [
"unicopia:food_types/love"
],
"tags": [ "unicopia:food_types/love" ],
"hunger": 1,
"saturation": 1
}
],
"default_effects": {
"toxicity": "fair",
"effects": [
{
"effect": "unicopia:food_poisoning",
@ -66,15 +53,12 @@
},
"effects": [
{
"tags": [
"unicopia:food_types/love"
],
"tags": [ "unicopia:food_types/love" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "safe",
"effects": [
{
"name": "Love Consumption",
@ -89,7 +73,6 @@
"unicopia:food_types/raw_fish"
],
"ailment": {
"toxicity": "fair",
"effects": [
{
"effect": "unicopia:food_poisoning",
@ -114,21 +97,19 @@
"unicopia:food_types/rotten_meat"
],
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tags": [
"unicopia:food_types/forage_edible",
"unicopia:food_types/forage_edible_filling",
"unicopia:food_types/forage_edible_filling"
],
"food_component": {
"hunger": 18,
"saturation": 9
},
"ailment": {
"toxicity": "fair",
"effects": [
{
"effect": "unicopia:food_poisoning",

View file

@ -1,11 +1,18 @@
{
"default_multiplier": 0.3,
"default_multiplier": 1,
"foraging_multiplier": 1,
"multipliers": [
{
"tags": [
"unicopia:food_types/cooked_fish"
],
"unicopia:food_types/candy",
"unicopia:food_types/desserts",
"unicopia:food_types/rocks"
],
"hunger": 2.5,
"saturation": 1.7
},
{
"tags": [ "unicopia:food_types/cooked_fish" ],
"hunger": 0.2,
"saturation": 0.2
},
@ -29,11 +36,39 @@
"saturation": 0
},
{
"tags": [
"unicopia:food_types/pinecone"
],
"tags": [ "unicopia:food_types/pinecone" ],
"hunger": 1,
"saturation": 1
}
],
"effects": [
{
"tags": [
"unicopia:food_types/candy",
"unicopia:food_types/rocks"
],
"food_component": {
"hunger": 5,
"saturation": 12,
"fastFood": true
},
"ailment": {
"effects": [ ]
}
},
{
"tags": [
"unicopia:food_types/desserts"
],
"food_component": {
"hunger": 12,
"saturation": 32,
"eatenQuickly": true,
"fastFood": true
},
"ailment": {
"effects": [ ]
}
}
]
}

View file

@ -19,9 +19,7 @@
"saturation": 0.6
},
{
"tags": [
"unicopia:food_types/rotten_meat"
],
"tags": [ "unicopia:food_types/rotten_meat" ],
"hunger": 0.3,
"saturation": 0.3
},
@ -35,29 +33,25 @@
"saturation": 0
},
{
"tags": [
"unicopia:food_types/pinecone"
],
"tags": [ "unicopia:food_types/pinecone" ],
"hunger": 1,
"saturation": 1
}
],
"effects": [
{
"tag": "unicopia:food_types/cooked_fish",
"tags": [ "unicopia:food_types/cooked_fish" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tag": "unicopia:food_types/raw_fish",
"tags": [ "unicopia:food_types/raw_fish" ],
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
@ -71,14 +65,12 @@
"saturation": 1
},
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tag": "unicopia:food_types/pinecone",
"tags": [ "unicopia:food_types/pinecone" ],
"ailment": {
"toxicity": "safe",
"effects": [
{
"type": "unicopia:healing",

View file

@ -14,7 +14,6 @@
"unicopia:food_types/pinecone"
],
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
}

View file

@ -3,16 +3,12 @@
"foraging_multiplier": 0.9,
"multipliers": [
{
"tags": [
"unicopia:food_types/cooked_meat"
],
"tags": [ "unicopia:food_types/cooked_meat" ],
"hunger": 1.5,
"saturation": 1.5
},
{
"tags": [
"unicopia:food_types/raw_meat"
],
"tags": [ "unicopia:food_types/raw_meat" ],
"hunger": 0.5,
"saturation": 0.6
},
@ -35,9 +31,7 @@
"saturation": 0
},
{
"tags": [
"unicopia:food_types/pinecone"
],
"tags": [ "unicopia:food_types/pinecone" ],
"hunger": 0.9,
"saturation": 0.9
}
@ -60,7 +54,6 @@
"saturation": 1
},
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
}

View file

@ -1,18 +1,14 @@
{
"default_multiplier": 0,
"default_multiplier": 0.5,
"foraging_multiplier": 1,
"multipliers": [
{
"tags": [
"unicopia:food_types/cooked_fish"
],
"tags": [ "unicopia:food_types/cooked_fish" ],
"hunger": 1.5,
"saturation": 1.5
},
{
"tags": [
"unicopia:food_types/raw_fish"
],
"tags": [ "unicopia:food_types/raw_fish" ],
"hunger": 0.5,
"saturation": 0.6
},
@ -35,25 +31,21 @@
"saturation": 0
},
{
"tags": [
"unicopia:food_types/pinecone"
],
"tags": [ "unicopia:food_types/pinecone" ],
"hunger": 0.9,
"saturation": 0.9
}
],
"effects": [
{
"tag": "unicopia:food_types/cooked_fish",
"tags": [ "unicopia:food_types/cooked_fish" ],
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tag": "unicopia:food_types/raw_fish",
"tags": [ "unicopia:food_types/raw_fish" ],
"ailment": {
"toxicity": "mild",
"effects": [
{
"effect": "unicopia:food_poisoning",

View file

@ -3,9 +3,7 @@
"foraging_multiplier": 0.7,
"multipliers": [
{
"tags": [
"unicopia:food_types/raw_sea_vegitable"
],
"tags": [ "unicopia:food_types/raw_sea_vegitable" ],
"hunger": 1,
"saturation": 1
},
@ -19,7 +17,6 @@
}
],
"default_effects": {
"toxicity": "fair",
"effects": [
{
"effect": "unicopia:food_poisoning",
@ -30,30 +27,27 @@
},
"effects": [
{
"tag": "unicopia:food_types/cooked_fish",
"tags": [ "unicopia:food_types/cooked_fish" ],
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tag": "unicopia:food_types/raw_fish",
"tags": [ "unicopia:food_types/raw_fish" ],
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tags": [
"unicopia:food_types/forage_edible",
"unicopia:food_types/forage_edible_filling",
"unicopia:food_types/forage_edible_filling"
],
"food_component": {
"hunger": 18,
"saturation": 9
},
"ailment": {
"toxicity": "fair",
"effects": [
{
"effect": "unicopia:food_poisoning",
@ -64,46 +58,42 @@
}
},
{
"tag": "unicopia:food_types/raw_sea_vegitable",
"tags": [ "unicopia:food_types/raw_sea_vegitable" ],
"food_component": {
"hunger": 2,
"saturation": 1
},
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tag": "unicopia:food_types/cooked_sea_vegitable",
"tags": [ "unicopia:food_types/cooked_sea_vegitable" ],
"food_component": {
"hunger": 6,
"saturation": 2
},
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tag": "unicopia:food_types/shells",
"tags": [ "unicopia:food_types/shells" ],
"food_component": {
"hunger": 3,
"saturation": 5
},
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
},
{
"tag": "unicopia:food_types/shelly",
"tags": [ "unicopia:food_types/shelly" ],
"food_component": {
"hunger": 6,
"saturation": 7
},
"ailment": {
"toxicity": "safe",
"effects": [ ]
}
}

View file

@ -23,11 +23,10 @@
"saturation": 0
},
{
"tags": [
"unicopia:food_types/pinecone"
],
"tags": [ "unicopia:food_types/pinecone" ],
"hunger": 0.9,
"saturation": 0.9
}
]
],
"effects": []
}

View file

@ -0,0 +1,10 @@
{
"replace": false,
"values": [
"unicopia:rock_candy",
"unicopia:candied_apple",
"minecraft:sugar",
{ "id": "bakersdelight:sweet_berry_cheesecake_slice", "required": false },
{ "id": "bakersdelight:cake_slice", "required": false }
]
}

View file

@ -0,0 +1,8 @@
{
"replace": false,
"values": [
"minecraft:cake",
"#unicopia:pies",
{ "id": "bakersdelight:sweet_berry_cheesecake", "required": false }
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"unicopia:rock_stew"
]
}

View file

@ -69,8 +69,8 @@
"client.MixinHeldItemRenderer",
"client.MixinInGameHud",
"client.MixinInGameHud$HeartType",
"client.MixinItem",
"client.MixinItemModels",
"client.MixinItemStack",
"client.MixinKeyboardInput",
"client.MixinLivingEntityRenderer",
"client.MixinModelPart",