CreateMod/src/main/java/com/simibubi/create/content/processing/sequenced/SequencedAssemblyRecipe.java
2023-05-21 21:48:39 +02:00

282 lines
8.9 KiB
Java

package com.simibubi.create.content.processing.sequenced;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.Create;
import com.simibubi.create.content.processing.recipe.ProcessingOutput;
import com.simibubi.create.content.processing.recipe.ProcessingRecipe;
import com.simibubi.create.foundation.fluid.FluidIngredient;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.Container;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.entity.player.ItemTooltipEvent;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.wrapper.RecipeWrapper;
public class SequencedAssemblyRecipe implements Recipe<RecipeWrapper> {
protected ResourceLocation id;
protected SequencedAssemblyRecipeSerializer serializer;
protected Ingredient ingredient;
protected List<SequencedRecipe<?>> sequence;
protected int loops;
protected ProcessingOutput transitionalItem;
public final List<ProcessingOutput> resultPool;
public SequencedAssemblyRecipe(ResourceLocation recipeId, SequencedAssemblyRecipeSerializer serializer) {
this.id = recipeId;
this.serializer = serializer;
sequence = new ArrayList<>();
resultPool = new ArrayList<>();
loops = 5;
}
public static <C extends Container, R extends ProcessingRecipe<C>> Optional<R> getRecipe(Level world, C inv,
RecipeType<R> type, Class<R> recipeClass) {
//return getRecipe(world, inv.getStackInSlot(0), type, recipeClass).filter(r -> r.matches(inv, world));
return getRecipes(world, inv.getItem(0), type, recipeClass).filter(r -> r.matches(inv, world))
.findFirst();
}
public static <R extends ProcessingRecipe<?>> Optional<R> getRecipe(Level world, ItemStack item,
RecipeType<R> type, Class<R> recipeClass) {
List<SequencedAssemblyRecipe> all = world.getRecipeManager()
.<RecipeWrapper, SequencedAssemblyRecipe>getAllRecipesFor(AllRecipeTypes.SEQUENCED_ASSEMBLY.getType());
for (SequencedAssemblyRecipe sequencedAssemblyRecipe : all) {
if (!sequencedAssemblyRecipe.appliesTo(item))
continue;
SequencedRecipe<?> nextRecipe = sequencedAssemblyRecipe.getNextRecipe(item);
ProcessingRecipe<?> recipe = nextRecipe.getRecipe();
if (recipe.getType() != type || !recipeClass.isInstance(recipe))
continue;
recipe.enforceNextResult(() -> sequencedAssemblyRecipe.advance(item));
return Optional.of(recipeClass.cast(recipe));
}
return Optional.empty();
}
public static <R extends ProcessingRecipe<?>> Stream<R> getRecipes(Level world, ItemStack item,
RecipeType<R> type, Class<R> recipeClass) {
List<SequencedAssemblyRecipe> all = world.getRecipeManager()
.<RecipeWrapper, SequencedAssemblyRecipe>getAllRecipesFor(AllRecipeTypes.SEQUENCED_ASSEMBLY.getType());
return all.stream()
.filter(it -> it.appliesTo(item))
.map(it -> Pair.of(it, it.getNextRecipe(item).getRecipe()))
.filter(it -> it.getSecond()
.getType() == type && recipeClass.isInstance(it.getSecond()))
.map(it -> {
it.getSecond()
.enforceNextResult(() -> it.getFirst().advance(item));
return it.getSecond();
})
.map(recipeClass::cast);
}
private ItemStack advance(ItemStack input) {
int step = getStep(input);
if ((step + 1) / sequence.size() >= loops)
return rollResult();
ItemStack advancedItem = ItemHandlerHelper.copyStackWithSize(getTransitionalItem(), 1);
CompoundTag itemTag = advancedItem.getOrCreateTag();
CompoundTag tag = new CompoundTag();
tag.putString("id", id.toString());
tag.putInt("Step", step + 1);
tag.putFloat("Progress", (step + 1f) / (sequence.size() * loops));
itemTag.put("SequencedAssembly", tag);
advancedItem.setTag(itemTag);
return advancedItem;
}
public int getLoops() {
return loops;
}
public void addAdditionalIngredientsAndMachines(List<Ingredient> list) {
sequence.forEach(sr -> sr.getAsAssemblyRecipe()
.addAssemblyIngredients(list));
Set<ItemLike> machines = new HashSet<>();
sequence.forEach(sr -> sr.getAsAssemblyRecipe()
.addRequiredMachines(machines));
machines.stream()
.map(Ingredient::of)
.forEach(list::add);
}
public void addAdditionalFluidIngredients(List<FluidIngredient> list) {
sequence.forEach(sr -> sr.getAsAssemblyRecipe()
.addAssemblyFluidIngredients(list));
}
private ItemStack rollResult() {
float totalWeight = 0;
for (ProcessingOutput entry : resultPool)
totalWeight += entry.getChance();
float number = Create.RANDOM.nextFloat() * totalWeight;
for (ProcessingOutput entry : resultPool) {
number -= entry.getChance();
if (number < 0)
return entry.getStack()
.copy();
}
return ItemStack.EMPTY;
}
private boolean appliesTo(ItemStack input) {
if (ingredient.test(input))
return true;
return input.hasTag() && getTransitionalItem().getItem() == input.getItem() && input.getTag()
.contains("SequencedAssembly") && input.getTag()
.getCompound("SequencedAssembly")
.getString("id")
.equals(id.toString());
}
private SequencedRecipe<?> getNextRecipe(ItemStack input) {
return sequence.get(getStep(input) % sequence.size());
}
private int getStep(ItemStack input) {
if (!input.hasTag())
return 0;
CompoundTag tag = input.getTag();
if (!tag.contains("SequencedAssembly"))
return 0;
int step = tag.getCompound("SequencedAssembly")
.getInt("Step");
return step;
}
@Override
public boolean matches(RecipeWrapper inv, Level p_77569_2_) {
return false;
}
@Override
public ItemStack assemble(RecipeWrapper p_77572_1_) {
return ItemStack.EMPTY;
}
@Override
public boolean canCraftInDimensions(int p_194133_1_, int p_194133_2_) {
return false;
}
@Override
public ItemStack getResultItem() {
return resultPool.get(0)
.getStack();
}
public float getOutputChance() {
float totalWeight = 0;
for (ProcessingOutput entry : resultPool)
totalWeight += entry.getChance();
return resultPool.get(0)
.getChance() / totalWeight;
}
@Override
public ResourceLocation getId() {
return id;
}
@Override
public RecipeSerializer<?> getSerializer() {
return serializer;
}
@Override
public boolean isSpecial() {
return true;
}
@Override
public RecipeType<?> getType() {
return AllRecipeTypes.SEQUENCED_ASSEMBLY.getType();
}
@OnlyIn(Dist.CLIENT)
public static void addToTooltip(ItemTooltipEvent event) {
ItemStack stack = event.getItemStack();
if (!stack.hasTag() || !stack.getTag()
.contains("SequencedAssembly"))
return;
CompoundTag compound = stack.getTag()
.getCompound("SequencedAssembly");
ResourceLocation resourceLocation = new ResourceLocation(compound.getString("id"));
Optional<? extends Recipe<?>> optionalRecipe = Minecraft.getInstance().level.getRecipeManager()
.byKey(resourceLocation);
if (!optionalRecipe.isPresent())
return;
Recipe<?> recipe = optionalRecipe.get();
if (!(recipe instanceof SequencedAssemblyRecipe))
return;
SequencedAssemblyRecipe sequencedAssemblyRecipe = (SequencedAssemblyRecipe) recipe;
int length = sequencedAssemblyRecipe.sequence.size();
int step = sequencedAssemblyRecipe.getStep(stack);
int total = length * sequencedAssemblyRecipe.loops;
List<Component> tooltip = event.getToolTip();
tooltip.add(Components.immutableEmpty());
tooltip.add(Lang.translateDirect("recipe.sequenced_assembly")
.withStyle(ChatFormatting.GRAY));
tooltip.add(Lang.translateDirect("recipe.assembly.progress", step, total)
.withStyle(ChatFormatting.DARK_GRAY));
int remaining = total - step;
for (int i = 0; i < length; i++) {
if (i >= remaining)
break;
SequencedRecipe<?> sequencedRecipe = sequencedAssemblyRecipe.sequence.get((i + step) % length);
Component textComponent = sequencedRecipe.getAsAssemblyRecipe()
.getDescriptionForAssembly();
if (i == 0)
tooltip.add(Lang.translateDirect("recipe.assembly.next", textComponent)
.withStyle(ChatFormatting.AQUA));
else
tooltip.add(Components.literal("-> ").append(textComponent)
.withStyle(ChatFormatting.DARK_AQUA));
}
}
public Ingredient getIngredient() {
return ingredient;
}
public List<SequencedRecipe<?>> getSequence() {
return sequence;
}
public ItemStack getTransitionalItem() {
return transitionalItem.getStack();
}
}