Schematic insta-placement now uses Schematicannon logic

This commit is contained in:
reidbhuntley 2021-06-17 22:47:38 -04:00
parent e55ae22d7d
commit 96e865be02
5 changed files with 434 additions and 284 deletions

View file

@ -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<BlockPos> 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<Entity> 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);
}
}

View file

@ -84,13 +84,14 @@ public class SchematicannonRenderer extends SafeTileEntityRenderer<Schematicanno
double yaw = 0; double yaw = 0;
double pitch = 40; double pitch = 40;
if (tile.target != null) { BlockPos target = tile.printer.getCurrentTarget();
if (target != null) {
// Calculate Angle of Cannon // Calculate Angle of Cannon
Vector3d diff = Vector3d.of(tile.target.subtract(pos)); Vector3d diff = Vector3d.of(target.subtract(pos));
if (tile.previousTarget != null) { if (tile.previousTarget != null) {
diff = (Vector3d.of(tile.previousTarget) diff = (Vector3d.of(tile.previousTarget)
.add(Vector3d.of(tile.target.subtract(tile.previousTarget)).scale(partialTicks))) .add(Vector3d.of(target.subtract(tile.previousTarget)).scale(partialTicks)))
.subtract(Vector3d.of(pos)); .subtract(Vector3d.of(pos));
} }

View file

@ -20,6 +20,7 @@ import com.simibubi.create.content.contraptions.relays.elementary.AbstractShaftB
import com.simibubi.create.content.schematics.ItemRequirement; import com.simibubi.create.content.schematics.ItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType; import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType;
import com.simibubi.create.content.schematics.MaterialChecklist; import com.simibubi.create.content.schematics.MaterialChecklist;
import com.simibubi.create.content.schematics.SchematicPrinter;
import com.simibubi.create.content.schematics.SchematicWorld; import com.simibubi.create.content.schematics.SchematicWorld;
import com.simibubi.create.content.schematics.item.SchematicItem; import com.simibubi.create.content.schematics.item.SchematicItem;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
@ -81,10 +82,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
STOPPED, PAUSED, RUNNING; STOPPED, PAUSED, RUNNING;
} }
public enum PrintStage {
BLOCKS, DEFERRED_BLOCKS, ENTITIES
}
// Inventory // Inventory
public SchematicannonInventory inventory; public SchematicannonInventory inventory;
@ -94,25 +91,18 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
public int neighbourCheckCooldown; public int neighbourCheckCooldown;
// Printer // Printer
private SchematicWorld blockReader; public SchematicPrinter printer;
public BlockPos currentPos;
public BlockPos schematicAnchor;
public boolean schematicLoaded;
public ItemStack missingItem; public ItemStack missingItem;
public boolean positionNotLoaded; public boolean positionNotLoaded;
public boolean hasCreativeCrate; public boolean hasCreativeCrate;
private int printerCooldown; private int printerCooldown;
private int skipsLeft; private int skipsLeft;
private boolean blockSkipped; private boolean blockSkipped;
private int printingEntityIndex;
private PrintStage printStage;
public BlockPos target;
public BlockPos previousTarget; public BlockPos previousTarget;
public LinkedHashSet<LazyOptional<IItemHandler>> attachedInventories; public LinkedHashSet<LazyOptional<IItemHandler>> attachedInventories;
public List<LaunchedItem> flyingBlocks; public List<LaunchedItem> flyingBlocks;
public MaterialChecklist checklist; public MaterialChecklist checklist;
public List<BlockPos> deferredBlocks;
// Gui information // Gui information
public float fuelLevel; public float fuelLevel;
@ -150,11 +140,9 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
inventory = new SchematicannonInventory(this); inventory = new SchematicannonInventory(this);
statusMsg = "idle"; statusMsg = "idle";
state = State.STOPPED; state = State.STOPPED;
printingEntityIndex = -1;
printStage = PrintStage.BLOCKS;
deferredBlocks = new LinkedList<>();
replaceMode = 2; replaceMode = 2;
checklist = new MaterialChecklist(); checklist = new MaterialChecklist();
printer = new SchematicPrinter();
} }
public void findInventories() { public void findInventories() {
@ -183,8 +171,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
protected void fromTag(BlockState blockState, CompoundNBT compound, boolean clientPacket) { protected void fromTag(BlockState blockState, CompoundNBT compound, boolean clientPacket) {
if (!clientPacket) { if (!clientPacket) {
inventory.deserializeNBT(compound.getCompound("Inventory")); inventory.deserializeNBT(compound.getCompound("Inventory"));
if (compound.contains("CurrentPos"))
currentPos = NBTUtil.readBlockPos(compound.getCompound("CurrentPos"));
} }
// Gui information // Gui information
@ -195,7 +181,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
state = State.valueOf(compound.getString("State")); state = State.valueOf(compound.getString("State"));
blocksPlaced = compound.getInt("AmountPlaced"); blocksPlaced = compound.getInt("AmountPlaced");
blocksToPlace = compound.getInt("AmountToPlace"); blocksToPlace = compound.getInt("AmountToPlace");
printingEntityIndex = compound.getInt("EntityProgress");
missingItem = null; missingItem = null;
if (compound.contains("MissingItem")) if (compound.contains("MissingItem"))
@ -208,14 +193,8 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
replaceTileEntities = options.getBoolean("ReplaceTileEntities"); replaceTileEntities = options.getBoolean("ReplaceTileEntities");
// Printer & Flying Blocks // Printer & Flying Blocks
if (compound.contains("PrintStage")) if (compound.contains("Printer"))
printStage = PrintStage.valueOf(compound.getString("PrintStage")); printer.fromTag(compound.getCompound("Printer"), clientPacket);
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("FlyingBlocks")) if (compound.contains("FlyingBlocks"))
readFlyingBlocks(compound); readFlyingBlocks(compound);
@ -263,8 +242,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
compound.put("Inventory", inventory.serializeNBT()); compound.put("Inventory", inventory.serializeNBT());
if (state == State.RUNNING) { if (state == State.RUNNING) {
compound.putBoolean("Running", true); 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.putString("State", state.name());
compound.putInt("AmountPlaced", blocksPlaced); compound.putInt("AmountPlaced", blocksPlaced);
compound.putInt("AmountToPlace", blocksToPlace); compound.putInt("AmountToPlace", blocksToPlace);
compound.putInt("EntityProgress", printingEntityIndex);
if (missingItem != null) if (missingItem != null)
compound.put("MissingItem", missingItem.serializeNBT()); compound.put("MissingItem", missingItem.serializeNBT());
@ -289,15 +265,9 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
compound.put("Options", options); compound.put("Options", options);
// Printer & Flying Blocks // Printer & Flying Blocks
compound.putString("PrintStage", printStage.name()); CompoundNBT printerData = new CompoundNBT();
printer.write(printerData);
if (target != null) compound.put("Printer", printerData);
compound.put("Target", NBTUtil.writeBlockPos(target));
ListNBT tagDeferredBlocks = new ListNBT();
for (BlockPos p : deferredBlocks)
tagDeferredBlocks.add(NBTUtil.writeBlockPos(p));
compound.put("DeferredBlocks", tagDeferredBlocks);
ListNBT tagFlyingBlocks = new ListNBT(); ListNBT tagFlyingBlocks = new ListNBT();
for (LaunchedItem b : flyingBlocks) for (LaunchedItem b : flyingBlocks)
@ -317,7 +287,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
} }
firstRenderTick = true; firstRenderTick = true;
previousTarget = target; previousTarget = printer.getCurrentTarget();
tickFlyingBlocks(); tickFlyingBlocks();
if (world.isRemote) if (world.isRemote)
@ -355,7 +325,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
// Skip if not Active // Skip if not Active
if (state == State.STOPPED) { if (state == State.STOPPED) {
if (schematicLoaded) if (printer.isLoaded())
resetPrinter(); resetPrinter();
return; return;
} }
@ -371,7 +341,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
return; return;
// Initialize Printer // Initialize Printer
if (!schematicLoaded) { if (!printer.isLoaded()) {
initializePrinter(blueprint); initializePrinter(blueprint);
return; return;
} }
@ -391,7 +361,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
return; return;
} }
// Update Target
if (hasCreativeCrate) { if (hasCreativeCrate) {
if (missingItem != null) { if (missingItem != null) {
missingItem = null; missingItem = null;
@ -399,23 +368,17 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
} }
} }
// Update Target
if (missingItem == null && !positionNotLoaded) { if (missingItem == null && !positionNotLoaded) {
do { if (!printer.advanceCurrentPos()) {
advanceCurrentPos(); finishedPrinting();
if (state == State.STOPPED) return;
return; }
} while (!blockReader.getBounds()
.isVecInside(currentPos));
sendUpdate = true; sendUpdate = true;
target = schematicAnchor.add(currentPos);
} }
boolean entityMode = printStage == PrintStage.ENTITIES;
// Check block // Check block
if (!getWorld().isAreaLoaded(target, 0)) { if (!getWorld().isAreaLoaded(printer.getCurrentTarget(), 0)) {
positionNotLoaded = true; positionNotLoaded = true;
statusMsg = "targetNotLoaded"; statusMsg = "targetNotLoaded";
state = State.PAUSED; state = State.PAUSED;
@ -427,24 +390,9 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
} }
} }
boolean shouldSkip = false; // Get item requirement
BlockState blockState = Blocks.AIR.getDefaultState(); ItemRequirement requirement = printer.getCurrentRequirement();
TileEntity tileEntity = null; if (requirement.isInvalid() || !printer.shouldPlaceCurrent(world, this::shouldPlace)) {
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()) {
statusMsg = "searching"; statusMsg = "searching";
blockSkipped = true; blockSkipped = true;
return; return;
@ -478,39 +426,16 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
// Success // Success
state = State.RUNNING; 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; ItemStack icon = requirement.isEmpty() || requiredItems.isEmpty() ? ItemStack.EMPTY : requiredItems.get(0).item;
if (entityMode) printer.handleCurrentTarget((target, blockState, tile) -> {
launchEntity(target, icon, blockReader.getEntities() // Launch block
.collect(Collectors.toList()) statusMsg = blockState.getBlock() != Blocks.AIR ? "placing" : "clearing";
.get(printingEntityIndex)); launchBlockOrBelt(target, icon, blockState, tile);
else if (AllBlocks.BELT.has(blockState)) { }, (target, entity) -> {
TileEntity te = blockReader.getTileEntity(currentPos.add(schematicAnchor)); // Launch entity
blockState = stripBeltIfNotLast(blockState); statusMsg = "placing";
if (te instanceof BeltTileEntity && AllBlocks.BELT.has(blockState)) launchEntity(target, icon, entity);
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);
}
printerCooldown = config().schematicannonDelay.get(); printerCooldown = config().schematicannonDelay.get();
fuelLevel -= getFuelUsageRate(); fuelLevel -= getFuelUsageRate();
@ -518,35 +443,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
missingItem = null; 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() { public double getFuelUsageRate() {
return hasCreativeCrate ? 0 : config().schematicannonFuelUsage.get() / 100f; return hasCreativeCrate ? 0 : config().schematicannonFuelUsage.get() / 100f;
} }
@ -568,40 +464,29 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
} }
// Load blocks into reader // Load blocks into reader
Template activeTemplate = SchematicItem.loadSchematic(blueprint); printer.loadSchematic(blueprint, world, true);
BlockPos anchor = NBTUtil.readBlockPos(blueprint.getTag()
.getCompound("Anchor"));
if (activeTemplate.getSize() if (printer.isWorldEmpty()) {
.equals(BlockPos.ZERO)) {
state = State.STOPPED; state = State.STOPPED;
statusMsg = "schematicExpired"; statusMsg = "schematicExpired";
inventory.setStackInSlot(0, ItemStack.EMPTY); inventory.setStackInSlot(0, ItemStack.EMPTY);
inventory.setStackInSlot(1, new ItemStack(AllItems.EMPTY_SCHEMATIC.get())); inventory.setStackInSlot(1, new ItemStack(AllItems.EMPTY_SCHEMATIC.get()));
printer.resetSchematic();
return; return;
} }
if (!anchor.withinDistance(getPos(), MAX_ANCHOR_DISTANCE)) { if (!printer.getAnchor().withinDistance(getPos(), MAX_ANCHOR_DISTANCE)) {
state = State.STOPPED; state = State.STOPPED;
statusMsg = "targetOutsideRange"; statusMsg = "targetOutsideRange";
printer.resetSchematic();
return; 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; state = State.PAUSED;
statusMsg = "ready"; statusMsg = "ready";
printingEntityIndex = -1;
printStage = PrintStage.BLOCKS;
deferredBlocks.clear();
updateChecklist(); updateChecklist();
sendUpdate = true; sendUpdate = true;
blocksToPlace += blocksPlaced; 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) { protected ItemStack getItemForBlock(BlockState blockState) {
@ -679,57 +564,6 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
return success; return success;
} }
protected void advanceCurrentPos() {
List<Entity> 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<Entity> 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() { public void finishedPrinting() {
inventory.setStackInSlot(0, ItemStack.EMPTY); inventory.setStackInSlot(0, ItemStack.EMPTY);
inventory.setStackInSlot(1, new ItemStack(AllItems.EMPTY_SCHEMATIC.get(), inventory.getStackInSlot(1) 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; state = State.STOPPED;
statusMsg = "finished"; statusMsg = "finished";
resetPrinter(); resetPrinter();
target = getPos().add(1, 0, 0);
AllSoundEvents.SCHEMATICANNON_FINISH.playOnServer(world, pos); AllSoundEvents.SCHEMATICANNON_FINISH.playOnServer(world, pos);
sendUpdate = true; sendUpdate = true;
} }
protected void resetPrinter() { protected void resetPrinter() {
schematicLoaded = false; printer.resetSchematic();
schematicAnchor = null;
currentPos = null;
blockReader = null;
missingItem = null; missingItem = null;
sendUpdate = true; sendUpdate = true;
printingEntityIndex = 0;
printStage = PrintStage.BLOCKS;
schematicProgress = 0; schematicProgress = 0;
blocksPlaced = 0; blocksPlaced = 0;
blocksToPlace = 0; blocksToPlace = 0;
deferredBlocks.clear();
} }
protected boolean shouldPlace(BlockPos pos, BlockState state, TileEntity te) { protected boolean shouldPlace(BlockPos pos, BlockState state, TileEntity te,
if (world == null) BlockState toReplace, BlockState toReplaceOther, boolean isNormalCube) {
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;
if (pos.withinDistance(getPos(), 2f)) if (pos.withinDistance(getPos(), 2f))
return false; return false;
if (!replaceTileEntities if (!replaceTileEntities
&& (toReplace.hasTileEntity() || (toReplaceOther != null && toReplaceOther.hasTileEntity()))) && (toReplace.hasTileEntity() || (toReplaceOther != null && toReplaceOther.hasTileEntity())))
return false; return false;
if (shouldIgnoreBlockState(state, te)) if (shouldIgnoreBlockState(state, te))
return false; return false;
boolean placingAir = state.getBlock().isAir(state, world, pos);
if (replaceMode == 3) if (replaceMode == 3)
return true; return true;
if (replaceMode == 2 && !placingAir) if (replaceMode == 2 && !placingAir)
return true; return true;
if (replaceMode == 1 if (replaceMode == 1
&& (state.isNormalCube(blockReader, pos.subtract(schematicAnchor)) || (!toReplace.isNormalCube(world, pos) && (isNormalCube || (!toReplace.isNormalCube(world, pos)
&& (toReplaceOther == null || !toReplaceOther.isNormalCube(world, pos)))) && (toReplaceOther == null || !toReplaceOther.isNormalCube(world, pos))))
&& !placingAir) && !placingAir)
return true; return true;
if (replaceMode == 0 && !toReplace.isNormalCube(world, pos) if (replaceMode == 0 && !toReplace.isNormalCube(world, pos)
&& (toReplaceOther == null || !toReplaceOther.isNormalCube(world, pos)) && !placingAir) && (toReplaceOther == null || !toReplaceOther.isNormalCube(world, pos)) && !placingAir)
return true; return true;
return false; return false;
@ -874,7 +680,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
return; return;
} }
if (!schematicLoaded) { if (!printer.isLoaded()) {
if (!blueprint.isEmpty()) if (!blueprint.isEmpty())
initializePrinter(blueprint); initializePrinter(blueprint);
return; return;
@ -889,8 +695,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
dontUpdateChecklist = true; dontUpdateChecklist = true;
inventory.extractItem(BookInput, 1, false); inventory.extractItem(BookInput, 1, false);
ItemStack stack = checklist.createItem(); ItemStack stack = checklist.createItem();
stack.setCount(inventory.getStackInSlot(BookOutput) stack.setCount(inventory.getStackInSlot(BookOutput).getCount() + 1);
.getCount() + 1);
inventory.setStackInSlot(BookOutput, stack); inventory.setStackInSlot(BookOutput, stack);
sendUpdate = true; sendUpdate = true;
return; return;
@ -900,6 +705,58 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
sendUpdate = true; 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) { protected void launchBelt(BlockPos target, BlockState state, int length) {
blocksPlaced++; blocksPlaced++;
ItemStack connector = AllItems.BELT_CONNECTOR.asStack(); 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) { protected void launchBlock(BlockPos target, ItemStack stack, BlockState state, @Nullable CompoundNBT data) {
if (!state.getBlock() if (!state.getBlock().isAir(state, world, target))
.isAir(state, world, target))
blocksPlaced++; blocksPlaced++;
flyingBlocks.add(new LaunchedItem.ForBlockState(this.getPos(), target, stack, state, data)); flyingBlocks.add(new LaunchedItem.ForBlockState(this.getPos(), target, stack, state, data));
playFiringSound(); playFiringSound();
@ -945,37 +801,10 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC
checklist.damageRequired.clear(); checklist.damageRequired.clear();
checklist.blocksNotLoaded = false; checklist.blocksNotLoaded = false;
if (schematicLoaded) { if (printer.isLoaded()) {
blocksToPlace = blocksPlaced; blocksToPlace = blocksPlaced;
for (BlockPos pos : blockReader.getAllPositions()) { blocksToPlace += printer.markAllBlockRequirements(checklist, world, this::shouldPlace);
BlockPos relPos = pos.add(schematicAnchor); printer.markAllEntityRequirements(checklist);
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);
});
} }
checklist.gathered.clear(); checklist.gathered.clear();
findInventories(); findInventories();

View file

@ -99,12 +99,13 @@ public class SchematicItem extends Item {
SchematicInstances.clearHash(blueprint); SchematicInstances.clearHash(blueprint);
} }
public static PlacementSettings getSettings(ItemStack blueprint) { public static PlacementSettings getSettings(ItemStack blueprint, boolean processNBT) {
CompoundNBT tag = blueprint.getTag(); CompoundNBT tag = blueprint.getTag();
PlacementSettings settings = new PlacementSettings(); PlacementSettings settings = new PlacementSettings();
settings.setRotation(Rotation.valueOf(tag.getString("Rotation"))); settings.setRotation(Rotation.valueOf(tag.getString("Rotation")));
settings.setMirror(Mirror.valueOf(tag.getString("Mirror"))); settings.setMirror(Mirror.valueOf(tag.getString("Mirror")));
settings.addProcessor(SchematicProcessor.INSTANCE); if (processNBT)
settings.addProcessor(SchematicProcessor.INSTANCE);
return settings; return settings;
} }

View file

@ -2,14 +2,19 @@ package com.simibubi.create.content.schematics.packet;
import java.util.function.Supplier; 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.SchematicProcessor;
import com.simibubi.create.content.schematics.item.SchematicItem; import com.simibubi.create.content.schematics.item.SchematicItem;
import com.simibubi.create.foundation.networking.SimplePacketBase; import com.simibubi.create.foundation.networking.SimplePacketBase;
import com.simibubi.create.foundation.utility.BlockHelper;
import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.network.PacketBuffer; 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.PlacementSettings;
import net.minecraft.world.gen.feature.template.Template; import net.minecraft.world.gen.feature.template.Template;
import net.minecraftforge.fml.network.NetworkEvent.Context; import net.minecraftforge.fml.network.NetworkEvent.Context;
@ -35,13 +40,22 @@ public class SchematicPlacePacket extends SimplePacketBase {
ServerPlayerEntity player = context.get().getSender(); ServerPlayerEntity player = context.get().getSender();
if (player == null) if (player == null)
return; return;
Template t = SchematicItem.loadSchematic(stack);
PlacementSettings settings = SchematicItem.getSettings(stack); World world = player.getServerWorld();
if (player.canUseCommandBlock()) SchematicPrinter printer = new SchematicPrinter();
settings.func_215220_b(SchematicProcessor.INSTANCE); // remove processor printer.loadSchematic(stack, world, !player.canUseCommandBlock());
settings.setIgnoreEntities(false);
t.place(player.getServerWorld(), NBTUtil.readBlockPos(stack.getTag().getCompound("Anchor")), while(printer.advanceCurrentPos()) {
settings, player.getRNG()); 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); context.get().setPacketHandled(true);
} }