Skip to content

Commit

Permalink
Loot functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Electro593 committed Jan 24, 2025
1 parent 52d5961 commit a9e65c7
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 310 deletions.
5 changes: 5 additions & 0 deletions src/main/java/de/dafuqs/spectrum/helpers/CodecHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
import com.mojang.serialization.*;
import net.minecraft.nbt.*;

import java.util.*;
import java.util.function.*;

public class CodecHelper {

public static <T> Codec<List<T>> singleOrList(Codec<T> codec) {
return Codec.withAlternative(codec.listOf(), codec, List::of);
}

public static <T> void fromNbt(Codec<T> codec, NbtElement nbt, Consumer<? super T> ifValid) {
if (nbt != null)
codec.decode(NbtOps.INSTANCE, nbt).result().map(Pair::getFirst).ifPresent(ifValid);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package de.dafuqs.spectrum.loot.functions;

import com.google.gson.*;
import com.mojang.datafixers.util.*;
import com.mojang.serialization.*;
import com.mojang.serialization.codecs.*;
import de.dafuqs.spectrum.*;
import de.dafuqs.spectrum.helpers.TimeHelper;
import de.dafuqs.spectrum.loot.*;
import de.dafuqs.spectrum.mixin.accessors.*;
import de.dafuqs.spectrum.recipe.titration_barrel.*;
import io.wispforest.owo.serialization.*;
import net.minecraft.item.*;
import net.minecraft.loot.condition.*;
import net.minecraft.loot.context.*;
import net.minecraft.loot.function.*;
import net.minecraft.loot.provider.number.*;
import net.minecraft.recipe.*;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.world.biome.*;
Expand All @@ -21,52 +23,61 @@

public class FermentRandomlyLootFunction extends ConditionalLootFunction {

final @Nullable Identifier fermentationRecipeIdentifier;
final @Nullable FermentationData fermentationData;
final LootNumberProvider daysFermented;
final LootNumberProvider thickness;
public static final MapCodec<FermentRandomlyLootFunction> CODEC = RecordCodecBuilder.mapCodec(i -> addConditionsField(i).and(i.group(
Codec.either(Identifier.CODEC, CodecUtils.toCodec(FermentationData.ENDEC)).fieldOf("fermentation").forGetter(c -> c.fermentation),
LootNumberProviderTypes.CODEC.fieldOf("days_fermented").forGetter(c -> c.daysFermented),
LootNumberProviderTypes.CODEC.fieldOf("days_fermented").forGetter(c -> c.thickness)
)).apply(i, FermentRandomlyLootFunction::new));

public FermentRandomlyLootFunction(List<LootCondition> conditions, @NotNull Identifier fermentationRecipeIdentifier, LootNumberProvider daysFermented, LootNumberProvider thickness) {
private final Either<Identifier, FermentationData> fermentation;
private final LootNumberProvider daysFermented;
private final LootNumberProvider thickness;

public FermentRandomlyLootFunction(List<LootCondition> conditions, Either<Identifier, FermentationData> fermentation, LootNumberProvider daysFermented, LootNumberProvider thickness) {
super(conditions);
this.fermentationRecipeIdentifier = fermentationRecipeIdentifier;
this.fermentationData = null;
this.fermentation = fermentation;
this.daysFermented = daysFermented;
this.thickness = thickness;
}

public FermentRandomlyLootFunction(List<LootCondition> conditions, @NotNull Identifier fermentationRecipeIdentifier, LootNumberProvider daysFermented, LootNumberProvider thickness) {
this(conditions, Either.left(fermentationRecipeIdentifier), daysFermented, thickness);
}

public FermentRandomlyLootFunction(List<LootCondition> conditions, @NotNull FermentationData fermentationData, LootNumberProvider daysFermented, LootNumberProvider thickness) {
super(conditions);
this.fermentationRecipeIdentifier = null;
this.fermentationData = fermentationData;
this.daysFermented = daysFermented;
this.thickness = thickness;
this(conditions, Either.right(fermentationData), daysFermented, thickness);
}

@Override
public LootFunctionType getType() {
public LootFunctionType<? extends ConditionalLootFunction> getType() {
return SpectrumLootFunctionTypes.FERMENT_RANDOMLY;
}

@Override
public ItemStack process(ItemStack stack, LootContext context) {
FermentationData fermentationData = null;
if (this.fermentationRecipeIdentifier != null) {
Optional<? extends RecipeEntry<?>> recipe = SpectrumCommon.minecraftServer.getRecipeManager().get(this.fermentationRecipeIdentifier);
if (recipe.isPresent() && recipe.get().value() instanceof TitrationBarrelRecipe titrationBarrelRecipe) {
fermentationData = titrationBarrelRecipe.getFermentationData();
FermentationData fermentationData = this.fermentation.map(
id -> {
var recipe = context.getWorld().getRecipeManager().get(id);
if (recipe.isPresent() && recipe.get().value() instanceof TitrationBarrelRecipe titrationBarrelRecipe) {
return titrationBarrelRecipe.getFermentationData();
} else {
SpectrumCommon.logError("A 'spectrum:ferment_randomly' loot function has set an invalid 'fermentation_recipe_id': " + id + " It has to match an existing Titration Barrel recipe.");
return null;
}
},
data -> this.fermentation.right().orElse(null)
);
if (fermentationData != null) {
var origin = context.get(LootContextParameters.ORIGIN);
if (origin != null) {
BlockPos pos = BlockPos.ofFloored(origin);
Biome biome = context.getWorld().getBiome(pos).value();
float downfall = ((BiomeAccessor)(Object) biome).getWeather().downfall();
return TitrationBarrelRecipe.getFermentedStack(fermentationData, this.thickness.nextInt(context), TimeHelper.secondsFromMinecraftDays(this.daysFermented.nextInt(context)), downfall, stack);
} else {
SpectrumCommon.logError("A 'spectrum:ferment_randomly' loot function has set an invalid 'fermentation_recipe_id': " + this.fermentationRecipeIdentifier + " It has to match an existing Titration Barrel recipe.");
SpectrumCommon.logError("A 'spectrum:ferment_randomly' loot function does not have access to 'origin'.");
}
}
if (fermentationData == null) {
fermentationData = this.fermentationData;
}
if (fermentationData != null) {
BlockPos pos = BlockPos.ofFloored(context.get(LootContextParameters.ORIGIN));
Biome biome = context.getWorld().getBiome(pos).value();
float downfall = ((BiomeAccessor)(Object) biome).getWeather().downfall();
return TitrationBarrelRecipe.getFermentedStack(fermentationData, this.thickness.nextInt(context), TimeHelper.secondsFromMinecraftDays(this.daysFermented.nextInt(context)), downfall, stack);
}
return stack;
}

Expand All @@ -78,40 +89,4 @@ public static ConditionalLootFunction.Builder<?> builder(Identifier fermentation
return builder((conditions) -> new FermentRandomlyLootFunction(conditions, fermentationRecipeIdentifier, daysFermented, thickness));
}

public static class Serializer extends ConditionalLootFunction.Serializer<FermentRandomlyLootFunction> {

private static final String FERMENTATION_RECIPE_ID_STRING = "fermentation_recipe_id";
private static final String FERMENTATION_DATA_STRING = "fermentation_data";
private static final String DAYS_FERMENTED_STRING = "days_fermented";
private static final String THICKNESS_STRING = "thickness";

@Override
public void toJson(JsonObject jsonObject, FermentRandomlyLootFunction lootFunction, JsonSerializationContext jsonSerializationContext) {
super.toJson(jsonObject, lootFunction, jsonSerializationContext);

if (lootFunction.fermentationRecipeIdentifier != null) {
jsonObject.addProperty(FERMENTATION_RECIPE_ID_STRING, lootFunction.fermentationRecipeIdentifier.toString());
} else {
jsonObject.add(FERMENTATION_DATA_STRING, lootFunction.fermentationData.toJson());
}
jsonObject.add(DAYS_FERMENTED_STRING, jsonSerializationContext.serialize(lootFunction.daysFermented));
jsonObject.add(THICKNESS_STRING, jsonSerializationContext.serialize(lootFunction.thickness));
}

@Override
public FermentRandomlyLootFunction fromJson(JsonObject jsonObject, JsonDeserializationContext jsonDeserializationContext, LootCondition[] lootConditions) {
LootNumberProvider daysFermented = JsonHelper.deserialize(jsonObject, DAYS_FERMENTED_STRING, jsonDeserializationContext, LootNumberProvider.class);
LootNumberProvider thickness = JsonHelper.deserialize(jsonObject, THICKNESS_STRING, jsonDeserializationContext, LootNumberProvider.class);

if (jsonObject.has(FERMENTATION_RECIPE_ID_STRING)) {
Identifier fermentationRecipeIdentifier = Identifier.tryParse(jsonObject.get(FERMENTATION_RECIPE_ID_STRING).getAsString());
return new FermentRandomlyLootFunction(lootConditions, fermentationRecipeIdentifier, daysFermented, thickness);
} else if (jsonObject.has(FERMENTATION_DATA_STRING)) {
FermentationData fermentationData = FermentationData.fromJson(jsonObject.get(FERMENTATION_DATA_STRING).getAsJsonObject());
return new FermentRandomlyLootFunction(lootConditions, fermentationData, daysFermented, thickness);
}

throw new JsonParseException("A 'ferment_randomly' loot function always needs to have either 'fermentation_data' or 'fermentation_recipe_id' set.");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package de.dafuqs.spectrum.loot.functions;

import com.google.gson.*;
import com.mojang.serialization.*;
import com.mojang.serialization.codecs.*;
import de.dafuqs.spectrum.api.energy.*;
import de.dafuqs.spectrum.api.energy.color.*;
import de.dafuqs.spectrum.api.item.*;
import de.dafuqs.spectrum.helpers.*;
import de.dafuqs.spectrum.loot.*;
import net.minecraft.entity.effect.*;
import net.minecraft.item.*;
Expand All @@ -13,72 +15,33 @@
import net.minecraft.loot.provider.number.*;
import net.minecraft.registry.*;
import net.minecraft.registry.entry.*;
import net.minecraft.util.*;

import java.util.*;

public class FillPotionFillableLootFunction extends ConditionalLootFunction {

record InkPoweredPotionTemplate(boolean ambient, boolean showParticles, LootNumberProvider duration,
List<RegistryEntry<StatusEffect>> statusEffects, int color, LootNumberProvider amplifier,
List<InkColor> inkColors, LootNumberProvider inkCost, boolean unidentifiable, boolean incurable) {

public static InkPoweredPotionTemplate fromJson(JsonObject jsonObject, JsonDeserializationContext jsonDeserializationContext) {
boolean ambient = JsonHelper.getBoolean(jsonObject, "ambient", false);
boolean showParticles = JsonHelper.getBoolean(jsonObject, "show_particles", false);
boolean unidentifiable = JsonHelper.getBoolean(jsonObject, "unidentifiable", false);
boolean incurable = JsonHelper.getBoolean(jsonObject, "incurable", false);
LootNumberProvider duration = JsonHelper.deserialize(jsonObject, "duration", jsonDeserializationContext, LootNumberProvider.class);
Set<RegistryEntry<StatusEffect>> statusEffects = new HashSet<>();
JsonElement statusEffectElement = jsonObject.get("status_effect");
if (statusEffectElement instanceof JsonArray jsonArray) {
for (JsonElement element : jsonArray) {
statusEffects.add(Registries.STATUS_EFFECT.getEntry(Identifier.tryParse(element.getAsString())));
}
} else {
statusEffects.add(Registries.STATUS_EFFECT.getEntry(Identifier.tryParse(statusEffectElement.getAsString())));
}

int color = JsonHelper.getInt(jsonObject, "color", -1);
LootNumberProvider amplifier = JsonHelper.deserialize(jsonObject, "amplifier", jsonDeserializationContext, LootNumberProvider.class);
LootNumberProvider inkCost = JsonHelper.deserialize(jsonObject, "ink_cost", jsonDeserializationContext, LootNumberProvider.class);

Set<InkColor> inkColors = new HashSet<>();
JsonElement colorElement = jsonObject.get("ink_color");
if (colorElement instanceof JsonArray jsonArray) {
for (JsonElement element : jsonArray) {
String s = element.getAsString();
inkColors.add(InkColor.ofIdString(s).orElseThrow());
}
} else {
String s = colorElement.getAsString();
inkColors.add(InkColor.ofIdString(s).orElseThrow());
}

return new InkPoweredPotionTemplate(ambient, showParticles, duration, statusEffects.stream().toList(), color, amplifier, inkColors.stream().toList(), inkCost, unidentifiable, incurable);
}
public static final MapCodec<FillPotionFillableLootFunction> CODEC = RecordCodecBuilder.mapCodec(i -> addConditionsField(i).and(
InkPoweredPotionTemplate.CODEC.forGetter(c -> c.template)
).apply(i, FillPotionFillableLootFunction::new));

public record InkPoweredPotionTemplate(
boolean ambient, boolean showParticles, LootNumberProvider duration,
List<RegistryEntry<StatusEffect>> statusEffects, int color, LootNumberProvider amplifier,
List<InkColor> inkColors, LootNumberProvider inkCost, boolean unidentifiable, boolean incurable
) {

public void toJson(JsonObject jsonObject, JsonSerializationContext jsonSerializationContext) {
jsonObject.addProperty("ambient", this.ambient);
jsonObject.addProperty("show_particles", this.showParticles);
jsonObject.add("duration", jsonSerializationContext.serialize(this.duration));
JsonArray statusEffectArray = new JsonArray();
for (RegistryEntry<StatusEffect> statusEffect : this.statusEffects) {
statusEffectArray.add(Registries.STATUS_EFFECT.getId(statusEffect.value()).toString());
}
jsonObject.add("status_effect", statusEffectArray);
jsonObject.addProperty("color", this.color);
jsonObject.addProperty("unidentifiable", this.unidentifiable);
jsonObject.addProperty("incurable", this.incurable);
jsonObject.add("amplifier", jsonSerializationContext.serialize(this.amplifier));
jsonObject.add("ink_cost", jsonSerializationContext.serialize(this.inkCost));

JsonArray inkColorArray = new JsonArray();
for (InkColor inkColor : this.inkColors) {
inkColorArray.add(inkColor.getID().toString());
}
jsonObject.add("ink_color", inkColorArray);
}
public static final MapCodec<InkPoweredPotionTemplate> CODEC = RecordCodecBuilder.mapCodec(i -> i.group(
Codec.BOOL.optionalFieldOf("ambient", false).forGetter(InkPoweredPotionTemplate::ambient),
Codec.BOOL.optionalFieldOf("show_particles", false).forGetter(InkPoweredPotionTemplate::showParticles),
LootNumberProviderTypes.CODEC.fieldOf("duration").forGetter(InkPoweredPotionTemplate::duration),
CodecHelper.singleOrList(Registries.STATUS_EFFECT.getEntryCodec()).fieldOf("status_effect").forGetter(InkPoweredPotionTemplate::statusEffects),
Codec.INT.optionalFieldOf("color", -1).forGetter(InkPoweredPotionTemplate::color),
LootNumberProviderTypes.CODEC.fieldOf("amplifier").forGetter(InkPoweredPotionTemplate::amplifier),
CodecHelper.singleOrList(InkColor.CODEC).fieldOf("ink_color").forGetter(InkPoweredPotionTemplate::inkColors),
LootNumberProviderTypes.CODEC.fieldOf("ink_cost").forGetter(InkPoweredPotionTemplate::inkCost),
Codec.BOOL.optionalFieldOf("unidentifiable", false).forGetter(InkPoweredPotionTemplate::unidentifiable),
Codec.BOOL.optionalFieldOf("incurable", false).forGetter(InkPoweredPotionTemplate::incurable)
).apply(i, InkPoweredPotionTemplate::new));

public InkPoweredStatusEffectInstance get(LootContext context) {
RegistryEntry<StatusEffect> statusEffect = this.statusEffects.get(context.getRandom().nextInt(this.statusEffects.size()));
Expand All @@ -90,29 +53,28 @@ public InkPoweredStatusEffectInstance get(LootContext context) {

}

final InkPoweredPotionTemplate template;
private final InkPoweredPotionTemplate template;

FillPotionFillableLootFunction(List<LootCondition> conditions, InkPoweredPotionTemplate template) {
super(conditions);
this.template = template;
}

@Override
public LootFunctionType getType() {
public LootFunctionType<? extends ConditionalLootFunction> getType() {
return SpectrumLootFunctionTypes.FILL_POTION_FILLABLE;
}

@Override
public ItemStack process(ItemStack stack, LootContext context) {
if (this.template == null) {
if (this.template == null)
return stack;
}
if (!(stack.getItem() instanceof InkPoweredPotionFillable inkPoweredPotionFillable)) {

if (!(stack.getItem() instanceof InkPoweredPotionFillable inkPoweredPotionFillable))
return stack;
}
if (inkPoweredPotionFillable.isFull(stack)) {

if (inkPoweredPotionFillable.isFull(stack))
return stack;
}

InkPoweredStatusEffectInstance effect = template.get(context);
inkPoweredPotionFillable.addOrUpgradeEffects(stack, List.of(effect));
Expand All @@ -124,18 +86,4 @@ public static ConditionalLootFunction.Builder<?> builder(InkPoweredPotionTemplat
return builder((conditions) -> new FillPotionFillableLootFunction(conditions, template));
}

public static class Serializer extends ConditionalLootFunction.Serializer<FillPotionFillableLootFunction> {

@Override
public void toJson(JsonObject jsonObject, FillPotionFillableLootFunction lootFunction, JsonSerializationContext jsonSerializationContext) {
super.toJson(jsonObject, lootFunction, jsonSerializationContext);
lootFunction.template.toJson(jsonObject, jsonSerializationContext);
}

@Override
public FillPotionFillableLootFunction fromJson(JsonObject jsonObject, JsonDeserializationContext jsonDeserializationContext, LootCondition[] lootConditions) {
return new FillPotionFillableLootFunction(lootConditions, InkPoweredPotionTemplate.fromJson(jsonObject, jsonDeserializationContext));
}
}

}
Loading

0 comments on commit a9e65c7

Please sign in to comment.