Plenty to process

- The Mechanical Mixer now supports custom mixing recipes
- The Mechanical Press now interacts with the basin to apply compressing recipes
- Added JEI integration for Mixing, Compressing, Sawing and Block Cutting recipes
This commit is contained in:
simibubi 2019-12-14 16:29:48 +01:00
parent 9bd07d4ed0
commit 43980d550d
22 changed files with 1167 additions and 243 deletions

View file

@ -0,0 +1,86 @@
package com.simibubi.create.compat.jei;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.gui.ScreenElementRenderer;
import net.minecraft.block.BlockState;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction.Axis;
public class AnimatedMixer extends AnimatedKinetics {
@Override
public int getWidth() {
return 50;
}
@Override
public int getHeight() {
return 150;
}
@Override
public void draw(int xOffset, int yOffset) {
GlStateManager.pushMatrix();
GlStateManager.enableDepthTest();
GlStateManager.translatef(xOffset, yOffset, 0);
GlStateManager.rotatef(-15.5f, 1, 0, 0);
GlStateManager.rotatef(22.5f, 0, 1, 0);
GlStateManager.translatef(-45, -5, 0);
GlStateManager.scaled(.45f, .45f, .45f);
GlStateManager.pushMatrix();
ScreenElementRenderer.renderBlock(this::cogwheel);
GlStateManager.popMatrix();
GlStateManager.pushMatrix();
ScreenElementRenderer.renderBlock(this::body);
GlStateManager.popMatrix();
GlStateManager.pushMatrix();
ScreenElementRenderer.renderBlock(this::pole);
GlStateManager.popMatrix();
GlStateManager.pushMatrix();
ScreenElementRenderer.renderBlock(this::head);
GlStateManager.popMatrix();
GlStateManager.pushMatrix();
ScreenElementRenderer.renderBlock(this::basin);
GlStateManager.popMatrix();
GlStateManager.popMatrix();
}
private BlockState cogwheel() {
float t = 25;
GlStateManager.translatef(t, -t, -t);
GlStateManager.rotated(getCurrentAngle() * 2, 0, 1, 0);
GlStateManager.translatef(-t, t, t);
return AllBlocks.SHAFTLESS_COGWHEEL.get().getDefaultState().with(BlockStateProperties.AXIS, Axis.Y);
}
private BlockState body() {
return AllBlocks.MECHANICAL_MIXER.get().getDefaultState();
}
private BlockState pole() {
GlStateManager.translatef(0, 51, 0);
return AllBlocks.MECHANICAL_MIXER_POLE.get().getDefaultState();
}
private BlockState head() {
float t = 25;
GlStateManager.translatef(0, 51, 0);
GlStateManager.translatef(t, -t, -t);
GlStateManager.rotated(getCurrentAngle() * 4, 0, 1, 0);
GlStateManager.translatef(-t, t, t);
return AllBlocks.MECHANICAL_MIXER_HEAD.get().getDefaultState();
}
private BlockState basin() {
GlStateManager.translatef(0, 85, 0);
return AllBlocks.BASIN.get().getDefaultState();
}
}

View file

