Skip to content

Commit

Permalink
Allow tool "composite part casting" provided the fluid is not also a …
Browse files Browse the repository at this point in the history
…material casting fluid
  • Loading branch information
KnightMiner committed May 19, 2024
1 parent dab6934 commit c1402da
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
import slimeknights.tconstruct.library.recipe.casting.ICastingContainer;
import slimeknights.tconstruct.library.recipe.casting.ICastingRecipe;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
Expand All @@ -25,7 +25,8 @@ public abstract class AbstractMaterialCastingRecipe extends AbstractCastingRecip
@Getter
private final RecipeSerializer<?> serializer;
protected final int itemCost;
protected Optional<MaterialFluidRecipe> cachedFluidRecipe = Optional.empty();
@Nullable
protected MaterialFluidRecipe cachedFluidRecipe = null;

public AbstractMaterialCastingRecipe(TypeAwareRecipeSerializer<?> serializer, ResourceLocation id, String group, Ingredient cast, int itemCost, boolean consumed, boolean switchSlots) {
super(serializer.getType(), id, group, cast, consumed, switchSlots);
Expand All @@ -34,16 +35,18 @@ public AbstractMaterialCastingRecipe(TypeAwareRecipeSerializer<?> serializer, Re
}

/** Gets the material fluid recipe for the given recipe */
protected Optional<MaterialFluidRecipe> getMaterialFluid(ICastingContainer inv) {
@Nullable
protected MaterialFluidRecipe getMaterialFluid(ICastingContainer inv) {
return MaterialCastingLookup.getCastingFluid(inv);
}

/** Gets the cached fluid recipe if it still matches, refetches if not */
protected Optional<MaterialFluidRecipe> getCachedMaterialFluid(ICastingContainer inv) {
Optional<MaterialFluidRecipe> fluidRecipe = cachedFluidRecipe;
if (fluidRecipe.filter(recipe -> recipe.matches(inv)).isEmpty()) {
@Nullable
protected MaterialFluidRecipe getCachedMaterialFluid(ICastingContainer inv) {
MaterialFluidRecipe fluidRecipe = cachedFluidRecipe;
if (fluidRecipe == null || !fluidRecipe.matches(inv)) {
fluidRecipe = getMaterialFluid(inv);
if (fluidRecipe.isPresent()) {
if (fluidRecipe != null) {
cachedFluidRecipe = fluidRecipe;
}
}
Expand All @@ -52,16 +55,20 @@ protected Optional<MaterialFluidRecipe> getCachedMaterialFluid(ICastingContainer

@Override
public int getCoolingTime(ICastingContainer inv) {
return getCachedMaterialFluid(inv)
.map(recipe -> ICastingRecipe.calcCoolingTime(recipe.getTemperature(), recipe.getFluidAmount(inv.getFluid()) * itemCost))
.orElse(1);
MaterialFluidRecipe recipe = getCachedMaterialFluid(inv);
if (recipe != null) {
return ICastingRecipe.calcCoolingTime(recipe.getTemperature(), recipe.getFluidAmount(inv.getFluid()) * itemCost);
}
return 1;
}

@Override
public int getFluidAmount(ICastingContainer inv) {
return getCachedMaterialFluid(inv)
.map(recipe -> recipe.getFluidAmount(inv.getFluid()))
.orElse(1) * this.itemCost;
MaterialFluidRecipe recipe = getCachedMaterialFluid(inv);
if (recipe != null) {
return recipe.getFluidAmount(inv.getFluid()) * itemCost;
}
return 1;
}

/** Resizes the list of the fluids with respect to the item cost */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
Expand All @@ -35,7 +34,7 @@ public CompositeCastingRecipe(TypeAwareRecipeSerializer<?> serializer, ResourceL
}

@Override
protected Optional<MaterialFluidRecipe> getMaterialFluid(ICastingContainer inv) {
protected MaterialFluidRecipe getMaterialFluid(ICastingContainer inv) {
return MaterialCastingLookup.getCompositeFluid(inv);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
import slimeknights.tconstruct.library.recipe.casting.ICastingContainer;
import slimeknights.tconstruct.library.tools.part.IMaterialItem;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -99,28 +99,30 @@ public static Collection<Entry<IMaterialItem>> getAllItemCosts() {
* @param inventory Inventory
* @return Recipe
*/
public static Optional<MaterialFluidRecipe> getCastingFluid(ICastingContainer inventory) {
// TODO: reconsider cache
@Nullable
public static MaterialFluidRecipe getCastingFluid(ICastingContainer inventory) {
// TODO: if we wished, we could turn this into a map from fluid to recipe
for (MaterialFluidRecipe recipe : CASTING_FLUIDS) {
if (recipe.matches(inventory)) {
return Optional.of(recipe);
return recipe;
}
}
return Optional.empty();
return null;
}

/**
* Gets the composite fluid recipe for the given inventory
* @param inventory Inventory
* @return Composite fluid recipe
*/
public static Optional<MaterialFluidRecipe> getCompositeFluid(ICastingContainer inventory) {
@Nullable
public static MaterialFluidRecipe getCompositeFluid(ICastingContainer inventory) {
for (MaterialFluidRecipe recipe : COMPOSITE_FLUIDS) {
if (recipe.matches(inventory)) {
return Optional.of(recipe);
return recipe;
}
}
return Optional.empty();
return null;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import slimeknights.mantle.recipe.helper.LoadableRecipeSerializer;
import slimeknights.mantle.recipe.helper.TypeAwareRecipeSerializer;
import slimeknights.tconstruct.library.json.TinkerLoadables;
import slimeknights.tconstruct.library.materials.definition.IMaterial;
import slimeknights.tconstruct.library.materials.definition.MaterialVariant;
import slimeknights.tconstruct.library.recipe.casting.DisplayCastingRecipe;
import slimeknights.tconstruct.library.recipe.casting.ICastingContainer;
Expand Down Expand Up @@ -48,7 +49,8 @@ public boolean matches(ICastingContainer inv, Level worldIn) {
if (!this.getCast().test(inv.getStack())) {
return false;
}
return getCachedMaterialFluid(inv).filter(recipe -> result.canUseMaterial(recipe.getOutput().getId())).isPresent();
MaterialFluidRecipe fluid = getCachedMaterialFluid(inv);
return fluid != null && result.canUseMaterial(fluid.getOutput().getId());
}

@Override
Expand All @@ -58,8 +60,8 @@ public ItemStack getResultItem() {

@Override
public ItemStack assemble(ICastingContainer inv) {
MaterialVariant material = getCachedMaterialFluid(inv).map(MaterialFluidRecipe::getOutput).orElse(MaterialVariant.UNKNOWN);
return result.withMaterial(material.getVariant());
MaterialFluidRecipe fluid = getCachedMaterialFluid(inv);
return result.withMaterial(fluid != null ? fluid.getOutput().getVariant() : IMaterial.UNKNOWN_ID);
}

/* JEI display */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,17 @@ public MaterialFluidRecipe(ResourceLocation id, FluidIngredient fluid, int tempe
MaterialCastingLookup.registerFluid(this);
}

/** Checks if the recipe matches the given inventory */
/** Checks if the recipe matches the given inventory, faster than {@link #matches(Fluid, MaterialVariantId)} when you do not already have the material. */
public boolean matches(ICastingContainer inv) {
if (output.isUnknown() || !fluid.test(inv.getFluid())) {
return false;
}
if (input != null) {
// if the input ID is null, want to avoid checking this
// not null means we should have a material and it failed to find
if (input.isUnknown()) {
return false;
}
return input.matchesVariant(inv.getStack());
}
return true;
return !output.isUnknown() && this.fluid.test(inv.getFluid())
// if the input ID is null, can skip fetching the input stack material
&& (input == null || input.matchesVariant(inv.getStack()));
}

/** Checks if this recipe is valid for the given fluid and material */
public boolean matches(Fluid fluid, MaterialVariantId material) {
return !output.isUnknown() && this.fluid.test(fluid)
&& (input == null || input.matchesVariant(material));
}

/** Gets the amount of fluid to cast this recipe */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.fluids.FluidStack;
import slimeknights.mantle.data.loadable.field.ContextKey;
import slimeknights.mantle.data.loadable.record.RecordLoadable;
Expand All @@ -14,6 +15,7 @@
import slimeknights.tconstruct.library.json.TinkerLoadables;
import slimeknights.tconstruct.library.materials.MaterialRegistry;
import slimeknights.tconstruct.library.materials.definition.MaterialVariant;
import slimeknights.tconstruct.library.materials.definition.MaterialVariantId;
import slimeknights.tconstruct.library.materials.stats.MaterialStatsId;
import slimeknights.tconstruct.library.recipe.casting.DisplayCastingRecipe;
import slimeknights.tconstruct.library.recipe.casting.ICastingContainer;
Expand All @@ -22,10 +24,12 @@
import slimeknights.tconstruct.library.tools.definition.module.material.ToolMaterialHook;
import slimeknights.tconstruct.library.tools.helper.ToolBuildHandler;
import slimeknights.tconstruct.library.tools.item.IModifiable;
import slimeknights.tconstruct.library.tools.nbt.MaterialIdNBT;
import slimeknights.tconstruct.library.tools.nbt.MaterialNBT;
import slimeknights.tconstruct.library.tools.nbt.ToolStack;
import slimeknights.tconstruct.library.tools.part.IMaterialItem;

import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
Expand All @@ -40,11 +44,51 @@ public class ToolCastingRecipe extends AbstractMaterialCastingRecipe implements
ToolCastingRecipe::new);

private final IModifiable result;
/** Last composite casting recipe to match, speeds up recipe lookup for cooling time and fluid amount */
@Nullable
private MaterialFluidRecipe cachedPartSwapping = null;
public ToolCastingRecipe(TypeAwareRecipeSerializer<?> serializer, ResourceLocation id, String group, Ingredient cast, int itemCost, IModifiable result) {
super(serializer, id, group, cast, itemCost, true, false);
this.result = result;
}

@Override
@Nullable
protected MaterialFluidRecipe getCachedMaterialFluid(ICastingContainer inv) {
ItemStack stack = inv.getStack();
// if its not part swapping, super is sufficient
if (stack.getItem() != result.asItem()) {
return super.getCachedMaterialFluid(inv);
}
// so we are part swapping, we might have a casting or a composite recipe. We only do composite if the fluid does not match casting
// start with the cached part swapping, can be either type. No need to check casting stat type here as it would never get cached if invalid
Fluid fluid = inv.getFluid();
List<MaterialStatsId> requirements = ToolMaterialHook.stats(result.getToolDefinition());
int indexToCheck = requirements.size() - 1;
MaterialVariantId currentMaterial = MaterialIdNBT.from(stack).getMaterial(indexToCheck);
if (cachedPartSwapping != null && cachedPartSwapping.matches(fluid, currentMaterial)) {
return cachedPartSwapping;
}
// cache did not match? try a casting recipe.
// note its possible we have a valid casting material that is just not valid for this tool, hence the extra check
// the casting recipe needs to match our stat type to be valid
MaterialFluidRecipe casting = super.getCachedMaterialFluid(inv);
// need to validate the stat type, since the super call will not check stat type
if (casting != null && requirements.get(indexToCheck).canUseMaterial(casting.getOutput().getId())) {
cachedPartSwapping = casting;
return casting;
}
// no casting? try composite.
// No need to validate stat type here (matches will handle it). we only check above to know if we can skip casting
for (MaterialFluidRecipe composite : MaterialCastingLookup.getAllCompositeFluids()) {
if (composite.matches(fluid, currentMaterial)) {
cachedPartSwapping = composite;
return composite;
}
}
return null;
}

@Override
public boolean matches(ICastingContainer inv, Level level) {
ItemStack cast = inv.getStack();
Expand All @@ -61,9 +105,8 @@ public boolean matches(ICastingContainer inv, Level level) {
return false;
}
// last material is the part, may be 1 or 2
MaterialStatsId requirement = requirements.get(numRequirements - 1);
return getCachedMaterialFluid(inv).filter(recipe -> requirement.canUseMaterial(recipe.getOutput().getId())).isPresent();

MaterialFluidRecipe recipe = getCachedMaterialFluid(inv);
return recipe != null && requirements.get(numRequirements - 1).canUseMaterial(recipe.getOutput().getId());
}

@Override
Expand All @@ -73,7 +116,8 @@ public ItemStack getResultItem() {

@Override
public ItemStack assemble(ICastingContainer inv) {
MaterialVariant material = getCachedMaterialFluid(inv).map(MaterialFluidRecipe::getOutput).orElse(MaterialVariant.UNKNOWN);
MaterialFluidRecipe fluid = getCachedMaterialFluid(inv);
MaterialVariant material = fluid != null ? fluid.getOutput() : MaterialVariant.UNKNOWN;
ItemStack cast = inv.getStack();
int requirements = ToolMaterialHook.stats(result.getToolDefinition()).size();
// if the cast is the result, we are part swapping, replace the last material
Expand Down

0 comments on commit c1402da

Please sign in to comment.