diff --git a/src/main/java/com/simibubi/create/content/schematics/SchematicPrinter.java b/src/main/java/com/simibubi/create/content/schematics/SchematicPrinter.java new file mode 100644 index 000000000..69a35ad9c --- /dev/null +++ b/src/main/java/com/simibubi/create/content/schematics/SchematicPrinter.java @@ -0,0 +1,305 @@ +package com.simibubi.create.content.schematics; + +import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllTags; +import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementChecks; +import com.simibubi.create.content.contraptions.relays.belt.BeltTileEntity; +import com.simibubi.create.content.schematics.block.SchematicannonTileEntity; + +import com.simibubi.create.content.schematics.item.SchematicItem; + +import com.simibubi.create.foundation.utility.BlockHelper; + +import com.simibubi.create.foundation.utility.IPartialSafeNBT; +import com.simibubi.create.foundation.utility.NBTProcessors; + +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.nbt.NBTUtil; +import net.minecraft.state.properties.BedPart; +import net.minecraft.state.properties.BlockStateProperties; +import net.minecraft.state.properties.DoubleBlockHalf; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MutableBoundingBox; +import net.minecraft.util.math.vector.Vector3i; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.template.PlacementSettings; +import net.minecraft.world.gen.feature.template.Template; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +public class SchematicPrinter { + public enum PrintStage { + BLOCKS, DEFERRED_BLOCKS, ENTITIES + } + + private boolean schematicLoaded; + private SchematicWorld blockReader; + private BlockPos schematicAnchor; + + private BlockPos currentPos; + private int printingEntityIndex; + private PrintStage printStage; + private List deferredBlocks; + + public SchematicPrinter() { + printingEntityIndex = -1; + printStage = PrintStage.BLOCKS; + deferredBlocks = new LinkedList<>(); + } + + public void fromTag(CompoundNBT compound, boolean clientPacket) { + if (compound.contains("CurrentPos")) + currentPos = NBTUtil.readBlockPos(compound.getCompound("CurrentPos")); + + printingEntityIndex = compound.getInt("EntityProgress"); + printStage = PrintStage.valueOf(compound.getString("PrintStage")); + compound.getList("DeferredBlocks", 10).stream() + .map(p -> NBTUtil.readBlockPos((CompoundNBT) p)) + .collect(Collectors.toCollection(() -> deferredBlocks)); + } + + public void write(CompoundNBT compound) { + if (currentPos != null) + compound.put("CurrentPos", NBTUtil.writeBlockPos(currentPos)); + + compound.putInt("EntityProgress", printingEntityIndex); + compound.putString("PrintStage", printStage.name()); + ListNBT tagDeferredBlocks = new ListNBT(); + for (BlockPos p : deferredBlocks) + tagDeferredBlocks.add(NBTUtil.writeBlockPos(p)); + compound.put("DeferredBlocks", tagDeferredBlocks); + } + + public void loadSchematic(ItemStack blueprint, World originalWorld, boolean processNBT) { + if (!blueprint.hasTag() || !blueprint.getTag().getBoolean("Deployed")) + return; + + Template activeTemplate = SchematicItem.loadSchematic(blueprint); + PlacementSettings settings = SchematicItem.getSettings(blueprint, processNBT); + + schematicAnchor = NBTUtil.readBlockPos(blueprint.getTag().getCompound("Anchor")); + blockReader = new SchematicWorld(schematicAnchor, originalWorld); + activeTemplate.place(blockReader, schematicAnchor, settings, blockReader.getRandom()); + + printingEntityIndex = -1; + printStage = PrintStage.BLOCKS; + deferredBlocks.clear(); + MutableBoundingBox bounds = blockReader.getBounds(); + currentPos = new BlockPos(bounds.minX - 1, bounds.minY, bounds.minZ); + schematicLoaded = true; + } + + public void resetSchematic() { + schematicLoaded = false; + schematicAnchor = null; + currentPos = null; + blockReader = null; + printingEntityIndex = -1; + printStage = PrintStage.BLOCKS; + deferredBlocks.clear(); + } + + public boolean isLoaded() { + return schematicLoaded; + } + + public BlockPos getCurrentTarget() { + if (!isLoaded()) + return null; + return schematicAnchor.add(currentPos); + } + + public PrintStage getPrintStage() { + return printStage; + } + + public BlockPos getAnchor() { + return schematicAnchor; + } + + public boolean isWorldEmpty() { + return blockReader.getBounds().getLength().equals(new Vector3i(0,0,0)); + } + + @FunctionalInterface + public interface BlockTargetHandler { + void handle(BlockPos target, BlockState blockState, TileEntity tileEntity); + } + @FunctionalInterface + public interface EntityTargetHandler { + void handle(BlockPos target, Entity entity); + } + + public void handleCurrentTarget(BlockTargetHandler blockHandler, EntityTargetHandler entityHandler) { + BlockPos target = getCurrentTarget(); + + if (printStage == PrintStage.ENTITIES) { + Entity entity = blockReader.getEntities() + .collect(Collectors.toList()) + .get(printingEntityIndex); + entityHandler.handle(target, entity); + } else { + BlockState blockState = BlockHelper.setZeroAge(blockReader.getBlockState(target)); + TileEntity tileEntity = blockReader.getTileEntity(target); + blockHandler.handle(target, blockState, tileEntity); + } + } + + @FunctionalInterface + public interface PlacementPredicate { + boolean shouldPlace(BlockPos target, BlockState blockState, TileEntity tileEntity, + BlockState toReplace, BlockState toReplaceOther, boolean isNormalCube); + } + + public boolean shouldPlaceCurrent(World world) { return shouldPlaceCurrent(world, (a,b,c,d,e,f) -> true); } + + public boolean shouldPlaceCurrent(World world, PlacementPredicate predicate) { + if (world == null) + return false; + + if (printStage == PrintStage.ENTITIES) + return true; + + return shouldPlaceBlock(world, predicate, getCurrentTarget()); + } + + public boolean shouldPlaceBlock(World world, PlacementPredicate predicate, BlockPos pos) { + BlockState state = BlockHelper.setZeroAge(blockReader.getBlockState(pos)); + TileEntity tileEntity = blockReader.getTileEntity(pos); + + BlockState toReplace = world.getBlockState(pos); + BlockState toReplaceOther = null; + if (state.contains(BlockStateProperties.BED_PART) && state.contains(BlockStateProperties.HORIZONTAL_FACING) + && state.get(BlockStateProperties.BED_PART) == BedPart.FOOT) + toReplaceOther = world.getBlockState(pos.offset(state.get(BlockStateProperties.HORIZONTAL_FACING))); + if (state.contains(BlockStateProperties.DOUBLE_BLOCK_HALF) + && state.get(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER) + toReplaceOther = world.getBlockState(pos.up()); + + if (!world.isBlockPresent(pos)) + return false; + if (!world.getWorldBorder().contains(pos)) + return false; + if (toReplace == state) + return false; + if (toReplace.getBlockHardness(world, pos) == -1 + || (toReplaceOther != null && toReplaceOther.getBlockHardness(world, pos) == -1)) + return false; + + boolean isNormalCube = state.isNormalCube(blockReader, currentPos); + return predicate.shouldPlace(pos, state, tileEntity, toReplace, toReplaceOther, isNormalCube); + } + + public ItemRequirement getCurrentRequirement() { + if (printStage == PrintStage.ENTITIES) + return ItemRequirement.of(blockReader.getEntities() + .collect(Collectors.toList()) + .get(printingEntityIndex)); + + BlockPos target = getCurrentTarget(); + BlockState blockState = BlockHelper.setZeroAge(blockReader.getBlockState(target)); + TileEntity tileEntity = blockReader.getTileEntity(target); + return ItemRequirement.of(blockState, tileEntity); + } + + public int markAllBlockRequirements(MaterialChecklist checklist, World world, PlacementPredicate predicate) { + int blocksToPlace = 0; + for (BlockPos pos : blockReader.getAllPositions()) { + BlockPos relPos = pos.add(schematicAnchor); + BlockState required = blockReader.getBlockState(relPos); + TileEntity requiredTE = blockReader.getTileEntity(relPos); + + if (!world.isAreaLoaded(pos.add(schematicAnchor), 0)) { + checklist.warnBlockNotLoaded(); + continue; + } + if (!shouldPlaceBlock(world, predicate, relPos)) + continue; + ItemRequirement requirement = ItemRequirement.of(required, requiredTE); + if (requirement.isEmpty()) + continue; + if (requirement.isInvalid()) + continue; + checklist.require(requirement); + blocksToPlace++; + } + return blocksToPlace; + } + + public void markAllEntityRequirements(MaterialChecklist checklist) { + blockReader.getEntities() + .forEach(entity -> { + ItemRequirement requirement = ItemRequirement.of(entity); + if (requirement.isEmpty()) + return; + if (requirement.isInvalid()) + return; + checklist.require(requirement); + }); + } + + public boolean advanceCurrentPos() { + List entities = blockReader.getEntities().collect(Collectors.toList()); + + do { + if (printStage == PrintStage.BLOCKS) { + while (tryAdvanceCurrentPos()) { + deferredBlocks.add(currentPos); + } + } + + if (printStage == PrintStage.DEFERRED_BLOCKS) { + if (deferredBlocks.isEmpty()) { + printStage = PrintStage.ENTITIES; + } else { + currentPos = deferredBlocks.remove(0); + } + } + + if (printStage == PrintStage.ENTITIES) { + if (printingEntityIndex + 1 < entities.size()) { + printingEntityIndex++; + currentPos = entities.get(printingEntityIndex).getBlockPos().subtract(schematicAnchor); + } else { + // Reached end of printing + return false; + } + } + } while (!blockReader.getBounds().isVecInside(currentPos)); + + // More things available to print + return true; + } + + public boolean tryAdvanceCurrentPos() { + currentPos = currentPos.offset(Direction.EAST); + MutableBoundingBox bounds = blockReader.getBounds(); + BlockPos posInBounds = currentPos.add(-bounds.minX, -bounds.minY, -bounds.minZ); + + if (posInBounds.getX() > bounds.getXSize()) + currentPos = new BlockPos(bounds.minX, currentPos.getY(), currentPos.getZ() + 1).west(); + if (posInBounds.getZ() > bounds.getZSize()) + currentPos = new BlockPos(currentPos.getX(), currentPos.getY() + 1, bounds.minZ).west(); + + // End of blocks reached + if (currentPos.getY() > bounds.getYSize()) { + printStage = PrintStage.DEFERRED_BLOCKS; + return false; + } + + return shouldDeferBlock(blockReader.getBlockState(getCurrentTarget())); + } + + public static boolean shouldDeferBlock(BlockState state) { + return state.getBlock().is(AllBlocks.GANTRY_CARRIAGE.get()) || BlockMovementChecks.isBrittle(state); + } +} diff --git a/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonRenderer.java b/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonRenderer.java index 6ac93df76..4ff8847c1 100644 --- a/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonRenderer.java +++ b/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonRenderer.java @@ -84,13 +84,14 @@ public class SchematicannonRenderer extends SafeTileEntityRenderer> attachedInventories; public List flyingBlocks; public MaterialChecklist checklist; - public List deferredBlocks; // Gui information public float fuelLevel; @@ -150,11 +140,9 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC inventory = new SchematicannonInventory(this); statusMsg = "idle"; state = State.STOPPED; - printingEntityIndex = -1; - printStage = PrintStage.BLOCKS; - deferredBlocks = new LinkedList<>(); replaceMode = 2; checklist = new MaterialChecklist(); + printer = new SchematicPrinter(); } public void findInventories() { @@ -183,8 +171,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC protected void fromTag(BlockState blockState, CompoundNBT compound, boolean clientPacket) { if (!clientPacket) { inventory.deserializeNBT(compound.getCompound("Inventory")); - if (compound.contains("CurrentPos")) - currentPos = NBTUtil.readBlockPos(compound.getCompound("CurrentPos")); } // Gui information @@ -195,7 +181,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC state = State.valueOf(compound.getString("State")); blocksPlaced = compound.getInt("AmountPlaced"); blocksToPlace = compound.getInt("AmountToPlace"); - printingEntityIndex = compound.getInt("EntityProgress"); missingItem = null; if (compound.contains("MissingItem")) @@ -208,14 +193,8 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC replaceTileEntities = options.getBoolean("ReplaceTileEntities"); // Printer & Flying Blocks - if (compound.contains("PrintStage")) - printStage = PrintStage.valueOf(compound.getString("PrintStage")); - if (compound.contains("Target")) - target = NBTUtil.readBlockPos(compound.getCompound("Target")); - if (compound.contains("DeferredBlocks")) - compound.getList("DeferredBlocks", 10).stream() - .map(p -> NBTUtil.readBlockPos((CompoundNBT) p)) - .collect(Collectors.toCollection(() -> deferredBlocks)); + if (compound.contains("Printer")) + printer.fromTag(compound.getCompound("Printer"), clientPacket); if (compound.contains("FlyingBlocks")) readFlyingBlocks(compound); @@ -263,8 +242,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC compound.put("Inventory", inventory.serializeNBT()); if (state == State.RUNNING) { compound.putBoolean("Running", true); - if (currentPos != null) - compound.put("CurrentPos", NBTUtil.writeBlockPos(currentPos)); } } @@ -276,7 +253,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC compound.putString("State", state.name()); compound.putInt("AmountPlaced", blocksPlaced); compound.putInt("AmountToPlace", blocksToPlace); - compound.putInt("EntityProgress", printingEntityIndex); if (missingItem != null) compound.put("MissingItem", missingItem.serializeNBT()); @@ -289,15 +265,9 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC compound.put("Options", options); // Printer & Flying Blocks - compound.putString("PrintStage", printStage.name()); - - if (target != null) - compound.put("Target", NBTUtil.writeBlockPos(target)); - - ListNBT tagDeferredBlocks = new ListNBT(); - for (BlockPos p : deferredBlocks) - tagDeferredBlocks.add(NBTUtil.writeBlockPos(p)); - compound.put("DeferredBlocks", tagDeferredBlocks); + CompoundNBT printerData = new CompoundNBT(); + printer.write(printerData); + compound.put("Printer", printerData); ListNBT tagFlyingBlocks = new ListNBT(); for (LaunchedItem b : flyingBlocks) @@ -317,7 +287,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC } firstRenderTick = true; - previousTarget = target; + previousTarget = printer.getCurrentTarget(); tickFlyingBlocks(); if (world.isRemote) @@ -355,7 +325,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC // Skip if not Active if (state == State.STOPPED) { - if (schematicLoaded) + if (printer.isLoaded()) resetPrinter(); return; } @@ -371,7 +341,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC return; // Initialize Printer - if (!schematicLoaded) { + if (!printer.isLoaded()) { initializePrinter(blueprint); return; } @@ -391,7 +361,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC return; } - // Update Target if (hasCreativeCrate) { if (missingItem != null) { missingItem = null; @@ -399,23 +368,17 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC } } + // Update Target if (missingItem == null && !positionNotLoaded) { - do { - advanceCurrentPos(); - if (state == State.STOPPED) - return; - - } while (!blockReader.getBounds() - .isVecInside(currentPos)); - + if (!printer.advanceCurrentPos()) { + finishedPrinting(); + return; + } sendUpdate = true; - target = schematicAnchor.add(currentPos); } - boolean entityMode = printStage == PrintStage.ENTITIES; - // Check block - if (!getWorld().isAreaLoaded(target, 0)) { + if (!getWorld().isAreaLoaded(printer.getCurrentTarget(), 0)) { positionNotLoaded = true; statusMsg = "targetNotLoaded"; state = State.PAUSED; @@ -427,24 +390,9 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC } } - boolean shouldSkip = false; - BlockState blockState = Blocks.AIR.getDefaultState(); - TileEntity tileEntity = null; - ItemRequirement requirement; - - if (entityMode) { - requirement = ItemRequirement.of(blockReader.getEntities() - .collect(Collectors.toList()) - .get(printingEntityIndex)); - - } else { - blockState = BlockHelper.setZeroAge(blockReader.getBlockState(target)); - tileEntity = blockReader.getTileEntity(target); - requirement = ItemRequirement.of(blockState, tileEntity); - shouldSkip = !shouldPlace(target, blockState, tileEntity); - } - - if (shouldSkip || requirement.isInvalid()) { + // Get item requirement + ItemRequirement requirement = printer.getCurrentRequirement(); + if (requirement.isInvalid() || !printer.shouldPlaceCurrent(world, this::shouldPlace)) { statusMsg = "searching"; blockSkipped = true; return; @@ -478,39 +426,16 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC // Success state = State.RUNNING; - if (blockState.getBlock() != Blocks.AIR || entityMode) - statusMsg = "placing"; - else - statusMsg = "clearing"; - ItemStack icon = requirement.isEmpty() || requiredItems.isEmpty() ? ItemStack.EMPTY : requiredItems.get(0).item; - if (entityMode) - launchEntity(target, icon, blockReader.getEntities() - .collect(Collectors.toList()) - .get(printingEntityIndex)); - else if (AllBlocks.BELT.has(blockState)) { - TileEntity te = blockReader.getTileEntity(currentPos.add(schematicAnchor)); - blockState = stripBeltIfNotLast(blockState); - if (te instanceof BeltTileEntity && AllBlocks.BELT.has(blockState)) - launchBelt(target, blockState, ((BeltTileEntity) te).beltLength); - else - launchBlock(target, icon, blockState, null); - } else { - CompoundNBT data = null; - TileEntity tile = blockReader.getTileEntity(target); - if (tile != null) { - if (AllBlockTags.SAFE_NBT.matches(blockState)) { - data = tile.write(new CompoundNBT()); - data = NBTProcessors.process(tile, data, true); - } else if (tile instanceof IPartialSafeNBT) { - data = new CompoundNBT(); - ((IPartialSafeNBT) tile).writeSafe(data, false); - data = NBTProcessors.process(tile, data, true); - } - } - - launchBlock(target, icon, blockState, data); - } + printer.handleCurrentTarget((target, blockState, tile) -> { + // Launch block + statusMsg = blockState.getBlock() != Blocks.AIR ? "placing" : "clearing"; + launchBlockOrBelt(target, icon, blockState, tile); + }, (target, entity) -> { + // Launch entity + statusMsg = "placing"; + launchEntity(target, icon, entity); + }); printerCooldown = config().schematicannonDelay.get(); fuelLevel -= getFuelUsageRate(); @@ -518,35 +443,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC missingItem = null; } - public BlockState stripBeltIfNotLast(BlockState blockState) { - // is highest belt? - boolean isLastSegment = false; - Direction facing = blockState.get(BeltBlock.HORIZONTAL_FACING); - BeltSlope slope = blockState.get(BeltBlock.SLOPE); - boolean positive = facing.getAxisDirection() == AxisDirection.POSITIVE; - boolean start = blockState.get(BeltBlock.PART) == BeltPart.START; - boolean end = blockState.get(BeltBlock.PART) == BeltPart.END; - - switch (slope) { - case DOWNWARD: - isLastSegment = start; - break; - case UPWARD: - isLastSegment = end; - break; - case HORIZONTAL: - case VERTICAL: - default: - isLastSegment = positive && end || !positive && start; - } - if (!isLastSegment) - blockState = (blockState.get(BeltBlock.PART) == BeltPart.MIDDLE) ? Blocks.AIR.getDefaultState() - : AllBlocks.SHAFT.getDefaultState() - .with(AbstractShaftBlock.AXIS, facing.rotateY() - .getAxis()); - return blockState; - } - public double getFuelUsageRate() { return hasCreativeCrate ? 0 : config().schematicannonFuelUsage.get() / 100f; } @@ -568,40 +464,29 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC } // Load blocks into reader - Template activeTemplate = SchematicItem.loadSchematic(blueprint); - BlockPos anchor = NBTUtil.readBlockPos(blueprint.getTag() - .getCompound("Anchor")); + printer.loadSchematic(blueprint, world, true); - if (activeTemplate.getSize() - .equals(BlockPos.ZERO)) { + if (printer.isWorldEmpty()) { state = State.STOPPED; statusMsg = "schematicExpired"; inventory.setStackInSlot(0, ItemStack.EMPTY); inventory.setStackInSlot(1, new ItemStack(AllItems.EMPTY_SCHEMATIC.get())); + printer.resetSchematic(); return; } - if (!anchor.withinDistance(getPos(), MAX_ANCHOR_DISTANCE)) { + if (!printer.getAnchor().withinDistance(getPos(), MAX_ANCHOR_DISTANCE)) { state = State.STOPPED; statusMsg = "targetOutsideRange"; + printer.resetSchematic(); return; } - schematicAnchor = anchor; - blockReader = new SchematicWorld(schematicAnchor, world); - PlacementSettings settings = SchematicItem.getSettings(blueprint); - activeTemplate.place(blockReader, schematicAnchor, settings, blockReader.getRandom()); - schematicLoaded = true; state = State.PAUSED; statusMsg = "ready"; - printingEntityIndex = -1; - printStage = PrintStage.BLOCKS; - deferredBlocks.clear(); updateChecklist(); sendUpdate = true; blocksToPlace += blocksPlaced; - MutableBoundingBox bounds = blockReader.getBounds(); - currentPos = currentPos != null ? currentPos.west() : new BlockPos(bounds.minX - 1, bounds.minY, bounds.minZ); } protected ItemStack getItemForBlock(BlockState blockState) { @@ -679,57 +564,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC return success; } - protected void advanceCurrentPos() { - List entities = blockReader.getEntities() - .collect(Collectors.toList()); - - if (printStage == PrintStage.BLOCKS) { - MutableBoundingBox bounds = blockReader.getBounds(); - while (tryAdvanceCurrentPos(bounds, entities)) { - deferredBlocks.add(currentPos); - } - } - - if (printStage == PrintStage.DEFERRED_BLOCKS) { - if (deferredBlocks.isEmpty()) { - printStage = PrintStage.ENTITIES; - } else { - currentPos = deferredBlocks.remove(0); - } - } - - if (printStage == PrintStage.ENTITIES) { - if (printingEntityIndex + 1 < entities.size()) { - printingEntityIndex++; - currentPos = entities.get(printingEntityIndex).getBlockPos().subtract(schematicAnchor); - } else { - finishedPrinting(); - } - } - } - - protected boolean tryAdvanceCurrentPos(MutableBoundingBox bounds, List entities) { - currentPos = currentPos.offset(Direction.EAST); - BlockPos posInBounds = currentPos.add(-bounds.minX, -bounds.minY, -bounds.minZ); - - if (posInBounds.getX() > bounds.getXSize()) - currentPos = new BlockPos(bounds.minX, currentPos.getY(), currentPos.getZ() + 1).west(); - if (posInBounds.getZ() > bounds.getZSize()) - currentPos = new BlockPos(currentPos.getX(), currentPos.getY() + 1, bounds.minZ).west(); - - // End of blocks reached - if (currentPos.getY() > bounds.getYSize()) { - printStage = PrintStage.DEFERRED_BLOCKS; - return false; - } - - return shouldDeferBlock(blockReader.getBlockState(schematicAnchor.add(currentPos))); - } - - public static boolean shouldDeferBlock(BlockState state) { - return state.getBlock().is(AllBlocks.GANTRY_CARRIAGE.get()) || BlockMovementChecks.isBrittle(state); - } - public void finishedPrinting() { inventory.setStackInSlot(0, ItemStack.EMPTY); inventory.setStackInSlot(1, new ItemStack(AllItems.EMPTY_SCHEMATIC.get(), inventory.getStackInSlot(1) @@ -737,71 +571,43 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC state = State.STOPPED; statusMsg = "finished"; resetPrinter(); - target = getPos().add(1, 0, 0); AllSoundEvents.SCHEMATICANNON_FINISH.playOnServer(world, pos); sendUpdate = true; } protected void resetPrinter() { - schematicLoaded = false; - schematicAnchor = null; - currentPos = null; - blockReader = null; + printer.resetSchematic(); missingItem = null; sendUpdate = true; - printingEntityIndex = 0; - printStage = PrintStage.BLOCKS; schematicProgress = 0; blocksPlaced = 0; blocksToPlace = 0; - deferredBlocks.clear(); } - protected boolean shouldPlace(BlockPos pos, BlockState state, TileEntity te) { - if (world == null) - return false; - BlockState toReplace = world.getBlockState(pos); - boolean placingAir = state.getBlock() - .isAir(state, world, pos); - - BlockState toReplaceOther = null; - if (state.contains(BlockStateProperties.BED_PART) && state.contains(BlockStateProperties.HORIZONTAL_FACING) - && state.get(BlockStateProperties.BED_PART) == BedPart.FOOT) - toReplaceOther = world.getBlockState(pos.offset(state.get(BlockStateProperties.HORIZONTAL_FACING))); - if (state.contains(BlockStateProperties.DOUBLE_BLOCK_HALF) - && state.get(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER) - toReplaceOther = world.getBlockState(pos.up()); - - if (!world.isBlockPresent(pos)) - return false; - if (!world.getWorldBorder() - .contains(pos)) - return false; - if (toReplace == state) - return false; - if (toReplace.getBlockHardness(world, pos) == -1 - || (toReplaceOther != null && toReplaceOther.getBlockHardness(world, pos) == -1)) - return false; + protected boolean shouldPlace(BlockPos pos, BlockState state, TileEntity te, + BlockState toReplace, BlockState toReplaceOther, boolean isNormalCube) { if (pos.withinDistance(getPos(), 2f)) return false; if (!replaceTileEntities - && (toReplace.hasTileEntity() || (toReplaceOther != null && toReplaceOther.hasTileEntity()))) + && (toReplace.hasTileEntity() || (toReplaceOther != null && toReplaceOther.hasTileEntity()))) return false; if (shouldIgnoreBlockState(state, te)) return false; + boolean placingAir = state.getBlock().isAir(state, world, pos); + if (replaceMode == 3) return true; if (replaceMode == 2 && !placingAir) return true; if (replaceMode == 1 - && (state.isNormalCube(blockReader, pos.subtract(schematicAnchor)) || (!toReplace.isNormalCube(world, pos) + && (isNormalCube || (!toReplace.isNormalCube(world, pos) && (toReplaceOther == null || !toReplaceOther.isNormalCube(world, pos)))) - && !placingAir) + && !placingAir) return true; if (replaceMode == 0 && !toReplace.isNormalCube(world, pos) - && (toReplaceOther == null || !toReplaceOther.isNormalCube(world, pos)) && !placingAir) + && (toReplaceOther == null || !toReplaceOther.isNormalCube(world, pos)) && !placingAir) return true; return false; @@ -874,7 +680,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC return; } - if (!schematicLoaded) { + if (!printer.isLoaded()) { if (!blueprint.isEmpty()) initializePrinter(blueprint); return; @@ -889,8 +695,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC dontUpdateChecklist = true; inventory.extractItem(BookInput, 1, false); ItemStack stack = checklist.createItem(); - stack.setCount(inventory.getStackInSlot(BookOutput) - .getCount() + 1); + stack.setCount(inventory.getStackInSlot(BookOutput).getCount() + 1); inventory.setStackInSlot(BookOutput, stack); sendUpdate = true; return; @@ -900,6 +705,58 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC sendUpdate = true; } + public static BlockState stripBeltIfNotLast(BlockState blockState) { + // is highest belt? + boolean isLastSegment = false; + Direction facing = blockState.get(BeltBlock.HORIZONTAL_FACING); + BeltSlope slope = blockState.get(BeltBlock.SLOPE); + boolean positive = facing.getAxisDirection() == AxisDirection.POSITIVE; + boolean start = blockState.get(BeltBlock.PART) == BeltPart.START; + boolean end = blockState.get(BeltBlock.PART) == BeltPart.END; + + switch (slope) { + case DOWNWARD: + isLastSegment = start; + break; + case UPWARD: + isLastSegment = end; + break; + case HORIZONTAL: + case VERTICAL: + default: + isLastSegment = positive && end || !positive && start; + } + if (!isLastSegment) + blockState = (blockState.get(BeltBlock.PART) == BeltPart.MIDDLE) ? Blocks.AIR.getDefaultState() + : AllBlocks.SHAFT.getDefaultState() + .with(AbstractShaftBlock.AXIS, facing.rotateY() + .getAxis()); + return blockState; + } + + protected void launchBlockOrBelt(BlockPos target, ItemStack icon, BlockState blockState, TileEntity tile) { + if (AllBlocks.BELT.has(blockState)) { + blockState = stripBeltIfNotLast(blockState); + if (tile instanceof BeltTileEntity && AllBlocks.BELT.has(blockState)) + launchBelt(target, blockState, ((BeltTileEntity) tile).beltLength); + else + launchBlock(target, icon, blockState, null); + } else { + CompoundNBT data = null; + if (tile != null) { + if (AllBlockTags.SAFE_NBT.matches(blockState)) { + data = tile.write(new CompoundNBT()); + data = NBTProcessors.process(tile, data, true); + } else if (tile instanceof IPartialSafeNBT) { + data = new CompoundNBT(); + ((IPartialSafeNBT) tile).writeSafe(data, false); + data = NBTProcessors.process(tile, data, true); + } + } + launchBlock(target, icon, blockState, data); + } + } + protected void launchBelt(BlockPos target, BlockState state, int length) { blocksPlaced++; ItemStack connector = AllItems.BELT_CONNECTOR.asStack(); @@ -908,8 +765,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC } protected void launchBlock(BlockPos target, ItemStack stack, BlockState state, @Nullable CompoundNBT data) { - if (!state.getBlock() - .isAir(state, world, target)) + if (!state.getBlock().isAir(state, world, target)) blocksPlaced++; flyingBlocks.add(new LaunchedItem.ForBlockState(this.getPos(), target, stack, state, data)); playFiringSound(); @@ -945,37 +801,10 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC checklist.damageRequired.clear(); checklist.blocksNotLoaded = false; - if (schematicLoaded) { + if (printer.isLoaded()) { blocksToPlace = blocksPlaced; - for (BlockPos pos : blockReader.getAllPositions()) { - BlockPos relPos = pos.add(schematicAnchor); - BlockState required = blockReader.getBlockState(relPos); - TileEntity requiredTE = blockReader.getTileEntity(relPos); - - if (!getWorld().isAreaLoaded(pos.add(schematicAnchor), 0)) { - checklist.warnBlockNotLoaded(); - continue; - } - if (!shouldPlace(pos.add(schematicAnchor), required, requiredTE)) - continue; - ItemRequirement requirement = ItemRequirement.of(required, blockReader.getTileEntity(relPos)); - if (requirement.isEmpty()) - continue; - if (requirement.isInvalid()) - continue; - checklist.require(requirement); - blocksToPlace++; - } - blockReader.getEntities() - .forEach(entity -> { - ItemRequirement requirement = ItemRequirement.of(entity); - if (requirement.isEmpty()) - return; - if (requirement.isInvalid()) - return; - checklist.require(requirement); - }); - + blocksToPlace += printer.markAllBlockRequirements(checklist, world, this::shouldPlace); + printer.markAllEntityRequirements(checklist); } checklist.gathered.clear(); findInventories(); diff --git a/src/main/java/com/simibubi/create/content/schematics/item/SchematicItem.java b/src/main/java/com/simibubi/create/content/schematics/item/SchematicItem.java index 7633d85b6..151e04350 100644 --- a/src/main/java/com/simibubi/create/content/schematics/item/SchematicItem.java +++ b/src/main/java/com/simibubi/create/content/schematics/item/SchematicItem.java @@ -99,12 +99,13 @@ public class SchematicItem extends Item { SchematicInstances.clearHash(blueprint); } - public static PlacementSettings getSettings(ItemStack blueprint) { + public static PlacementSettings getSettings(ItemStack blueprint, boolean processNBT) { CompoundNBT tag = blueprint.getTag(); PlacementSettings settings = new PlacementSettings(); settings.setRotation(Rotation.valueOf(tag.getString("Rotation"))); settings.setMirror(Mirror.valueOf(tag.getString("Mirror"))); - settings.addProcessor(SchematicProcessor.INSTANCE); + if (processNBT) + settings.addProcessor(SchematicProcessor.INSTANCE); return settings; } diff --git a/src/main/java/com/simibubi/create/content/schematics/packet/SchematicPlacePacket.java b/src/main/java/com/simibubi/create/content/schematics/packet/SchematicPlacePacket.java index d429ddda8..13e5c584b 100644 --- a/src/main/java/com/simibubi/create/content/schematics/packet/SchematicPlacePacket.java +++ b/src/main/java/com/simibubi/create/content/schematics/packet/SchematicPlacePacket.java @@ -2,14 +2,19 @@ package com.simibubi.create.content.schematics.packet; import java.util.function.Supplier; +import com.simibubi.create.content.schematics.SchematicPrinter; import com.simibubi.create.content.schematics.SchematicProcessor; import com.simibubi.create.content.schematics.item.SchematicItem; import com.simibubi.create.foundation.networking.SimplePacketBase; +import com.simibubi.create.foundation.utility.BlockHelper; + import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.NBTUtil; import net.minecraft.network.PacketBuffer; +import net.minecraft.world.World; import net.minecraft.world.gen.feature.template.PlacementSettings; import net.minecraft.world.gen.feature.template.Template; import net.minecraftforge.fml.network.NetworkEvent.Context; @@ -35,13 +40,22 @@ public class SchematicPlacePacket extends SimplePacketBase { ServerPlayerEntity player = context.get().getSender(); if (player == null) return; - Template t = SchematicItem.loadSchematic(stack); - PlacementSettings settings = SchematicItem.getSettings(stack); - if (player.canUseCommandBlock()) - settings.func_215220_b(SchematicProcessor.INSTANCE); // remove processor - settings.setIgnoreEntities(false); - t.place(player.getServerWorld(), NBTUtil.readBlockPos(stack.getTag().getCompound("Anchor")), - settings, player.getRNG()); + + World world = player.getServerWorld(); + SchematicPrinter printer = new SchematicPrinter(); + printer.loadSchematic(stack, world, !player.canUseCommandBlock()); + + while(printer.advanceCurrentPos()) { + if (!printer.shouldPlaceCurrent(world)) + continue; + + printer.handleCurrentTarget((pos, state, tile) -> { + CompoundNBT tileData = tile != null ? tile.write(new CompoundNBT()) : null; + BlockHelper.placeSchematicBlock(world, state, pos, null, tileData); + }, (pos, entity) -> { + world.addEntity(entity); + }); + } }); context.get().setPacketHandled(true); }