More unfinished basin business

- Fixed players flailing their limbs around while standing still on a moving contraption
- Attempted to reduce drag of remote player positions while on a contraption
- Contraptions no longer log out with the player riding them
- Attribute filters are now made of brass
- Added the ability to disable auto-compat with vanilla recipe types in the configs
- Added a recipe type for custom basin/press compacting
- Basins can now process items and liquids in recipes
- Input items/fluids of a basin can now be extracted or reused in further processing
- A basin diagonally below another basin with collect outputs of recipes processed in the top basin for ease of automation
- (Temporary debug recipes)
This commit is contained in:
simibubi 2020-10-06 20:43:13 +02:00
parent b14e94929c
commit 378164b8b9
63 changed files with 1685 additions and 716 deletions

View file

@ -18,7 +18,7 @@ a579c40c43dc2174afb66f42d00d0c4a0efaaeee assets/create/blockstates/andesite_bric
11908c2f8603e61bec88010bc6d0890e6339c6b1 assets/create/blockstates/andesite_funnel.json
398922758a6219544e5b85c91c9cf8a543b437e5 assets/create/blockstates/andesite_pillar.json
1d2d8081581e07d9be4b382aede4f2de4401cc6b assets/create/blockstates/andesite_tunnel.json
f9fa6aa530eb0891a74eadfbebc663172a57147a assets/create/blockstates/basin.json
e555e3c2b2d3f01440e48db4ba88f7e00fd99b6f assets/create/blockstates/basin.json
f25693a9429f6337149ff24f27900dc4eb82a7c2 assets/create/blockstates/belt.json
cf9045eb16e5299a1d917c4cb536289f49411276 assets/create/blockstates/birch_window.json
94a1a91403eb4b035fec48071e7fcae57a8a6abd assets/create/blockstates/birch_window_pane.json
@ -1045,7 +1045,7 @@ b0f664dd6de3d0ee9afcb6223fbcd53b97fa0d65 assets/create/models/item/andesite_cobb
7490819e7e5445019b6b8cb2538f12a5b6717a46 assets/create/models/item/andesite_funnel.json
75b8b00c2418b9660d35a7fabd0774925cf1c02f assets/create/models/item/andesite_pillar.json
c0e35daccfb398947532e9499d6bda963387cd9c assets/create/models/item/andesite_tunnel.json
bf1fc6bdf7fca6f1958a2d3e96202c1cecb50669 assets/create/models/item/basin.json
421e481b7fbca4c4a1080ed703401eb25375e087 assets/create/models/item/basin.json
1da382e7e58eaa9788f5b1d92221ccac573e068f assets/create/models/item/belt_connector.json
9044243882cfd49a2827e1b910a4c9b0e46daa47 assets/create/models/item/birch_window.json
6ed49f59ea91068ef68720f43e67a9237594bdf0 assets/create/models/item/birch_window_pane.json
@ -1481,7 +1481,7 @@ d531f87f425d199aee4777a588c1cd6cab6f5173 data/create/advancements/recipes/create
2eef3201017af03f6a2f0f015645e3ff5e25d9c1 data/create/advancements/recipes/create.base/crafting/curiosities/wand_of_symmetry.json
d97d96e1b2ddd25df15fe1ef1c3d084f15bb9789 data/create/advancements/recipes/create.base/crafting/kinetics/adjustable_pulley.json
92416ced6ede6965fd728e1c7336bb05a3e41ea2 data/create/advancements/recipes/create.base/crafting/kinetics/analog_lever.json
2105b4f1fd9a170a100efc083a794fdb9e068924 data/create/advancements/recipes/create.base/crafting/kinetics/attribute_filter.json
3e9753006da898d4569bbeabf95997e8c90847c8 data/create/advancements/recipes/create.base/crafting/kinetics/attribute_filter.json
bec8c280b717306f87050b08a418feab53be71cb data/create/advancements/recipes/create.base/crafting/kinetics/basin.json
5af08853632fb5970fe542b3ecbde0ad16d64714 data/create/advancements/recipes/create.base/crafting/kinetics/belt_connector.json
80d87f1dde60adb5334e0cff25a9f0b7f67c1526 data/create/advancements/recipes/create.base/crafting/kinetics/black_seat.json
@ -2428,6 +2428,8 @@ d9021504be855cd2d4d91503a82b84233052adb0 data/create/recipes/blasting/gold_ingot
c323b106e88b7de77fea71ff12494abdbb818d15 data/create/recipes/chiseled_limestone_from_limestone_stonecutting.json
da9a919b476954c1de34826aa7706bf6056a8f12 data/create/recipes/chiseled_scoria_from_scoria_stonecutting.json
09faa4ddcf9f3907dcdb3ab3e8b68c1deb2486e5 data/create/recipes/chiseled_weathered_limestone_from_weathered_limestone_stonecutting.json
c3cfdc9552a23e4749c42e71fbddd153b76ca708 data/create/recipes/compacting/ice.json
5758a1804287c261e1c953bda599d8495ba7c40a data/create/recipes/compacting/temp_gabbro.json
19526da3a59fc136654ff1bc93c0251581f397a9 data/create/recipes/crafting/appliances/dough.json
7b5f863dda3d05a79cb85943a178eba0bd8a7dc7 data/create/recipes/crafting/appliances/slime_ball.json
b159ba84428eee6ef6e23df1766f2a18f2c8a63e data/create/recipes/crafting/appliances/tree_fertilizer.json
@ -2437,7 +2439,7 @@ b159ba84428eee6ef6e23df1766f2a18f2c8a63e data/create/recipes/crafting/appliances
fcbc04d0a7eaf820a74bc7e4736a4a581e0a9dff data/create/recipes/crafting/curiosities/wand_of_symmetry.json
696df0fe5f8e29220ea15527f8c119c39b418819 data/create/recipes/crafting/kinetics/adjustable_pulley.json
88de51b451469698665b7319e5b9cfb9a87ae3e0 data/create/recipes/crafting/kinetics/analog_lever.json
6912101930aae627820783c27358dcf2ff4016aa data/create/recipes/crafting/kinetics/attribute_filter.json
cf1f3a6306d47025cebe153cf05949ef69ccbe5a data/create/recipes/crafting/kinetics/attribute_filter.json
059d12526529b2896ed583555373afa31839a0de data/create/recipes/crafting/kinetics/basin.json
dcf98e667d321fb4bd9fa6dfec7927a84cdbd5d6 data/create/recipes/crafting/kinetics/belt_connector.json
1123903a11b13448b61cf8f8a5dc2e8013d39ac0 data/create/recipes/crafting/kinetics/black_seat.json
@ -2675,6 +2677,7 @@ ddda28bb6efc43b7e3149756daf53e1664187283 data/create/recipes/dolomite_cobbleston
500ecdfdcf34e9d26256948e206aab4f0b79e659 data/create/recipes/dolomite_cobblestone_wall_from_dolomite_cobblestone_stonecutting.json
ff39e629b242ae91e23aec86b0a1f757dd938305 data/create/recipes/dolomite_pillar.json
b4a8d14d9a20e812e0acb691b5b511a87e8b0576 data/create/recipes/dolomite_pillar_from_dolomite_stonecutting.json
ae6698363e49f7cb5f2ed52c6b8805bebed31fa2 data/create/recipes/emptying/water_bottle.json
0e11aa1accb71ed62e212f23a7069b7b7b4e8119 data/create/recipes/fancy_andesite_bricks_from_andesite_stonecutting.json
8b86fc9a9416adeaab3f26192a73a481887675c3 data/create/recipes/fancy_andesite_bricks_slab.json
c7b762b25c7a6705dba3e922e981be851ac4f36b data/create/recipes/fancy_andesite_bricks_slab_from_fancy_andesite_bricks_stonecutting.json
@ -2860,6 +2863,7 @@ e7bfaa806d57573d060fac0a5e7a84f345b8bb85 data/create/recipes/mixing/andesite_all
76939e4d3e5b615ae21d14c0ff7b917a622bcf80 data/create/recipes/mixing/chromatic_compound.json
d9a3dff1288d675ab812eef1eb73cb27dcc71bd2 data/create/recipes/mixing/crushed_brass.json
00b165ea38d834c7955440e87062004a8182c3f8 data/create/recipes/mixing/gunpowder.json
35c4e8a765479861f307afb9ec650f912f92b998 data/create/recipes/mixing/temp_cobble.json
1998c6f84f871d6da58ec29d729401d18f8f1aa1 data/create/recipes/mossy_andesite_from_andesite_stonecutting.json
89929d9cb11b5c589b2ecfa821c61add1ef7b62b data/create/recipes/mossy_dark_scoria_from_dark_scoria_stonecutting.json
4b8b1191dd3a21294293dc5ad237af89b849df28 data/create/recipes/mossy_diorite_from_diorite_stonecutting.json

View file