@ -14,6 +14,12 @@ import net.minecraft.util.Direction.Axis;
public class AnimatedPress extends AnimatedKinetics {
private boolean basin;
public AnimatedPress(boolean basin) {
this.basin = basin;
}
@Override
public int getWidth() {
return 50;
@ -45,6 +51,12 @@ public class AnimatedPress extends AnimatedKinetics {
GlStateManager.pushMatrix();
ScreenElementRenderer.renderBlock(this::head);
GlStateManager.popMatrix();
if (basin) {
GlStateManager.pushMatrix();
ScreenElementRenderer.renderBlock(this::basin);
GlStateManager.popMatrix();
}
GlStateManager.popMatrix();
}
@ -79,5 +91,10 @@ public class AnimatedPress extends AnimatedKinetics {
return AllBlocks.MECHANICAL_PRESS_HEAD.get().getDefaultState().with(BlockStateProperties.HORIZONTAL_FACING,
Direction.EAST);
}
private BlockState basin() {
GlStateManager.translatef(0, 85, 0);
return AllBlocks.BASIN.get().getDefaultState();
}
}

View file

@ -0,0 +1,59 @@
package com.simibubi.create.compat.jei;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.gui.ScreenElementRenderer;
import com.simibubi.create.modules.contraptions.components.saw.SawBlock;
import net.minecraft.block.BlockState;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
public class AnimatedSaw extends AnimatedKinetics {
@Override
public int getWidth() {
return 50;
}
@Override
public int getHeight() {
return 50;
}
@Override
public void draw(int xOffset, int yOffset) {
GlStateManager.pushMatrix();
GlStateManager.enableDepthTest();
GlStateManager.translatef(xOffset, yOffset, 0);
GlStateManager.rotatef(-15.5f, 1, 0, 0);
GlStateManager.rotatef(22.5f, 0, 1, 0);
GlStateManager.translatef(-45, -5, 0);
GlStateManager.scaled(.6f, .6f, .6f);
GlStateManager.pushMatrix();
ScreenElementRenderer.renderBlock(this::shaft);
GlStateManager.popMatrix();
GlStateManager.pushMatrix();
ScreenElementRenderer.renderBlock(this::block);
GlStateManager.popMatrix();
GlStateManager.popMatrix();
}
private BlockState shaft() {
float t = 25;
GlStateManager.translatef(t, -t, t);
GlStateManager.rotated(-getCurrentAngle() * 2, 0, 0, 1);
GlStateManager.translatef(-t, t, -t);
return AllBlocks.SHAFT.get().getDefaultState().with(BlockStateProperties.AXIS, Axis.X);
}
private BlockState block() {
return AllBlocks.SAW.get().getDefaultState().with(BlockStateProperties.FACING, Direction.UP)
.with(SawBlock.RUNNING, true).with(SawBlock.AXIS_ALONG_FIRST_COORDINATE, true);
}
}

View file

@ -0,0 +1,136 @@
package com.simibubi.create.compat.jei;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.compat.jei.BlockCuttingCategory.CondensedBlockCuttingRecipe;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.Lang;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.IRecipeLayout;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IGuiItemStackGroup;
import mezz.jei.api.ingredients.IIngredients;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.item.crafting.StonecuttingRecipe;
import net.minecraft.util.ResourceLocation;
public class BlockCuttingCategory implements IRecipeCategory<CondensedBlockCuttingRecipe> {
private AnimatedSaw saw;
private static ResourceLocation ID = new ResourceLocation(Create.ID, "block_cutting");
private IDrawable icon;
private IDrawable background = new EmptyBackground(177, 70);
public BlockCuttingCategory() {
icon = new DoubleItemIcon(() -> new ItemStack(AllBlocks.SAW.get()),
() -> new ItemStack(Items.STONE_BRICK_STAIRS));
saw = new AnimatedSaw();
}
@Override
public IDrawable getIcon() {
return icon;
}
@Override
public ResourceLocation getUid() {
return ID;
}
@Override
public Class<? extends CondensedBlockCuttingRecipe> getRecipeClass() {
return CondensedBlockCuttingRecipe.class;
}
@Override
public String getTitle() {
return Lang.translate("recipe.block_cutting");
}
@Override
public IDrawable getBackground() {
return background;
}
@Override
public void setIngredients(CondensedBlockCuttingRecipe recipe, IIngredients ingredients) {
ingredients.setInputIngredients(recipe.getIngredients());
ingredients.setOutputs(VanillaTypes.ITEM, recipe.getOutputs());
}
@Override
public void setRecipe(IRecipeLayout recipeLayout, CondensedBlockCuttingRecipe recipe, IIngredients ingredients) {
IGuiItemStackGroup itemStacks = recipeLayout.getItemStacks();
itemStacks.init(0, true, 4, 4);
itemStacks.set(0, Arrays.asList(recipe.getIngredients().get(0).getMatchingStacks()));
List<ItemStack> results = recipe.getOutputs();
for (int outputIndex = 0; outputIndex < results.size(); outputIndex++) {
int xOffset = (outputIndex % 5) * 19;
int yOffset = (outputIndex / 5) * -19;
itemStacks.init(outputIndex + 1, false, 77 + xOffset, 47 + yOffset);
itemStacks.set(outputIndex + 1, results.get(outputIndex).getStack());
}
}
@Override
public void draw(CondensedBlockCuttingRecipe recipe, double mouseX, double mouseY) {
ScreenResources.JEI_SLOT.draw(4, 4);
int size = recipe.getOutputs().size();
for (int i = 0; i < size; i++) {
int xOffset = (i % 5) * 19;
int yOffset = (i / 5) * -19;
ScreenResources.JEI_SLOT.draw(77 + xOffset, 47 + yOffset);
}
ScreenResources.JEI_DOWN_ARROW.draw(31, 6);
ScreenResources.JEI_SHADOW.draw(19, 55);
saw.draw(33, 35);
}
public static class CondensedBlockCuttingRecipe extends StonecuttingRecipe {
List<ItemStack> outputs = new ArrayList<>();
public CondensedBlockCuttingRecipe(Ingredient ingredient) {
super(new ResourceLocation(""), "", ingredient, ItemStack.EMPTY);
}
public void addOutput(ItemStack stack) {
outputs.add(stack);
}
public List<ItemStack> getOutputs() {
return outputs;
}
public static List<CondensedBlockCuttingRecipe> condenseRecipes(List<IRecipe<?>> stoneCuttingRecipes) {
List<CondensedBlockCuttingRecipe> condensed = new ArrayList<>();
Recipes: for (IRecipe<?> recipe : stoneCuttingRecipes) {
Ingredient i1 = recipe.getIngredients().get(0);
for (CondensedBlockCuttingRecipe condensedRecipe : condensed) {
if (ItemHelper.matchIngredients(i1, condensedRecipe.getIngredients().get(0))) {
condensedRecipe.addOutput(recipe.getRecipeOutput());
continue Recipes;
}
}
CondensedBlockCuttingRecipe cr = new CondensedBlockCuttingRecipe(i1);
cr.addOutput(recipe.getRecipeOutput());
condensed.add(cr);
}
return condensed;
}
}
}

View file

@ -3,24 +3,32 @@ package com.simibubi.create.compat.jei;
import java.util.List;
import java.util.stream.Collectors;
import com.google.common.base.Predicate;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllRecipes;
import com.simibubi.create.Create;
import com.simibubi.create.compat.jei.BlockCuttingCategory.CondensedBlockCuttingRecipe;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.modules.contraptions.components.press.MechanicalPressTileEntity;
import com.simibubi.create.modules.contraptions.processing.StochasticOutput;
import com.simibubi.create.modules.logistics.block.inventories.FlexcrateScreen;
import com.simibubi.create.modules.schematics.block.SchematicannonScreen;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.gui.ingredient.IGuiItemStackGroup;
import mezz.jei.api.registration.IGuiHandlerRegistration;
import mezz.jei.api.registration.IRecipeCatalystRegistration;
import mezz.jei.api.registration.IRecipeCategoryRegistration;
import mezz.jei.api.registration.IRecipeRegistration;
import mezz.jei.api.registration.ISubtypeRegistration;
import net.minecraft.block.Blocks;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.ICraftingRecipe;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.StringTextComponent;
@ -36,6 +44,10 @@ public class CreateJEI implements IModPlugin {
private PressingCategory pressingCategory;
private BlastingViaFanCategory blastingCategory;
private BlockzapperUpgradeCategory blockzapperCategory;
private MixingCategory mixingCategory;
private SawingCategory sawingCategory;
private BlockCuttingCategory blockCuttingCategory;
private PackingCategory packingCategory;
@Override
public ResourceLocation getPluginUid() {
@ -49,6 +61,10 @@ public class CreateJEI implements IModPlugin {
smokingCategory = new SmokingViaFanCategory();
blastingCategory = new BlastingViaFanCategory();
blockzapperCategory = new BlockzapperUpgradeCategory();
mixingCategory = new MixingCategory();
sawingCategory = new SawingCategory();
blockCuttingCategory = new BlockCuttingCategory();
packingCategory = new PackingCategory();
}
@Override
@ -59,7 +75,8 @@ public class CreateJEI implements IModPlugin {
@Override
public void registerCategories(IRecipeCategoryRegistration registration) {
registration.addRecipeCategories(crushingCategory, splashingCategory, pressingCategory, smokingCategory,
blastingCategory, blockzapperCategory);
blastingCategory, blockzapperCategory, mixingCategory, sawingCategory, blockCuttingCategory,
packingCategory);
}
@Override
@ -72,6 +89,19 @@ public class CreateJEI implements IModPlugin {
registration.addRecipes(findRecipesByType(IRecipeType.SMOKING), smokingCategory.getUid());
registration.addRecipes(findRecipesByTypeExcluding(IRecipeType.SMELTING, IRecipeType.SMOKING),
blastingCategory.getUid());
registration.addRecipes(findRecipes(AllRecipes.MIXING), mixingCategory.getUid());
registration
.addRecipes(
findRecipes(r -> r.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS
&& !MechanicalPressTileEntity.canCompress(r.getIngredients())),
mixingCategory.getUid());
registration.addRecipes(findRecipes(AllRecipes.CUTTING), sawingCategory.getUid());
registration.addRecipes(
CondensedBlockCuttingRecipe.condenseRecipes(findRecipesByType(IRecipeType.STONECUTTING)),
blockCuttingCategory.getUid());
registration.addRecipes(findRecipes(
r -> (r instanceof ICraftingRecipe) && MechanicalPressTileEntity.canCompress(r.getIngredients())),
packingCategory.getUid());
}
@Override
@ -91,6 +121,13 @@ public class CreateJEI implements IModPlugin {
registration.addRecipeCatalyst(blastingFan, blastingCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(AllBlocks.MECHANICAL_PRESS.get()), pressingCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(AllItems.PLACEMENT_HANDGUN.get()), blockzapperCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(AllBlocks.MECHANICAL_MIXER.get()), mixingCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(AllBlocks.BASIN.get()), mixingCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(AllBlocks.SAW.get()), sawingCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(AllBlocks.SAW.get()), blockCuttingCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(Blocks.STONECUTTER), blockCuttingCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(AllBlocks.MECHANICAL_PRESS.get()), packingCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(AllBlocks.BASIN.get()), packingCategory.getUid());
}
@Override
@ -103,6 +140,11 @@ public class CreateJEI implements IModPlugin {
return findRecipesByType(recipe.type);
}
private static List<IRecipe<?>> findRecipes(Predicate<IRecipe<?>> pred) {
return Minecraft.getInstance().world.getRecipeManager().getRecipes().stream().filter(pred)
.collect(Collectors.toList());
}
private static List<IRecipe<?>> findRecipesByType(IRecipeType<?> type) {
return Minecraft.getInstance().world.getRecipeManager().getRecipes().stream().filter(r -> r.getType() == type)
.collect(Collectors.toList());
@ -129,4 +171,15 @@ public class CreateJEI implements IModPlugin {
return byType;
}
static void addStochasticTooltip(IGuiItemStackGroup itemStacks, List<StochasticOutput> results) {
itemStacks.addTooltipCallback((slotIndex, input, ingredient, tooltip) -> {
if (input)
return;
StochasticOutput output = results.get(slotIndex - 1);
if (output.getChance() != 1)
tooltip.add(1, TextFormatting.GOLD
+ Lang.translate("recipe.processing.chance", (int) (output.getChance() * 100)));
});
}
}

View file

@ -19,7 +19,6 @@ import mezz.jei.api.ingredients.IIngredients;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.TextFormatting;
public class CrushingCategory implements IRecipeCategory<CrushingRecipe> {
@ -78,14 +77,7 @@ public class CrushingCategory implements IRecipeCategory<CrushingRecipe> {
itemStacks.set(outputIndex + 1, results.get(outputIndex).getStack());
}
itemStacks.addTooltipCallback((slotIndex, input, ingredient, tooltip) -> {
if (input)
return;
StochasticOutput output = results.get(slotIndex - 1);
if (output.getChance() != 1)
tooltip.add(1, TextFormatting.GOLD
+ Lang.translate("recipe.processing.chance", (int) (output.getChance() * 100)));
});
CreateJEI.addStochasticTooltip(itemStacks, results);
}
@Override

View file

@ -0,0 +1,113 @@
package com.simibubi.create.compat.jei;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.Lang;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.IRecipeLayout;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IGuiItemStackGroup;
import mezz.jei.api.ingredients.IIngredients;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
public class MixingCategory implements IRecipeCategory<IRecipe<?>> {
private AnimatedMixer mixer;
private static ResourceLocation ID = new ResourceLocation(Create.ID, "mixing");
private IDrawable icon;
private IDrawable background = new EmptyBackground(177, 70);
public MixingCategory() {
icon = new DoubleItemIcon(() -> new ItemStack(AllBlocks.MECHANICAL_MIXER.get()),
() -> new ItemStack(AllBlocks.BASIN.get()));
mixer = new AnimatedMixer();
}
@Override
public IDrawable getIcon() {
return icon;
}
@Override
public ResourceLocation getUid() {
return ID;
}
@Override
public String getTitle() {
return Lang.translate("recipe.mixing");
}
@Override
public IDrawable getBackground() {
return background;
}
@Override
public void setIngredients(IRecipe<?> recipe, IIngredients ingredients) {
ingredients.setInputIngredients(recipe.getIngredients());
ingredients.setOutput(VanillaTypes.ITEM, recipe.getRecipeOutput());
}
@Override
public void setRecipe(IRecipeLayout recipeLayout, IRecipe<?> recipe, IIngredients ingredients) {
IGuiItemStackGroup itemStacks = recipeLayout.getItemStacks();
NonNullList<Ingredient> recipeIngredients = recipe.getIngredients();
List<Pair<Ingredient, MutableInt>> actualIngredients = ItemHelper.condenseIngredients(recipeIngredients);
int size = actualIngredients.size();
int xOffset = size < 3 ? (3 - size) * 19 / 2 : 0;
int i = 0;
while (i < size) {
Pair<Ingredient, MutableInt> ingredient = actualIngredients.get(i);
itemStacks.init(i, true, 16 + xOffset + (i % 3) * 19, 50 - (i / 3) * 19);
List<ItemStack> asList = Arrays.asList(ingredient.getKey().getMatchingStacks());
itemStacks.set(i, asList.stream().map(stack -> {
stack = stack.copy();
stack.setCount(ingredient.getRight().getValue());
return stack;
}).collect(Collectors.toList()));
i++;
}
itemStacks.init(i, false, 141, 50);
itemStacks.set(i, recipe.getRecipeOutput().getStack());
}
@Override
public void draw(IRecipe<?> recipe, double mouseX, double mouseY) {
List<Pair<Ingredient, MutableInt>> actualIngredients = ItemHelper.condenseIngredients(recipe.getIngredients());
int size = actualIngredients.size();
int xOffset = size < 3 ? (3 - size) * 19 / 2 : 0;
for (int i = 0; i < size; i++)
ScreenResources.JEI_SLOT.draw(16 + xOffset + (i % 3) * 19, 50 - (i / 3) * 19);
ScreenResources.JEI_SLOT.draw(141, 50);
ScreenResources.JEI_DOWN_ARROW.draw(136, 32);
ScreenResources.JEI_SHADOW.draw(81, 57);
mixer.draw(getBackground().getWidth() / 2 + 20, 8);
}
@SuppressWarnings("unchecked")
@Override
public Class<? extends IRecipe<?>> getRecipeClass() {
return (Class<? extends IRecipe<?>>) IRecipe.class;
}
}

View file

@ -0,0 +1,100 @@
package com.simibubi.create.compat.jei;
import java.util.Arrays;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.utility.Lang;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.IRecipeLayout;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IGuiItemStackGroup;
import mezz.jei.api.ingredients.IIngredients;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
public class PackingCategory implements IRecipeCategory<IRecipe<?>> {
private AnimatedPress press;
private static ResourceLocation ID = new ResourceLocation(Create.ID, "packing");
private IDrawable icon;
private IDrawable background = new EmptyBackground(177, 70);
public PackingCategory() {
icon = new DoubleItemIcon(() -> new ItemStack(AllBlocks.MECHANICAL_PRESS.get()),
() -> new ItemStack(AllBlocks.BASIN.get()));
press = new AnimatedPress(true);
}
@Override
public IDrawable getIcon() {
return icon;
}
@Override
public ResourceLocation getUid() {
return ID;
}
@SuppressWarnings("unchecked")
@Override
public Class<? extends IRecipe<?>> getRecipeClass() {
return (Class<? extends IRecipe<?>>) IRecipe.class;
}
@Override
public String getTitle() {
return Lang.translate("recipe.packing");
}
@Override
public IDrawable getBackground() {
return background;
}
@Override
public void setIngredients(IRecipe<?> recipe, IIngredients ingredients) {
ingredients.setInputIngredients(recipe.getIngredients());
ingredients.setOutput(VanillaTypes.ITEM, recipe.getRecipeOutput());
}
@Override
public void setRecipe(IRecipeLayout recipeLayout, IRecipe<?> recipe, IIngredients ingredients) {
IGuiItemStackGroup itemStacks = recipeLayout.getItemStacks();
int i = 0;
NonNullList<Ingredient> ingredients2 = recipe.getIngredients();
int size = ingredients2.size();
int rows = size == 4 ? 2 : 3;
while (i < size) {
Ingredient ingredient = ingredients2.get(i);
itemStacks.init(i, true, (rows == 2 ? 26 : 17) + (i % rows) * 19, 50 - (i / rows) * 19);
itemStacks.set(i, Arrays.asList(ingredient.getMatchingStacks()));
i++;
}
itemStacks.init(i, false, 141, 50);
itemStacks.set(i, recipe.getRecipeOutput());
}
@Override
public void draw(IRecipe<?> recipe, double mouseX, double mouseY) {
NonNullList<Ingredient> ingredients2 = recipe.getIngredients();
int size = ingredients2.size();
int rows = size == 4 ? 2 : 3;
for (int i = 0; i < size; i++) {
ScreenResources.JEI_SLOT.draw((rows == 2 ? 26 : 17) + (i % rows) * 19, 50 - (i / rows) * 19);
}
ScreenResources.JEI_SLOT.draw(141, 50);
ScreenResources.JEI_DOWN_ARROW.draw(136, 32);
ScreenResources.JEI_SHADOW.draw(81, 57);
press.draw(getBackground().getWidth() / 2 + 20, 8);
}
}

View file

@ -30,7 +30,7 @@ public class PressingCategory implements IRecipeCategory<PressingRecipe> {
public PressingCategory() {
icon = new DoubleItemIcon(() -> new ItemStack(AllBlocks.MECHANICAL_PRESS.get()),
() -> new ItemStack(AllItems.IRON_SHEET.get()));
press = new AnimatedPress();
press = new AnimatedPress(false);
}
@Override
@ -75,6 +75,8 @@ public class PressingCategory implements IRecipeCategory<PressingRecipe> {
itemStacks.init(outputIndex + 1, false, 131 + 19 * outputIndex, 50);
itemStacks.set(outputIndex + 1, results.get(outputIndex).getStack());
}
CreateJEI.addStochasticTooltip(itemStacks, results);
}
@Override
@ -83,8 +85,9 @@ public class PressingCategory implements IRecipeCategory<PressingRecipe> {
ScreenResources.JEI_SLOT.draw(131, 50);
if (recipe.getRollableResults().size() > 1)
ScreenResources.JEI_SLOT.draw(131 + 19, 50);
ScreenResources.JEI_SHADOW.draw(61, 41);
ScreenResources.JEI_LONG_ARROW.draw(52, 54);
press.draw(getBackground().getWidth() / 2, 20);
press.draw(getBackground().getWidth() / 2, 8);
}
}

View file

@ -0,0 +1,98 @@
package com.simibubi.create.compat.jei;
import java.util.Arrays;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.modules.contraptions.components.saw.CuttingRecipe;
import com.simibubi.create.modules.contraptions.processing.StochasticOutput;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.IRecipeLayout;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IGuiItemStackGroup;
import mezz.jei.api.ingredients.IIngredients;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
public class SawingCategory implements IRecipeCategory<CuttingRecipe> {
private AnimatedSaw saw;
private static ResourceLocation ID = new ResourceLocation(Create.ID, "sawing");
private IDrawable icon;
private IDrawable background = new EmptyBackground(177, 70);
public SawingCategory() {
icon = new DoubleItemIcon(() -> new ItemStack(AllBlocks.SAW.get()), () -> new ItemStack(Items.OAK_LOG));
saw = new AnimatedSaw();
}
@Override
public IDrawable getIcon() {
return icon;
}
@Override
public ResourceLocation getUid() {
return ID;
}
@Override
public Class<? extends CuttingRecipe> getRecipeClass() {
return CuttingRecipe.class;
}
@Override
public String getTitle() {
return Lang.translate("recipe.sawing");
}
@Override
public IDrawable getBackground() {
return background;
}
@Override
public void setIngredients(CuttingRecipe recipe, IIngredients ingredients) {
ingredients.setInputIngredients(recipe.getIngredients());
ingredients.setOutputs(VanillaTypes.ITEM, recipe.getPossibleOutputs());
}
@Override
public void setRecipe(IRecipeLayout recipeLayout, CuttingRecipe recipe, IIngredients ingredients) {
IGuiItemStackGroup itemStacks = recipeLayout.getItemStacks();
itemStacks.init(0, true, 43, 4);
itemStacks.set(0, Arrays.asList(recipe.getIngredients().get(0).getMatchingStacks()));
List<StochasticOutput> results = recipe.getRollableResults();
for (int outputIndex = 0; outputIndex < results.size(); outputIndex++) {
int xOffset = outputIndex % 2 == 0 ? 0 : 19;
int yOffset = (outputIndex / 2) * -19;
itemStacks.init(outputIndex + 1, false, 117 + xOffset, 47 + yOffset);
itemStacks.set(outputIndex + 1, results.get(outputIndex).getStack());
}
CreateJEI.addStochasticTooltip(itemStacks, results);
}
@Override
public void draw(CuttingRecipe recipe, double mouseX, double mouseY) {
ScreenResources.JEI_SLOT.draw(43, 4);
int size = recipe.getRollableResults().size();
for (int i = 0; i < size; i++) {
int xOffset = i % 2 == 0 ? 0 : 19;
int yOffset = (i / 2) * -19;
ScreenResources.JEI_SLOT.draw(117 + xOffset, 47 + yOffset);
}
ScreenResources.JEI_DOWN_ARROW.draw(70, 6);
ScreenResources.JEI_SHADOW.draw(58, 55);
saw.draw(72, 35);
}
}

View file

@ -23,7 +23,6 @@ import net.minecraft.block.FlowingFluidBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.TextFormatting;
public class SplashingCategory extends ProcessingViaFanCategory<SplashingRecipe> {
@ -83,14 +82,7 @@ public class SplashingCategory extends ProcessingViaFanCategory<SplashingRecipe>
itemStacks.set(outputIndex + 1, results.get(outputIndex).getStack());
}
itemStacks.addTooltipCallback((slotIndex, input, ingredient, tooltip) -> {
if (input)
return;
StochasticOutput output = results.get(slotIndex - 1);
if (output.getChance() != 1)
tooltip.add(1, TextFormatting.GOLD
+ Lang.translate("recipe.processing.chance", (int) (output.getChance() * 100)));
});
CreateJEI.addStochasticTooltip(itemStacks, results);
}
@Override

View file

@ -3,7 +3,12 @@ package com.simibubi.create.foundation.item;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.Pair;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.NonNullList;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
@ -33,7 +38,7 @@ public class ItemHelper {
if (stack.getCount() > 0)
stacks.add(stack);
}
public static boolean isSameInventory(IItemHandler h1, IItemHandler h2) {
if (h1 == null || h2 == null)
return false;
@ -46,4 +51,38 @@ public class ItemHelper {
return true;
}
public static List<Pair<Ingredient, MutableInt>> condenseIngredients(NonNullList<Ingredient> recipeIngredients) {
List<Pair<Ingredient, MutableInt>> actualIngredients = new ArrayList<>();
Ingredients: for (Ingredient igd : recipeIngredients) {
for (Pair<Ingredient, MutableInt> pair : actualIngredients) {
ItemStack[] stacks1 = pair.getKey().getMatchingStacks();
ItemStack[] stacks2 = igd.getMatchingStacks();
if (stacks1.length == stacks2.length) {
for (int i = 0; i <= stacks1.length; i++) {
if (i == stacks1.length) {
pair.getValue().increment();
continue Ingredients;
}
if (!ItemStack.areItemsEqual(stacks1[i], stacks2[i]))
break;
}
}
}
actualIngredients.add(Pair.of(igd, new MutableInt(1)));
}
return actualIngredients;
}
public static boolean matchIngredients(Ingredient i1, Ingredient i2) {
ItemStack[] stacks1 = i1.getMatchingStacks();
ItemStack[] stacks2 = i2.getMatchingStacks();
if (stacks1.length == stacks2.length) {
for (int i = 0; i < stacks1.length; i++)
if (!ItemStack.areItemsEqual(stacks1[i], stacks2[i]))
return false;
return true;
}
return false;
}
}

View file

@ -5,7 +5,6 @@ import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
@ -13,7 +12,6 @@ import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.world.World;
/**
@ -26,38 +24,7 @@ import net.minecraft.world.World;
*/
public class RecipeFinder {
private static Cache<Object, StartedSearch> cachedSearches = CacheBuilder.newBuilder().build();
public static class StartedSearch {
List<IRecipe<?>> findings;
public StartedSearch(List<IRecipe<?>> findings) {
this.findings = findings;
}
public RecipeStream<IRecipe<?>> search() {
return new RecipeStream<>(findings.stream());
}
public static class RecipeStream<R extends IRecipe<?>> {
Stream<R> stream;
public RecipeStream(Stream<R> stream) {
this.stream = stream;
}
@SuppressWarnings("unchecked")
public <X extends IRecipe<?>> RecipeStream<X> assumeType(IRecipeType<X> type) {
return (RecipeStream<X>) this;
}
public List<R> filter(Predicate<R> condition) {
return stream.filter(condition).collect(Collectors.toList());
}
}
}
private static Cache<Object, List<IRecipe<?>>> cachedSearches = CacheBuilder.newBuilder().build();
/**
* Find all IRecipes matching the condition predicate. If this search is made
@ -69,7 +36,7 @@ public class RecipeFinder {
* @param conditions
* @return A started search to continue with more specific conditions.
*/
public static StartedSearch get(@Nullable Object cacheKey, World world, Predicate<IRecipe<?>> conditions) {
public static List<IRecipe<?>> get(@Nullable Object cacheKey, World world, Predicate<IRecipe<?>> conditions) {
if (cacheKey == null)
return startSearch(world, conditions);
@ -79,13 +46,13 @@ public class RecipeFinder {
e.printStackTrace();
}
return new StartedSearch(Collections.emptyList());
return Collections.emptyList();
}
private static StartedSearch startSearch(World world, Predicate<? super IRecipe<?>> conditions) {
private static List<IRecipe<?>> startSearch(World world, Predicate<? super IRecipe<?>> conditions) {
List<IRecipe<?>> list = world.getRecipeManager().getRecipes().stream().filter(conditions)
.collect(Collectors.toList());
return new StartedSearch(list);
return list;
}
}

View file

@ -3,68 +3,49 @@ package com.simibubi.create.modules.contraptions.components.mixer;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import com.simibubi.create.AllPackets;
import com.simibubi.create.AllRecipes;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.processing.BasinTileEntity;
import com.simibubi.create.modules.contraptions.components.press.MechanicalPressTileEntity;
import com.simibubi.create.modules.contraptions.processing.BasinOperatingTileEntity;
import com.simibubi.create.modules.contraptions.processing.BasinTileEntity.BasinInventory;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.BucketItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.item.crafting.ShapelessRecipe;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.particles.ItemParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
public class MechanicalMixerTileEntity extends KineticTileEntity {
public class MechanicalMixerTileEntity extends BasinOperatingTileEntity {
private static Object shapelessOrMixingRecipesKey = new Object();
public int runningTicks;
public int processingTicks;
public boolean running;
public boolean checkBasin;
public boolean basinRemoved;
public int minIngredients;
public int currentValue;
public int lastModified;
private ShapelessRecipe lastRecipe;
private LazyOptional<IItemHandler> basinInv = LazyOptional.empty();
private List<ItemStack> inputs;
public MechanicalMixerTileEntity() {
super(AllTileEntities.MECHANICAL_MIXER.type);
checkBasin = true;
minIngredients = currentValue = 1;
lastModified = -1;
processingTicks = -1;
}
@Override
public void onSpeedChanged(float prevSpeed) {
super.onSpeedChanged(prevSpeed);
checkBasin = true;
}
public float getRenderedHeadOffset(float partialTicks) {
int localTick = 0;
float offset = 0;
@ -132,7 +113,6 @@ public class MechanicalMixerTileEntity extends KineticTileEntity {
@Override
public void tick() {
super.tick();
if (world.isRemote && lastModified != -1) {
if (lastModified++ > 10) {
@ -141,20 +121,14 @@ public class MechanicalMixerTileEntity extends KineticTileEntity {
}
}
if (runningTicks == 40) {
super.tick();
if (runningTicks >= 40) {
running = false;
runningTicks = 0;
return;
}
if (basinRemoved) {
basinRemoved = false;
if (running) {
runningTicks = 40;
return;
}
}
float speed = Math.abs(getSpeed());
if (running) {
if (world.isRemote && runningTicks == 20)
@ -163,58 +137,21 @@ public class MechanicalMixerTileEntity extends KineticTileEntity {
if (!world.isRemote && runningTicks == 20) {
if (processingTicks < 0) {
processingTicks = (MathHelper.log2((int) (8000 / speed))) * 15 + 1;
return;
}
processingTicks--;
if (processingTicks == 0) {
runningTicks++;
processingTicks = -1;
applyRecipe();
sendData();
} else {
processingTicks--;
if (processingTicks == 0) {
runningTicks++;
processingTicks = -1;
applyBasinRecipe();
sendData();
}
}
}
if (runningTicks != 20)
runningTicks++;
return;
}
if (!isSpeedRequirementFulfilled())
return;
if (!checkBasin)
return;
checkBasin = false;
TileEntity basinTE = world.getTileEntity(pos.down(2));
if (basinTE == null || !(basinTE instanceof BasinTileEntity))
return;
if (!basinInv.isPresent())
basinInv = basinTE.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
if (!basinInv.isPresent())
return;
if (world.isRemote)
return;
gatherInputs();
if (matchRecipe(lastRecipe)) {
running = true;
runningTicks = 0;
sendData();
return;
}
List<IRecipe<?>> shapelessRecipe = world.getRecipeManager().getRecipes().parallelStream()
.filter(recipe -> recipe.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS)
.filter(this::matchRecipe).sorted((r1, r2) -> r1.getIngredients().size() - r2.getIngredients().size())
.collect(Collectors.toList());
if (shapelessRecipe.isEmpty())
return;
running = true;
runningTicks = 0;
lastRecipe = (ShapelessRecipe) shapelessRecipe.get(0);
sendData();
}
public void renderParticles() {
@ -240,64 +177,20 @@ public class MechanicalMixerTileEntity extends KineticTileEntity {
}
}
public void gatherInputs() {
BasinInventory inv = (BasinInventory) basinInv.orElse(null);
inputs = new ArrayList<>();
IItemHandlerModifiable inputHandler = inv.getInputHandler();
for (int slot = 0; slot < inputHandler.getSlots(); ++slot) {
ItemStack itemstack = inputHandler.extractItem(slot, inputHandler.getSlotLimit(slot), true);
if (!itemstack.isEmpty()) {
inputs.add(itemstack);
}
}
@Override
protected <C extends IInventory> boolean matchStaticFilters(IRecipe<C> r) {
return (r.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS || r.getType() == AllRecipes.MIXING.type)
&& !MechanicalPressTileEntity.canCompress(r.getIngredients());
}
public void applyRecipe() {
if (lastRecipe == null)
return;
if (!basinInv.isPresent())
return;
BasinInventory inv = (BasinInventory) basinInv.orElse(null);
if (inv == null)
return;
IItemHandlerModifiable inputs = inv.getInputHandler();
IItemHandlerModifiable outputs = inv.getOutputHandler();
int buckets = 0;
Ingredients: for (Ingredient ingredient : lastRecipe.getIngredients()) {
for (int slot = 0; slot < inputs.getSlots(); slot++) {
if (!ingredient.test(inputs.extractItem(slot, 1, true)))
continue;
ItemStack extracted = inputs.extractItem(slot, 1, false);
if (extracted.getItem() instanceof BucketItem)
buckets++;
continue Ingredients;
}
// something wasn't found
return;
}
ItemHandlerHelper.insertItemStacked(outputs, lastRecipe.getRecipeOutput().copy(), false);
if (buckets > 0)
ItemHandlerHelper.insertItemStacked(outputs, new ItemStack(Items.BUCKET, buckets), false);
// Continue mixing
gatherInputs();
if (matchRecipe(lastRecipe)) {
runningTicks = 20;
sendData();
}
}
public <C extends IInventory> boolean matchRecipe(IRecipe<C> recipe) {
if (!(recipe instanceof ShapelessRecipe))
@Override
protected <C extends IInventory> boolean matchBasinRecipe(IRecipe<C> recipe) {
if (recipe == null)
return false;
if (recipe.getIngredients().size() < minIngredients)
return false;
ShapelessRecipe shapelessRecipe = (ShapelessRecipe) recipe;
NonNullList<Ingredient> ingredients = shapelessRecipe.getIngredients();
NonNullList<Ingredient> ingredients = recipe.getIngredients();
if (!ingredients.stream().allMatch(Ingredient::isSimple))
return false;
@ -321,4 +214,33 @@ public class MechanicalMixerTileEntity extends KineticTileEntity {
return true;
}
@Override
public void startProcessingBasin() {
if (running)
return;
super.startProcessingBasin();
running = true;
runningTicks = 0;
}
@Override
public boolean continueWithPreviousRecipe() {
runningTicks = 20;
return true;
}
@Override
protected void basinRemoved() {
super.basinRemoved();
if (running) {
runningTicks = 40;
running = false;
}
}
@Override
protected Object getRecipeCacheKey() {
return shapelessOrMixingRecipesKey;
}
}

View file

@ -11,6 +11,7 @@ import com.simibubi.create.foundation.block.SyncedTileEntity;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.AllShapes;
import com.simibubi.create.modules.contraptions.base.HorizontalKineticBlock;
import com.simibubi.create.modules.contraptions.components.press.MechanicalPressTileEntity.Mode;
import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.BeltAttachmentState;
import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.IBeltAttachment;
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock;
@ -62,7 +63,7 @@ public class MechanicalPressBlock extends HorizontalKineticBlock
if (worldIn.isBlockPowered(pos)) {
if (!te.finished && !te.running && te.getSpeed() != 0)
te.start(false);
te.start(Mode.WORLD);
} else {
te.finished = false;
}
@ -139,7 +140,7 @@ public class MechanicalPressBlock extends HorizontalKineticBlock
return false;
state.processingDuration = 1;
pressTe.start(true);
pressTe.start(Mode.BELT);
return true;
}
@ -156,6 +157,10 @@ public class MechanicalPressBlock extends HorizontalKineticBlock
if (pressTe.running) {
if (pressTe.runningTicks == 30) {
Optional<PressingRecipe> recipe = pressTe.getRecipe(transportedStack.stack);
pressTe.pressedItems.clear();
pressTe.pressedItems.add(transportedStack.stack);
if (!recipe.isPresent())
return false;
ItemStack out = recipe.get().getRecipeOutput().copy();
@ -167,6 +172,7 @@ public class MechanicalPressBlock extends HorizontalKineticBlock
TileEntity controllerTE = te.getWorld().getTileEntity(te.getController());
if (controllerTE != null && controllerTE instanceof BeltTileEntity)
((SyncedTileEntity) controllerTE).sendData();
pressTe.sendData();
}
return true;
}

View file

@ -1,31 +1,43 @@
package com.simibubi.create.modules.contraptions.components.press;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import com.simibubi.create.AllRecipes;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import com.simibubi.create.modules.contraptions.relays.belt.TransportedItemStack;
import com.simibubi.create.modules.contraptions.processing.BasinOperatingTileEntity;
import com.simibubi.create.modules.contraptions.processing.BasinTileEntity.BasinInventory;
import com.simibubi.create.modules.logistics.InWorldProcessing;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.ICraftingRecipe;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.particles.ItemParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.NonNullList;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.RecipeWrapper;
public class MechanicalPressTileEntity extends KineticTileEntity {
public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
private static Object compressingRecipesKey = new Object();
public List<ItemStack> pressedItems = new ArrayList<>();
public static class PressingInv extends RecipeWrapper {
public PressingInv() {
@ -33,20 +45,33 @@ public class MechanicalPressTileEntity extends KineticTileEntity {
}
}
enum Mode {
WORLD(1), BELT(19f / 16f), BASIN(22f / 16f)
;
float headOffset;
private Mode(float headOffset) {
this.headOffset = headOffset;
}
}
private static PressingInv pressingInv = new PressingInv();
public int runningTicks;
public boolean running;
public boolean beltMode;
public Mode mode;
public boolean finished;
public MechanicalPressTileEntity() {
super(AllTileEntities.MECHANICAL_PRESS.type);
mode = Mode.WORLD;
}
@Override
public void read(CompoundNBT compound) {
running = compound.getBoolean("Running");
beltMode = compound.getBoolean("OnBelt");
mode = Mode.values()[compound.getInt("Mode")];
finished = compound.getBoolean("Finished");
runningTicks = compound.getInt("Ticks");
super.read(compound);
@ -55,37 +80,67 @@ public class MechanicalPressTileEntity extends KineticTileEntity {
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.putBoolean("Running", running);
compound.putBoolean("OnBelt", beltMode);
compound.putInt("Mode", mode.ordinal());
compound.putBoolean("Finished", finished);
compound.putInt("Ticks", runningTicks);
return super.write(compound);
}
@Override
public CompoundNBT writeToClient(CompoundNBT tag) {
ListNBT particleItems = new ListNBT();
pressedItems.forEach(stack -> particleItems.add(stack.serializeNBT()));
tag.put("ParticleItems", particleItems);
return super.writeToClient(tag);
}
@Override
public void readClientUpdate(CompoundNBT tag) {
super.readClientUpdate(tag);
ListNBT particleItems = tag.getList("ParticleItems", NBT.TAG_COMPOUND);
particleItems.forEach(nbt -> pressedItems.add(ItemStack.read((CompoundNBT) nbt)));
spawnParticles();
}
@Override
public AxisAlignedBB getRenderBoundingBox() {
return new AxisAlignedBB(pos).expand(0, -1.5, 0);
return new AxisAlignedBB(pos).expand(0, -1.5, 0).expand(0, 1, 0);
}
public float getRenderedHeadOffset(float partialTicks) {
if (running) {
if (runningTicks < 40) {
float num = (runningTicks - 1 + partialTicks) / 30f;
return MathHelper.clamp(num * num * num, 0, beltMode ? 1 + 3 / 16f : 1);
return MathHelper.clamp(num * num * num, 0, mode.headOffset);
}
if (runningTicks >= 40) {
return MathHelper.clamp(((60 - runningTicks) + 1 - partialTicks) / 20f, 0, beltMode ? 1 + 3 / 16f : 1);
return MathHelper.clamp(((60 - runningTicks) + 1 - partialTicks) / 20f * mode.headOffset, 0,
mode.headOffset);
}
}
return 0;
}
public void start(boolean onBelt) {
beltMode = onBelt;
public void start(Mode mode) {
this.mode = mode;
running = true;
runningTicks = 0;
pressedItems.clear();
sendData();
}
public boolean inWorld() {
return mode == Mode.WORLD;
}
public boolean onBelt() {
return mode == Mode.BELT;
}
public boolean onBasin() {
return mode == Mode.BASIN;
}
@Override
public void tick() {
super.tick();
@ -95,16 +150,18 @@ public class MechanicalPressTileEntity extends KineticTileEntity {
if (runningTicks == 30) {
if (!beltMode) {
AxisAlignedBB bb = new AxisAlignedBB(pos.down(beltMode ? 2 : 1));
if (inWorld()) {
AxisAlignedBB bb = new AxisAlignedBB(pos.down(1));
pressedItems.clear();
for (Entity entity : world.getEntitiesWithinAABBExcludingEntity(null, bb)) {
if (!(entity instanceof ItemEntity))
continue;
ItemEntity itemEntity = (ItemEntity) entity;
makeParticleEffect(entity.getPositionVec(), itemEntity.getItem());
if (!world.isRemote) {
pressedItems.add(itemEntity.getItem());
sendData();
Optional<PressingRecipe> recipe = getRecipe(itemEntity.getItem());
if (recipe.isPresent())
InWorldProcessing.applyRecipeOn(itemEntity, recipe.get());
@ -112,18 +169,22 @@ public class MechanicalPressTileEntity extends KineticTileEntity {
}
}
if (beltMode && world.isRemote) {
TileEntity te = world.getTileEntity(pos.down(2));
if (te != null && te instanceof BeltTileEntity) {
BeltTileEntity beltTE = (BeltTileEntity) te;
TileEntity controller = world.getTileEntity(beltTE.getController());
if (controller != null && controller instanceof BeltTileEntity) {
TransportedItemStack stackAtOffset = ((BeltTileEntity) controller).getInventory()
.getStackAtOffset(beltTE.index);
if (stackAtOffset != null)
makeParticleEffect(VecHelper.getCenterOf(pos.down(2)).add(0, 5 / 16f, 0),
stackAtOffset.stack);
if (onBasin()) {
if (!world.isRemote) {
pressedItems.clear();
applyBasinRecipe();
IItemHandler orElse = basinInv.orElse(null);
if (basinInv.isPresent() && orElse instanceof BasinInventory) {
BasinInventory inv = (BasinInventory) orElse;
for (int slot = 0; slot < inv.getInputHandler().getSlots(); slot++) {
ItemStack stackInSlot = inv.getStackInSlot(slot);
if (stackInSlot.isEmpty())
continue;
pressedItems.add(stackInSlot);
}
}
sendData();
}
}
@ -136,9 +197,18 @@ public class MechanicalPressTileEntity extends KineticTileEntity {
if (!world.isRemote && runningTicks > 60) {
finished = true;
if (!beltMode)
if (inWorld())
finished = world.isBlockPowered(pos);
running = false;
if (onBasin()) {
gatherInputs();
if (matchBasinRecipe(lastRecipe)) {
startProcessingBasin();
}
}
pressedItems.clear();
sendData();
return;
}
@ -146,12 +216,41 @@ public class MechanicalPressTileEntity extends KineticTileEntity {
runningTicks++;
}
public void makeParticleEffect(Vec3d pos, ItemStack stack) {
protected void spawnParticles() {
if (pressedItems.isEmpty())
return;
if (mode == Mode.BASIN) {
pressedItems.forEach(stack -> makeCompactingParticleEffect(VecHelper.getCenterOf(pos.down(2)), stack));
}
if (mode == Mode.BELT) {
pressedItems.forEach(
stack -> makePressingParticleEffect(VecHelper.getCenterOf(pos.down(2)).add(0, 8 / 16f, 0), stack));
}
if (mode == Mode.WORLD) {
pressedItems.forEach(
stack -> makePressingParticleEffect(VecHelper.getCenterOf(pos.down(1)).add(0, -1 / 4f, 0), stack));
}
pressedItems.clear();
}
public void makePressingParticleEffect(Vec3d pos, ItemStack stack) {
if (world.isRemote) {
for (int i = 0; i < 20; i++) {
Vec3d motion = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .25f).mul(1, 0, 1);
Vec3d motion = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .125f).mul(1, 0, 1);
world.addParticle(new ItemParticleData(ParticleTypes.ITEM, stack), pos.x, pos.y - .25f, pos.z, motion.x,
motion.y + .125f, motion.z);
}
}
}
public void makeCompactingParticleEffect(Vec3d pos, ItemStack stack) {
if (world.isRemote) {
for (int i = 0; i < 20; i++) {
Vec3d motion = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .175f).mul(1, 0, 1);
world.addParticle(new ItemParticleData(ParticleTypes.ITEM, stack), pos.x, pos.y, pos.z, motion.x,
motion.y, motion.z);
motion.y + .25f, motion.z);
}
}
}
@ -163,4 +262,59 @@ public class MechanicalPressTileEntity extends KineticTileEntity {
return recipe;
}
public static boolean canCompress(NonNullList<Ingredient> ingredients) {
return (ingredients.size() == 4 || ingredients.size() == 9)
&& ItemHelper.condenseIngredients(ingredients).size() == 1;
}
@Override
protected <C extends IInventory> boolean matchStaticFilters(IRecipe<C> recipe) {
return recipe instanceof ICraftingRecipe && canCompress(recipe.getIngredients());
}
@Override
protected <C extends IInventory> boolean matchBasinRecipe(IRecipe<C> recipe) {
if (recipe == null)
return false;
NonNullList<Ingredient> ingredients = recipe.getIngredients();
if (!ingredients.stream().allMatch(Ingredient::isSimple))
return false;
List<ItemStack> remaining = new ArrayList<>();
inputs.forEach(stack -> remaining.add(stack.copy()));
Ingredients: for (Ingredient ingredient : ingredients) {
for (ItemStack stack : remaining) {
if (stack.isEmpty())
continue;
if (ingredient.test(stack)) {
stack.shrink(1);
continue Ingredients;
}
}
return false;
}
return true;
}
@Override
protected Object getRecipeCacheKey() {
return compressingRecipesKey;
}
@Override
public void startProcessingBasin() {
if (running)
return;
super.startProcessingBasin();
start(Mode.BASIN);
}
@Override
protected void basinRemoved() {
pressedItems.clear();
super.basinRemoved();
}
}

View file

@ -5,8 +5,8 @@ import static com.simibubi.create.modules.contraptions.components.saw.SawBlock.R
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import com.google.common.base.Predicates;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllRecipes;
import com.simibubi.create.AllTileEntities;
@ -16,8 +16,6 @@ import com.simibubi.create.foundation.utility.TreeCutter.Tree;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.recipe.RecipeConditions;
import com.simibubi.create.foundation.utility.recipe.RecipeFinder;
import com.simibubi.create.foundation.utility.recipe.RecipeFinder.StartedSearch;
import com.simibubi.create.foundation.utility.recipe.RecipeFinder.StartedSearch.RecipeStream;
import com.simibubi.create.modules.contraptions.components.actors.BlockBreakingKineticTileEntity;
import com.simibubi.create.modules.contraptions.processing.ProcessingInventory;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
@ -283,11 +281,11 @@ public class SawTileEntity extends BlockBreakingKineticTileEntity implements IHa
}
private List<? extends IRecipe<?>> getRecipes() {
StartedSearch startedSearch = RecipeFinder.get(cuttingRecipesKey, world,
List<IRecipe<?>> startedSearch = RecipeFinder.get(cuttingRecipesKey, world,
RecipeConditions.isOfType(IRecipeType.STONECUTTING, AllRecipes.Types.CUTTING));
RecipeStream<IRecipe<?>> search = startedSearch.search();
return search.filter(Predicates.and(RecipeConditions.outputMatchesFilter(filter),
RecipeConditions.firstIngredientMatches(inventory.getStackInSlot(0))));
return startedSearch.stream().filter(RecipeConditions.outputMatchesFilter(filter))
.filter(RecipeConditions.firstIngredientMatches(inventory.getStackInSlot(0)))
.collect(Collectors.toList());
}
public void insertItem(ItemEntity entity) {

View file

@ -0,0 +1,166 @@
package com.simibubi.create.modules.contraptions.processing;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import com.simibubi.create.foundation.utility.recipe.RecipeFinder;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.processing.BasinTileEntity.BasinInventory;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.BucketItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
public abstract class BasinOperatingTileEntity extends KineticTileEntity {
public boolean checkBasin;
public boolean basinRemoved;
protected IRecipe<?> lastRecipe;
protected LazyOptional<IItemHandler> basinInv = LazyOptional.empty();
protected List<ItemStack> inputs;
public BasinOperatingTileEntity(TileEntityType<?> typeIn) {
super(typeIn);
checkBasin = true;
}
@Override
public void onSpeedChanged(float prevSpeed) {
super.onSpeedChanged(prevSpeed);
checkBasin = true;
}
public void gatherInputs() {
BasinInventory inv = (BasinInventory) basinInv.orElse(null);
inputs = new ArrayList<>();
IItemHandlerModifiable inputHandler = inv.getInputHandler();
for (int slot = 0; slot < inputHandler.getSlots(); ++slot) {
ItemStack itemstack = inputHandler.extractItem(slot, inputHandler.getSlotLimit(slot), true);
if (!itemstack.isEmpty()) {
inputs.add(itemstack);
}
}
}
@Override
public void tick() {
super.tick();
if (basinRemoved) {
basinRemoved = false;
basinRemoved();
sendData();
return;
}
if (!isSpeedRequirementFulfilled())
return;
if (!isCheckingBasin())
return;
if (!checkBasin)
return;
checkBasin = false;
TileEntity basinTE = world.getTileEntity(pos.down(2));
if (basinTE == null || !(basinTE instanceof BasinTileEntity))
return;
if (!basinInv.isPresent())
basinInv = basinTE.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
if (!basinInv.isPresent())
return;
if (world.isRemote)
return;
gatherInputs();
// if (matchBasinRecipe(lastRecipe)) {
// startProcessingBasin();
// sendData();
// return;
// }
List<IRecipe<?>> recipes = getMatchingRecipes();
if (recipes.isEmpty())
return;
lastRecipe = recipes.get(0);
startProcessingBasin();
sendData();
}
protected boolean isCheckingBasin() {
return true;
}
public void startProcessingBasin() {
}
public boolean continueWithPreviousRecipe() {
return true;
}
public void applyBasinRecipe() {
if (lastRecipe == null)
return;
if (!basinInv.isPresent())
return;
BasinInventory inv = (BasinInventory) basinInv.orElse(null);
if (inv == null)
return;
IItemHandlerModifiable inputs = inv.getInputHandler();
IItemHandlerModifiable outputs = inv.getOutputHandler();
int buckets = 0;
Ingredients: for (Ingredient ingredient : lastRecipe.getIngredients()) {
for (int slot = 0; slot < inputs.getSlots(); slot++) {
if (!ingredient.test(inputs.extractItem(slot, 1, true)))
continue;
ItemStack extracted = inputs.extractItem(slot, 1, false);
if (extracted.getItem() instanceof BucketItem)
buckets++;
continue Ingredients;
}
// something wasn't found
return;
}
ItemHandlerHelper.insertItemStacked(outputs, lastRecipe.getRecipeOutput().copy(), false);
if (buckets > 0)
ItemHandlerHelper.insertItemStacked(outputs, new ItemStack(Items.BUCKET, buckets), false);
// Continue mixing
gatherInputs();
if (matchBasinRecipe(lastRecipe)) {
continueWithPreviousRecipe();
sendData();
}
}
protected List<IRecipe<?>> getMatchingRecipes() {
List<IRecipe<?>> list = RecipeFinder.get(getRecipeCacheKey(), world, this::matchStaticFilters);
return list.stream().filter(this::matchBasinRecipe)
.sorted((r1, r2) -> -r1.getIngredients().size() + r2.getIngredients().size())
.collect(Collectors.toList());
}
protected void basinRemoved() {
}
protected abstract <C extends IInventory> boolean matchStaticFilters(IRecipe<C> recipe);
protected abstract <C extends IInventory> boolean matchBasinRecipe(IRecipe<C> recipe);
protected abstract Object getRecipeCacheKey();
}

View file

@ -2,7 +2,6 @@ package com.simibubi.create.modules.contraptions.processing;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.block.SyncedTileEntity;
import com.simibubi.create.modules.contraptions.components.mixer.MechanicalMixerTileEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
@ -27,7 +26,7 @@ public class BasinTileEntity extends SyncedTileEntity implements ITickableTileEn
markDirty();
}
};
public class BasinInputInventory extends RecipeWrapper {
public BasinInputInventory() {
super(inputInventory);
@ -99,15 +98,15 @@ public class BasinTileEntity extends SyncedTileEntity implements ITickableTileEn
compound.put("OutputItems", outputInventory.serializeNBT());
return compound;
}
public void onEmptied() {
TileEntity te = world.getTileEntity(pos.up(2));
if (te == null)
return;
if (te instanceof MechanicalMixerTileEntity)
((MechanicalMixerTileEntity) te).basinRemoved = true;
if (te instanceof BasinOperatingTileEntity)
((BasinOperatingTileEntity) te).basinRemoved = true;
}
@Override
public void remove() {
onEmptied();
@ -131,8 +130,8 @@ public class BasinTileEntity extends SyncedTileEntity implements ITickableTileEn
TileEntity te = world.getTileEntity(pos.up(2));
if (te == null)
return;
if (te instanceof MechanicalMixerTileEntity)
((MechanicalMixerTileEntity) te).checkBasin = true;
if (te instanceof BasinOperatingTileEntity)
((BasinOperatingTileEntity) te).checkBasin = true;
}

View file

@ -43,7 +43,9 @@ public class ProcessingRecipeSerializer<T extends ProcessingRecipe<?>>
results.add(new StochasticOutput(itemstack, chance));
}
int duration = JSONUtils.getInt(json, "processingTime");
int duration = -1;
if (JSONUtils.hasField(json, "processingTime"))
duration = JSONUtils.getInt(json, "processingTime");
return this.factory.create(recipeId, s, ingredients, results, duration);
}

View file

@ -184,7 +184,11 @@
"create.recipe.smokingViaFan.fan": "Fan behind Fire",
"create.recipe.blastingViaFan": "Bulk Smelting",
"create.recipe.blastingViaFan.fan": "Fan behind Lava",
"create.recipe.pressing": "Mechanical Press",
"create.recipe.pressing": "Pressing",
"create.recipe.mixing": "Mixing",
"create.recipe.packing": "Compressing",
"create.recipe.sawing": "Sawing",
"create.recipe.block_cutting": "Block Cutting",
"create.recipe.blockzapperUpgrade": "Handheld Blockzapper",
"create.recipe.processing.chance": "%1$s%% Chance",
@ -571,7 +575,7 @@
"block.create.mechanical_mixer.tooltip.condition1": "When above Basin",
"block.create.mechanical_mixer.tooltip.behaviour1": "Starts to mix items in the basin whenever all necessary ingredients are present.",
"block.create.mechanical_mixer.tooltip.condition2": "When used with Wrench",
"block.create.mechanical_mixer.tooltip.behaviour2": "_Configures_ the minimum amount of _different_ _ingredients_ required before the mixer should begin. Use this option to rule out unwanted recipes with similar but less ingredients.",
"block.create.mechanical_mixer.tooltip.behaviour2": "_Configures_ the minimum amount of _total_ _ingredients_ for applied recipes. Use this option to _rule_ _out_ _unwanted_ _recipes_ with similar but less ingredients.",
"block.create.mechanical_piston.tooltip": "MECHANICAL PISTON",
"block.create.mechanical_piston.tooltip.summary": "A more advanced version of the _Piston,_ using _Rotational_ _Force_ to precisely move attached structures. _Piston_ _Extension_ _Poles_ at the rear define the _Range_ of this Device. Without extensions, the piston will not move. Use _Translation_ _Chassis_ to move more than a single line of blocks.",

View file

@ -0,0 +1,18 @@
{
"type": "create:mixing",
"group": "minecraft:misc",
"ingredients": [
{
"item": "minecraft:andesite"
},
{
"item": "minecraft:iron_nugget"
}
],
"results": [
{
"item": "create:andesite_alloy_cube",
"count": 1
}
]
}