-
Notifications
You must be signed in to change notification settings - Fork 187
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fluid crafting table recipes (#2152)
- Loading branch information
Showing
15 changed files
with
546 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
src/main/java/com/gregtechceu/gtceu/api/recipe/ShapedFluidContainerRecipe.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
package com.gregtechceu.gtceu.api.recipe; | ||
|
||
import com.gregtechceu.gtceu.api.recipe.ingredient.FluidContainerIngredient; | ||
import com.gregtechceu.gtceu.core.mixins.ShapedRecipeAccessor; | ||
|
||
import net.minecraft.core.NonNullList; | ||
import net.minecraft.network.FriendlyByteBuf; | ||
import net.minecraft.resources.ResourceLocation; | ||
import net.minecraft.util.GsonHelper; | ||
import net.minecraft.world.inventory.CraftingContainer; | ||
import net.minecraft.world.item.ItemStack; | ||
import net.minecraft.world.item.crafting.CraftingBookCategory; | ||
import net.minecraft.world.item.crafting.Ingredient; | ||
import net.minecraft.world.item.crafting.RecipeSerializer; | ||
import net.minecraft.world.item.crafting.ShapedRecipe; | ||
|
||
import com.google.gson.JsonObject; | ||
import com.mojang.datafixers.util.Pair; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
import java.util.Map; | ||
|
||
// TODO shapeless fluid container recipes | ||
public class ShapedFluidContainerRecipe extends ShapedRecipe { | ||
|
||
public static final RecipeSerializer<ShapedFluidContainerRecipe> SERIALIZER = new Serializer(); | ||
|
||
public ShapedFluidContainerRecipe(ResourceLocation id, String group, CraftingBookCategory category, | ||
int width, int height, | ||
NonNullList<Ingredient> recipeItems, ItemStack result, | ||
boolean showNotification) { | ||
super(id, group, category, width, height, recipeItems, result, showNotification); | ||
} | ||
|
||
public ShapedFluidContainerRecipe(ResourceLocation id, String group, CraftingBookCategory category, | ||
int width, int height, | ||
NonNullList<Ingredient> recipeItems, ItemStack result) { | ||
this(id, group, category, width, height, recipeItems, result, true); | ||
} | ||
|
||
@Override | ||
public @NotNull NonNullList<ItemStack> getRemainingItems(@NotNull CraftingContainer inv) { | ||
NonNullList<ItemStack> items = NonNullList.withSize(inv.getContainerSize(), ItemStack.EMPTY); | ||
|
||
// figure out all the fluid container ingredients' remainders. | ||
int replacedSlot = -1; | ||
OUTER_LOOP: | ||
for (int x = 0; x <= inv.getWidth() - this.getWidth(); ++x) { | ||
for (int y = 0; y <= inv.getHeight() - this.getHeight(); ++y) { | ||
var stack = this.findFluidReplacement(inv, x, y, false); | ||
if (stack.getFirst() != -1) { | ||
items.set(stack.getFirst(), stack.getSecond()); | ||
replacedSlot = stack.getFirst(); | ||
break OUTER_LOOP; | ||
} | ||
|
||
stack = this.findFluidReplacement(inv, x, y, true); | ||
if (stack.getFirst() != -1) { | ||
items.set(stack.getFirst(), stack.getSecond()); | ||
replacedSlot = stack.getFirst(); | ||
break OUTER_LOOP; | ||
} | ||
} | ||
} | ||
|
||
for (int i = 0; i < items.size(); ++i) { | ||
if (i == replacedSlot) { | ||
continue; | ||
} | ||
ItemStack item = inv.getItem(i); | ||
if (item.hasCraftingRemainingItem()) { | ||
items.set(i, item.getCraftingRemainingItem()); | ||
} | ||
} | ||
|
||
return items; | ||
} | ||
|
||
/** | ||
* Checks if the region of a crafting inventory is match for the recipe. | ||
*/ | ||
private Pair<Integer, ItemStack> findFluidReplacement(CraftingContainer inv, int width, int height, | ||
boolean mirrored) { | ||
for (int x = 0; x < inv.getWidth(); ++x) { | ||
for (int y = 0; y < inv.getHeight(); ++y) { | ||
int offsetX = x - width; | ||
int offsetY = y - height; | ||
Ingredient ingredient = Ingredient.EMPTY; | ||
if (offsetX >= 0 && offsetY >= 0 && offsetX < this.getWidth() && offsetY < this.getHeight()) { | ||
if (mirrored) { | ||
ingredient = this.getIngredients() | ||
.get(this.getWidth() - offsetX - 1 + offsetY * this.getWidth()); | ||
} else { | ||
ingredient = this.getIngredients().get(offsetX + offsetY * this.getWidth()); | ||
} | ||
} | ||
|
||
if (ingredient instanceof FluidContainerIngredient fluidContainerIngredient) { | ||
int slot = x + y * inv.getWidth(); | ||
ItemStack stack = inv.getItem(slot); | ||
if (fluidContainerIngredient.test(stack)) { | ||
return Pair.of(slot, fluidContainerIngredient.getExtractedStack(stack)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return Pair.of(-1, ItemStack.EMPTY); | ||
} | ||
|
||
public static class Serializer implements RecipeSerializer<ShapedFluidContainerRecipe> { | ||
|
||
@Override | ||
public ShapedFluidContainerRecipe fromJson(ResourceLocation recipeId, JsonObject json) { | ||
String group = GsonHelper.getAsString(json, "group", ""); | ||
CraftingBookCategory category = CraftingBookCategory.CODEC | ||
.byName(GsonHelper.getAsString(json, "category", null), CraftingBookCategory.MISC); | ||
Map<String, Ingredient> key = ShapedRecipeAccessor.callKeyFromJson(GsonHelper.getAsJsonObject(json, "key")); | ||
String[] pattern = ShapedRecipeAccessor | ||
.callShrink(ShapedRecipeAccessor.callPatternFromJson(GsonHelper.getAsJsonArray(json, "pattern"))); | ||
int xSize = pattern[0].length(); | ||
int ySize = pattern.length; | ||
NonNullList<Ingredient> dissolved = ShapedRecipeAccessor.callDissolvePattern(pattern, key, xSize, ySize); | ||
ItemStack result = ShapedEnergyTransferRecipe.itemStackFromJson(GsonHelper.getAsJsonObject(json, "result")); | ||
boolean showNotification = GsonHelper.getAsBoolean(json, "show_notification", true); | ||
return new ShapedFluidContainerRecipe(recipeId, group, category, | ||
xSize, ySize, | ||
dissolved, result, | ||
showNotification); | ||
} | ||
|
||
@Override | ||
public ShapedFluidContainerRecipe fromNetwork(ResourceLocation recipeId, FriendlyByteBuf buffer) { | ||
int xSize = buffer.readVarInt(); | ||
int ySize = buffer.readVarInt(); | ||
CraftingBookCategory category = buffer.readEnum(CraftingBookCategory.class); | ||
String group = buffer.readUtf(); | ||
NonNullList<Ingredient> ingredients = NonNullList.withSize(xSize * ySize, Ingredient.EMPTY); | ||
ingredients.replaceAll($ -> Ingredient.fromNetwork(buffer)); | ||
ItemStack result = buffer.readItem(); | ||
boolean showNotification = buffer.readBoolean(); | ||
return new ShapedFluidContainerRecipe(recipeId, group, category, | ||
xSize, ySize, | ||
ingredients, result, | ||
showNotification); | ||
} | ||
|
||
@Override | ||
public void toNetwork(FriendlyByteBuf buffer, ShapedFluidContainerRecipe recipe) { | ||
buffer.writeVarInt(recipe.getWidth()); | ||
buffer.writeVarInt(recipe.getHeight()); | ||
buffer.writeEnum(recipe.category()); | ||
buffer.writeUtf(recipe.getGroup()); | ||
for (Ingredient ingredient : recipe.getIngredients()) { | ||
ingredient.toNetwork(buffer); | ||
} | ||
buffer.writeItem(((ShapedRecipeAccessor) recipe).getResult()); | ||
buffer.writeBoolean(recipe.showNotification()); | ||
} | ||
} | ||
} |
146 changes: 146 additions & 0 deletions
146
src/main/java/com/gregtechceu/gtceu/api/recipe/ingredient/FluidContainerIngredient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package com.gregtechceu.gtceu.api.recipe.ingredient; | ||
|
||
import com.gregtechceu.gtceu.GTCEu; | ||
import com.gregtechceu.gtceu.api.data.tag.TagUtil; | ||
import com.gregtechceu.gtceu.utils.InfiniteFluidTransfer; | ||
|
||
import com.lowdragmc.lowdraglib.side.fluid.*; | ||
|
||
import net.minecraft.core.registries.BuiltInRegistries; | ||
import net.minecraft.network.FriendlyByteBuf; | ||
import net.minecraft.resources.ResourceLocation; | ||
import net.minecraft.tags.TagKey; | ||
import net.minecraft.util.GsonHelper; | ||
import net.minecraft.world.item.ItemStack; | ||
import net.minecraft.world.item.crafting.Ingredient; | ||
import net.minecraft.world.level.material.Fluid; | ||
import net.minecraftforge.common.ForgeHooks; | ||
import net.minecraftforge.common.crafting.IIngredientSerializer; | ||
|
||
import com.google.gson.JsonElement; | ||
import com.google.gson.JsonObject; | ||
import com.mojang.serialization.Codec; | ||
import lombok.Getter; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import java.util.Arrays; | ||
import java.util.stream.Stream; | ||
|
||
import javax.annotation.Nonnull; | ||
|
||
public class FluidContainerIngredient extends Ingredient { | ||
|
||
public static final ResourceLocation TYPE = GTCEu.id("fluid_container"); | ||
|
||
public static final Codec<FluidContainerIngredient> CODEC = FluidIngredient.CODEC.xmap( | ||
FluidContainerIngredient::new, FluidContainerIngredient::getFluid); | ||
|
||
@Getter | ||
private final FluidIngredient fluid; | ||
|
||
public FluidContainerIngredient(FluidIngredient fluid) { | ||
super(Stream.empty()); | ||
this.fluid = fluid; | ||
} | ||
|
||
public FluidContainerIngredient(FluidStack fluidStack) { | ||
this(FluidIngredient.of(TagUtil.createFluidTag(BuiltInRegistries.FLUID.getKey(fluidStack.getFluid()).getPath()), | ||
fluidStack.getAmount())); | ||
} | ||
|
||
public FluidContainerIngredient(TagKey<Fluid> tag, int amount) { | ||
this(FluidIngredient.of(tag, amount, null)); | ||
} | ||
|
||
private ItemStack[] cachedStacks; | ||
|
||
@Nonnull | ||
@Override | ||
public ItemStack[] getItems() { | ||
if (cachedStacks == null) | ||
cachedStacks = Arrays.stream(this.fluid.getStacks()) | ||
.map(stack -> stack.getFluid().getBucket().getDefaultInstance()) | ||
.filter(s -> !s.isEmpty()) | ||
.toArray(ItemStack[]::new); | ||
return this.cachedStacks; | ||
} | ||
|
||
@Override | ||
public JsonElement toJson() { | ||
JsonObject json = new JsonObject(); | ||
json.addProperty("type", TYPE.toString()); | ||
json.add("fluid", fluid.toJson()); | ||
return json; | ||
} | ||
|
||
@Override | ||
public boolean isEmpty() { | ||
return this.fluid.isEmpty(); | ||
} | ||
|
||
@Override | ||
public boolean test(@Nullable ItemStack stack) { | ||
if (stack == null || stack.isEmpty()) | ||
return false; | ||
IFluidTransfer transfer = FluidTransferHelper.getFluidTransfer(stack); | ||
return transfer != null && this.extractFrom(transfer, true); | ||
} | ||
|
||
@Override | ||
public boolean isSimple() { | ||
return false; | ||
} | ||
|
||
public ItemStack getExtractedStack(ItemStack input) { | ||
FluidActionResult result = FluidTransferHelper.tryEmptyContainer(input, | ||
new InfiniteFluidTransfer(1), | ||
(int) this.fluid.getAmount(), | ||
ForgeHooks.getCraftingPlayer(), | ||
true); | ||
if (result.success) { | ||
return result.result; | ||
} | ||
return input; | ||
} | ||
|
||
public boolean extractFrom(IFluidTransfer handler, boolean simulate) { | ||
for (int tank = 0; tank < handler.getTanks(); tank++) { | ||
FluidStack inTank = handler.getFluidInTank(tank); | ||
if (fluid.test(inTank)) { | ||
FluidStack toExtract = inTank.copy(fluid.getAmount()); | ||
FluidStack extractedSim = handler.drain(toExtract, true); | ||
if (extractedSim.getAmount() >= fluid.getAmount()) { | ||
if (!simulate) | ||
handler.drain(toExtract, false); | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
public static FluidContainerIngredient fromJson(JsonObject json) { | ||
return SERIALIZER.parse(json); | ||
} | ||
|
||
public static final IIngredientSerializer<FluidContainerIngredient> SERIALIZER = new IIngredientSerializer<>() { | ||
|
||
@Override | ||
public @NotNull FluidContainerIngredient parse(FriendlyByteBuf buffer) { | ||
FluidIngredient fluid = FluidIngredient.fromNetwork(buffer); | ||
return new FluidContainerIngredient(fluid); | ||
} | ||
|
||
@Override | ||
public @NotNull FluidContainerIngredient parse(JsonObject json) { | ||
FluidIngredient fluid = FluidIngredient.fromJson(GsonHelper.getAsJsonObject(json, "fluid")); | ||
return new FluidContainerIngredient(fluid); | ||
} | ||
|
||
@Override | ||
public void write(FriendlyByteBuf buffer, FluidContainerIngredient ingredient) { | ||
ingredient.fluid.toNetwork(buffer); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.