@ -1,7 +1,22 @@
{
"variants": {
"": {
"model": "create:block/basin"
"facing=down": {
"model": "create:block/basin/block"
},
"facing=north": {
"model": "create:block/basin/block_directional",
"y": 180
},
"facing=south": {
"model": "create:block/basin/block_directional"
},
"facing=west": {
"model": "create:block/basin/block_directional",
"y": 90
},
"facing=east": {
"model": "create:block/basin/block_directional",
"y": 270
}
}
}

View file

@ -149,8 +149,8 @@
},
{
"when": {
"sticky_north": "true",
"axis": "x"
"axis": "x",
"sticky_north": "true"
},
"apply": {
"model": "create:block/radial_chassis_side_x_sticky"
@ -158,8 +158,8 @@
},
{
"when": {
"sticky_north": "true",
"axis": "y"
"axis": "y",
"sticky_north": "true"
},
"apply": {
"model": "create:block/radial_chassis_side_y_sticky",
@ -168,8 +168,8 @@
},
{
"when": {
"sticky_north": "true",
"axis": "z"
"axis": "z",
"sticky_north": "true"
},
"apply": {
"model": "create:block/radial_chassis_side_x_sticky",
@ -178,8 +178,8 @@
},
{
"when": {
"sticky_north": "false",
"axis": "x"
"axis": "x",
"sticky_north": "false"
},
"apply": {
"model": "create:block/radial_chassis_side_x"
@ -187,8 +187,8 @@
},
{
"when": {
"sticky_north": "false",
"axis": "y"
"axis": "y",
"sticky_north": "false"
},
"apply": {
"model": "create:block/radial_chassis_side_y",
@ -197,8 +197,8 @@
},
{
"when": {
"sticky_north": "false",
"axis": "z"
"axis": "z",
"sticky_north": "false"
},
"apply": {
"model": "create:block/radial_chassis_side_x",

View file

@ -1,3 +1,3 @@
{
"parent": "create:block/basin"
"parent": "create:block/basin/block"
}

View file

@ -11,7 +11,7 @@
"conditions": {
"items": [
{
"item": "create:andesite_alloy"
"tag": "forge:ingots/brass"
}
]
}

View file

@ -0,0 +1,14 @@
{
"type": "create:compacting",
"ingredients": [
{
"item": "minecraft:ice"
}
],
"results": [
{
"fluid": "minecraft:water",
"amount": 250
}
]
}

View file

@ -0,0 +1,18 @@
{
"type": "create:compacting",
"ingredients": [
{
"item": "minecraft:cobblestone"
},
{
"fluidTag": "minecraft:lava",
"amount": 250
}
],
"results": [
{
"item": "create:gabbro",
"count": 1
}
]
}

View file

@ -8,7 +8,7 @@
"tag": "minecraft:wool"
},
"A": {
"tag": "forge:nuggets/copper"
"tag": "forge:nuggets/brass"
}
},
"result": {

View file

@ -0,0 +1,18 @@
{
"type": "create:emptying",
"ingredients": [
{
"item": "minecraft:potion"
}
],
"results": [
{
"item": "minecraft:glass_bottle",
"count": 1
},
{
"fluid": "minecraft:water",
"amount": 250
}
]
}

View file

@ -0,0 +1,19 @@
{
"type": "create:mixing",
"ingredients": [
{
"fluidTag": "minecraft:water",
"amount": 250
},
{
"fluidTag": "minecraft:lava",
"amount": 250
}
],
"results": [
{
"item": "minecraft:cobblestone",
"count": 1
}
]
}

View file

@ -73,6 +73,7 @@ import com.simibubi.create.content.contraptions.fluids.tank.FluidTankGenerator;
import com.simibubi.create.content.contraptions.fluids.tank.FluidTankItem;
import com.simibubi.create.content.contraptions.fluids.tank.FluidTankModel;
import com.simibubi.create.content.contraptions.processing.BasinBlock;
import com.simibubi.create.content.contraptions.processing.BasinGenerator;
import com.simibubi.create.content.contraptions.processing.BasinMovementBehaviour;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlockItem;
@ -408,9 +409,10 @@ public class AllBlocks {
public static final BlockEntry<BasinBlock> BASIN = REGISTRATE.block("basin", BasinBlock::new)
.initialProperties(SharedProperties::stone)
.blockstate((ctx, prov) -> prov.simpleBlock(ctx.getEntry(), AssetLookup.standardModel(ctx, prov)))
.blockstate(new BasinGenerator()::generate)
.onRegister(addMovementBehaviour(new BasinMovementBehaviour()))
.simpleItem()
.item()
.transform(customItemModel("_", "block"))
.register();
public static final BlockEntry<BlazeBurnerBlock> BLAZE_BURNER =

View file

@ -1,5 +1,6 @@
package com.simibubi.create;
import java.util.Optional;
import java.util.function.Supplier;
import com.simibubi.create.compat.jei.ConversionRecipe;
@ -7,10 +8,13 @@ import com.simibubi.create.content.contraptions.components.crafter.MechanicalCra
import com.simibubi.create.content.contraptions.components.crusher.CrushingRecipe;
import com.simibubi.create.content.contraptions.components.fan.SplashingRecipe;
import com.simibubi.create.content.contraptions.components.millstone.MillingRecipe;
import com.simibubi.create.content.contraptions.components.mixer.CompactingRecipe;
import com.simibubi.create.content.contraptions.components.mixer.MixingRecipe;
import com.simibubi.create.content.contraptions.components.press.PressingRecipe;
import com.simibubi.create.content.contraptions.components.saw.CuttingRecipe;
import com.simibubi.create.content.contraptions.fluids.actors.FillingRecipe;
import com.simibubi.create.content.contraptions.processing.BasinRecipe;
import com.simibubi.create.content.contraptions.processing.EmptyingRecipe;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipe;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeFactory;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipeSerializer;
@ -25,22 +29,26 @@ import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.ShapedRecipe;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.World;
import net.minecraftforge.event.RegistryEvent;
public enum AllRecipeTypes {
BLOCKZAPPER_UPGRADE(BlockzapperUpgradeRecipe.Serializer::new, IRecipeType.CRAFTING),
MECHANICAL_CRAFTING(MechanicalCraftingRecipe.Serializer::new),
CONVERSION(processingSerializer(ConversionRecipe::new)),
CRUSHING(processingSerializer(CrushingRecipe::new)),
CUTTING(processingSerializer(CuttingRecipe::new)),
MILLING(processingSerializer(MillingRecipe::new)),
BASIN(processingSerializer(BasinRecipe::new)),
MIXING(processingSerializer(MixingRecipe::new)),
COMPACTING(processingSerializer(CompactingRecipe::new)),
PRESSING(processingSerializer(PressingRecipe::new)),
SANDPAPER_POLISHING(processingSerializer(SandPaperPolishingRecipe::new)),
SPLASHING(processingSerializer(SplashingRecipe::new)),
FILLING(processingSerializer(FillingRecipe::new)),
EMPTYING(processingSerializer(EmptyingRecipe::new)),
;
@ -89,4 +97,9 @@ public enum AllRecipeTypes {
public <T extends IRecipeType<?>> T getType() {
return (T) type;
}
public <C extends IInventory, T extends IRecipe<C>> Optional<T> find(C inv, World world) {
return world.getRecipeManager()
.getRecipe(getType(), inv, world);
}
}

View file

@ -23,10 +23,12 @@ import com.simibubi.create.compat.jei.category.PressingCategory;
import com.simibubi.create.compat.jei.category.SawingCategory;
import com.simibubi.create.compat.jei.category.SmokingViaFanCategory;
import com.simibubi.create.compat.jei.category.SplashingCategory;
import com.simibubi.create.content.contraptions.components.mixer.MixingRecipe;
import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity;
import com.simibubi.create.content.contraptions.processing.BasinRecipe;
import com.simibubi.create.content.logistics.block.inventories.AdjustableCrateScreen;
import com.simibubi.create.content.schematics.block.SchematicannonScreen;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.config.CRecipes;
import com.simibubi.create.foundation.utility.Lang;
import mezz.jei.api.IModPlugin;
@ -96,44 +98,62 @@ public class CreateJEI implements IModPlugin {
@Override
public void registerCategories(IRecipeCategoryRegistration registration) {
registration
.addRecipeCategories(millingCategory, crushingCategory, splashingCategory, pressingCategory,
smokingCategory, blastingCategory, blockzapperCategory, mixingCategory, sawingCategory,
blockCuttingCategory, packingCategory, polishingCategory, mysteryConversionCategory,
mechanicalCraftingCategory);
registration.addRecipeCategories(millingCategory, crushingCategory, splashingCategory, pressingCategory,
smokingCategory, blastingCategory, blockzapperCategory, mixingCategory, sawingCategory,
blockCuttingCategory, packingCategory, polishingCategory, mysteryConversionCategory,
mechanicalCraftingCategory);
}
@Override
public void registerRecipes(IRecipeRegistration registration) {
CRecipes recipeConfig = AllConfigs.SERVER.recipes;
registration.addRecipes(findRecipes(AllRecipeTypes.MILLING), millingCategory.getUid());
registration.addRecipes(findRecipes(AllRecipeTypes.CRUSHING), crushingCategory.getUid());
registration.addRecipes(findRecipesByTypeExcluding(AllRecipeTypes.MILLING.getType(), AllRecipeTypes.CRUSHING.getType()),
crushingCategory.getUid());
registration.addRecipes(
findRecipesByTypeExcluding(AllRecipeTypes.MILLING.getType(), AllRecipeTypes.CRUSHING.getType()),
crushingCategory.getUid());
registration.addRecipes(findRecipes(AllRecipeTypes.SPLASHING), splashingCategory.getUid());
registration.addRecipes(findRecipes(AllRecipeTypes.PRESSING), pressingCategory.getUid());
registration.addRecipes(findRecipesById(AllRecipeTypes.BLOCKZAPPER_UPGRADE.serializer.getRegistryName()),
blockzapperCategory.getUid());
blockzapperCategory.getUid());
registration.addRecipes(findRecipesByType(IRecipeType.SMOKING), smokingCategory.getUid());
registration.addRecipes(findRecipesByTypeExcluding(IRecipeType.SMELTING, IRecipeType.SMOKING),
blastingCategory.getUid());
blastingCategory.getUid());
registration.addRecipes(findRecipes(AllRecipeTypes.MIXING), mixingCategory.getUid());
registration.addRecipes(findRecipes(r -> r.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS
&& !MechanicalPressTileEntity.canCompress(r.getIngredients())).stream().map(MixingRecipe::convertShapeless)
.collect(Collectors.toList()),
if (recipeConfig.allowShapelessInMixer.get())
registration.addRecipes(findRecipes(r -> r.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS
&& !MechanicalPressTileEntity.canCompress(r.getIngredients())).stream()
.map(BasinRecipe::convert)
.collect(Collectors.toList()),
mixingCategory.getUid());
registration.addRecipes(findRecipes(AllRecipeTypes.CUTTING), sawingCategory.getUid());
registration.addRecipes(
if (recipeConfig.allowStonecuttingOnSaw.get())
registration.addRecipes(
CondensedBlockCuttingRecipe.condenseRecipes(findRecipesByType(IRecipeType.STONECUTTING)),
blockCuttingCategory.getUid());
registration.addRecipes(findRecipes(
r -> (r instanceof ICraftingRecipe) && MechanicalPressTileEntity.canCompress(r.getIngredients())),
registration.addRecipes(findRecipes(AllRecipeTypes.COMPACTING), packingCategory.getUid());
if (recipeConfig.allowShapedSquareInPress.get())
registration.addRecipes(findRecipes(
r -> (r instanceof ICraftingRecipe) && MechanicalPressTileEntity.canCompress(r.getIngredients()))
.stream()
.map(BasinRecipe::convert)
.collect(Collectors.toList()),
packingCategory.getUid());
registration.addRecipes(findRecipes(AllRecipeTypes.SANDPAPER_POLISHING), polishingCategory.getUid());
registration.addRecipes(MysteriousItemConversionCategory.getRecipes(), mysteryConversionCategory.getUid());
registration.addRecipes(findRecipes(r -> (r.getType() == AllRecipeTypes.MECHANICAL_CRAFTING.type)),
mechanicalCraftingCategory.getUid());
registration.addRecipes(findRecipes(r -> (r.getType() == IRecipeType.CRAFTING
&& r.getType() != AllRecipeTypes.MECHANICAL_CRAFTING.type) && (r instanceof ShapedRecipe)),
mechanicalCraftingCategory.getUid());
if (recipeConfig.allowRegularCraftingInCrafter.get())
registration.addRecipes(findRecipes(
r -> (r.getType() == IRecipeType.CRAFTING && r.getType() != AllRecipeTypes.MECHANICAL_CRAFTING.type)
&& (r instanceof ShapedRecipe)),
mechanicalCraftingCategory.getUid());
}
@ -141,17 +161,13 @@ public class CreateJEI implements IModPlugin {
public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) {
ItemStack fan = new ItemStack(AllBlocks.ENCASED_FAN.get());
ItemStack splashingFan = fan
.copy()
.setDisplayName(new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.splashing.fan")));
ItemStack smokingFan = fan
.copy()
.setDisplayName(
new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.smokingViaFan.fan")));
ItemStack blastingFan = fan
.copy()
.setDisplayName(
new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.blastingViaFan.fan")));
ItemStack splashingFan = fan.copy()
.setDisplayName(new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.splashing.fan")));
ItemStack smokingFan = fan.copy()
.setDisplayName(new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.smokingViaFan.fan")));
ItemStack blastingFan = fan.copy()
.setDisplayName(
new StringTextComponent(TextFormatting.RESET + Lang.translate("recipe.blastingViaFan.fan")));
registration.addRecipeCatalyst(new ItemStack(AllBlocks.MILLSTONE.get()), millingCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(AllBlocks.CRUSHING_WHEEL.get()), crushingCategory.getUid());
@ -170,9 +186,8 @@ public class CreateJEI implements IModPlugin {
registration.addRecipeCatalyst(new ItemStack(AllBlocks.BASIN.get()), packingCategory.getUid());
registration.addRecipeCatalyst(AllItems.SAND_PAPER.asStack(), polishingCategory.getUid());
registration.addRecipeCatalyst(AllItems.RED_SAND_PAPER.asStack(), polishingCategory.getUid());
registration
.addRecipeCatalyst(new ItemStack(AllBlocks.MECHANICAL_CRAFTER.get()),
mechanicalCraftingCategory.getUid());
registration.addRecipeCatalyst(new ItemStack(AllBlocks.MECHANICAL_CRAFTER.get()),
mechanicalCraftingCategory.getUid());
}
@Override
@ -186,30 +201,29 @@ public class CreateJEI implements IModPlugin {
}
private static List<IRecipe<?>> findRecipes(Predicate<IRecipe<?>> pred) {
return Minecraft.getInstance().world
.getRecipeManager()
.getRecipes()
.stream()
.filter(pred)
.collect(Collectors.toList());
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());
return Minecraft.getInstance().world.getRecipeManager()
.getRecipes()
.stream()
.filter(r -> r.getType() == type)
.collect(Collectors.toList());
}
private static List<IRecipe<?>> findRecipesById(ResourceLocation id) {
return Minecraft.getInstance().world
.getRecipeManager()
.getRecipes()
.stream()
.filter(r -> r.getSerializer().getRegistryName().equals(id))
.collect(Collectors.toList());
return Minecraft.getInstance().world.getRecipeManager()
.getRecipes()
.stream()
.filter(r -> r.getSerializer()
.getRegistryName()
.equals(id))
.collect(Collectors.toList());
}
private static List<IRecipe<?>> findRecipesByTypeExcluding(IRecipeType<?> type, IRecipeType<?> excludingType) {
@ -217,10 +231,14 @@ public class CreateJEI implements IModPlugin {
List<IRecipe<?>> byExcludingType = findRecipesByType(excludingType);
byType.removeIf(recipe -> {
for (IRecipe<?> r : byExcludingType) {
ItemStack[] matchingStacks = recipe.getIngredients().get(0).getMatchingStacks();
ItemStack[] matchingStacks = recipe.getIngredients()
.get(0)
.getMatchingStacks();
if (matchingStacks.length == 0)
return true;
if (r.getIngredients().get(0).test(matchingStacks[0]))
if (r.getIngredients()
.get(0)
.test(matchingStacks[0]))
return true;
}
return false;

View file

@ -0,0 +1,88 @@
package com.simibubi.create.compat.jei.category;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.mutable.MutableInt;
import com.simibubi.create.content.contraptions.processing.BasinRecipe;
import com.simibubi.create.content.contraptions.processing.HeatCondition;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.Pair;
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 net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.NonNullList;
public class BasinCategory extends CreateRecipeCategory<BasinRecipe> {
public BasinCategory(String id, IDrawable icon, IDrawable background) {
super(id, icon, background);
}
@Override
public Class<? extends BasinRecipe> getRecipeClass() {
return BasinRecipe.class;
}
@Override
public void setIngredients(BasinRecipe recipe, IIngredients ingredients) {
ingredients.setInputIngredients(recipe.getIngredients());
ingredients.setOutput(VanillaTypes.ITEM, recipe.getRecipeOutput());
}
@Override
public void setRecipe(IRecipeLayout recipeLayout, BasinRecipe 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 yOffset = recipe.getRequiredHeat() != HeatCondition.NONE ? 30 : 10;
int i;
for (i = 0; i < actualIngredients.size(); i++) {
itemStacks.init(i, true, 16 + xOffset + (i % 3) * 19, 50 - (i / 3) * 19 + yOffset);
List<ItemStack> stacks = new ArrayList<>();
Pair<Ingredient, MutableInt> pair = actualIngredients.get(i);
Ingredient ingredient = pair.getFirst();
MutableInt amount = pair.getSecond();
for (ItemStack itemStack : ingredient.getMatchingStacks()) {
ItemStack stack = itemStack.copy();
stack.setCount(amount.getValue());
stacks.add(stack);
}
itemStacks.set(i, stacks);
}
itemStacks.init(i, false, 141, 50 + yOffset);
itemStacks.set(i, recipe.getRecipeOutput()
.getStack());
}
@Override
public void draw(BasinRecipe 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;
HeatCondition requiredHeat = recipe.getRequiredHeat();
int yOffset = requiredHeat != HeatCondition.NONE ? 30 : 10;
for (int i = 0; i < size; i++)
AllGuiTextures.JEI_SLOT.draw(16 + xOffset + (i % 3) * 19, 50 - (i / 3) * 19 + yOffset);
AllGuiTextures.JEI_SLOT.draw(141, 50 + yOffset);
AllGuiTextures.JEI_DOWN_ARROW.draw(136, 32 + yOffset);
AllGuiTextures.JEI_SHADOW.draw(81, 57 + yOffset);
}
}

View file

@ -1,28 +1,12 @@
package com.simibubi.create.compat.jei.category;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.mutable.MutableInt;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.compat.jei.category.animations.AnimatedBlazeBurner;
import com.simibubi.create.compat.jei.category.animations.AnimatedMixer;
import com.simibubi.create.content.contraptions.components.mixer.MixingRecipe;
import com.simibubi.create.content.contraptions.processing.BasinRecipe;
import com.simibubi.create.content.contraptions.processing.HeatCondition;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.Pair;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.IRecipeLayout;
import mezz.jei.api.gui.ingredient.IGuiItemStackGroup;
import mezz.jei.api.ingredients.IIngredients;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.NonNullList;
public class MixingCategory extends CreateRecipeCategory<MixingRecipe> {
public class MixingCategory extends BasinCategory {
private AnimatedMixer mixer = new AnimatedMixer();
private AnimatedBlazeBurner heater = new AnimatedBlazeBurner();
@ -33,63 +17,9 @@ public class MixingCategory extends CreateRecipeCategory<MixingRecipe> {
}
@Override
public Class<? extends MixingRecipe> getRecipeClass() {
return MixingRecipe.class;
}
@Override
public void setIngredients(MixingRecipe recipe, IIngredients ingredients) {
ingredients.setInputIngredients(recipe.getIngredients());
ingredients.setOutput(VanillaTypes.ITEM, recipe.getRecipeOutput());
}
@Override
public void setRecipe(IRecipeLayout recipeLayout, MixingRecipe 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 yOffset = recipe.getRequiredHeat() != HeatCondition.NONE ? 30 : 10;
int i;
for (i = 0; i < actualIngredients.size(); i++) {
itemStacks.init(i, true, 16 + xOffset + (i % 3) * 19, 50 - (i / 3) * 19 + yOffset);
List<ItemStack> stacks = new ArrayList<>();
Pair<Ingredient, MutableInt> pair = actualIngredients.get(i);
Ingredient ingredient = pair.getFirst();
MutableInt amount = pair.getSecond();
for (ItemStack itemStack : ingredient.getMatchingStacks()) {
ItemStack stack = itemStack.copy();
stack.setCount(amount.getValue());
stacks.add(stack);
}
itemStacks.set(i, stacks);
}
itemStacks.init(i, false, 141, 50 + yOffset);
itemStacks.set(i, recipe.getRecipeOutput()
.getStack());
}
@Override
public void draw(MixingRecipe 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;
public void draw(BasinRecipe recipe, double mouseX, double mouseY) {
super.draw(recipe, mouseX, mouseY);
HeatCondition requiredHeat = recipe.getRequiredHeat();
int yOffset = requiredHeat != HeatCondition.NONE ? 30 : 10;
for (int i = 0; i < size; i++)
AllGuiTextures.JEI_SLOT.draw(16 + xOffset + (i % 3) * 19, 50 - (i / 3) * 19 + yOffset);
AllGuiTextures.JEI_SLOT.draw(141, 50 + yOffset);
AllGuiTextures.JEI_DOWN_ARROW.draw(136, 32 + yOffset);
AllGuiTextures.JEI_SHADOW.draw(81, 57 + yOffset);
if (requiredHeat != HeatCondition.NONE)
heater.withHeat(requiredHeat.visualizeAsBlazeBurner())
.draw(getBackground().getWidth() / 2 + 3, 55);

View file

@ -4,18 +4,16 @@ import java.util.Arrays;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.compat.jei.category.animations.AnimatedPress;
import com.simibubi.create.content.contraptions.processing.BasinRecipe;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.IRecipeLayout;
import mezz.jei.api.gui.ingredient.IGuiItemStackGroup;
import mezz.jei.api.ingredients.IIngredients;
import net.minecraft.item.crafting.ICraftingRecipe;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.NonNullList;
public class PackingCategory extends CreateRecipeCategory<IRecipe<?>> {
public class PackingCategory extends BasinCategory {
private AnimatedPress press = new AnimatedPress(true);
@ -25,18 +23,12 @@ public class PackingCategory extends CreateRecipeCategory<IRecipe<?>> {
}
@Override
public Class<? extends IRecipe<?>> getRecipeClass() {
return ICraftingRecipe.class;
}
@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) {
public void setRecipe(IRecipeLayout recipeLayout, BasinRecipe recipe, IIngredients ingredients) {
if (!recipe.convertedRecipe) {
super.setRecipe(recipeLayout, recipe, ingredients);
return;
}
IGuiItemStackGroup itemStacks = recipeLayout.getItemStacks();
int i = 0;
@ -55,16 +47,21 @@ public class PackingCategory extends CreateRecipeCategory<IRecipe<?>> {
}
@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++) {
AllGuiTextures.JEI_SLOT.draw((rows == 2 ? 26 : 17) + (i % rows) * 19, 50 - (i / rows) * 19);
public void draw(BasinRecipe recipe, double mouseX, double mouseY) {
if (!recipe.convertedRecipe) {
super.draw(recipe, mouseX, mouseY);
} else {
NonNullList<Ingredient> ingredients2 = recipe.getIngredients();
int size = ingredients2.size();
int rows = size == 4 ? 2 : 3;
for (int i = 0; i < size; i++)
AllGuiTextures.JEI_SLOT.draw((rows == 2 ? 26 : 17) + (i % rows) * 19, 50 - (i / rows) * 19);
AllGuiTextures.JEI_SLOT.draw(141, 50);
AllGuiTextures.JEI_DOWN_ARROW.draw(136, 32);
AllGuiTextures.JEI_SHADOW.draw(81, 57);
}
AllGuiTextures.JEI_SLOT.draw(141, 50);
AllGuiTextures.JEI_DOWN_ARROW.draw(136, 32);
AllGuiTextures.JEI_SHADOW.draw(81, 57);
press.draw(getBackground().getWidth() / 2 + 6, 30);
}

View file

@ -15,6 +15,7 @@ import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicates;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.Pointing;
import net.minecraft.block.BlockState;
@ -139,16 +140,16 @@ public class RecipeGridHandler {
public static ItemStack tryToApplyRecipe(World world, GroupedItems items) {
items.calcStats();
CraftingInventory craftinginventory = new MechanicalCraftingInventory(items);
ItemStack result = world.getRecipeManager()
.getRecipe(IRecipeType.CRAFTING, craftinginventory, world)
.map(r -> r.getCraftingResult(craftinginventory))
.orElse(null);
if (result == null)
ItemStack result = null;
if (AllConfigs.SERVER.recipes.allowRegularCraftingInCrafter.get())
result = world.getRecipeManager()
.getRecipe(AllRecipeTypes.MECHANICAL_CRAFTING.getType(), craftinginventory, world)
.getRecipe(IRecipeType.CRAFTING, craftinginventory, world)
.map(r -> r.getCraftingResult(craftinginventory))
.orElse(null);
if (result == null)
result = AllRecipeTypes.MECHANICAL_CRAFTING.find(craftinginventory, world)
.map(r -> r.getCraftingResult(craftinginventory))
.orElse(null);
return result;
}

View file

@ -206,11 +206,9 @@ public class CrushingWheelControllerTileEntity extends SmartTileEntity {
}
public Optional<ProcessingRecipe<RecipeWrapper>> findRecipe() {
Optional<ProcessingRecipe<RecipeWrapper>> crushingRecipe = world.getRecipeManager()
.getRecipe(AllRecipeTypes.CRUSHING.getType(), wrapper, world);
Optional<ProcessingRecipe<RecipeWrapper>> crushingRecipe = AllRecipeTypes.CRUSHING.find(wrapper, world);
if (!crushingRecipe.isPresent())
crushingRecipe = world.getRecipeManager()
.getRecipe(AllRecipeTypes.MILLING.getType(), wrapper, world);
crushingRecipe = AllRecipeTypes.MILLING.find(wrapper, world);
return crushingRecipe;
}

View file

@ -65,8 +65,7 @@ public class MillstoneTileEntity extends KineticTileEntity {
RecipeWrapper inventoryIn = new RecipeWrapper(inputInv);
if (lastRecipe == null || !lastRecipe.matches(inventoryIn, world)) {
Optional<MillingRecipe> recipe = world.getRecipeManager()
.getRecipe(AllRecipeTypes.MILLING.getType(), inventoryIn, world);
Optional<MillingRecipe> recipe = AllRecipeTypes.MILLING.find(inventoryIn, world);
if (!recipe.isPresent()) {
timer = 100;
sendData();
@ -86,8 +85,7 @@ public class MillstoneTileEntity extends KineticTileEntity {
RecipeWrapper inventoryIn = new RecipeWrapper(inputInv);
if (lastRecipe == null || !lastRecipe.matches(inventoryIn, world)) {
Optional<MillingRecipe> recipe = world.getRecipeManager()
.getRecipe(AllRecipeTypes.MILLING.getType(), inventoryIn, world);
Optional<MillingRecipe> recipe = AllRecipeTypes.MILLING.find(inventoryIn, world);
if (!recipe.isPresent())
return;
lastRecipe = recipe.get();
@ -153,8 +151,7 @@ public class MillstoneTileEntity extends KineticTileEntity {
if (lastRecipe != null && lastRecipe.matches(inventoryIn, world))
return true;
return world.getRecipeManager()
.getRecipe(AllRecipeTypes.MILLING.getType(), inventoryIn, world)
return AllRecipeTypes.MILLING.find(inventoryIn, world)
.isPresent();
}

View file

@ -0,0 +1,13 @@
package com.simibubi.create.content.contraptions.components.mixer;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.content.contraptions.processing.BasinRecipe;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeParams;
public class CompactingRecipe extends BasinRecipe {
public CompactingRecipe(ProcessingRecipeParams params) {
super(AllRecipeTypes.COMPACTING, params);
}
}

View file

@ -1,37 +1,29 @@
package com.simibubi.create.content.contraptions.components.mixer;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity;
import com.simibubi.create.content.contraptions.processing.BasinOperatingTileEntity;
import com.simibubi.create.content.contraptions.processing.BasinTileEntity;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel;
import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.advancement.ITriggerable;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.item.SmartInventory;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.particles.ItemParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.TileEntityType;
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.fluids.FluidStack;
public class MechanicalMixerTileEntity extends BasinOperatingTileEntity {
@ -99,13 +91,6 @@ public class MechanicalMixerTileEntity extends BasinOperatingTileEntity {
super.write(compound, clientPacket);
}
@Override
public void lazyTick() {
super.lazyTick();
if (world != null && world.isRemote && running && !basinItemInv.isPresent())
updateBasin();
}
@Override
public void tick() {
super.tick();
@ -168,45 +153,11 @@ public class MechanicalMixerTileEntity extends BasinOperatingTileEntity {
@Override
protected <C extends IInventory> boolean matchStaticFilters(IRecipe<C> r) {
return (r.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS || r.getType() == AllRecipeTypes.MIXING.type)
return ((r.getSerializer() == IRecipeSerializer.CRAFTING_SHAPELESS
&& AllConfigs.SERVER.recipes.allowShapelessInMixer.get()) || r.getType() == AllRecipeTypes.MIXING.type)
&& !MechanicalPressTileEntity.canCompress(r.getIngredients());
}
@Override
protected <C extends IInventory> boolean matchBasinRecipe(IRecipe<C> recipe) {
if (!super.matchBasinRecipe(recipe))
return false;
NonNullList<Ingredient> ingredients = recipe.getIngredients();
List<ItemStack> remainingItems = new ArrayList<>();
itemInputs.forEach(stack -> remainingItems.add(stack.copy()));
List<FluidStack> remainingFluids = new ArrayList<>();
fluidInputs.forEach(stack -> remainingFluids.add(stack.copy()));
// TODO: match fluid inputs
// Sort by leniency
List<Ingredient> sortedIngredients = new LinkedList<>(ingredients);
sortedIngredients.sort(Comparator.comparingInt(i -> i.getMatchingStacks().length));
Ingredients: for (Ingredient ingredient : sortedIngredients) {
for (ItemStack stack : remainingItems) {
if (stack.isEmpty())
continue;
if (ingredient.test(stack)) {
stack.shrink(1);
continue Ingredients;
}
}
return false;
}
if (!(recipe instanceof MixingRecipe))
return true;
return ((MixingRecipe) recipe).getRequiredHeat()
.testBlazeBurner(getHeatLevel());
}
@Override
public void startProcessingBasin() {
if (running && runningTicks <= 20)
@ -239,13 +190,10 @@ public class MechanicalMixerTileEntity extends BasinOperatingTileEntity {
protected boolean isRunning() {
return running;
}
private HeatLevel getHeatLevel() {
if (world == null)
return HeatLevel.NONE;
BlockState state = world.getBlockState(pos.down(3));
if (state.has(BlazeBurnerBlock.HEAT_LEVEL))
return state.get(BlazeBurnerBlock.HEAT_LEVEL);
return AllTags.AllBlockTags.FAN_HEATERS.matches(state) ? HeatLevel.SMOULDERING : HeatLevel.NONE;
@Override
protected Optional<ITriggerable> getProcessedRecipeTrigger() {
return Optional.of(AllTriggers.MIXER_MIX);
}
}

View file

@ -1,98 +1,13 @@
package com.simibubi.create.content.contraptions.components.mixer;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.Nonnull;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipe;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder;
import com.simibubi.create.content.contraptions.processing.BasinRecipe;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeParams;
import com.simibubi.create.foundation.item.SmartInventory;
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.world.World;
public class MixingRecipe extends ProcessingRecipe<SmartInventory> {
/**
* For JEI purposes only
*/
public static MixingRecipe convertShapeless(IRecipe<?> recipe) {
return new ProcessingRecipeBuilder<>(MixingRecipe::new, recipe.getId())
.withItemIngredients(recipe.getIngredients())
.withSingleItemOutput(recipe.getRecipeOutput())
.build();
}
public class MixingRecipe extends BasinRecipe {
public MixingRecipe(ProcessingRecipeParams params) {
super(AllRecipeTypes.MIXING, params);
}
@Override
protected int getMaxInputCount() {
return 9;
}
@Override
protected int getMaxOutputCount() {
return 1;// TODO increase
}
@Override
protected int getMaxFluidInputCount() {
return 2;
}
@Override
protected int getMaxFluidOutputCount() {
return 1;// TODO increase?
}
@Override
protected boolean canRequireHeat() {
return true;
}
@Override
public boolean matches(SmartInventory inv, @Nonnull World worldIn) {
if (inv.isEmpty())
return false;
NonNullList<Ingredient> ingredients = this.getIngredients();
if (!ingredients.stream()
.allMatch(Ingredient::isSimple))
return false;
List<ItemStack> remaining = new ArrayList<>();
for (int slot = 0; slot < inv.getSizeInventory(); ++slot) {
ItemStack itemstack = inv.getStackInSlot(slot);
if (!itemstack.isEmpty()) {
remaining.add(itemstack.copy());
}
}
// sort by leniency
List<Ingredient> sortedIngredients = new LinkedList<>(ingredients);
sortedIngredients.sort(Comparator.comparingInt(i -> i.getMatchingStacks().length));
Ingredients: for (Ingredient ingredient : sortedIngredients) {
for (ItemStack stack : remaining) {
if (stack.isEmpty())
continue;
if (ingredient.test(stack)) {
stack.shrink(1);
continue Ingredients;
}
}
return false;
}
return true;
}
}

View file

@ -10,6 +10,8 @@ import com.simibubi.create.content.contraptions.processing.BasinOperatingTileEnt
import com.simibubi.create.content.contraptions.processing.BasinTileEntity;
import com.simibubi.create.content.logistics.InWorldProcessing;
import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.advancement.ITriggerable;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.item.SmartInventory;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
@ -41,29 +43,12 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
private static final Object compressingRecipesKey = new Object();
public List<ItemStack> pressedItems = new ArrayList<>();
public static class PressingInv extends RecipeWrapper {
public PressingInv() {
super(new ItemStackHandler(1));
}
}
enum Mode {
WORLD(1), BELT(19f / 16f), BASIN(22f / 16f)
;
float headOffset;
Mode(float headOffset) {
this.headOffset = headOffset;
}
}
private static final PressingInv pressingInv = new PressingInv();
public BeltProcessingBehaviour processingBehaviour;
public int prevRunningTicks;
public int runningTicks;
static final int CYCLE = 240;
public boolean running;
public Mode mode;
public boolean finished;
@ -87,7 +72,7 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
running = compound.getBoolean("Running");
mode = Mode.values()[compound.getInt("Mode")];
finished = compound.getBoolean("Finished");
runningTicks = compound.getInt("Ticks");
prevRunningTicks = runningTicks = compound.getInt("Ticks");
super.read(compound, clientPacket);
if (clientPacket) {
@ -105,8 +90,10 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
compound.putInt("Ticks", runningTicks);
super.write(compound, clientPacket);
if (clientPacket)
if (clientPacket) {
compound.put("ParticleItems", NBTHelper.writeCompoundList(pressedItems, ItemStack::serializeNBT));
pressedItems.clear();
}
}
@Override
@ -116,15 +103,12 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
}
public float getRenderedHeadOffset(float partialTicks) {
if (running) {
if (runningTicks < 40) {
float num = (runningTicks - 1 + partialTicks) / 30f;
return MathHelper.clamp(num * num * num, 0, mode.headOffset);
}
return MathHelper.clamp(((60 - runningTicks) + 1 - partialTicks) / 20f * mode.headOffset, 0,
mode.headOffset);
}
return 0;
if (!running)
return 0;
float ticks = MathHelper.lerp(partialTicks, prevRunningTicks, runningTicks);
if (runningTicks < (CYCLE * 2) / 3)
return (float) MathHelper.clamp(Math.pow(ticks / CYCLE * 2, 3), 0, mode.headOffset);
return MathHelper.clamp((CYCLE - ticks) / CYCLE * 3 * mode.headOffset, 0, mode.headOffset);
}
public void start(Mode mode) {
@ -150,50 +134,11 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
if (!running || world == null)
return;
if (runningTicks == 30) {
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;
if (!world.isRemote) {
pressedItems.add(itemEntity.getItem());
sendData();
Optional<PressingRecipe> recipe = getRecipe(itemEntity.getItem());
if (recipe.isPresent()) {
InWorldProcessing.applyRecipeOn(itemEntity, recipe.get());
AllTriggers.triggerForNearbyPlayers(AllTriggers.BONK, world, pos, 4);
}
}
}
}
if (onBasin()) {
if (!world.isRemote) {
pressedItems.clear();
applyBasinRecipe();
Optional<BasinTileEntity> basin = getBasin();
SmartInventory inputs = basin.get()
.getInputInventory();
if (basin.isPresent()) {
for (int slot = 0; slot < inputs.getSlots(); slot++) {
ItemStack stackInSlot = inputs.getStackInSlot(slot);
if (stackInSlot.isEmpty())
continue;
pressedItems.add(stackInSlot);
}
}
sendData();
}
}
if (runningTicks == CYCLE / 2) {
if (inWorld())
applyPressingInWorld();
if (onBasin())
applyCompactingOnBasin();
if (!world.isRemote) {
world.playSound(null, getPos(), AllSoundEvents.MECHANICAL_PRESS_ITEM_BREAK.get(), SoundCategory.BLOCKS,
.5f, 1f);
@ -202,105 +147,125 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
}
}
if (!world.isRemote && runningTicks > 60) {
if (!world.isRemote && runningTicks > CYCLE) {
finished = true;
if (inWorld())
finished = world.isBlockPowered(pos);
running = false;
if (onBasin()) {
gatherInputs();
if (matchBasinRecipe(lastRecipe)) {
startProcessingBasin();
}
}
if (onBasin() && matchBasinRecipe(currentRecipe))
startProcessingBasin();
pressedItems.clear();
sendData();
return;
}
runningTicks++;
prevRunningTicks = runningTicks;
runningTicks += getRunningTickSpeed();
if (prevRunningTicks < CYCLE / 2 && runningTicks >= CYCLE / 2)
runningTicks = CYCLE / 2;
}
protected void applyCompactingOnBasin() {
if (world.isRemote)
return;
pressedItems.clear();
applyBasinRecipe();
Optional<BasinTileEntity> basin = getBasin();
SmartInventory inputs = basin.get()
.getInputInventory();
if (basin.isPresent()) {
for (int slot = 0; slot < inputs.getSlots(); slot++) {
ItemStack stackInSlot = inputs.getStackInSlot(slot);
if (stackInSlot.isEmpty())
continue;
pressedItems.add(stackInSlot);
}
}
sendData();
}
protected void applyPressingInWorld() {
AxisAlignedBB bb = new AxisAlignedBB(pos.down(1));
pressedItems.clear();
if (world.isRemote)
return;
for (Entity entity : world.getEntitiesWithinAABBExcludingEntity(null, bb)) {
if (!(entity instanceof ItemEntity))
continue;
ItemEntity itemEntity = (ItemEntity) entity;
pressedItems.add(itemEntity.getItem());
sendData();
Optional<PressingRecipe> recipe = getRecipe(itemEntity.getItem());
if (!recipe.isPresent())
continue;
InWorldProcessing.applyRecipeOn(itemEntity, recipe.get());
AllTriggers.triggerForNearbyPlayers(AllTriggers.BONK, world, pos, 4);
}
}
public int getRunningTickSpeed() {
if (getSpeed() == 0)
return 0;
return (int) MathHelper.lerp(MathHelper.clamp(Math.abs(getSpeed()) / 512f, 0, 1), 1, 60);
}
protected void spawnParticles() {
if (pressedItems.isEmpty())
return;
if (mode == Mode.BASIN) {
if (mode == Mode.BASIN)
pressedItems.forEach(stack -> makeCompactingParticleEffect(VecHelper.getCenterOf(pos.down(2)), stack));
}
if (mode == Mode.BELT) {
if (mode == Mode.BELT)
pressedItems.forEach(stack -> makePressingParticleEffect(VecHelper.getCenterOf(pos.down(2))
.add(0, 8 / 16f, 0), stack));
}
if (mode == Mode.WORLD) {
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 != null && world.isRemote) {
for (int i = 0; i < 20; i++) {
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);
}
if (world == null || !world.isRemote)
return;
for (int i = 0; i < 20; i++) {
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 != null && 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 + .25f, motion.z);
}
if (world == null || !world.isRemote)
return;
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 + .25f, motion.z);
}
}
private static final RecipeWrapper pressingInv = new RecipeWrapper(new ItemStackHandler(1));
public Optional<PressingRecipe> getRecipe(ItemStack item) {
pressingInv.setInventorySlotContents(0, item);
return world.getRecipeManager()
.getRecipe(AllRecipeTypes.PRESSING.getType(), pressingInv, world);
return AllRecipeTypes.PRESSING.find(pressingInv, world);
}
public static boolean canCompress(NonNullList<Ingredient> ingredients) {
return (ingredients.size() == 4 || ingredients.size() == 9) && ItemHelper.condenseIngredients(ingredients)
.size() == 1;
return AllConfigs.SERVER.recipes.allowShapedSquareInPress.get()
&& (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 (!super.matchBasinRecipe(recipe))
return false;
NonNullList<Ingredient> ingredients = recipe.getIngredients();
List<ItemStack> remainingItems = new ArrayList<>();
itemInputs.forEach(stack -> remainingItems.add(stack.copy()));
Ingredients: for (Ingredient ingredient : ingredients) {
for (ItemStack stack : remainingItems) {
if (stack.isEmpty())
continue;
if (ingredient.test(stack)) {
stack.shrink(1);
continue Ingredients;
}
}
return false;
}
return true;
return (recipe instanceof ICraftingRecipe && canCompress(recipe.getIngredients()))
|| recipe.getType() == AllRecipeTypes.COMPACTING.type;
}
@Override
@ -310,7 +275,7 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
@Override
public void startProcessingBasin() {
if (running && runningTicks <= 30)
if (running && runningTicks <= CYCLE / 2)
return;
super.startProcessingBasin();
start(Mode.BASIN);
@ -329,4 +294,21 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
return running;
}
@Override
protected Optional<ITriggerable> getProcessedRecipeTrigger() {
return Optional.of(AllTriggers.PRESS_COMPACT);
}
enum Mode {
WORLD(1), BELT(19f / 16f), BASIN(22f / 16f)
;
float headOffset;
Mode(float headOffset) {
this.headOffset = headOffset;
}
}
}

View file

@ -3,21 +3,21 @@ package com.simibubi.create.content.contraptions.components.press;
import javax.annotation.ParametersAreNonnullByDefault;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity.PressingInv;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipe;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeParams;
import net.minecraft.world.World;
import net.minecraftforge.items.wrapper.RecipeWrapper;
@ParametersAreNonnullByDefault
public class PressingRecipe extends ProcessingRecipe<MechanicalPressTileEntity.PressingInv> {
public class PressingRecipe extends ProcessingRecipe<RecipeWrapper> {
public PressingRecipe(ProcessingRecipeParams params) {
super(AllRecipeTypes.PRESSING, params);
}
@Override
public boolean matches(PressingInv inv, World worldIn) {
public boolean matches(RecipeWrapper inv, World worldIn) {
if (inv.isEmpty())
return false;
return ingredients.get(0)

View file

@ -8,9 +8,11 @@ import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import com.google.common.base.Predicate;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.content.contraptions.components.actors.BlockBreakingKineticTileEntity;
import com.simibubi.create.content.contraptions.processing.ProcessingInventory;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.DirectBeltInputBehaviour;
@ -264,8 +266,10 @@ public class SawTileEntity extends BlockBreakingKineticTileEntity {
}
private List<? extends IRecipe<?>> getRecipes() {
List<IRecipe<?>> startedSearch = RecipeFinder.get(cuttingRecipesKey, world,
RecipeConditions.isOfType(IRecipeType.STONECUTTING, AllRecipeTypes.CUTTING.getType()));
Predicate<IRecipe<?>> types = AllConfigs.SERVER.recipes.allowStonecuttingOnSaw.get()
? RecipeConditions.isOfType(IRecipeType.STONECUTTING, AllRecipeTypes.CUTTING.getType())
: RecipeConditions.isOfType(AllRecipeTypes.CUTTING.getType());
List<IRecipe<?>> startedSearch = RecipeFinder.get(cuttingRecipesKey, world, types);
return startedSearch.stream()
.filter(RecipeConditions.outputMatchesFilter(filtering))
.filter(RecipeConditions.firstIngredientMatches(inventory.getStackInSlot(0)))

View file

@ -9,13 +9,13 @@ import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
import com.simibubi.create.AllMovementBehaviours;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableObject;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.content.contraptions.components.actors.BlockBreakingMovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ClientMotionPacket;
import com.simibubi.create.foundation.collision.ContinuousOBBCollider.ContinuousSeparationManifold;
@ -28,6 +28,7 @@ import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.CocoaBlock;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
@ -37,6 +38,7 @@ import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.ReuseableStream;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.IBooleanFunction;
import net.minecraft.util.math.shapes.ISelectionContext;
@ -44,6 +46,9 @@ import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
public class ContraptionCollider {
@ -54,14 +59,20 @@ public class ContraptionCollider {
for (Iterator<WeakReference<ContraptionEntity>> iterator = list.iterator(); iterator.hasNext();) {
WeakReference<ContraptionEntity> weakReference = iterator.next();
ContraptionEntity contraptionEntity = weakReference.get();
if (contraptionEntity == null || !contraptionEntity.isAlive()) {
if (contraptionEntity == null) {
iterator.remove();
continue;
}
if (!contraptionEntity.isAlive())
continue;
collideEntities(contraptionEntity);
}
}
enum PlayerType {
NONE, CLIENT, REMOTE, SERVER
}
private static void collideEntities(ContraptionEntity contraptionEntity) {
World world = contraptionEntity.getEntityWorld();
Contraption contraption = contraptionEntity.getContraption();
@ -87,8 +98,10 @@ public class ContraptionCollider {
for (Entity entity : world.getEntitiesWithinAABB((EntityType<?>) null, bounds.grow(2)
.expand(0, 32, 0), contraptionEntity::canCollideWith)) {
boolean player = entity instanceof PlayerEntity;
boolean serverPlayer = player && !world.isRemote;
PlayerType playerType = getPlayerType(entity);
if (playerType == PlayerType.REMOTE)
continue;
// Init matrix
if (rotation == null) {
@ -199,7 +212,7 @@ public class ContraptionCollider {
totalResponse = rotation.transform(totalResponse);
rotation.transpose();
if (futureCollision.isTrue() && !serverPlayer) {
if (futureCollision.isTrue() && playerType != PlayerType.SERVER) {
if (motionResponse.y != entityMotion.y) {
entity.setMotion(entityMotion.mul(1, 0, 1)
.add(0, motionResponse.y, 0));
@ -212,7 +225,7 @@ public class ContraptionCollider {
entity.fallDistance = 0;
entity.onGround = true;
contraptionEntity.collidingEntities.add(entity);
if (!serverPlayer)
if (playerType != PlayerType.SERVER)
contactPointMotion = contraptionEntity.getContactPointMotion(entityPosition);
}
@ -236,7 +249,7 @@ public class ContraptionCollider {
if (!hardCollision && surfaceCollision.isFalse())
continue;
if (serverPlayer && entity instanceof ServerPlayerEntity) {
if (playerType == PlayerType.SERVER && entity instanceof ServerPlayerEntity) {
((ServerPlayerEntity) entity).connection.floatingTickCount = 0;
continue;
}
@ -249,8 +262,15 @@ public class ContraptionCollider {
entityPosition.z + allowedMovement.z);
entity.setMotion(entityMotion);
if (!serverPlayer && player)
AllPackets.channel.sendToServer(new ClientMotionPacket(entityMotion, true));
if (playerType != PlayerType.CLIENT)
continue;
double d0 = entity.getX() - entity.prevPosX - contactPointMotion.x;
double d1 = entity.getZ() - entity.prevPosZ - contactPointMotion.z;
float limbSwing = MathHelper.sqrt(d0 * d0 + d1 * d1) * 4.0F;
if (limbSwing > 1.0F)
limbSwing = 1.0F;
AllPackets.channel.sendToServer(new ClientMotionPacket(entityMotion, true, limbSwing));
}
}
@ -296,6 +316,21 @@ public class ContraptionCollider {
return vec3d;
}
private static PlayerType getPlayerType(Entity entity) {
if (!(entity instanceof PlayerEntity))
return PlayerType.NONE;
if (!entity.world.isRemote)
return PlayerType.SERVER;
MutableBoolean isClient = new MutableBoolean(false);
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> isClient.setValue(isClientPlayerEntity(entity)));
return isClient.booleanValue() ? PlayerType.CLIENT : PlayerType.REMOTE;
}
@OnlyIn(Dist.CLIENT)
private static boolean isClientPlayerEntity(Entity entity) {
return entity instanceof ClientPlayerEntity;
}
private static ReuseableStream<VoxelShape> getPotentiallyCollidedShapes(World world, Contraption contraption,
AxisAlignedBB localBB) {
@ -395,10 +430,10 @@ public class ContraptionCollider {
BlockInfo blockInfo = contraption.blocks.get(pos);
if (AllMovementBehaviours.hasMovementBehaviour(blockInfo.state.getBlock())) {
MovementBehaviour movementBehaviour = AllMovementBehaviours.getMovementBehaviour(blockInfo.state.getBlock());
MovementBehaviour movementBehaviour =
AllMovementBehaviours.getMovementBehaviour(blockInfo.state.getBlock());
if (movementBehaviour instanceof BlockBreakingMovementBehaviour) {
BlockBreakingMovementBehaviour behaviour =
(BlockBreakingMovementBehaviour) movementBehaviour;
BlockBreakingMovementBehaviour behaviour = (BlockBreakingMovementBehaviour) movementBehaviour;
if (!behaviour.canBreak(world, colliderPos, collidedState)
&& !collidedState.getCollisionShape(world, pos)
.isEmpty()) {

View file

@ -78,6 +78,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
final List<Entity> collidingEntities = new ArrayList<>();
private boolean isSerializingFurnaceCart;
private boolean attachedExtraInventories;
private boolean prevPosInvalid;
private static final Ingredient FUEL_ITEMS = Ingredient.fromItems(Items.COAL, Items.CHARCOAL);
private static final DataParameter<Boolean> STALLED =
@ -107,6 +108,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
isSerializingFurnaceCart = false;
attachedExtraInventories = false;
forcedAngle = -1;
prevPosInvalid = true;
}
public static ContraptionEntity createMounted(World world, Contraption contraption, float initialAngle) {
@ -289,26 +291,32 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
remove();
return;
}
prevPosX = getX();
prevPosY = getY();
prevPosZ = getZ();
prevPosInvalid = false;
if (!initialized)
contraptionInitialize();
checkController();
Entity mountedEntity = getRidingEntity();
if (mountedEntity != null) {
tickAsPassenger(mountedEntity);
super.tick();
return;
}
if (getMotion().length() < 1 / 4098f)
setMotion(Vec3d.ZERO);
move(getMotion().x, getMotion().y, getMotion().z);
if (ContraptionCollider.collideBlocks(this))
getController().collided();
tickActors(getPositionVec().subtract(prevPosX, prevPosY, prevPosZ));
Vec3d movement = getPositionVec().subtract(prevPosX, prevPosY, prevPosZ);
tickActors(movement);
prevYaw = yaw;
prevPitch = pitch;
@ -439,8 +447,6 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
furnaceCart.deserializeNBT(nbt);
}
}
super.tick();
}
public void tickActors(Vec3d movementVector) {
@ -846,6 +852,9 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
}
public Vec3d getContactPointMotion(Vec3d globalContactPoint) {
if (prevPosInvalid)
return Vec3d.ZERO;
Vec3d positionVec = getPositionVec();
Vec3d conMotion = positionVec.subtract(getPrevPositionVec());
Vec3d conAngularMotion = getRotationVec().subtract(getPrevRotationVec());
@ -906,5 +915,10 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
public void setCoupledCart(UUID id) {
dataManager.set(COUPLED_CART, Optional.ofNullable(id));
}
@Override
public boolean isOnePlayerRiding() {
return false;
}
}

View file

@ -4,6 +4,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
@ -28,7 +29,8 @@ public class ContraptionHandler {
if (!(entity instanceof ContraptionEntity))
return;
try {
List<WeakReference<ContraptionEntity>> list = activeContraptions.get(world, ArrayList::new);
List<WeakReference<ContraptionEntity>> list =
activeContraptions.get(world, () -> Collections.synchronizedList(new ArrayList<>()));
ContraptionEntity contraption = (ContraptionEntity) entity;
list.add(new WeakReference<>(contraption));
} catch (ExecutionException e) {

View file

@ -1,4 +1,5 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionInteractionPacket;
@ -9,6 +10,8 @@ import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.entity.player.RemoteClientPlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
@ -20,11 +23,38 @@ import net.minecraft.world.gen.feature.template.Template.BlockInfo;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.InputEvent.ClickInputEvent;
import net.minecraftforge.event.TickEvent.Phase;
import net.minecraftforge.event.TickEvent.PlayerTickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber
public class ContraptionInteractionHandler {
public class ContraptionHandlerClient {
@SubscribeEvent
@OnlyIn(Dist.CLIENT)
public static void preventRemotePlayersWalkingAnimations(PlayerTickEvent event) {
if (event.phase == Phase.START)
return;
if (!(event.player instanceof RemoteClientPlayerEntity))
return;
RemoteClientPlayerEntity remotePlayer = (RemoteClientPlayerEntity) event.player;
CompoundNBT data = remotePlayer.getPersistentData();
if (!data.contains("LastOverrideLimbSwingUpdate"))
return;
int lastOverride = data.getInt("LastOverrideLimbSwingUpdate");
data.putInt("LastOverrideLimbSwingUpdate", lastOverride + 1);
if (lastOverride > 5) {
data.remove("LastOverrideLimbSwingUpdate");
data.remove("OverrideLimbSwing");
return;
}
float limbSwing = data.getFloat("OverrideLimbSwing");
remotePlayer.prevPosX = remotePlayer.getX() - (limbSwing / 4);
remotePlayer.prevPosZ = remotePlayer.getZ();
}
@SubscribeEvent
@OnlyIn(Dist.CLIENT)
@ -38,11 +68,12 @@ public class ContraptionInteractionHandler {
if (!event.isUseItem())
return;
Vec3d origin = RaycastHelper.getTraceOrigin(player);
double reach = mc.playerController.getBlockReachDistance();
if (mc.objectMouseOver != null && mc.objectMouseOver.getHitVec() != null)
reach = Math.min(mc.objectMouseOver.getHitVec().distanceTo(origin), reach);
if (mc.objectMouseOver != null && mc.objectMouseOver.getHitVec() != null)
reach = Math.min(mc.objectMouseOver.getHitVec()
.distanceTo(origin), reach);
Vec3d target = RaycastHelper.getTraceTarget(player, reach, origin);
for (ContraptionEntity contraptionEntity : mc.world.getEntitiesWithinAABB(ContraptionEntity.class,
new AxisAlignedBB(origin, target))) {
@ -75,11 +106,10 @@ public class ContraptionInteractionHandler {
Hand hand = event.getHand();
Direction face = rayTraceResult.getFace();
BlockPos pos = rayTraceResult.getPos();
if (!contraptionEntity.handlePlayerInteraction(player, pos, face, hand))
return;
AllPackets.channel.sendToServer(new ContraptionInteractionPacket(contraptionEntity, hand,
pos, face));
AllPackets.channel.sendToServer(new ContraptionInteractionPacket(contraptionEntity, hand, pos, face));
event.setCanceled(true);
event.setSwingHand(false);
}

View file

@ -2,26 +2,31 @@ package com.simibubi.create.content.contraptions.components.structureMovement.sy
import java.util.function.Supplier;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.fml.network.NetworkEvent.Context;
import net.minecraftforge.fml.network.PacketDistributor;
public class ClientMotionPacket extends SimplePacketBase {
private Vec3d motion;
private boolean onGround;
private float limbSwing;
public ClientMotionPacket(Vec3d motion, boolean onGround) {
public ClientMotionPacket(Vec3d motion, boolean onGround, float limbSwing) {
this.motion = motion;
this.onGround = onGround;
this.limbSwing = limbSwing;
}
public ClientMotionPacket(PacketBuffer buffer) {
motion = new Vec3d(buffer.readFloat(), buffer.readFloat(), buffer.readFloat());
onGround = buffer.readBoolean();
limbSwing = buffer.readFloat();
}
@Override
@ -30,6 +35,7 @@ public class ClientMotionPacket extends SimplePacketBase {
buffer.writeFloat((float) motion.y);
buffer.writeFloat((float) motion.z);
buffer.writeBoolean(onGround);
buffer.writeFloat(limbSwing);
}
@Override
@ -47,6 +53,8 @@ public class ClientMotionPacket extends SimplePacketBase {
sender.fallDistance = 0;
sender.connection.floatingTickCount = 0;
}
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> sender),
new LimbSwingUpdatePacket(sender.getEntityId(), sender.getPositionVec(), limbSwing));
});
context.get()
.setPacketHandled(true);

View file

@ -0,0 +1,62 @@
package com.simibubi.create.content.contraptions.components.structureMovement.sync;
import java.util.function.Supplier;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.client.Minecraft;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.fml.network.NetworkEvent.Context;
public class LimbSwingUpdatePacket extends SimplePacketBase {
private int entityId;
private Vec3d position;
private float limbSwing;
public LimbSwingUpdatePacket(int entityId, Vec3d position, float limbSwing) {
this.entityId = entityId;
this.position = position;
this.limbSwing = limbSwing;
}
public LimbSwingUpdatePacket(PacketBuffer buffer) {
entityId = buffer.readInt();
position = new Vec3d(buffer.readFloat(), buffer.readFloat(), buffer.readFloat());
limbSwing = buffer.readFloat();
}
@Override
public void write(PacketBuffer buffer) {
buffer.writeInt(entityId);
buffer.writeFloat((float) position.x);
buffer.writeFloat((float) position.y);
buffer.writeFloat((float) position.z);
buffer.writeFloat(limbSwing);
}
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> {
ClientWorld world = Minecraft.getInstance().world;
if (world == null)
return;
Entity entity = world.getEntityByID(entityId);
if (entity == null)
return;
CompoundNBT data = entity.getPersistentData();
data.putInt("LastOverrideLimbSwingUpdate", 0);
data.putFloat("OverrideLimbSwing", limbSwing);
entity.setPositionAndRotationDirect(position.x, position.y, position.z, entity.rotationYaw,
entity.rotationPitch, 2, false);
});
context.get()
.setPacketHandled(true);
}
}

View file

@ -24,8 +24,7 @@ public class FillingBySpout {
public static boolean canItemBeFilled(World world, ItemStack stack) {
wrapper.setInventorySlotContents(0, stack);
if (world.getRecipeManager()
.getRecipe(AllRecipeTypes.FILLING.getType(), wrapper, world)
if (AllRecipeTypes.FILLING.find(wrapper, world)
.isPresent())
return true;
@ -44,8 +43,7 @@ public class FillingBySpout {
public static int getRequiredAmountForItem(World world, ItemStack stack, FluidStack availableFluid) {
wrapper.setInventorySlotContents(0, stack);
Optional<IRecipe<RecipeWrapper>> recipe = world.getRecipeManager()
.getRecipe(AllRecipeTypes.FILLING.getType(), wrapper, world);
Optional<IRecipe<RecipeWrapper>> recipe = AllRecipeTypes.FILLING.find(wrapper, world);
if (recipe.isPresent()) {
FillingRecipe fillingRecipe = (FillingRecipe) recipe.get();
FluidIngredient requiredFluid = fillingRecipe.getRequiredFluid();
@ -71,8 +69,7 @@ public class FillingBySpout {
availableFluid.shrink(requiredAmount);
wrapper.setInventorySlotContents(0, stack);
Optional<IRecipe<RecipeWrapper>> recipe = world.getRecipeManager()
.getRecipe(AllRecipeTypes.FILLING.getType(), wrapper, world);
Optional<IRecipe<RecipeWrapper>> recipe = AllRecipeTypes.FILLING.find(wrapper, world);
if (recipe.isPresent()) {
FillingRecipe fillingRecipe = (FillingRecipe) recipe.get();
FluidIngredient requiredFluid = fillingRecipe.getRequiredFluid();

View file

@ -9,16 +9,23 @@ import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.state.DirectionProperty;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
@ -26,14 +33,22 @@ import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.ItemStackHandler;
public class BasinBlock extends Block implements ITE<BasinTileEntity>, IWrenchable {
public static final DirectionProperty FACING = BlockStateProperties.FACING_EXCEPT_UP;
public BasinBlock(Properties p_i48440_1_) {
super(p_i48440_1_);
setDefaultState(getDefaultState().with(FACING, Direction.DOWN));
}
@Override
@ -41,6 +56,11 @@ public class BasinBlock extends Block implements ITE<BasinTileEntity>, IWrenchab
return true;
}
@Override
protected void fillStateContainer(Builder<Block, BlockState> p_206840_1_) {
super.fillStateContainer(p_206840_1_.add(FACING));
}
@Override
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
return AllTileEntities.BASIN.create();
@ -54,12 +74,16 @@ public class BasinBlock extends Block implements ITE<BasinTileEntity>, IWrenchab
@Override
public ActionResultType onUse(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn,
BlockRayTraceResult hit) {
if (!player.getHeldItem(handIn)
.isEmpty())
return ActionResultType.PASS;
ItemStack heldItem = player.getHeldItem(handIn);
try {
BasinTileEntity te = getTileEntity(worldIn, pos);
if (!heldItem.isEmpty()) {
if (tryEmptyItemIntoBasin(worldIn, player, handIn, heldItem, te))
return ActionResultType.SUCCESS;
return ActionResultType.PASS;
}
IItemHandlerModifiable inv = te.itemCapability.orElse(new ItemStackHandler(1));
for (int slot = 0; slot < inv.getSlots(); slot++) {
player.inventory.placeItemBackInInventory(worldIn, inv.getStackInSlot(slot));
@ -72,6 +96,30 @@ public class BasinBlock extends Block implements ITE<BasinTileEntity>, IWrenchab
return ActionResultType.SUCCESS;
}
protected boolean tryEmptyItemIntoBasin(World worldIn, PlayerEntity player, Hand handIn, ItemStack heldItem,
BasinTileEntity te) {
if (!EmptyingByBasin.canItemBeEmptied(worldIn, heldItem))
return false;
Pair<FluidStack, ItemStack> emptyItem = EmptyingByBasin.emptyItem(worldIn, heldItem, true);
LazyOptional<IFluidHandler> capability = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY);
IFluidHandler tank = capability.orElse(null);
FluidStack fluidStack = emptyItem.getFirst();
if (tank == null || fluidStack.getAmount() != tank.fill(fluidStack, FluidAction.SIMULATE))
return false;
if (worldIn.isRemote)
return true;
EmptyingByBasin.emptyItem(worldIn, heldItem, false);
tank.fill(fluidStack, FluidAction.EXECUTE);
if (heldItem.isEmpty())
player.setHeldItem(handIn, emptyItem.getSecond());
else
player.inventory.placeItemBackInInventory(worldIn, emptyItem.getSecond());
return true;
}
@Override
public void onLanded(IBlockReader worldIn, Entity entityIn) {
super.onLanded(worldIn, entityIn);
@ -102,6 +150,11 @@ public class BasinBlock extends Block implements ITE<BasinTileEntity>, IWrenchab
return AllShapes.BASIN_BLOCK_SHAPE;
}
@Override
public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState p_220082_4_, boolean p_220082_5_) {
updateDiagonalNeighbours(state, world, pos);
}
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader reader, BlockPos pos, ISelectionContext ctx) {
if (ctx.getEntity() instanceof ItemEntity)
@ -111,6 +164,7 @@ public class BasinBlock extends Block implements ITE<BasinTileEntity>, IWrenchab
@Override
public void onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) {
updateDiagonalNeighbours(state, worldIn, pos);
if (!state.hasTileEntity() || state.getBlock() == newState.getBlock())
return;
TileEntityBehaviour.destroy(worldIn, pos, FilteringBehaviour.TYPE);
@ -140,4 +194,35 @@ public class BasinBlock extends Block implements ITE<BasinTileEntity>, IWrenchab
return BasinTileEntity.class;
}
@Override
public BlockState getStateForPlacement(BlockItemUseContext ctx) {
BlockState state = super.getStateForPlacement(ctx);
World world = ctx.getWorld();
BlockPos pos = ctx.getPos();
return updateDiagonalState(state, world, pos);
}
protected void updateDiagonalNeighbours(BlockState state, World world, BlockPos pos) {
for (Direction direction : Iterate.horizontalDirections) {
BlockPos toUpdate = pos.up()
.offset(direction);
BlockState stateToUpdate = world.getBlockState(toUpdate);
BlockState updated = updateDiagonalState(stateToUpdate, world, toUpdate);
if (stateToUpdate != updated && !world.isRemote)
world.setBlockState(toUpdate, updated);
}
}
public static BlockState updateDiagonalState(BlockState state, IBlockReader world, BlockPos pos) {
if (!(state.getBlock() instanceof BasinBlock))
return state;
for (Direction direction : Iterate.horizontalDirections) {
BlockState diagonaloutputBasin = world.getBlockState(pos.down()
.offset(direction));
if (diagonaloutputBasin.getBlock() instanceof BasinBlock)
return state.with(FACING, direction);
}
return state.with(FACING, Direction.DOWN);
}
}

View file

@ -0,0 +1,32 @@
package com.simibubi.create.content.contraptions.processing;
import com.simibubi.create.foundation.data.AssetLookup;
import com.simibubi.create.foundation.data.SpecialBlockStateGen;
import com.tterrag.registrate.providers.DataGenContext;
import com.tterrag.registrate.providers.RegistrateBlockstateProvider;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraftforge.client.model.generators.ModelFile;
public class BasinGenerator extends SpecialBlockStateGen {
@Override
protected int getXRotation(BlockState state) {
return 0;
}
@Override
protected int getYRotation(BlockState state) {
return horizontalAngle(state.get(BasinBlock.FACING));
}
@Override
public <T extends Block> ModelFile getModel(DataGenContext<Block, T> ctx, RegistrateBlockstateProvider prov,
BlockState state) {
if (state.get(BasinBlock.FACING).getAxis().isVertical())
return AssetLookup.partialBaseModel(ctx, prov);
return AssetLookup.partialBaseModel(ctx, prov, "directional");
}
}

View file

@ -1,27 +0,0 @@
package com.simibubi.create.content.contraptions.processing;
import com.simibubi.create.foundation.item.SmartInventory;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.ItemHandlerHelper;
public class BasinInputInventory extends SmartInventory {
public BasinInputInventory(int slots, BasinTileEntity te) {
super(slots, te);
}
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) {
// Only insert if no other slot already has a 'full' stack of this item
for (int i = 0; i < getSlots(); i++) {
ItemStack stackInSlot = getStackInSlot(i);
if (ItemHandlerHelper.canItemStacksStack(stack, stackInSlot)
&& stackInSlot.getCount() == getStackLimit(i, stackInSlot))
return stack;
}
return super.insertItem(slot, stack, simulate);
}
}

View file

@ -0,0 +1,23 @@
package com.simibubi.create.content.contraptions.processing;
import com.simibubi.create.foundation.item.SmartInventory;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.ItemHandlerHelper;
public class BasinInventory extends SmartInventory {
public BasinInventory(int slots, BasinTileEntity te) {
super(slots, te, 16, true);
}
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) {
// Only insert if no other slot already has a stack of this item
for (int i = 0; i < getSlots(); i++)
if (i != slot && ItemHandlerHelper.canItemStacksStack(stack, inv.getStackInSlot(i)))
return stack;
return super.insertItem(slot, stack, simulate);
}
}

View file

@ -1,49 +1,29 @@
package com.simibubi.create.content.contraptions.processing;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.advancement.SimpleTrigger;
import com.simibubi.create.foundation.item.SmartInventory;
import com.simibubi.create.foundation.advancement.ITriggerable;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.simple.DeferralBehaviour;
import com.simibubi.create.foundation.utility.recipe.RecipeFinder;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.NonNullList;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
public abstract class BasinOperatingTileEntity extends KineticTileEntity {
public DeferralBehaviour basinChecker;
public boolean basinRemoved;
protected IRecipe<?> lastRecipe;
protected LazyOptional<IItemHandler> basinItemInv = LazyOptional.empty();
protected List<ItemStack> itemInputs;
protected LazyOptional<IFluidHandler> basinFluidInv = LazyOptional.empty();
protected List<FluidStack> fluidInputs;
protected IRecipe<?> currentRecipe;
public BasinOperatingTileEntity(TileEntityType<?> typeIn) {
super(typeIn);
itemInputs = new ArrayList<>();
fluidInputs = new ArrayList<>();
}
@Override
@ -58,29 +38,10 @@ public abstract class BasinOperatingTileEntity extends KineticTileEntity {
super.onSpeedChanged(prevSpeed);
if (getSpeed() == 0)
basinRemoved = true;
basinRemoved = false;
basinChecker.scheduleUpdate();
}
public void gatherInputs() {
itemInputs.clear();
basinItemInv.ifPresent(handler -> {
for (int slot = 0; slot < handler.getSlots(); ++slot) {
ItemStack itemstack = handler.getStackInSlot(slot);
if (!itemstack.isEmpty())
itemInputs.add(itemstack);
}
});
fluidInputs.clear();
basinFluidInv.ifPresent(handler -> {
for (int tank = 0; tank < handler.getTanks(); tank++) {
FluidStack fluidInTank = handler.getFluidInTank(tank);
if (!fluidInTank.isEmpty())
fluidInputs.add(fluidInTank);
}
});
}
@Override
public void tick() {
if (basinRemoved) {
@ -100,28 +61,13 @@ public abstract class BasinOperatingTileEntity extends KineticTileEntity {
return true;
if (isRunning())
return false;
Optional<BasinTileEntity> basinTe = getBasin();
if (!basinTe.isPresent())
return true;
if (!basinItemInv.isPresent())
basinItemInv = basinTe.get()
.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
if (!basinFluidInv.isPresent())
basinFluidInv = basinTe.get()
.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY);
if (!basinFluidInv.isPresent() || !basinItemInv.isPresent())
return true;
if (world == null || world.isRemote)
return true;
gatherInputs();
List<IRecipe<?>> recipes = getMatchingRecipes();
if (recipes.isEmpty())
return true;
lastRecipe = recipes.get(0);
currentRecipe = recipes.get(0);
startProcessingBasin();
sendData();
return true;
@ -135,58 +81,37 @@ public abstract class BasinOperatingTileEntity extends KineticTileEntity {
return true;
}
public void applyBasinRecipe() {
if (lastRecipe == null)
return;
if (!basinItemInv.isPresent() || !basinFluidInv.isPresent())
return;
protected <C extends IInventory> boolean matchBasinRecipe(IRecipe<C> recipe) {
if (recipe == null)
return false;
Optional<BasinTileEntity> basin = getBasin();
if (!basin.isPresent())
return false;
return BasinRecipe.match(basin.get(), recipe);
}
protected void applyBasinRecipe() {
if (currentRecipe == null)
return;
SmartInventory inputs = basin.get().getInputInventory();
SmartInventory outputs = basin.get().getOutputInventory();
List<ItemStack> containers = new ArrayList<>();
NonNullList<Ingredient> ingredients = lastRecipe.getIngredients();
Ingredients: for (int i = 0; i < ingredients.size(); i++) {
Ingredient ingredient = ingredients.get(i);
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.hasContainerItem())
containers.add(extracted.getContainerItem()
.copy());
continue Ingredients;
}
// something wasn't found
Optional<BasinTileEntity> optionalBasin = getBasin();
if (!optionalBasin.isPresent())
return;
}
if (world != null && !world.isRemote) {
SimpleTrigger trigger = AllTriggers.MIXER_MIX;
if (AllTileEntities.MECHANICAL_PRESS.is(this))
trigger = AllTriggers.PRESS_COMPACT;
AllTriggers.triggerForNearbyPlayers(trigger, world, pos, 4);
}
outputs.allowInsertion();
ItemHandlerHelper.insertItemStacked(outputs, lastRecipe.getRecipeOutput()
.copy(), false); // TODO only works for single item output
containers.forEach(stack -> ItemHandlerHelper.insertItemStacked(outputs, stack, false));
outputs.forbidInsertion();
BasinTileEntity basin = optionalBasin.get();
if (!BasinRecipe.apply(basin, currentRecipe))
return;
Optional<ITriggerable> processedRecipeTrigger = getProcessedRecipeTrigger();
if (world != null && !world.isRemote && processedRecipeTrigger.isPresent())
AllTriggers.triggerForNearbyPlayers(processedRecipeTrigger.get(), world, pos, 4);
basin.inputTank.sendDataImmediately();
// Continue mixing
gatherInputs();
if (matchBasinRecipe(lastRecipe)) {
if (matchBasinRecipe(currentRecipe)) {
continueWithPreviousRecipe();
sendData();
}
getBasin().ifPresent(BasinTileEntity::notifyChangeOfContents);
basin.notifyChangeOfContents();
}
protected List<IRecipe<?>> getMatchingRecipes() {
@ -210,28 +135,13 @@ public abstract class BasinOperatingTileEntity extends KineticTileEntity {
return Optional.empty();
return Optional.of((BasinTileEntity) basinTE);
}
protected Optional<ITriggerable> getProcessedRecipeTrigger() {
return Optional.empty();
}
protected abstract <C extends IInventory> boolean matchStaticFilters(IRecipe<C> recipe);
protected <C extends IInventory> boolean matchBasinRecipe(IRecipe<C> recipe) {
if (recipe == null)
return false;
Optional<BasinTileEntity> basin = getBasin();
if (!basin.isPresent())
return false;
BasinTileEntity basinTileEntity = basin.get();
if (!basinTileEntity.getFilter()
.test(recipe.getRecipeOutput()))
return false;
NonNullList<Ingredient> ingredients = recipe.getIngredients();
if (!ingredients.stream()
.allMatch(ingredient -> (ingredient.isSimple() || ingredient.getMatchingStacks().length == 1)))
return false;
return true;
}
protected abstract Object getRecipeCacheKey();
}

View file

@ -0,0 +1,199 @@
package com.simibubi.create.content.contraptions.processing;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.Nonnull;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeParams;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel;
import com.simibubi.create.foundation.fluid.FluidIngredient;
import com.simibubi.create.foundation.item.SmartInventory;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.fluid.SmartFluidTankBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.fluid.SmartFluidTankBehaviour.TankSegment;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.world.World;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
public class BasinRecipe extends ProcessingRecipe<SmartInventory> {
public static boolean match(BasinTileEntity basin, IRecipe<?> recipe) {
FilteringBehaviour filter = basin.getFilter();
if (filter == null || !filter.test(recipe.getRecipeOutput()))
return false;
return apply(basin, recipe, true);
}
public static boolean apply(BasinTileEntity basin, IRecipe<?> recipe) {
return apply(basin, recipe, false);
}
private static boolean apply(BasinTileEntity basin, IRecipe<?> recipe, boolean test) {
boolean isBasinRecipe = recipe instanceof BasinRecipe;
IItemHandler availableItems = basin.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
.orElse(null);
IFluidHandler availableFluids = basin.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY)
.orElse(null);
if (availableItems == null || availableFluids == null)
return false;
HeatLevel heat = basin.getHeatLevel();
if (isBasinRecipe && !((BasinRecipe) recipe).getRequiredHeat()
.testBlazeBurner(heat))
return false;
List<ItemStack> recipeOutputItems = new ArrayList<>();
List<FluidStack> recipeOutputFluids = new ArrayList<>();
List<Ingredient> ingredients = new LinkedList<>(recipe.getIngredients());
ingredients.sort(Comparator.comparingInt(i -> i.getMatchingStacks().length));
List<FluidIngredient> fluidIngredients =
isBasinRecipe ? ((BasinRecipe) recipe).getFluidIngredients() : Collections.emptyList();
for (boolean simulate : Iterate.trueAndFalse) {
if (!simulate && test)
return true;
int[] extractedItemsFromSlot = new int[availableItems.getSlots()];
int[] extractedFluidsFromTank = new int[availableFluids.getTanks()];
Ingredients: for (int i = 0; i < ingredients.size(); i++) {
Ingredient ingredient = ingredients.get(i);
for (int slot = 0; slot < availableItems.getSlots(); slot++) {
if (simulate && availableItems.getStackInSlot(slot)
.getCount() <= extractedItemsFromSlot[slot])
continue;
ItemStack extracted = availableItems.extractItem(slot, 1, true);
if (!ingredient.test(extracted))
continue;
if (!simulate)
availableItems.extractItem(slot, 1, false);
else if (extracted.hasContainerItem())
recipeOutputItems.add(extracted.getContainerItem()
.copy());
extractedItemsFromSlot[slot]++;
continue Ingredients;
}
// something wasn't found
return false;
}
boolean fluidsAffected = false;
FluidIngredients: for (int i = 0; i < fluidIngredients.size(); i++) {
FluidIngredient fluidIngredient = fluidIngredients.get(i);
int amountRequired = fluidIngredient.getRequiredAmount();
for (int tank = 0; tank < availableFluids.getTanks(); tank++) {
FluidStack fluidStack = availableFluids.getFluidInTank(tank);
if (simulate && fluidStack.getAmount() <= extractedFluidsFromTank[tank])
continue;
if (!fluidIngredient.test(fluidStack))
continue;
int drainedAmount = Math.min(amountRequired, fluidStack.getAmount());
if (!simulate) {
fluidStack.shrink(drainedAmount);
fluidsAffected = true;
}
amountRequired -= drainedAmount;
if (amountRequired != 0)
continue;
extractedFluidsFromTank[tank] += drainedAmount;
continue FluidIngredients;
}
// something wasn't found
return false;
}
if (fluidsAffected) {
basin.getBehaviour(SmartFluidTankBehaviour.INPUT)
.foreach(TankSegment::onFluidStackChanged);
basin.getBehaviour(SmartFluidTankBehaviour.OUTPUT)
.foreach(TankSegment::onFluidStackChanged);
}
if (simulate) {
if (recipe instanceof BasinRecipe) {
recipeOutputItems.addAll(((BasinRecipe) recipe).rollResults());
recipeOutputFluids.addAll(((BasinRecipe) recipe).getFluidResults());
} else
recipeOutputItems.add(recipe.getRecipeOutput());
}
if (!basin.acceptOutputs(recipeOutputItems, recipeOutputFluids, simulate))
return false;
}
return true;
}
/**
* For JEI purposes only
*/
public boolean convertedRecipe;
public static BasinRecipe convert(IRecipe<?> recipe) {
BasinRecipe basinRecipe = new ProcessingRecipeBuilder<>(BasinRecipe::new, recipe.getId())
.withItemIngredients(recipe.getIngredients())
.withSingleItemOutput(recipe.getRecipeOutput())
.build();
basinRecipe.convertedRecipe = true;
return basinRecipe;
}
protected BasinRecipe(AllRecipeTypes type, ProcessingRecipeParams params) {
super(type, params);
}
public BasinRecipe(ProcessingRecipeParams params) {
this(AllRecipeTypes.BASIN, params);
}
@Override
protected int getMaxInputCount() {
return 9;
}
@Override
protected int getMaxOutputCount() {
return 4;
}
@Override
protected int getMaxFluidInputCount() {
return 2;
}
@Override
protected int getMaxFluidOutputCount() {
return 2;
}
@Override
protected boolean canRequireHeat() {
return true;
}
@Override
public boolean matches(SmartInventory inv, @Nonnull World worldIn) {
return false;
}
}

View file

@ -79,16 +79,19 @@ public class BasinRenderer extends SmartTileEntityRenderer<BasinTileEntity> {
if (tankSegment.getRenderedFluid()
.isEmpty())
continue;
totalUnits += tankSegment.getTotalUnits(partialTicks);
float units = tankSegment.getTotalUnits(partialTicks);
if (units < 1)
continue;
totalUnits += units;
renderedFluids++;
}
}
if (renderedFluids == 0)
return 0;
if (totalUnits == 0)
if (totalUnits < 1)
return 0;
float fluidLevel = MathHelper.clamp(totalUnits / 2000, 0, 1);
float xMin = 2 / 16f;
@ -105,8 +108,11 @@ public class BasinRenderer extends SmartTileEntityRenderer<BasinTileEntity> {
FluidStack renderedFluid = tankSegment.getRenderedFluid();
if (renderedFluid.isEmpty())
continue;
float partial = tankSegment.getTotalUnits(partialTicks) / totalUnits;
float units = tankSegment.getTotalUnits(partialTicks);
if (units < 1)
continue;
float partial = units / totalUnits;
xMax += partial * 12 / 16f;
FluidRenderer.renderTiledFluidBB(renderedFluid, xMin, yMin, zMin, xMax, yMax, zMax, buffer, ms, light,
false);
@ -114,7 +120,7 @@ public class BasinRenderer extends SmartTileEntityRenderer<BasinTileEntity> {
xMin = xMax;
}
}
return fluidLevel;
}

View file

@ -5,6 +5,9 @@ import java.util.Optional;
import javax.annotation.Nonnull;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel;
import com.simibubi.create.foundation.fluid.CombinedTankWrapper;
import com.simibubi.create.foundation.item.SmartInventory;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
@ -13,30 +16,36 @@ import com.simibubi.create.foundation.tileEntity.behaviour.ValueBoxTransform;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.DirectBeltInputBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.fluid.SmartFluidTankBehaviour;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.wrapper.CombinedInvWrapper;
public class BasinTileEntity extends SmartTileEntity implements ITickableTileEntity {
public BasinInputInventory inputInventory;
public BasinInventory inputInventory;
public SmartFluidTankBehaviour inputTank;
protected SmartInventory outputInventory;
protected SmartFluidTankBehaviour outputTank;
@ -48,12 +57,11 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt
public BasinTileEntity(TileEntityType<? extends BasinTileEntity> type) {
super(type);
inputInventory = new BasinInputInventory(9, this);
inputInventory.withMaxStackSize(8)
.forbidExtraction();
outputInventory = new SmartInventory(9, this).forbidInsertion();
itemCapability = LazyOptional.of(() -> new CombinedInvWrapper(inputInventory, outputInventory));
inputInventory = new BasinInventory(9, this);
inputInventory.whenContentsChanged(() -> contentsChanged = true);
outputInventory = new BasinInventory(9, this).forbidInsertion();
itemCapability = LazyOptional.of(() -> new CombinedInvWrapper(inputInventory, outputInventory));
contentsChanged = true;
}
@ -65,7 +73,8 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt
.forRecipes();
behaviours.add(filtering);
inputTank = new SmartFluidTankBehaviour(SmartFluidTankBehaviour.INPUT, this, 2, 1000, true).forbidExtraction();
inputTank = new SmartFluidTankBehaviour(SmartFluidTankBehaviour.INPUT, this, 2, 1000, true)
.whenFluidUpdates(() -> contentsChanged = true);
outputTank = new SmartFluidTankBehaviour(SmartFluidTankBehaviour.OUTPUT, this, 2, 1000, true).forbidInsertion();
behaviours.add(inputTank);
behaviours.add(outputTank);
@ -112,6 +121,16 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt
return super.getCapability(cap, side);
}
@Override
public void notifyUpdate() {
super.notifyUpdate();
}
@Override
public void lazyTick() {
super.lazyTick();
}
@Override
public void tick() {
super.tick();
@ -119,6 +138,17 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt
return;
contentsChanged = false;
getOperator().ifPresent(te -> te.basinChecker.scheduleUpdate());
for (Direction offset : Iterate.horizontalDirections) {
BlockPos toUpdate = pos.up()
.offset(offset);
BlockState stateToUpdate = world.getBlockState(toUpdate);
if (stateToUpdate.getBlock() instanceof BasinBlock && stateToUpdate.get(BasinBlock.FACING) == offset.getOpposite()) {
TileEntity te = world.getTileEntity(toUpdate);
if (te instanceof BasinTileEntity)
((BasinTileEntity) te).contentsChanged = true;
}
}
}
private Optional<BasinOperatingTileEntity> getOperator() {
@ -152,11 +182,64 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt
return 256;
}
public boolean acceptOutputs(List<ItemStack> outputItems, List<FluidStack> outputFluids, boolean simulate) {
outputInventory.allowInsertion();
outputTank.allowInsertion();
boolean acceptOutputsInner = acceptOutputsInner(outputItems, outputFluids, simulate);
outputInventory.forbidInsertion();
outputTank.forbidInsertion();
return acceptOutputsInner;
}
private boolean acceptOutputsInner(List<ItemStack> outputItems, List<FluidStack> outputFluids, boolean simulate) {
BlockState blockState = getBlockState();
if (!(blockState.getBlock() instanceof BasinBlock))
return false;
Direction direction = blockState.get(BasinBlock.FACING);
IItemHandler targetInv = null;
IFluidHandler targetTank = null;
if (direction == Direction.DOWN) {
// No output basin, gather locally
targetInv = outputInventory;
targetTank = outputTank.getCapability()
.orElse(null);
} else {
// Output basin, try moving items to it
TileEntity te = world.getTileEntity(pos.down()
.offset(direction));
if (!(te instanceof BasinTileEntity))
return false;
targetInv = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
.orElse(null);
targetTank = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY)
.orElse(null);
}
if (targetInv == null)
return false;
for (ItemStack itemStack : outputItems)
if (!ItemHandlerHelper.insertItemStacked(targetInv, itemStack.copy(), simulate)
.isEmpty())
return false;
if (targetTank == null)
return false;
for (FluidStack fluidStack : outputFluids)
if (targetTank.fill(fluidStack.copy(), simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE) != fluidStack
.getAmount())
return false;
return true;
}
class BasinValueBox extends ValueBoxTransform.Sided {
@Override
protected Vec3d getSouthLocation() {
return VecHelper.voxelSpace(8, 12, 16);
return VecHelper.voxelSpace(8, 12, 15.75);
}
@Override
@ -171,4 +254,11 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt
inputInventory.deserializeNBT(compound.getCompound("InputItems"));
outputInventory.deserializeNBT(compound.getCompound("OutputItems"));
}
public HeatLevel getHeatLevel() {
BlockState state = world.getBlockState(pos.down(1));
if (state.has(BlazeBurnerBlock.HEAT_LEVEL))
return state.get(BlazeBurnerBlock.HEAT_LEVEL);
return AllTags.AllBlockTags.FAN_HEATERS.matches(state) ? HeatLevel.SMOULDERING : HeatLevel.NONE;
}
}

View file

@ -0,0 +1,75 @@
package com.simibubi.create.content.contraptions.processing;
import java.util.List;
import java.util.Optional;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.RecipeWrapper;
public class EmptyingByBasin {
static RecipeWrapper wrapper = new RecipeWrapper(new ItemStackHandler(1));
public static boolean canItemBeEmptied(World world, ItemStack stack) {
wrapper.setInventorySlotContents(0, stack);
if (AllRecipeTypes.EMPTYING.find(wrapper, world)
.isPresent())
return true;
LazyOptional<IFluidHandlerItem> capability =
stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY);
IFluidHandlerItem tank = capability.orElse(null);
if (tank == null)
return false;
for (int i = 0; i < tank.getTanks(); i++) {
if (tank.getFluidInTank(i)
.getAmount() > 0)
return true;
}
return false;
}
public static Pair<FluidStack, ItemStack> emptyItem(World world, ItemStack stack, boolean simulate) {
FluidStack resultingFluid = FluidStack.EMPTY;
ItemStack resultingItem = ItemStack.EMPTY;
wrapper.setInventorySlotContents(0, stack);
Optional<IRecipe<RecipeWrapper>> recipe = AllRecipeTypes.EMPTYING.find(wrapper, world);
if (recipe.isPresent()) {
EmptyingRecipe emptyingRecipe = (EmptyingRecipe) recipe.get();
List<ItemStack> results = emptyingRecipe.rollResults();
if (!simulate)
stack.shrink(1);
resultingItem = results.isEmpty() ? ItemStack.EMPTY : results.get(0);
resultingFluid = emptyingRecipe.getResultingFluid();
return Pair.of(resultingFluid, resultingItem);
}
ItemStack split = stack.copy();
split.setCount(1);
LazyOptional<IFluidHandlerItem> capability =
split.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY);
IFluidHandlerItem tank = capability.orElse(null);
if (tank == null)
return Pair.of(resultingFluid, resultingItem);
resultingFluid = tank.drain(1000, simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE);
resultingItem = tank.getContainer()
.copy();
if (!simulate)
stack.shrink(1);
return Pair.of(resultingFluid, resultingItem);
}
}

View file

@ -0,0 +1,42 @@
package com.simibubi.create.content.contraptions.processing;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.content.contraptions.processing.ProcessingRecipeBuilder.ProcessingRecipeParams;
import net.minecraft.world.World;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.items.wrapper.RecipeWrapper;
public class EmptyingRecipe extends ProcessingRecipe<RecipeWrapper> {
public EmptyingRecipe(ProcessingRecipeParams params) {
super(AllRecipeTypes.EMPTYING, params);
}
@Override
public boolean matches(RecipeWrapper inv, World p_77569_2_) {
return ingredients.get(0).test(inv.getStackInSlot(0));
}
@Override
protected int getMaxInputCount() {
return 1;
}
@Override
protected int getMaxOutputCount() {
return 1;
}
@Override
protected int getMaxFluidOutputCount() {
return 1;
}
public FluidStack getResultingFluid() {
if (fluidResults.isEmpty())
throw new IllegalStateException("Emptying Recipe: " + id.toString() + " has no fluid output!");
return fluidResults.get(0);
}
}

View file

@ -120,8 +120,7 @@ public class InWorldProcessing {
public static boolean isWashable(ItemStack stack, World world) {
splashingInv.setInventorySlotContents(0, stack);
Optional<SplashingRecipe> recipe = world.getRecipeManager()
.getRecipe(AllRecipeTypes.SPLASHING.getType(), splashingInv, world);
Optional<SplashingRecipe> recipe = AllRecipeTypes.SPLASHING.find(splashingInv, world);
return recipe.isPresent();
}
@ -176,8 +175,7 @@ public class InWorldProcessing {
private static List<ItemStack> process(ItemStack stack, Type type, World world) {
if (type == Type.SPLASHING) {
splashingInv.setInventorySlotContents(0, stack);
Optional<SplashingRecipe> recipe = world.getRecipeManager()
.getRecipe(AllRecipeTypes.SPLASHING.getType(), splashingInv, world);
Optional<SplashingRecipe> recipe = AllRecipeTypes.SPLASHING.find(splashingInv, world);
if (recipe.isPresent())
return applyRecipeOn(stack, recipe.get());
return null;

View file

@ -127,10 +127,10 @@ public interface ItemAttribute {
this.test = test;
}
private static boolean testRecipe(ItemStack s, World w, IRecipeType<? extends IRecipe<IInventory>> smelting) {
private static boolean testRecipe(ItemStack s, World w, IRecipeType<? extends IRecipe<IInventory>> type) {
RECIPE_WRAPPER.setInventorySlotContents(0, s.copy());
return w.getRecipeManager()
.getRecipe(smelting, RECIPE_WRAPPER, w)
.getRecipe(type, RECIPE_WRAPPER, w)
.isPresent();
}

View file

@ -27,6 +27,8 @@ public class CKinetics extends ConfigBase {
public ConfigInt maxPistonPoles = i(64, 1, "maxPistonPoles", Comments.maxPistonPoles);
public ConfigInt maxRopeLength = i(128, 1, "maxRopeLength", Comments.maxRopeLength);
public ConfigInt maxCartCouplingLength = i(32, 1, "maxCartCouplingLength", Comments.maxCartCouplingLength);
public CStress stressValues = nested(0, CStress::new, Comments.stress);
public ConfigGroup state = group(0, "stats", Comments.stats);
public ConfigFloat mediumSpeed = f(30, 0, 4096, "mediumSpeed", Comments.rpm, Comments.mediumSpeed);
@ -37,8 +39,6 @@ public class CKinetics extends ConfigBase {
public ConfigFloat mediumCapacity = f(128, 0, 4096, "mediumCapacity", Comments.su, Comments.mediumCapacity);
public ConfigFloat highCapacity = f(512, 0, 65535, "highCapacity", Comments.su, Comments.highCapacity);
public CStress stressValues = nested(0, CStress::new, Comments.stress);
@Override
public String getName() {
return "kinetics";

View file

@ -0,0 +1,26 @@
package com.simibubi.create.foundation.config;
public class CRecipes extends ConfigBase {
public ConfigBool allowShapelessInMixer = b(true, "allowShapelessInMixer", Comments.allowShapelessInMixer);
public ConfigBool allowShapedSquareInPress = b(true, "allowShapedSquareInPress", Comments.allowShapedSquareInPress);
public ConfigBool allowRegularCraftingInCrafter = b(true, "allowRegularCraftingInCrafter", Comments.allowRegularCraftingInCrafter);
public ConfigBool allowStonecuttingOnSaw = b(true, "allowStonecuttingOnSaw", Comments.allowStonecuttingOnSaw);
@Override
public String getName() {
return "recipes";
}
private static class Comments {
static String allowShapelessInMixer =
"When true, allows any shapeless crafting recipes to be processed by a Mechanical Mixer + Basin.";
static String allowShapedSquareInPress =
"When true, allows any single-ingredient 2x2 or 3x3 crafting recipes to be processed by a Mechanical Press + Basin.";
static String allowRegularCraftingInCrafter =
"When true, allows any standard crafting recipes to be processed by Mechanical Crafters.";
static String allowStonecuttingOnSaw =
"When true, allows any stonecutting recipes to be processed by a Mechanical Saw.";
}
}

View file

@ -6,6 +6,7 @@ public class CServer extends ConfigBase {
public ConfigInt tickrateSyncTimer =
i(20, 5, "tickrateSyncTimer", "[in Ticks]", Comments.tickrateSyncTimer, Comments.tickrateSyncTimer2);
public CRecipes recipes = nested(0, CRecipes::new, Comments.recipes);
public CKinetics kinetics = nested(0, CKinetics::new, Comments.kinetics);
public CFluids fluids = nested(0, CFluids::new, Comments.fluids);
public CLogistics logistics = nested(0, CLogistics::new, Comments.logistics);
@ -19,6 +20,7 @@ public class CServer extends ConfigBase {
}
private static class Comments {
static String recipes = "Packmakers' control panel for internal recipe compat";
static String schematics = "Everything related to Schematic tools";
static String kinetics = "Parameters and abilities of Create's kinetic mechanisms";
static String fluids = "Create's liquid manipulation tools";

View file

@ -0,0 +1,35 @@
package com.simibubi.create.foundation.data.recipe;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.content.palettes.AllPaletteBlocks;
import net.minecraft.data.DataGenerator;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.Items;
import net.minecraft.tags.FluidTags;
public class CompactingRecipeGen extends ProcessingRecipeGen {
GeneratedRecipe
TEMPGABBRO = create("temp_gabbro", b -> b
.require(Items.COBBLESTONE)
.require(FluidTags.LAVA, 250)
.output(AllPaletteBlocks.GABBRO.get(), 1)),
ICE = create("ice", b -> b
.require(Items.ICE)
.output(Fluids.WATER, 250))
;
public CompactingRecipeGen(DataGenerator p_i48262_1_) {
super(p_i48262_1_);
}
@Override
protected AllRecipeTypes getRecipeType() {
return AllRecipeTypes.COMPACTING;
}
}

View file

@ -0,0 +1,33 @@
package com.simibubi.create.foundation.data.recipe;
import com.simibubi.create.AllRecipeTypes;
import net.minecraft.data.DataGenerator;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.potion.PotionUtils;
import net.minecraft.potion.Potions;
import net.minecraftforge.common.crafting.NBTIngredient;
public class EmptyingRecipeGen extends ProcessingRecipeGen {
GeneratedRecipe
WATER_BOTTLE = create("water_bottle", b -> b
.require(NBTIngredient.fromStacks(PotionUtils.addPotionToItemStack(new ItemStack(Items.POTION), Potions.WATER)))
.output(Fluids.WATER, 250)
.output(Items.GLASS_BOTTLE))
;
public EmptyingRecipeGen(DataGenerator p_i48262_1_) {
super(p_i48262_1_);
}
@Override
protected AllRecipeTypes getRecipeType() {
return AllRecipeTypes.EMPTYING;
}
}

View file

@ -8,6 +8,7 @@ import com.simibubi.create.content.contraptions.processing.HeatCondition;
import net.minecraft.block.Blocks;
import net.minecraft.data.DataGenerator;
import net.minecraft.item.Items;
import net.minecraft.tags.FluidTags;
import net.minecraft.tags.ItemTags;
import net.minecraftforge.common.Tags;
@ -15,6 +16,11 @@ public class MixingRecipeGen extends ProcessingRecipeGen {
GeneratedRecipe
TEMPCOBBLE = create("temp_cobble", b -> b
.require(FluidTags.WATER, 250)
.require(FluidTags.LAVA, 250)
.output(Blocks.COBBLESTONE, 1)),
BRASS_INGOT = create("brass_ingot", b -> b.require(I.copper())
.require(I.zinc())
.output(AllItems.BRASS_INGOT.get(), 2)

View file

@ -32,8 +32,10 @@ public abstract class ProcessingRecipeGen extends CreateRecipeProvider {
generators.add(new WashingRecipeGen(gen));
generators.add(new PolishingRecipeGen(gen));
generators.add(new MixingRecipeGen(gen));
generators.add(new CompactingRecipeGen(gen));
generators.add(new PressingRecipeGen(gen));
generators.add(new FillingRecipeGen(gen));
generators.add(new EmptyingRecipeGen(gen));
gen.addProvider(new IDataProvider() {

View file

@ -201,9 +201,9 @@ public class StandardRecipeGen extends CreateRecipeProvider {
.key('A', Tags.Items.NUGGETS_IRON)
.patternLine("ASA")),
ATTRIBUTE_FILTER = create(AllItems.ATTRIBUTE_FILTER).unlockedBy(I::andesite)
ATTRIBUTE_FILTER = create(AllItems.ATTRIBUTE_FILTER).unlockedByTag(I::brass)
.viaShaped(b -> b.key('S', ItemTags.WOOL)
.key('A', I.copperNugget())
.key('A', I.brassNugget())
.patternLine("ASA")),
BRASS_HAND = create(AllItems.BRASS_HAND).unlockedByTag(I::brass)

View file

@ -62,6 +62,9 @@ public class CombinedTankWrapper implements IFluidHandler {
@Override
public int fill(FluidStack resource, FluidAction action) {
if (resource.isEmpty())
return 0;
int filled = 0;
resource = resource.copy();
@ -85,6 +88,9 @@ public class CombinedTankWrapper implements IFluidHandler {
@Override
public FluidStack drain(FluidStack resource, FluidAction action) {
if (resource.isEmpty())
return resource;
FluidStack drained = FluidStack.EMPTY;
resource = resource.copy();

View file

@ -74,6 +74,9 @@ public class FluidHelper {
int amount = JSONUtils.getInt(json, "amount");
FluidStack stack = new FluidStack(fluid, amount);
if (!json.has("nbt"))
return stack;
try {
JsonElement element = json.get("nbt");
stack.setTag(JsonToNBT.getTagFromJson(

View file

@ -10,17 +10,29 @@ import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.RecipeWrapper;
public class SmartInventory extends RecipeWrapper implements IItemHandlerModifiableIntermediate, INBTSerializable<CompoundNBT> {
public class SmartInventory extends RecipeWrapper
implements IItemHandlerModifiableIntermediate, INBTSerializable<CompoundNBT> {
private boolean extractionAllowed;
private boolean insertionAllowed;
private int stackSize;
protected boolean extractionAllowed;
protected boolean insertionAllowed;
protected boolean stackNonStackables;
protected int stackSize;
public SmartInventory(int slots, SyncedTileEntity te) {
super(new SyncedStackHandler(slots, te));
this(slots, te, 64, false);
}
public SmartInventory(int slots, SyncedTileEntity te, int stackSize, boolean stackNonStackables) {
super(new SyncedStackHandler(slots, te, stackNonStackables, stackSize));
this.stackNonStackables = stackNonStackables;
insertionAllowed = true;
extractionAllowed = true;
stackSize = 64;
this.stackSize = stackSize;
}
public SmartInventory whenContentsChanged(Runnable updateCallback) {
((SyncedStackHandler) inv).whenContentsChange(updateCallback);
return this;
}
public SmartInventory allowInsertion() {
@ -43,11 +55,6 @@ public class SmartInventory extends RecipeWrapper implements IItemHandlerModifia
return this;
}
public SmartInventory withMaxStackSize(int stackSize) {
this.stackSize = stackSize;
return this;
}
@Override
public int getSlots() {
return inv.getSlots();
@ -64,6 +71,11 @@ public class SmartInventory extends RecipeWrapper implements IItemHandlerModifia
public ItemStack extractItem(int slot, int amount, boolean simulate) {
if (!extractionAllowed)
return ItemStack.EMPTY;
if (stackNonStackables) {
ItemStack extractItem = inv.extractItem(slot, amount, true);
if (!extractItem.isEmpty() && extractItem.getMaxStackSize() < extractItem.getCount())
amount = extractItem.getMaxStackSize();
}
return inv.extractItem(slot, amount, simulate);
}
@ -86,7 +98,7 @@ public class SmartInventory extends RecipeWrapper implements IItemHandlerModifia
public ItemStack getStackInSlot(int slot) {
return super.getStackInSlot(slot);
}
public int getStackLimit(int slot, @Nonnull ItemStack stack) {
return Math.min(getSlotLimit(slot), stack.getMaxStackSize());
}
@ -100,7 +112,7 @@ public class SmartInventory extends RecipeWrapper implements IItemHandlerModifia
public void deserializeNBT(CompoundNBT nbt) {
getInv().deserializeNBT(nbt);
}
private SyncedStackHandler getInv() {
return (SyncedStackHandler) inv;
}
@ -108,18 +120,34 @@ public class SmartInventory extends RecipeWrapper implements IItemHandlerModifia
private static class SyncedStackHandler extends ItemStackHandler {
private SyncedTileEntity te;
private boolean stackNonStackables;
private int stackSize;
private Runnable updateCallback;
public SyncedStackHandler(int slots, SyncedTileEntity te) {
public SyncedStackHandler(int slots, SyncedTileEntity te, boolean stackNonStackables, int stackSize) {
super(slots);
this.te = te;
this.stackNonStackables = stackNonStackables;
this.stackSize = stackSize;
}
@Override
protected void onContentsChanged(int slot) {
super.onContentsChanged(slot);
if (updateCallback != null)
updateCallback.run();
te.notifyUpdate();
}
@Override
public int getSlotLimit(int slot) {
return Math.min(stackNonStackables ? 64 : super.getSlotLimit(slot), stackSize);
}
public void whenContentsChange(Runnable updateCallback) {
this.updateCallback = updateCallback;
}
}
@Override

View file

@ -10,6 +10,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.glu
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ClientMotionPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionInteractionPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionSeatMappingPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.LimbSwingUpdatePacket;
import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingCreationPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingSyncPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.train.PersistantDataPacket;
@ -68,6 +69,7 @@ public enum AllPackets {
MINECART_COUPLING_SYNC(MinecartCouplingSyncPacket.class, MinecartCouplingSyncPacket::new),
CONTRAPTION_SEAT_MAPPING(ContraptionSeatMappingPacket.class, ContraptionSeatMappingPacket::new),
PERSISTANT_DATA(PersistantDataPacket.class, PersistantDataPacket::new),
LIMBSWING_UPDATE(LimbSwingUpdatePacket.class, LimbSwingUpdatePacket::new),
;

View file

@ -34,6 +34,7 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour {
protected LazyOptional<? extends IFluidHandler> capability;
protected boolean extractionAllowed;
protected boolean insertionAllowed;
protected Runnable fluidUpdateCallback;
private BehaviourType<SmartFluidTankBehaviour> behaviourType;
@ -55,6 +56,13 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour {
handlers[i] = tankSegment.tank;
}
capability = LazyOptional.of(() -> new InternalFluidHandler(handlers, enforceVariety));
fluidUpdateCallback = () -> {
};
}
public SmartFluidTankBehaviour whenFluidUpdates(Runnable fluidUpdateCallback) {
this.fluidUpdateCallback = fluidUpdateCallback;
return this;
}
public SmartFluidTankBehaviour allowInsertion() {
@ -76,7 +84,7 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour {
extractionAllowed = false;
return this;
}
@Override
public void initialize() {
super.initialize();
@ -84,7 +92,7 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour {
return;
foreach(ts -> {
ts.fluidLevel.forceNextSync();
ts.onFluidStackChanged(ts.tank.getFluid());
ts.onFluidStackChanged();
});
}
@ -94,8 +102,8 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour {
if (syncCooldown > 0) {
syncCooldown--;
if (syncCooldown == 0 && queuedSync)
tileEntity.sendData();
if (syncCooldown == 0 && queuedSync)
updateFluids();
}
foreach(te -> {
@ -108,7 +116,7 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour {
public void sendDataImmediately() {
syncCooldown = 0;
queuedSync = false;
tileEntity.sendData();
updateFluids();
}
public void sendDataLazily() {
@ -116,11 +124,16 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour {
queuedSync = true;
return;
}
tileEntity.sendData();
updateFluids();
queuedSync = false;
syncCooldown = SYNC_RATE;
}
protected void updateFluids() {
fluidUpdateCallback.run();
tileEntity.sendData();
}
@Override
public void remove() {
super.remove();
@ -174,36 +187,36 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour {
index.increment();
});
}
class InternalFluidHandler extends CombinedTankWrapper {
public InternalFluidHandler(IFluidHandler[] handlers, boolean enforceVariety) {
super(handlers);
if (enforceVariety)
enforceVariety();
}
@Override
public int fill(FluidStack resource, FluidAction action) {
if (!insertionAllowed)
return 0;
return super.fill(resource, action);
}
@Override
public FluidStack drain(FluidStack resource, FluidAction action) {
if (!extractionAllowed)
return FluidStack.EMPTY;
return super.drain(resource, action);
}
@Override
public FluidStack drain(int maxDrain, FluidAction action) {
if (!extractionAllowed)
return FluidStack.EMPTY;
return super.drain(maxDrain, action);
}
}
public class TankSegment {
@ -213,14 +226,14 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour {
protected FluidStack renderedFluid;
public TankSegment(int capacity) {
tank = new SmartFluidTank(1000, f -> onFluidStackChanged(f));
tank = new SmartFluidTank(1000, f -> onFluidStackChanged());
fluidLevel = LerpedFloat.linear()
.startWithValue(0)
.chase(0, .25, Chaser.EXP);
renderedFluid = FluidStack.EMPTY;
}
protected void onFluidStackChanged(FluidStack newFluidStack) {
public void onFluidStackChanged() {
if (!tileEntity.hasWorld())
return;
fluidLevel.chase(tank.getFluidAmount() / (float) tank.getCapacity(), .25, Chaser.EXP);
@ -235,7 +248,7 @@ public class SmartFluidTankBehaviour extends TileEntityBehaviour {
public LerpedFloat getFluidLevel() {
return fluidLevel;
}
public float getTotalUnits(float partialTicks) {
return fluidLevel.getValue(partialTicks) * tank.getCapacity();
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,237 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"1": "create:block/basin_side",
"2": "create:block/basin_canal",
"12": "create:block/basin",
"particle": "create:block/basin"
},
"elements": [
{
"name": "Side1",
"from": [14, 2, 0],
"to": [16, 16, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 24, 8]},
"faces": {
"north": {"uv": [0, 0, 2, 14], "texture": "#1"},
"east": {"uv": [0, 0, 16, 14], "texture": "#1"},
"south": {"uv": [14, 0, 16, 14], "texture": "#1"},
"west": {"uv": [0, 0, 16, 14], "texture": "#1"},
"up": {"uv": [0, 0, 2, 16], "rotation": 180, "texture": "#12"},
"down": {"uv": [0, 0, 2, 16], "rotation": 180, "texture": "#12"}
}
},
{
"name": "BasinBottom",
"from": [2, 0, 2],
"to": [14, 2, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 25, 8]},
"faces": {
"north": {"uv": [2, 14, 14, 16], "texture": "#1"},
"east": {"uv": [2, 14, 14, 16], "texture": "#1"},
"south": {"uv": [2, 14, 14, 16], "texture": "#1"},
"west": {"uv": [2, 14, 14, 16], "texture": "#1"},
"up": {"uv": [2, 2, 14, 14], "rotation": 180, "texture": "#12"},
"down": {"uv": [2, 2, 14, 14], "rotation": 180, "texture": "#12"}
}
},
{
"name": "Side4",
"from": [2, 2, 14],
"to": [14, 16, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 24, 8]},
"faces": {
"north": {"uv": [2, 0, 14, 14], "texture": "#1"},
"south": {"uv": [2, 0, 14, 14], "texture": "#1"},
"up": {"uv": [2, 0, 14, 2], "rotation": 180, "texture": "#12"},
"down": {"uv": [2, 14, 14, 16], "rotation": 180, "texture": "#12"}
}
},
{
"name": "Side2",
"from": [2, 2, 0],
"to": [14, 16, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 24, 8]},
"faces": {
"north": {"uv": [2, 0, 14, 14], "texture": "#1"},
"south": {"uv": [2, 0, 14, 14], "texture": "#1"},
"up": {"uv": [2, 14, 14, 16], "rotation": 180, "texture": "#12"},
"down": {"uv": [2, 0, 14, 2], "rotation": 180, "texture": "#12"}
}
},
{
"name": "Side3",
"from": [0, 2, 0],
"to": [2, 16, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 40, 8]},
"faces": {
"north": {"uv": [14, 0, 16, 14], "texture": "#1"},
"east": {"uv": [0, 0, 16, 14], "texture": "#1"},
"south": {"uv": [0, 0, 2, 14], "texture": "#1"},
"west": {"uv": [0, 0, 16, 14], "texture": "#1"},
"up": {"uv": [14, 0, 16, 16], "rotation": 180, "texture": "#12"},
"down": {"uv": [14, 0, 16, 16], "rotation": 180, "texture": "#12"}
}
},
{
"from": [12, 0, 14],
"to": [13, 2, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [5, 8, 8]},
"faces": {
"north": {"uv": [14, 10, 16, 11], "rotation": 90, "texture": "#2"},
"east": {"uv": [14, 10, 16, 12], "rotation": 90, "texture": "#2"},
"south": {"uv": [0, 0, 10, 2], "texture": "#2"},
"west": {"uv": [14, 10, 16, 12], "texture": "#2"},
"up": {"uv": [14, 10, 15, 12], "texture": "#2"},
"down": {"uv": [15, 10, 16, 12], "rotation": 180, "texture": "#2"}
}
},
{
"from": [3, 0, 14],
"to": [4, 2, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [-4, 8, 8]},
"faces": {
"north": {"uv": [14, 10, 16, 11], "rotation": 90, "texture": "#2"},
"east": {"uv": [14, 10, 16, 12], "rotation": 90, "texture": "#2"},
"south": {"uv": [0, 0, 10, 2], "texture": "#2"},
"west": {"uv": [14, 10, 16, 12], "texture": "#2"},
"up": {"uv": [14, 10, 15, 12], "texture": "#2"},
"down": {"uv": [15, 10, 16, 12], "rotation": 180, "texture": "#2"}
}
},
{
"from": [4, 0, 14],
"to": [12, 1, 17],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 8, 9]},
"faces": {
"north": {"uv": [8, 14, 9, 15], "rotation": 90, "texture": "#2"},
"east": {"uv": [14, 10, 16, 12], "rotation": 90, "texture": "#2"},
"south": {"uv": [7, 15, 15, 16], "texture": "#2"},
"west": {"uv": [14, 10, 16, 12], "texture": "#2"},
"up": {"uv": [11, 10, 12, 11], "texture": "#2"},
"down": {"uv": [7, 13, 15, 16], "rotation": 180, "texture": "#2"}
}
},
{
"from": [12, 0, 16],
"to": [13, 10, 17],
"rotation": {"angle": 0, "axis": "y", "origin": [5, 8, 9]},
"faces": {
"north": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"},
"east": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"},
"south": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"},
"west": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"},
"up": {"uv": [6, 12, 7, 13], "rotation": 180, "texture": "#2"},
"down": {"uv": [15, 12, 16, 13], "rotation": 180, "texture": "#2"}
}
},
{
"from": [4, 9, 16],
"to": [12, 10, 17],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 8, 9]},
"faces": {
"north": {"uv": [8, 9, 16, 10], "texture": "#2"},
"south": {"uv": [8, 9, 16, 10], "texture": "#2"},
"up": {"uv": [8, 9, 16, 10], "rotation": 180, "texture": "#2"}
}
},
{
"from": [3, 0, 16],
"to": [4, 10, 17],
"rotation": {"angle": 0, "axis": "y", "origin": [-4, 8, 9]},
"faces": {
"north": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"},
"east": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"},
"south": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"},
"west": {"uv": [6, 12, 16, 13], "rotation": 90, "texture": "#2"},
"up": {"uv": [6, 12, 7, 13], "rotation": 180, "texture": "#2"},
"down": {"uv": [15, 12, 16, 13], "rotation": 180, "texture": "#2"}
}
},
{
"from": [5, 1, 13],
"to": [11, 2, 22],
"rotation": {"angle": 22.5, "axis": "x", "origin": [8, 9, 17]},
"faces": {
"north": {"uv": [9, 7, 15, 8], "texture": "#2"},
"east": {"uv": [0, 0, 9, 1], "texture": "#2"},
"south": {"uv": [9, 7, 15, 8], "texture": "#2"},
"west": {"uv": [0, 0, 9, 1], "texture": "#2"},
"up": {"uv": [1, 0, 7, 9], "rotation": 180, "texture": "#2"},
"down": {"uv": [1, 0, 7, 9], "texture": "#2"}
}
},
{
"from": [5, 8, 13],
"to": [11, 9, 22],
"rotation": {"angle": 22.5, "axis": "x", "origin": [8, 9, 17]},
"faces": {
"north": {"uv": [9, 0, 15, 1], "texture": "#2"},
"east": {"uv": [0, 0, 9, 1], "texture": "#2"},
"south": {"uv": [9, 0, 15, 1], "texture": "#2"},
"west": {"uv": [0, 0, 9, 1], "texture": "#2"},
"up": {"uv": [1, 0, 7, 9], "texture": "#2"},
"down": {"uv": [1, 0, 7, 9], "rotation": 180, "texture": "#2"}
}
},
{
"from": [5, 2, 14],
"to": [11, 8, 20],
"rotation": {"angle": 22.5, "axis": "x", "origin": [8, 9, 17]},
"faces": {
"north": {"uv": [9, 1, 15, 7], "texture": "#2"},
"south": {"uv": [9, 1, 15, 7], "texture": "#2"}
}
},
{
"from": [4, 1, 13],
"to": [5, 9, 22],
"rotation": {"angle": 22.5, "axis": "x", "origin": [1, 9, 17]},
"faces": {
"north": {"uv": [15, 0, 16, 8], "texture": "#2"},
"east": {"uv": [0, 0, 8, 9], "rotation": 90, "texture": "#2"},
"south": {"uv": [8, 0, 9, 8], "texture": "#2"},
"west": {"uv": [0, 0, 8, 9], "rotation": 270, "texture": "#2"},
"up": {"uv": [0, 0, 1, 9], "texture": "#2"},
"down": {"uv": [0, 0, 1, 9], "rotation": 180, "texture": "#2"}
}
},
{
"from": [11, 1, 13],
"to": [12, 9, 22],
"rotation": {"angle": 22.5, "axis": "x", "origin": [8, 9, 17]},
"faces": {
"north": {"uv": [8, 0, 9, 8], "texture": "#2"},
"east": {"uv": [0, 0, 8, 9], "rotation": 90, "texture": "#2"},
"south": {"uv": [15, 0, 16, 8], "texture": "#2"},
"west": {"uv": [0, 0, 8, 9], "rotation": 90, "texture": "#2"},
"up": {"uv": [0, 0, 1, 9], "texture": "#2"},
"down": {"uv": [0, 0, 1, 9], "rotation": 180, "texture": "#2"}
}
}
],
"groups": [
{
"name": "Basins",
"origin": [8, 8, 8],
"children": [
{
"name": "Basin top",
"origin": [8, 8, 8],
"children": [0, 1, 2, 3, 4]
}
]
},
{
"name": "Canal",
"origin": [8, 8, 8],
"children": [
{
"name": "Frame",
"origin": [8, 8, 8],
"children": [5, 6, 7, 8, 9, 10]
}, 11, 12, 13, 14, 15]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

After

Width:  |  Height:  |  Size: 259 B