diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/ValueBoxRenderer.java b/src/main/java/com/simibubi/create/foundation/behaviour/ValueBoxRenderer.java index 702a4b94a..d5fd3b5f4 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/ValueBoxRenderer.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/ValueBoxRenderer.java @@ -22,6 +22,7 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.item.BlockItem; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.tags.BlockTags; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.Vec3d; @@ -96,6 +97,8 @@ public class ValueBoxRenderer { return NUDGE; if (block instanceof FenceBlock) return NUDGE; + if (block.isIn(BlockTags.BUTTONS)) + return NUDGE; if (block == Blocks.END_ROD) return NUDGE; } diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/base/SmartTileEntity.java b/src/main/java/com/simibubi/create/foundation/behaviour/base/SmartTileEntity.java index 68c924840..e16da9cd5 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/base/SmartTileEntity.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/base/SmartTileEntity.java @@ -17,12 +17,15 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka private Map, TileEntityBehaviour> behaviours; private boolean initialized; private boolean firstNbtRead; + private int lazyTickRate; + private int lazyTickCounter; public SmartTileEntity(TileEntityType tileEntityTypeIn) { super(tileEntityTypeIn); behaviours = new HashMap<>(); initialized = false; firstNbtRead = true; + setLazyTickRate(10); ArrayList list = new ArrayList<>(); addBehaviours(list); @@ -45,6 +48,11 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka initialized = true; } + if (lazyTickCounter-- <= 0) { + lazyTickCounter = lazyTickRate; + lazyTick(); + } + behaviours.values().forEach(TileEntityBehaviour::tick); } @@ -83,6 +91,15 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka super.remove(); } + public void setLazyTickRate(int slowTickRate) { + this.lazyTickRate = slowTickRate; + this.lazyTickCounter = slowTickRate; + } + + public void lazyTick() { + + } + protected void forEachBehaviour(Consumer action) { behaviours.values().forEach(tb -> { if (!tb.isPaused()) diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/inventory/InventoryManagementBehaviour.java b/src/main/java/com/simibubi/create/foundation/behaviour/inventory/InventoryManagementBehaviour.java index 6da0fae2d..763d757f9 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/inventory/InventoryManagementBehaviour.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/inventory/InventoryManagementBehaviour.java @@ -43,6 +43,7 @@ public class InventoryManagementBehaviour extends TileEntityBehaviour { public void initialize() { super.initialize(); attachments.get().forEach(offset -> inventories.put(offset, findInventory(offset))); + lazyTick(); } @Override diff --git a/src/main/java/com/simibubi/create/modules/contraptions/base/KineticTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/base/KineticTileEntity.java index 060d5501d..fbeb20ac0 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/base/KineticTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/base/KineticTileEntity.java @@ -272,6 +272,8 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick @Override public void tick() { + super.tick(); + if (world.isRemote) return; if (speedChangeCounter > 0) diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/ConnectedInputHandler.java b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/ConnectedInputHandler.java index a5560c027..7133fb0fd 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/ConnectedInputHandler.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/ConnectedInputHandler.java @@ -36,7 +36,6 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.Vec3d; -import net.minecraft.world.IEnviromentBlockReader; import net.minecraft.world.World; import net.minecraftforge.common.util.Constants.NBT; import net.minecraftforge.event.entity.player.PlayerInteractEvent; @@ -126,8 +125,8 @@ public class ConnectedInputHandler { } public static void toggleConnection(World world, BlockPos pos, BlockPos pos2) { - MechanicalCrafterTileEntity crafter1 = getCrafter(world, pos); - MechanicalCrafterTileEntity crafter2 = getCrafter(world, pos2); + MechanicalCrafterTileEntity crafter1 = CrafterHelper.getCrafter(world, pos); + MechanicalCrafterTileEntity crafter2 = CrafterHelper.getCrafter(world, pos2); if (crafter1 == null || crafter2 == null) return; @@ -136,7 +135,7 @@ public class ConnectedInputHandler { BlockPos controllerPos2 = crafter2.getPos().add(crafter2.input.data.get(0)); if (controllerPos1.equals(controllerPos2)) { - MechanicalCrafterTileEntity controller = getCrafter(world, controllerPos1); + MechanicalCrafterTileEntity controller = CrafterHelper.getCrafter(world, controllerPos1); Set positions = controller.input.data.stream().map(l -> controllerPos1.add(l)) .collect(Collectors.toSet()); @@ -168,9 +167,9 @@ public class ConnectedInputHandler { } if (!crafter1.input.isController) - crafter1 = getCrafter(world, controllerPos1); + crafter1 = CrafterHelper.getCrafter(world, controllerPos1); if (!crafter2.input.isController) - crafter2 = getCrafter(world, controllerPos2); + crafter2 = CrafterHelper.getCrafter(world, controllerPos2); if (crafter1 == null || crafter2 == null) return; @@ -217,18 +216,6 @@ public class ConnectedInputHandler { crafter1.input.data.add(BlockPos.ZERO.subtract(crafter2.input.data.get(0))); } - public static MechanicalCrafterTileEntity getCrafter(IEnviromentBlockReader reader, BlockPos pos) { - TileEntity te = reader.getTileEntity(pos); - if (!(te instanceof MechanicalCrafterTileEntity)) - return null; - return (MechanicalCrafterTileEntity) te; - } - - public static ConnectedInput getInput(IEnviromentBlockReader reader, BlockPos pos) { - MechanicalCrafterTileEntity crafter = getCrafter(reader, pos); - return crafter == null ? null : crafter.input; - } - private static void modifyAndUpdate(World world, BlockPos pos, Consumer callback) { TileEntity te = world.getTileEntity(pos); if (!(te instanceof MechanicalCrafterTileEntity)) @@ -258,13 +245,13 @@ public class ConnectedInputHandler { public IItemHandler getItemHandler(World world, BlockPos pos) { if (!isController) { BlockPos controllerPos = pos.add(data.get(0)); - ConnectedInput input = getInput(world, controllerPos); + ConnectedInput input = CrafterHelper.getInput(world, controllerPos); if (input == this || input == null || !input.isController) return new ItemStackHandler(); return input.getItemHandler(world, controllerPos); } - List list = data.stream().map(l -> getCrafter(world, pos.add(l))) + List list = data.stream().map(l -> CrafterHelper.getCrafter(world, pos.add(l))) .filter(Predicates.notNull()).map(crafter -> crafter.inventory).collect(Collectors.toList()); return new CombinedInvWrapper(Arrays.copyOf(list.toArray(), list.size(), IItemHandlerModifiable[].class)); } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/CrafterHelper.java b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/CrafterHelper.java new file mode 100644 index 000000000..857c9e184 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/CrafterHelper.java @@ -0,0 +1,21 @@ +package com.simibubi.create.modules.contraptions.components.crafter; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IEnviromentBlockReader; + +public class CrafterHelper { + + public static MechanicalCrafterTileEntity getCrafter(IEnviromentBlockReader reader, BlockPos pos) { + TileEntity te = reader.getTileEntity(pos); + if (!(te instanceof MechanicalCrafterTileEntity)) + return null; + return (MechanicalCrafterTileEntity) te; + } + + public static ConnectedInputHandler.ConnectedInput getInput(IEnviromentBlockReader reader, BlockPos pos) { + MechanicalCrafterTileEntity crafter = getCrafter(reader, pos); + return crafter == null ? null : crafter.input; + } + +} diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/InputCTBehaviour.java b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/InputCTBehaviour.java index d2b37ac89..80bf4d996 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/InputCTBehaviour.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/InputCTBehaviour.java @@ -31,8 +31,8 @@ public class InputCTBehaviour extends ConnectedTextureBehaviour { if (state.get(HORIZONTAL_FACING) != other.get(HORIZONTAL_FACING)) return false; - ConnectedInput input1 = ConnectedInputHandler.getInput(reader, pos); - ConnectedInput input2 = ConnectedInputHandler.getInput(reader, otherPos); + ConnectedInput input1 = CrafterHelper.getInput(reader, pos); + ConnectedInput input2 = CrafterHelper.getInput(reader, otherPos); if (input1 == null || input2 == null) return false; diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterBlock.java index b20cf277c..d34381c0f 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterBlock.java @@ -1,6 +1,7 @@ package com.simibubi.create.modules.contraptions.components.crafter; import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllItems; import com.simibubi.create.foundation.block.IWithTileEntity; import com.simibubi.create.foundation.block.connected.ConnectedTextureBehaviour; import com.simibubi.create.foundation.block.connected.IHaveConnectedTextures; @@ -9,6 +10,7 @@ import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.modules.contraptions.base.HorizontalKineticBlock; import com.simibubi.create.modules.contraptions.components.crafter.ConnectedInputHandler.ConnectedInput; +import com.simibubi.create.modules.contraptions.components.crafter.MechanicalCrafterTileEntity.Phase; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -90,14 +92,26 @@ public class MechanicalCrafterBlock extends HorizontalKineticBlock @Override public void onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) { + if (state.getBlock() == newState.getBlock()) { + if (getTargetDirection(state) != getTargetDirection(newState)) { + MechanicalCrafterTileEntity crafter = CrafterHelper.getCrafter(worldIn, pos); + if (crafter != null) + crafter.blockChanged(); + } + } + if (state.hasTileEntity() && state.getBlock() != newState.getBlock()) { + MechanicalCrafterTileEntity crafter = CrafterHelper.getCrafter(worldIn, pos); + if (crafter != null) + crafter.ejectWholeGrid(); + for (Direction direction : Direction.values()) { if (direction.getAxis() == state.get(HORIZONTAL_FACING).getAxis()) continue; BlockPos otherPos = pos.offset(direction); - ConnectedInput thisInput = ConnectedInputHandler.getInput(worldIn, pos); - ConnectedInput otherInput = ConnectedInputHandler.getInput(worldIn, otherPos); + ConnectedInput thisInput = CrafterHelper.getInput(worldIn, pos); + ConnectedInput otherInput = CrafterHelper.getInput(worldIn, otherPos); if (thisInput == null || otherInput == null) continue; @@ -130,6 +144,7 @@ public class MechanicalCrafterBlock extends HorizontalKineticBlock public ActionResultType onWrenched(BlockState state, ItemUseContext context) { if (context.getFace() == state.get(HORIZONTAL_FACING)) { context.getWorld().setBlockState(context.getPos(), state.cycle(POINTING)); + withTileEntityDo(context.getWorld(), context.getPos(), TileEntity::markDirty); return ActionResultType.SUCCESS; } @@ -148,6 +163,12 @@ public class MechanicalCrafterBlock extends HorizontalKineticBlock MechanicalCrafterTileEntity crafter = (MechanicalCrafterTileEntity) te; if (hit.getFace() == state.get(HORIZONTAL_FACING)) { + + if (crafter.phase != Phase.IDLE && !AllItems.WRENCH.typeOf(heldItem)) { + crafter.ejectWholeGrid(); + return true; + } + ItemStack inSlot = crafter.inventory.getStackInSlot(0); if (inSlot.isEmpty()) return false; diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterTileEntity.java index 301b8255b..c0476dc01 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterTileEntity.java @@ -1,12 +1,34 @@ package com.simibubi.create.modules.contraptions.components.crafter; +import static com.simibubi.create.modules.contraptions.base.HorizontalKineticBlock.HORIZONTAL_FACING; + +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.lang3.tuple.Pair; + +import com.simibubi.create.AllBlocks; import com.simibubi.create.AllTileEntities; +import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; +import com.simibubi.create.foundation.behaviour.inventory.InsertingBehaviour; +import com.simibubi.create.foundation.behaviour.inventory.InventoryManagementBehaviour.Attachments; +import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.modules.contraptions.base.KineticTileEntity; import com.simibubi.create.modules.contraptions.components.crafter.ConnectedInputHandler.ConnectedInput; +import com.simibubi.create.modules.contraptions.components.crafter.MechanicalCrafterBlock.Pointing; +import com.simibubi.create.modules.contraptions.components.crafter.RecipeGridHandler.GroupedItems; +import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity; +import net.minecraft.entity.item.ItemEntity; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; +import net.minecraft.particles.ItemParticleData; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.CapabilityItemHandler; @@ -16,7 +38,7 @@ import net.minecraftforge.items.ItemStackHandler; public class MechanicalCrafterTileEntity extends KineticTileEntity { enum Phase { - IDLE, ACCEPTING, ASSEMBLING, EXPORTING + IDLE, ACCEPTING, ASSEMBLING, EXPORTING, WAITING, CRAFTING, INSERTING; } protected ItemStackHandler inventory = new ItemStackHandler(1) { @@ -30,19 +52,53 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity { return ItemStack.EMPTY; }; + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { + if (phase != Phase.IDLE) + return stack; + return super.insertItem(slot, stack, simulate); + }; + protected void onContentsChanged(int slot) { + if (!getStackInSlot(slot).isEmpty() && phase == Phase.IDLE) + checkCompletedRecipe(); markDirty(); sendData(); }; }; + protected GroupedItems groupedItems = new GroupedItems(); protected ConnectedInput input = new ConnectedInput(); protected LazyOptional invSupplier = LazyOptional.of(() -> input.getItemHandler(world, pos)); protected boolean reRender; + protected Phase phase; + protected int countDown; + + protected GroupedItems groupedItemsBeforeCraft; // for rendering on client + private InsertingBehaviour inserting; public MechanicalCrafterTileEntity() { super(AllTileEntities.MECHANICAL_CRAFTER.type); + setLazyTickRate(20); + phase = Phase.IDLE; + groupedItemsBeforeCraft = new GroupedItems(); + } + + @Override + public void addBehaviours(List behaviours) { + super.addBehaviours(behaviours); + inserting = new InsertingBehaviour(this, Attachments.toward(this::getTargetFacing)); + behaviours.add(inserting); + } + + public void blockChanged() { + removeBehaviour(InsertingBehaviour.TYPE); + inserting = new InsertingBehaviour(this, Attachments.toward(this::getTargetFacing)); + putBehaviour(inserting); + } + + public Direction getTargetFacing() { + return MechanicalCrafterBlock.getTargetDirection(world.getBlockState(pos)); } @Override @@ -53,9 +109,18 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity { @Override public CompoundNBT write(CompoundNBT compound) { compound.put("Inventory", inventory.serializeNBT()); + CompoundNBT inputNBT = new CompoundNBT(); input.write(inputNBT); compound.put("ConnectedInput", inputNBT); + + CompoundNBT groupedItemsNBT = new CompoundNBT(); + groupedItems.write(groupedItemsNBT); + compound.put("GroupedItems", groupedItemsNBT); + + compound.putString("Phase", phase.name()); + compound.putInt("CountDown", countDown); + return super.write(compound); } @@ -72,13 +137,35 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity { public void readClientUpdate(CompoundNBT tag) { if (tag.contains("Redraw")) world.notifyBlockUpdate(getPos(), getBlockState(), getBlockState(), 16); + + Phase phaseBefore = phase; + GroupedItems before = this.groupedItems; + super.readClientUpdate(tag); + + if (phaseBefore != phase && phase == Phase.CRAFTING) + groupedItemsBeforeCraft = before; + if (phaseBefore == Phase.EXPORTING && phase == Phase.WAITING) { + Direction facing = getBlockState().get(MechanicalCrafterBlock.HORIZONTAL_FACING); + Vec3d vec = new Vec3d(facing.getDirectionVec()).scale(.75).add(VecHelper.getCenterOf(pos)); + Direction targetDirection = MechanicalCrafterBlock.getTargetDirection(getBlockState()); + vec = vec.add(new Vec3d(targetDirection.getDirectionVec()).scale(1)); + world.addParticle(ParticleTypes.CRIT, vec.x, vec.y, vec.z, 0, 0, 0); + } + } @Override public void read(CompoundNBT compound) { inventory.deserializeNBT(compound.getCompound("Inventory")); input.read(compound.getCompound("ConnectedInput")); + groupedItems = GroupedItems.read(compound.getCompound("GroupedItems")); + phase = Phase.IDLE; + String name = compound.getString("Phase"); + for (Phase phase : Phase.values()) + if (phase.name().equals(name)) + this.phase = phase; + countDown = compound.getInt("CountDown"); super.read(compound); } @@ -88,10 +175,244 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity { super.remove(); } + public int getCountDownSpeed() { + if (getSpeed() == 0) + return 0; + return MathHelper.clamp((int) Math.abs(getSpeed() / 2), 1, 250); + } + + @Override + public void tick() { + super.tick(); + + if (phase == Phase.ACCEPTING) + return; + + if (phase == Phase.ASSEMBLING) { + countDown -= getCountDownSpeed(); + if (countDown < 0) { + countDown = 0; + if (world.isRemote) + return; + if (RecipeGridHandler.getTargetingCrafter(this) != null) { + phase = Phase.EXPORTING; + countDown = 1000; + sendData(); + return; + } + ItemStack result = RecipeGridHandler.tryToApplyRecipe(world, groupedItems); + if (result != null) { + groupedItems = new GroupedItems(result); + phase = Phase.CRAFTING; + countDown = 2000; + sendData(); + return; + } + ejectWholeGrid(); + return; + } + } + + if (phase == Phase.EXPORTING) { + countDown -= getCountDownSpeed(); + + if (countDown < 0) { + countDown = 0; + if (world.isRemote) + return; + + MechanicalCrafterTileEntity targetingCrafter = RecipeGridHandler.getTargetingCrafter(this); + if (targetingCrafter == null) { + ejectWholeGrid(); + return; + } + + Pointing pointing = getBlockState().get(MechanicalCrafterBlock.POINTING); + groupedItems.mergeOnto(targetingCrafter.groupedItems, pointing); + groupedItems = new GroupedItems(); + phase = Phase.WAITING; + countDown = 0; + sendData(); + targetingCrafter.continueIfAllPrecedingFinished(); + targetingCrafter.sendData(); + return; + } + } + + if (phase == Phase.CRAFTING) { + + if (world.isRemote) { + Direction facing = getBlockState().get(MechanicalCrafterBlock.HORIZONTAL_FACING); + float progress = countDown / 2000f; + Vec3d facingVec = new Vec3d(facing.getDirectionVec()); + Vec3d vec = facingVec.scale(.65).add(VecHelper.getCenterOf(pos)); + Vec3d offset = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .125f) + .mul(VecHelper.planeByNormal(facingVec)).normalize().scale(progress * .5f).add(vec); + if (progress > .5f) + world.addParticle(ParticleTypes.CRIT, offset.x, offset.y, offset.z, 0, 0, 0); + + if (!groupedItemsBeforeCraft.grid.isEmpty() && progress < .5f) { + if (groupedItems.grid.containsKey(Pair.of(0, 0))) { + ItemStack stack = groupedItems.grid.get(Pair.of(0, 0)); + groupedItemsBeforeCraft = new GroupedItems(); + + for (int i = 0; i < 10; i++) { + Vec3d randVec = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .125f) + .mul(VecHelper.planeByNormal(facingVec)).normalize().scale(.25f); + Vec3d offset2 = randVec.add(vec); + randVec = randVec.scale(.35f); + world.addParticle(new ItemParticleData(ParticleTypes.ITEM, stack), offset2.x, offset2.y, + offset2.z, randVec.x, randVec.y, randVec.z); + } + } + } + } + + countDown -= getCountDownSpeed(); + if (countDown < 0) { + countDown = 0; + if (world.isRemote) + return; + tryInsert(); + return; + } + } + + if (phase == Phase.INSERTING) { + if (!world.isRemote && isTargetingBelt()) + tryInsert(); + return; + } + + } + + protected boolean isTargetingBelt() { + BlockPos targetPos = pos.offset(getTargetFacing()); + if (!AllBlocks.BELT.typeOf(world.getBlockState(targetPos))) + return false; + TileEntity te = world.getTileEntity(targetPos); + if (te == null || !(te instanceof BeltTileEntity)) + return false; + return ((KineticTileEntity) te).getSpeed() != 0; + } + + public void tryInsert() { + if (inserting.getInventory() == null && !isTargetingBelt()) { + ejectWholeGrid(); + return; + } + + boolean chagedPhase = phase != Phase.INSERTING; + final List> inserted = new LinkedList<>(); + + groupedItems.grid.forEach((pair, stack) -> { + if (isTargetingBelt()) { + Direction facing = getTargetFacing(); + BlockPos targetPos = pos.offset(facing); + BeltTileEntity te = (BeltTileEntity) world.getTileEntity(targetPos); + if (te.tryInsertingFromSide(facing, stack, false)) + inserted.add(pair); + return; + } + + ItemStack remainder = inserting.insert(stack.copy(), false); + if (!remainder.isEmpty()) + stack.setCount(remainder.getCount()); + else + inserted.add(pair); + }); + + inserted.forEach(groupedItems.grid::remove); + if (groupedItems.grid.isEmpty()) + ejectWholeGrid(); + else + phase = Phase.INSERTING; + if (!inserted.isEmpty() || chagedPhase) + sendData(); + } + + public void ejectWholeGrid() { + List chain = RecipeGridHandler.getAllCraftersOfChain(this); + if (chain == null) + return; + chain.forEach(MechanicalCrafterTileEntity::eject); + } + + public void eject() { + Vec3d ejectPos = VecHelper.getCenterOf(pos) + .add(new Vec3d(getBlockState().get(HORIZONTAL_FACING).getDirectionVec()).scale(.75f)); + groupedItems.grid.forEach((pair, stack) -> dropItem(ejectPos, stack)); + if (!inventory.getStackInSlot(0).isEmpty()) + dropItem(ejectPos, inventory.getStackInSlot(0)); + phase = Phase.IDLE; + groupedItems = new GroupedItems(); + inventory.setStackInSlot(0, ItemStack.EMPTY); + sendData(); + } + + public void dropItem(Vec3d ejectPos, ItemStack stack) { + ItemEntity itemEntity = new ItemEntity(world, ejectPos.x, ejectPos.y, ejectPos.z, stack); + itemEntity.setDefaultPickupDelay(); + world.addEntity(itemEntity); + } + + @Override + public void lazyTick() { + super.lazyTick(); + if (world.isRemote) + return; + if (phase == Phase.IDLE && craftingItemPresent()) + checkCompletedRecipe(); + if (phase == Phase.INSERTING) + tryInsert(); + } + + public boolean craftingItemPresent() { + return !inventory.getStackInSlot(0).isEmpty(); + } + + protected void checkCompletedRecipe() { + if (getSpeed() == 0) + return; + if (world.isRemote) + return; + List chain = RecipeGridHandler.getAllCraftersOfChainIf(this, + MechanicalCrafterTileEntity::craftingItemPresent); + if (chain == null) + return; + chain.forEach(MechanicalCrafterTileEntity::begin); + } + + protected void begin() { + phase = Phase.ACCEPTING; + groupedItems = new GroupedItems(inventory.getStackInSlot(0)); + inventory.setStackInSlot(0, ItemStack.EMPTY); + if (RecipeGridHandler.getPrecedingCrafters(this).isEmpty()) { + phase = Phase.ASSEMBLING; + countDown = 500; + } + sendData(); + } + + protected void continueIfAllPrecedingFinished() { + List preceding = RecipeGridHandler.getPrecedingCrafters(this); + if (preceding == null) { + ejectWholeGrid(); + return; + } + + for (MechanicalCrafterTileEntity mechanicalCrafterTileEntity : preceding) + if (mechanicalCrafterTileEntity.phase != Phase.WAITING) + return; + + phase = Phase.ASSEMBLING; + countDown = Math.max(100, getCountDownSpeed() + 1); + } + @Override public LazyOptional getCapability(Capability cap, Direction side) { if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { - if (getBlockState().get(MechanicalCrafterBlock.HORIZONTAL_FACING) == side) + if (getBlockState().get(HORIZONTAL_FACING) == side) return LazyOptional.empty(); return invSupplier.cast(); } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterTileEntityRenderer.java b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterTileEntityRenderer.java index d24923098..871da3ed3 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterTileEntityRenderer.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/crafter/MechanicalCrafterTileEntityRenderer.java @@ -3,10 +3,15 @@ package com.simibubi.create.modules.contraptions.components.crafter; import com.mojang.blaze3d.platform.GlStateManager; import com.simibubi.create.AllBlocks; import com.simibubi.create.CreateClient; +import com.simibubi.create.foundation.block.render.SpriteShiftEntry; +import com.simibubi.create.foundation.block.render.SpriteShifter; import com.simibubi.create.foundation.utility.AngleHelper; +import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.SuperByteBuffer; import com.simibubi.create.foundation.utility.TessellatorHelper; import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer; +import com.simibubi.create.modules.contraptions.components.crafter.MechanicalCrafterTileEntity.Phase; +import com.simibubi.create.modules.contraptions.components.crafter.RecipeGridHandler.GroupedItems; import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; @@ -21,11 +26,15 @@ import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.util.Direction; import net.minecraft.util.Direction.Axis; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @SuppressWarnings("deprecation") public class MechanicalCrafterTileEntityRenderer extends TileEntityRenderer { + public static SpriteShiftEntry animatedTexture = SpriteShifter.get("block/crafter_thingies", + "block/crafter_thingies"); + @Override public void render(MechanicalCrafterTileEntity te, double x, double y, double z, float partialTicks, int destroyStage) { @@ -36,11 +45,19 @@ public class MechanicalCrafterTileEntityRenderer extends TileEntityRenderer { + GlStateManager.pushMatrix(); + GlStateManager.translatef(pair.getKey() * spacing, pair.getValue() * spacing, 0); + TessellatorHelper.fightZFighting(pair.hashCode() + te.getPos().hashCode()); + Minecraft.getInstance().getItemRenderer().renderItem(stack, TransformType.FIXED); + GlStateManager.popMatrix(); + }); + + GlStateManager.popMatrix(); + + if (te.phase == Phase.CRAFTING) { + items = te.groupedItems; + float progress = MathHelper.clamp((1000 - te.countDown + te.getCountDownSpeed() * partialTicks) / 1000f, + 0, 1); + float earlyProgress = MathHelper.clamp(progress * 2, 0, 1); + float lateProgress = MathHelper.clamp(progress * 2 - 1, 0, 1); + + GlStateManager.rotated(earlyProgress * 2 * 360, 0, 0, 1); + float upScaling = earlyProgress * 1.125f; + float downScaling = 1 + (1 - lateProgress) * .125f; + GlStateManager.scaled(upScaling, upScaling, upScaling); + GlStateManager.scaled(downScaling, downScaling, downScaling); + + items.grid.forEach((pair, stack) -> { + Minecraft.getInstance().getItemRenderer().renderItem(stack, TransformType.FIXED); + }); + } + + } RenderHelper.disableStandardItemLighting(); } @@ -70,13 +147,24 @@ public class MechanicalCrafterTileEntityRenderer extends TileEntityRenderer getAllCraftersOfChain(MechanicalCrafterTileEntity root) { + return getAllCraftersOfChainIf(root, Predicates.alwaysTrue()); + } + + public static List getAllCraftersOfChainIf(MechanicalCrafterTileEntity root, + Predicate test) { + List crafters = new ArrayList<>(); + List> frontier = new ArrayList<>(); + Set visited = new HashSet<>(); + frontier.add(Pair.of(root, null)); + + while (!frontier.isEmpty()) { + Pair pair = frontier.remove(0); + MechanicalCrafterTileEntity current = pair.getKey(); + MechanicalCrafterTileEntity last = pair.getValue(); + + if (visited.contains(current) || !test.test(current)) + return null; + crafters.add(current); + visited.add(current); + + MechanicalCrafterTileEntity target = getTargetingCrafter(current); + if (target != last && target != null) + frontier.add(Pair.of(target, current)); + for (MechanicalCrafterTileEntity preceding : getPrecedingCrafters(current)) + if (preceding != last) + frontier.add(Pair.of(preceding, current)); + } + + return crafters; + } + + public static MechanicalCrafterTileEntity getTargetingCrafter(MechanicalCrafterTileEntity crafter) { + BlockState state = crafter.getBlockState(); + if (!AllBlocks.MECHANICAL_CRAFTER.typeOf(state)) + return null; + + BlockPos targetPos = crafter.getPos().offset(MechanicalCrafterBlock.getTargetDirection(state)); + MechanicalCrafterTileEntity targetTE = CrafterHelper.getCrafter(crafter.getWorld(), targetPos); + if (targetTE == null) + return null; + + BlockState targetState = targetTE.getBlockState(); + if (!AllBlocks.MECHANICAL_CRAFTER.typeOf(targetState)) + return null; + if (state.get(HORIZONTAL_FACING) != targetState.get(HORIZONTAL_FACING)) + return null; + return targetTE; + } + + public static List getPrecedingCrafters(MechanicalCrafterTileEntity crafter) { + BlockPos pos = crafter.getPos(); + World world = crafter.getWorld(); + List crafters = new ArrayList<>(); + BlockState blockState = crafter.getBlockState(); + if (!AllBlocks.MECHANICAL_CRAFTER.typeOf(blockState)) + return crafters; + + Direction blockFacing = blockState.get(HORIZONTAL_FACING); + Direction blockPointing = MechanicalCrafterBlock.getTargetDirection(blockState); + for (Direction facing : Direction.values()) { + if (blockFacing.getAxis() == facing.getAxis()) + continue; + if (blockPointing == facing) + continue; + + BlockPos neighbourPos = pos.offset(facing); + BlockState neighbourState = world.getBlockState(neighbourPos); + if (!AllBlocks.MECHANICAL_CRAFTER.typeOf(neighbourState)) + continue; + if (MechanicalCrafterBlock.getTargetDirection(neighbourState) != facing.getOpposite()) + continue; + if (blockFacing != neighbourState.get(HORIZONTAL_FACING)) + continue; + MechanicalCrafterTileEntity te = CrafterHelper.getCrafter(world, neighbourPos); + if (te == null) + continue; + + crafters.add(te); + } + + return crafters; + } + + public static ItemStack tryToApplyRecipe(World world, GroupedItems items) { + CraftingInventory craftinginventory = getCraftingInventory(items); + ItemStack result = world.getRecipeManager().getRecipe(IRecipeType.CRAFTING, craftinginventory, world) + .map(r -> r.getCraftingResult(craftinginventory)).orElse(null); + return result; + } + + private static CraftingInventory getCraftingInventory(GroupedItems items) { + items.calcStats(); + CraftingInventory craftinginventory = new CraftingInventory(new Container(null, -1) { + public boolean canInteractWith(PlayerEntity playerIn) { + return false; + } + }, items.width, items.height); + + for (int y = 0; y < items.height; y++) { + for (int x = 0; x < items.width; x++) { + ItemStack stack = items.grid.get(Pair.of(x + items.minX, y + items.minY)); + craftinginventory.setInventorySlotContents(x + (items.height - y - 1) * items.width, + stack == null ? ItemStack.EMPTY : stack.copy()); + } + } + + return craftinginventory; + } + + public static class GroupedItems { + Map, ItemStack> grid = new HashMap<>(); + int minX, minY, maxX, maxY, width, height; + boolean statsReady; + + public GroupedItems() { + } + + public GroupedItems(ItemStack stack) { + grid.put(Pair.of(0, 0), stack); + } + + public void mergeOnto(GroupedItems other, Pointing pointing) { + int xOffset = pointing == Pointing.LEFT ? 1 : pointing == Pointing.RIGHT ? -1 : 0; + int yOffset = pointing == Pointing.DOWN ? 1 : pointing == Pointing.UP ? -1 : 0; + grid.forEach((pair, stack) -> other.grid.put(Pair.of(pair.getKey() + xOffset, pair.getValue() + yOffset), + stack)); + other.statsReady = false; + } + + public void write(CompoundNBT nbt) { + ListNBT gridNBT = new ListNBT(); + grid.forEach((pair, stack) -> { + CompoundNBT entry = new CompoundNBT(); + entry.putInt("x", pair.getKey()); + entry.putInt("y", pair.getValue()); + entry.put("item", stack.serializeNBT()); + gridNBT.add(entry); + }); + nbt.put("Grid", gridNBT); + } + + public static GroupedItems read(CompoundNBT nbt) { + GroupedItems items = new GroupedItems(); + ListNBT gridNBT = nbt.getList("Grid", NBT.TAG_COMPOUND); + gridNBT.forEach(inbt -> { + CompoundNBT entry = (CompoundNBT) inbt; + int x = entry.getInt("x"); + int y = entry.getInt("y"); + ItemStack stack = ItemStack.read(entry.getCompound("item")); + items.grid.put(Pair.of(x, y), stack); + }); + return items; + } + + public void calcStats() { + if (statsReady) + return; + statsReady = true; + + minX = 0; + minY = 0; + maxX = 0; + maxY = 0; + + for (Pair pair : grid.keySet()) { + int x = pair.getKey(); + int y = pair.getValue(); + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + } + + width = maxX - minX + 1; + height = maxY - minY + 1; + } + + } + +} diff --git a/src/main/resources/assets/create/textures/block/crafter_thingies.png b/src/main/resources/assets/create/textures/block/crafter_thingies.png index ff4c31aed..89501d9c8 100644 Binary files a/src/main/resources/assets/create/textures/block/crafter_thingies.png and b/src/main/resources/assets/create/textures/block/crafter_thingies.png differ