From c4513020dfff1df4ba1cb1a1547f212562f01340 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Tue, 5 May 2020 20:21:22 +0200 Subject: [PATCH] Schematics and Entities - Fixed sidedness issue with glue effect packets - Fixed duplication issue with Schematic tables - Schematics can now include entities - Schematicannons can now print: - superglue - (filled) item frames - (equipped) armor stands - end crystals - boats and minecarts - paintings - Special blocks/entities placed by a schematicannon can now specify custom item requirements (used currently for belts & superglue) - Fixed superglue not being rotated/mirrored correctly --- .../create/foundation/item/ItemHelper.java | 18 +- .../BlockBreakingMovementBehaviour.java | 2 - .../contraptions/glue/GlueEffectPacket.java | 2 + .../contraptions/glue/SuperGlueEntity.java | 58 ++-- .../contraptions/relays/belt/BeltBlock.java | 20 +- .../ISpecialBlockItemRequirement.java | 11 + .../ISpecialEntityItemRequirement.java | 9 + .../modules/schematics/ItemRequirement.java | 125 +++++++ .../modules/schematics/MaterialChecklist.java | 53 ++- .../modules/schematics/SchematicWorld.java | 21 ++ .../schematics/ServerSchematicLoader.java | 2 - .../schematics/block/LaunchedBlock.java | 36 -- .../schematics/block/LaunchedItem.java | 164 +++++++++ .../block/SchematicTableTileEntity.java | 2 + .../schematics/block/SchematicannonBlock.java | 6 - .../block/SchematicannonRenderer.java | 41 ++- .../block/SchematicannonScreen.java | 36 +- .../block/SchematicannonTileEntity.java | 319 +++++++++++------- .../client/SchematicAndQuillHandler.java | 2 +- .../packet/SchematicPlacePacket.java | 5 +- .../resources/assets/create/lang/en_us.json | 6 +- .../assets/create/textures/item/belt.png | Bin 258 -> 401 bytes .../create/textures/item/brass_ingot.png | Bin 508 -> 444 bytes 23 files changed, 696 insertions(+), 242 deletions(-) create mode 100644 src/main/java/com/simibubi/create/modules/schematics/ISpecialBlockItemRequirement.java create mode 100644 src/main/java/com/simibubi/create/modules/schematics/ISpecialEntityItemRequirement.java create mode 100644 src/main/java/com/simibubi/create/modules/schematics/ItemRequirement.java delete mode 100644 src/main/java/com/simibubi/create/modules/schematics/block/LaunchedBlock.java create mode 100644 src/main/java/com/simibubi/create/modules/schematics/block/LaunchedItem.java diff --git a/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java b/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java index cec27ea45..92f049790 100644 --- a/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java +++ b/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java @@ -127,17 +127,27 @@ public class ItemHelper { return false; } + public static enum ExtractionCountMode { + EXACTLY, UPTO + } + public static ItemStack extract(IItemHandler inv, Predicate test, boolean simulate) { - return extract(inv, test, -1, simulate); + return extract(inv, test, ExtractionCountMode.UPTO, AllConfigs.SERVER.logistics.extractorAmount.get(), + simulate); } public static ItemStack extract(IItemHandler inv, Predicate test, int exactAmount, boolean simulate) { + return extract(inv, test, ExtractionCountMode.EXACTLY, exactAmount, simulate); + } + + public static ItemStack extract(IItemHandler inv, Predicate test, ExtractionCountMode mode, int amount, + boolean simulate) { ItemStack extracting = ItemStack.EMPTY; - boolean amountRequired = exactAmount != -1; + boolean amountRequired = mode == ExtractionCountMode.EXACTLY; boolean checkHasEnoughItems = amountRequired; boolean hasEnoughItems = !checkHasEnoughItems; - int maxExtractionCount = hasEnoughItems ? AllConfigs.SERVER.logistics.extractorAmount.get() : exactAmount; boolean potentialOtherMatch = false; + int maxExtractionCount = amount; Extraction: do { extracting = ItemStack.EMPTY; @@ -186,7 +196,7 @@ public class ItemHelper { } while (true); - if (amountRequired && extracting.getCount() < exactAmount) + if (amountRequired && extracting.getCount() < amount) return ItemStack.EMPTY; return extracting; diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/actors/BlockBreakingMovementBehaviour.java b/src/main/java/com/simibubi/create/modules/contraptions/components/actors/BlockBreakingMovementBehaviour.java index 6cdb097b7..cfaa5a87f 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/actors/BlockBreakingMovementBehaviour.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/actors/BlockBreakingMovementBehaviour.java @@ -1,14 +1,12 @@ package com.simibubi.create.modules.contraptions.components.actors; import com.simibubi.create.foundation.utility.BlockHelper; -import com.simibubi.create.foundation.utility.Debug; import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionEntity; import com.simibubi.create.modules.contraptions.components.contraptions.MovementBehaviour; import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext; import net.minecraft.block.BlockState; import net.minecraft.block.FallingBlock; -import net.minecraft.block.material.Material; import net.minecraft.entity.Entity; import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.item.minecart.AbstractMinecartEntity; diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/glue/GlueEffectPacket.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/glue/GlueEffectPacket.java index 02dfe0b48..fd15c0be4 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/glue/GlueEffectPacket.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/glue/GlueEffectPacket.java @@ -9,6 +9,7 @@ import net.minecraft.network.PacketBuffer; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.network.NetworkEvent.Context; @@ -36,6 +37,7 @@ public class GlueEffectPacket extends SimplePacketBase { buffer.writeBoolean(fullBlock); } + @OnlyIn(Dist.CLIENT) public void handle(Supplier context) { context.get().enqueueWork(() -> DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> { Minecraft mc = Minecraft.getInstance(); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/glue/SuperGlueEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/glue/SuperGlueEntity.java index e21d2cb27..775e74b0c 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/glue/SuperGlueEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/glue/SuperGlueEntity.java @@ -8,6 +8,9 @@ import com.simibubi.create.AllEntities; import com.simibubi.create.AllItems; import com.simibubi.create.AllPackets; import com.simibubi.create.AllSoundEvents; +import com.simibubi.create.modules.schematics.ISpecialEntityItemRequirement; +import com.simibubi.create.modules.schematics.ItemRequirement; +import com.simibubi.create.modules.schematics.ItemRequirement.ItemUseType; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.player.ClientPlayerEntity; @@ -46,7 +49,7 @@ import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; import net.minecraftforge.fml.network.NetworkHooks; import net.minecraftforge.fml.network.PacketDistributor; -public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnData { +public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnData, ISpecialEntityItemRequirement { private int validationTimer; protected BlockPos hangingPosition; @@ -64,7 +67,8 @@ public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnDat } @Override - protected void registerData() {} + protected void registerData() { + } public int getWidthPixels() { return 12; @@ -104,34 +108,32 @@ public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnDat protected void updateBoundingBox() { if (this.getFacingDirection() != null) { - this.posX = - (double) this.hangingPosition.getX() + 0.5 - (double) this.getFacingDirection().getXOffset() * 0.5; - this.posY = - (double) this.hangingPosition.getY() + 0.5 - (double) this.getFacingDirection().getYOffset() * 0.5; - this.posZ = - (double) this.hangingPosition.getZ() + 0.5 - (double) this.getFacingDirection().getZOffset() * 0.5; - double d1 = (double) this.getWidthPixels(); - double d2 = (double) this.getHeightPixels(); - double d3 = (double) this.getWidthPixels(); + double offset = 0.5 - 1 / 256d; + this.posX = hangingPosition.getX() + 0.5 - facingDirection.getXOffset() * offset; + this.posY = hangingPosition.getY() + 0.5 - facingDirection.getYOffset() * offset; + this.posZ = hangingPosition.getZ() + 0.5 - facingDirection.getZOffset() * offset; + double w = getWidthPixels(); + double h = getHeightPixels(); + double l = getWidthPixels(); Axis axis = this.getFacingDirection().getAxis(); double depth = 2 - 1 / 128f; switch (axis) { case X: - d1 = depth; + w = depth; break; case Y: - d2 = depth; + h = depth; break; case Z: - d3 = depth; + l = depth; } - d1 = d1 / 32.0D; - d2 = d2 / 32.0D; - d3 = d3 / 32.0D; - this.setBoundingBox(new AxisAlignedBB(this.posX - d1, this.posY - d2, this.posZ - d3, this.posX + d1, - this.posY + d2, this.posZ + d3)); + w = w / 32.0D; + h = h / 32.0D; + l = l / 32.0D; + this.setBoundingBox(new AxisAlignedBB(this.posX - w, this.posY - h, this.posZ - l, this.posX + w, + this.posY + h, this.posZ + l)); } } @@ -317,13 +319,13 @@ public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnDat if (this.getFacingDirection().getAxis() != Direction.Axis.Y) { switch (transformRotation) { case CLOCKWISE_180: - this.facingDirection = this.getFacingDirection().getOpposite(); + facingDirection = facingDirection.getOpposite(); break; case COUNTERCLOCKWISE_90: - this.facingDirection = this.getFacingDirection().rotateYCCW(); + facingDirection = facingDirection.rotateYCCW(); break; case CLOCKWISE_90: - this.facingDirection = this.getFacingDirection().rotateY(); + facingDirection = facingDirection.rotateY(); default: break; } @@ -356,10 +358,12 @@ public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnDat } @Override - public void onStruckByLightning(LightningBoltEntity lightningBolt) {} + public void onStruckByLightning(LightningBoltEntity lightningBolt) { + } @Override - public void recalculateSize() {} + public void recalculateSize() { + } public static EntityType.Builder build(EntityType.Builder builder) { @SuppressWarnings("unchecked") @@ -387,4 +391,10 @@ public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnDat public Direction getFacingDirection() { return facingDirection; } + + @Override + public ItemRequirement getRequiredItems() { + return new ItemRequirement(ItemUseType.DAMAGE, AllItems.SUPER_GLUE.get()); + } + } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltBlock.java index 14127dc93..7c1ae4708 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltBlock.java @@ -1,5 +1,6 @@ package com.simibubi.create.modules.contraptions.relays.belt; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -16,6 +17,9 @@ import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.modules.contraptions.base.HorizontalKineticBlock; import com.simibubi.create.modules.contraptions.relays.belt.transport.BeltMovementHandler.TransportedEntityInfo; import com.simibubi.create.modules.logistics.block.belts.tunnel.BeltTunnelBlock; +import com.simibubi.create.modules.schematics.ISpecialBlockItemRequirement; +import com.simibubi.create.modules.schematics.ItemRequirement; +import com.simibubi.create.modules.schematics.ItemRequirement.ItemUseType; import net.minecraft.block.Block; import net.minecraft.block.BlockRenderType; @@ -63,7 +67,7 @@ import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; public class BeltBlock extends HorizontalKineticBlock - implements IHaveNoBlockItem, ITE, IHaveColorHandler { + implements IHaveNoBlockItem, ITE, IHaveColorHandler, ISpecialBlockItemRequirement { public static final IProperty SLOPE = EnumProperty.create("slope", Slope.class); public static final IProperty PART = EnumProperty.create("part", Part.class); @@ -603,5 +607,19 @@ public class BeltBlock extends HorizontalKineticBlock public Class getTileEntityClass() { return BeltTileEntity.class; } + + @Override + public ItemRequirement getRequiredItems(BlockState state) { + List required = new ArrayList<>(); + if (state.get(PART) != Part.MIDDLE) + required.add(new ItemStack(AllBlocks.SHAFT.get())); + if (state.get(CASING)) + required.add(new ItemStack(AllBlocks.BRASS_CASING.get())); + if (state.get(PART) == Part.START) + required.add(AllItems.BELT_CONNECTOR.asStack()); + if (required.isEmpty()) + return ItemRequirement.NONE; + return new ItemRequirement(ItemUseType.CONSUME, required); + } } diff --git a/src/main/java/com/simibubi/create/modules/schematics/ISpecialBlockItemRequirement.java b/src/main/java/com/simibubi/create/modules/schematics/ISpecialBlockItemRequirement.java new file mode 100644 index 000000000..c0832dee3 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/schematics/ISpecialBlockItemRequirement.java @@ -0,0 +1,11 @@ +package com.simibubi.create.modules.schematics; + +import net.minecraft.block.BlockState; + +public interface ISpecialBlockItemRequirement { + + default ItemRequirement getRequiredItems(BlockState state) { + return ItemRequirement.INVALID; + } + +} diff --git a/src/main/java/com/simibubi/create/modules/schematics/ISpecialEntityItemRequirement.java b/src/main/java/com/simibubi/create/modules/schematics/ISpecialEntityItemRequirement.java new file mode 100644 index 000000000..914eb8827 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/schematics/ISpecialEntityItemRequirement.java @@ -0,0 +1,9 @@ +package com.simibubi.create.modules.schematics; + +public interface ISpecialEntityItemRequirement { + + default ItemRequirement getRequiredItems() { + return ItemRequirement.INVALID; + } + +} diff --git a/src/main/java/com/simibubi/create/modules/schematics/ItemRequirement.java b/src/main/java/com/simibubi/create/modules/schematics/ItemRequirement.java new file mode 100644 index 000000000..3c08adf47 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/schematics/ItemRequirement.java @@ -0,0 +1,125 @@ +package com.simibubi.create.modules.schematics; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.item.ArmorStandEntity; +import net.minecraft.entity.item.BoatEntity; +import net.minecraft.entity.item.ItemFrameEntity; +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.state.properties.BlockStateProperties; +import net.minecraft.state.properties.SlabType; + +public class ItemRequirement { + + public enum ItemUseType { + CONSUME, DAMAGE + } + + ItemUseType usage; + List requiredItems; + + public static ItemRequirement INVALID = new ItemRequirement(); + public static ItemRequirement NONE = new ItemRequirement(); + + private ItemRequirement() { + } + + public ItemRequirement(ItemUseType usage, Item item) { + this(usage, Arrays.asList(new ItemStack(item))); + } + + public ItemRequirement(ItemUseType usage, List requiredItems) { + this.usage = usage; + this.requiredItems = requiredItems; + } + + public static ItemRequirement of(BlockState state) { + Block block = state.getBlock(); + if (block == Blocks.AIR) + return NONE; + if (block instanceof ISpecialBlockItemRequirement) + return ((ISpecialBlockItemRequirement) block).getRequiredItems(state); + + Item item = BlockItem.BLOCK_TO_ITEM.getOrDefault(state.getBlock(), Items.AIR); + + // double slab needs two items + if (state.has(BlockStateProperties.SLAB_TYPE) && state.get(BlockStateProperties.SLAB_TYPE) == SlabType.DOUBLE) + return new ItemRequirement(ItemUseType.CONSUME, Arrays.asList(new ItemStack(item, 2))); + + return item == Items.AIR ? INVALID : new ItemRequirement(ItemUseType.CONSUME, item); + } + + public static ItemRequirement of(Entity entity) { + EntityType type = entity.getType(); + + if (entity instanceof ISpecialEntityItemRequirement) + return ((ISpecialEntityItemRequirement) entity).getRequiredItems(); + + if (type == EntityType.ITEM_FRAME) { + ItemFrameEntity ife = (ItemFrameEntity) entity; + ItemStack frame = new ItemStack(Items.ITEM_FRAME); + ItemStack displayedItem = ife.getDisplayedItem(); + if (displayedItem.isEmpty()) + return new ItemRequirement(ItemUseType.CONSUME, Items.ITEM_FRAME); + return new ItemRequirement(ItemUseType.CONSUME, Arrays.asList(frame, displayedItem)); + } + + if (type == EntityType.PAINTING) + return new ItemRequirement(ItemUseType.CONSUME, Items.PAINTING); + + if (type == EntityType.ARMOR_STAND) { + List requirements = new ArrayList<>(); + ArmorStandEntity armorStandEntity = (ArmorStandEntity) entity; + armorStandEntity.getEquipmentAndArmor().forEach(requirements::add); + requirements.add(new ItemStack(Items.ARMOR_STAND)); + return new ItemRequirement(ItemUseType.CONSUME, requirements); + } + + if (entity instanceof AbstractMinecartEntity) { + AbstractMinecartEntity minecartEntity = (AbstractMinecartEntity) entity; + return new ItemRequirement(ItemUseType.CONSUME, minecartEntity.getCartItem().getItem()); + } + + if (entity instanceof BoatEntity) { + BoatEntity boatEntity = (BoatEntity) entity; + return new ItemRequirement(ItemUseType.CONSUME, boatEntity.getItemBoat().getItem()); + } + + if (type == EntityType.END_CRYSTAL) + return new ItemRequirement(ItemUseType.CONSUME, Items.END_CRYSTAL); + + return INVALID; + } + + public boolean isEmpty() { + return NONE == this; + } + + public boolean isInvalid() { + return INVALID == this; + } + + public List getRequiredItems() { + return requiredItems; + } + + public ItemUseType getUsage() { + return usage; + } + + public static boolean validate(ItemStack required, ItemStack present) { + return required.isEmpty() || required.getItem() == present.getItem(); + } + +} diff --git a/src/main/java/com/simibubi/create/modules/schematics/MaterialChecklist.java b/src/main/java/com/simibubi/create/modules/schematics/MaterialChecklist.java index e91796803..c7e758b04 100644 --- a/src/main/java/com/simibubi/create/modules/schematics/MaterialChecklist.java +++ b/src/main/java/com/simibubi/create/modules/schematics/MaterialChecklist.java @@ -7,6 +7,9 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import com.google.common.collect.Sets; +import com.simibubi.create.modules.schematics.ItemRequirement.ItemUseType; + import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; @@ -21,10 +24,12 @@ public class MaterialChecklist { public Map gathered; public Map required; + public Map damageRequired; public boolean blocksNotLoaded; public MaterialChecklist() { required = new HashMap<>(); + damageRequired = new HashMap<>(); gathered = new HashMap<>(); } @@ -32,16 +37,33 @@ public class MaterialChecklist { blocksNotLoaded = true; } - public void require(Item item) { - if (required.containsKey(item)) - required.put(item, required.get(item) + 1); + public void require(ItemRequirement requirement) { + if (requirement.isEmpty()) + return; + if (requirement.isInvalid()) + return; + + for (ItemStack stack : requirement.requiredItems) { + if (requirement.getUsage() == ItemUseType.DAMAGE) + putOrIncrement(damageRequired, stack); + if (requirement.getUsage() == ItemUseType.CONSUME) + putOrIncrement(required, stack); + } + } + + private void putOrIncrement(Map map, ItemStack stack) { + Item item = stack.getItem(); + if (item == Items.AIR) + return; + if (map.containsKey(item)) + map.put(item, map.get(item) + stack.getCount()); else - required.put(item, 1); + map.put(item, stack.getCount()); } public void collect(ItemStack stack) { Item item = stack.getItem(); - if (required.containsKey(item)) + if (required.containsKey(item) || damageRequired.containsKey(item)) if (gathered.containsKey(item)) gathered.put(item, gathered.get(item) + stack.getCount()); else @@ -65,19 +87,19 @@ public class MaterialChecklist { string = new StringBuilder("{\"text\":\""); } - List keys = new ArrayList<>(required.keySet()); + List keys = new ArrayList<>(Sets.union(required.keySet(), damageRequired.keySet())); Collections.sort(keys, (item1, item2) -> { Locale locale = Locale.ENGLISH; - String name1 = new TranslationTextComponent(((Item) item1).getTranslationKey()).getFormattedText() - .toLowerCase(locale); - String name2 = new TranslationTextComponent(((Item) item2).getTranslationKey()).getFormattedText() - .toLowerCase(locale); + String name1 = + new TranslationTextComponent(((Item) item1).getTranslationKey()).getFormattedText().toLowerCase(locale); + String name2 = + new TranslationTextComponent(((Item) item2).getTranslationKey()).getFormattedText().toLowerCase(locale); return name1.compareTo(name2); }); List completed = new ArrayList<>(); for (Item item : keys) { - int amount = required.get(item); + int amount = getRequiredAmount(item); if (gathered.containsKey(item)) amount -= gathered.get(item); @@ -106,7 +128,7 @@ public class MaterialChecklist { } itemsWritten++; - string.append(gatheredEntry(new ItemStack(item), required.get(item))); + string.append(gatheredEntry(new ItemStack(item), getRequiredAmount(item))); } string.append("\"}"); @@ -120,6 +142,13 @@ public class MaterialChecklist { return book; } + public Integer getRequiredAmount(Item item) { + int amount = required.getOrDefault(item, 0); + if (damageRequired.containsKey(item)) + amount += Math.ceil(damageRequired.get(item) / (float) new ItemStack(item).getMaxDamage()); + return amount; + } + private String gatheredEntry(ItemStack item, int amount) { int stacks = amount / 64; int remainder = amount % 64; diff --git a/src/main/java/com/simibubi/create/modules/schematics/SchematicWorld.java b/src/main/java/com/simibubi/create/modules/schematics/SchematicWorld.java index cdc1024ec..6d9bcc605 100644 --- a/src/main/java/com/simibubi/create/modules/schematics/SchematicWorld.java +++ b/src/main/java/com/simibubi/create/modules/schematics/SchematicWorld.java @@ -1,5 +1,6 @@ package com.simibubi.create.modules.schematics; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -14,6 +15,8 @@ import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; +import net.minecraft.entity.item.ArmorStandEntity; +import net.minecraft.entity.item.ItemFrameEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.fluid.Fluid; import net.minecraft.fluid.IFluidState; @@ -31,6 +34,7 @@ public class SchematicWorld extends WrappedWorld { private Map blocks; private Map tileEntities; + private List entities; private Cuboid bounds; public BlockPos anchor; public boolean renderMode; @@ -41,11 +45,28 @@ public class SchematicWorld extends WrappedWorld { this.tileEntities = new HashMap<>(); this.bounds = new Cuboid(); this.anchor = anchor; + this.entities = new ArrayList<>(); } public Set getAllPositions() { return blocks.keySet(); } + + @Override + public boolean addEntity(Entity entityIn) { + if (entityIn instanceof ItemFrameEntity) + ((ItemFrameEntity) entityIn).getDisplayedItem().setTag(null); + if (entityIn instanceof ArmorStandEntity) { + ArmorStandEntity armorStandEntity = (ArmorStandEntity) entityIn; + armorStandEntity.getEquipmentAndArmor().forEach(stack -> stack.setTag(null)); + } + + return entities.add(entityIn); + } + + public List getEntities() { + return entities; + } @Override public TileEntity getTileEntity(BlockPos pos) { diff --git a/src/main/java/com/simibubi/create/modules/schematics/ServerSchematicLoader.java b/src/main/java/com/simibubi/create/modules/schematics/ServerSchematicLoader.java index 2470cf8a3..214c5654d 100644 --- a/src/main/java/com/simibubi/create/modules/schematics/ServerSchematicLoader.java +++ b/src/main/java/com/simibubi/create/modules/schematics/ServerSchematicLoader.java @@ -25,7 +25,6 @@ import com.simibubi.create.modules.schematics.item.SchematicItem; import net.minecraft.block.BlockState; import net.minecraft.entity.player.ServerPlayerEntity; -import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TranslationTextComponent; @@ -234,7 +233,6 @@ public class ServerSchematicLoader { if (table == null) return; table.finishUpload(); - table.inventory.setStackInSlot(0, ItemStack.EMPTY); table.inventory.setStackInSlot(1, SchematicItem.create(schematic, player.getName().getFormattedText())); } catch (IOException e) { diff --git a/src/main/java/com/simibubi/create/modules/schematics/block/LaunchedBlock.java b/src/main/java/com/simibubi/create/modules/schematics/block/LaunchedBlock.java deleted file mode 100644 index 0f9b5300d..000000000 --- a/src/main/java/com/simibubi/create/modules/schematics/block/LaunchedBlock.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.simibubi.create.modules.schematics.block; - -import net.minecraft.block.BlockState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; - -public class LaunchedBlock { - - private final SchematicannonTileEntity te; - public int totalTicks; - public int ticksRemaining; - public BlockPos target; - public BlockState state; - - public LaunchedBlock(SchematicannonTileEntity schematicannonTileEntity, BlockPos target, BlockState state) { - te = schematicannonTileEntity; - this.target = target; - this.state = state; - totalTicks = (int) (Math.max(10, MathHelper.sqrt(MathHelper.sqrt(target.distanceSq(te.getPos()))) * 4f)); - ticksRemaining = totalTicks; - } - - public LaunchedBlock(SchematicannonTileEntity schematicannonTileEntity, BlockPos target, BlockState state, - int ticksLeft, int total) { - te = schematicannonTileEntity; - this.target = target; - this.state = state; - this.totalTicks = total; - this.ticksRemaining = ticksLeft; - } - - public void update() { - if (ticksRemaining > 0) - ticksRemaining--; - } -} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/modules/schematics/block/LaunchedItem.java b/src/main/java/com/simibubi/create/modules/schematics/block/LaunchedItem.java new file mode 100644 index 000000000..629c02af0 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/schematics/block/LaunchedItem.java @@ -0,0 +1,164 @@ +package com.simibubi.create.modules.schematics.block; + +import java.util.Optional; + +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.NBTUtil; +import net.minecraft.state.properties.BlockStateProperties; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; + +public abstract class LaunchedItem { + + public int totalTicks; + public int ticksRemaining; + public BlockPos target; + public ItemStack stack; + + private LaunchedItem(BlockPos start, BlockPos target, ItemStack stack) { + this(target, stack, ticksForDistance(start, target), ticksForDistance(start, target)); + } + + private static int ticksForDistance(BlockPos start, BlockPos target) { + return (int) (Math.max(10, MathHelper.sqrt(MathHelper.sqrt(target.distanceSq(start))) * 4f)); + } + + LaunchedItem() { + } + + private LaunchedItem(BlockPos target, ItemStack stack, int ticksLeft, int total) { + this.target = target; + this.stack = stack; + this.totalTicks = total; + this.ticksRemaining = ticksLeft; + } + + public boolean update(World world) { + if (ticksRemaining > 0) { + ticksRemaining--; + return false; + } + if (world.isRemote) + return false; + + place(world); + return true; + } + + public CompoundNBT serializeNBT() { + CompoundNBT c = new CompoundNBT(); + c.putInt("TotalTicks", totalTicks); + c.putInt("TicksLeft", ticksRemaining); + c.put("Stack", stack.serializeNBT()); + c.put("Target", NBTUtil.writeBlockPos(target)); + return c; + } + + public static LaunchedItem fromNBT(CompoundNBT c) { + LaunchedItem launched = + c.contains("BlockState") ? new LaunchedItem.ForBlockState() : new LaunchedItem.ForEntity(); + launched.readNBT(c); + return launched; + } + + abstract void place(World world); + + void readNBT(CompoundNBT c) { + target = NBTUtil.readBlockPos(c.getCompound("Target")); + ticksRemaining = c.getInt("TicksLeft"); + totalTicks = c.getInt("TotalTicks"); + stack = ItemStack.read(c.getCompound("Stack")); + } + + public static class ForBlockState extends LaunchedItem { + public BlockState state; + + ForBlockState() { + } + + public ForBlockState(BlockPos start, BlockPos target, ItemStack stack, BlockState state) { + super(start, target, stack); + this.state = state; + } + + @Override + public CompoundNBT serializeNBT() { + CompoundNBT serializeNBT = super.serializeNBT(); + serializeNBT.put("BlockState", NBTUtil.writeBlockState(state)); + return serializeNBT; + } + + @Override + void readNBT(CompoundNBT nbt) { + super.readNBT(nbt); + state = NBTUtil.readBlockState(nbt.getCompound("BlockState")); + } + + @Override + void place(World world) { + // Piston + if (state.has(BlockStateProperties.EXTENDED)) + state = state.with(BlockStateProperties.EXTENDED, false); + + world.setBlockState(target, state, 18); + state.getBlock().onBlockPlacedBy(world, target, state, null, stack); + } + + } + + public static class ForEntity extends LaunchedItem { + public Entity entity; + private CompoundNBT deferredTag; + + ForEntity() { + } + + public ForEntity(BlockPos start, BlockPos target, ItemStack stack, Entity entity) { + super(start, target, stack); + this.entity = entity; + } + + @Override + public boolean update(World world) { + if (deferredTag != null && entity == null) { + try { + Optional loadEntityUnchecked = EntityType.loadEntityUnchecked(deferredTag, world); + if (!loadEntityUnchecked.isPresent()) + return true; + entity = loadEntityUnchecked.get(); + } catch (Exception var3) { + return true; + } + deferredTag = null; + } + return super.update(world); + } + + @Override + public CompoundNBT serializeNBT() { + CompoundNBT serializeNBT = super.serializeNBT(); + if (entity != null) + serializeNBT.put("Entity", entity.serializeNBT()); + return serializeNBT; + } + + @Override + void readNBT(CompoundNBT nbt) { + super.readNBT(nbt); + if (nbt.contains("Entity")) + deferredTag = nbt.getCompound("Entity"); + } + + @Override + void place(World world) { + world.addEntity(entity); + } + + } + +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/modules/schematics/block/SchematicTableTileEntity.java b/src/main/java/com/simibubi/create/modules/schematics/block/SchematicTableTileEntity.java index 968ff81b5..272e45ced 100644 --- a/src/main/java/com/simibubi/create/modules/schematics/block/SchematicTableTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/schematics/block/SchematicTableTileEntity.java @@ -7,6 +7,7 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.INamedContainerProvider; +import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.network.PacketBuffer; import net.minecraft.tileentity.ITickableTileEntity; @@ -102,6 +103,7 @@ public class SchematicTableTileEntity extends SyncedTileEntity implements ITicka uploadingProgress = 0; uploadingSchematic = schematic; sendUpdate = true; + inventory.setStackInSlot(0, ItemStack.EMPTY); } public void finishUpload() { diff --git a/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonBlock.java b/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonBlock.java index 55b6f9dc1..8081ce0b7 100644 --- a/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonBlock.java +++ b/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonBlock.java @@ -17,7 +17,6 @@ import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.IBlockReader; -import net.minecraft.world.IWorldReader; import net.minecraft.world.World; import net.minecraftforge.fml.network.NetworkHooks; @@ -52,11 +51,6 @@ public class SchematicannonBlock extends Block implements ITE { @Override @@ -56,14 +60,14 @@ public class SchematicannonRenderer extends SafeTileEntityRenderer block.totalTicks - 10) { - recoil = Math.max(recoil, (block.ticksRemaining + 1 - partialTicks) - block.totalTicks + 10); + if ((launched.ticksRemaining + 1 - partialTicks) > launched.totalTicks - 10) { + recoil = Math.max(recoil, (launched.ticksRemaining + 1 - partialTicks) - launched.totalTicks + 10); } // Render particles for launch - if (block.ticksRemaining == block.totalTicks && tileEntityIn.firstRenderTick) { + if (launched.ticksRemaining == launched.totalTicks && tileEntityIn.firstRenderTick) { tileEntityIn.firstRenderTick = false; for (int i = 0; i < 10; i++) { Random r = tileEntityIn.getWorld().getRandom(); diff --git a/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonScreen.java b/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonScreen.java index 9bfff0b58..e189ea4a5 100644 --- a/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonScreen.java +++ b/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonScreen.java @@ -26,7 +26,6 @@ import net.minecraft.client.gui.widget.Widget; import net.minecraft.client.renderer.Rectangle2d; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.item.BlockItem; import net.minecraft.item.ItemStack; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; @@ -89,9 +88,8 @@ public class SchematicannonScreen extends AbstractSimiContainerScreen(4); replaceLevelIndicators = new Vector<>(4); - List icons = ImmutableList.of(ScreenResources.I_DONT_REPLACE, - ScreenResources.I_REPLACE_SOLID, ScreenResources.I_REPLACE_ANY, - ScreenResources.I_REPLACE_EMPTY); + List icons = ImmutableList.of(ScreenResources.I_DONT_REPLACE, ScreenResources.I_REPLACE_SOLID, + ScreenResources.I_REPLACE_ANY, ScreenResources.I_REPLACE_EMPTY); List toolTips = ImmutableList.of(Lang.translate("gui.schematicannon.option.dontReplaceSolid"), Lang.translate("gui.schematicannon.option.replaceWithSolid"), Lang.translate("gui.schematicannon.option.replaceWithAny"), @@ -215,10 +213,9 @@ public class SchematicannonScreen extends AbstractSimiContainerScreen= fuelX && mouseY >= fuelY && mouseX <= fuelX + ScreenResources.SCHEMATICANNON_FUEL.width && mouseY <= fuelY + ScreenResources.SCHEMATICANNON_FUEL.height) { container.getTileEntity(); + double fuelUsageRate = te.getFuelUsageRate(); int shotsLeft = (int) (te.fuelLevel / fuelUsageRate); int shotsLeftWithItems = (int) (shotsLeft + te.inventory.getStackInSlot(4).getCount() * (te.getFuelAddedByGunPowder() / fuelUsageRate)); - renderTooltip(ImmutableList.of(Lang.translate(_gunpowderLevel, "" + (int) (te.fuelLevel * 100)), - GRAY + Lang.translate(_shotsRemaining, "" + TextFormatting.BLUE + shotsLeft), - GRAY + Lang.translate(_shotsRemainingWithBackup, "" + TextFormatting.BLUE + shotsLeftWithItems)), - mouseX, mouseY); + + List tooltip = new ArrayList<>(); + tooltip.add(Lang.translate(_gunpowderLevel, "" + (int) (te.fuelLevel * 100))); + tooltip.add(GRAY + Lang.translate(_shotsRemaining, "" + TextFormatting.BLUE + shotsLeft)); + if (shotsLeftWithItems != shotsLeft) + tooltip.add(GRAY + + Lang.translate(_shotsRemainingWithBackup, "" + TextFormatting.BLUE + shotsLeftWithItems)); + + renderTooltip(tooltip, mouseX, mouseY); } - if (te.missingBlock != null) { + if (te.missingItem != null) { int missingBlockX = guiLeft + 145, missingBlockY = guiTop + 25; if (mouseX >= missingBlockX && mouseY >= missingBlockY && mouseX <= missingBlockX + 16 && mouseY <= missingBlockY + 16) { - renderTooltip(new ItemStack(BlockItem.BLOCK_TO_ITEM.get(te.missingBlock.getBlock())), mouseX, mouseY); + renderTooltip(te.missingItem, mouseX, mouseY); } } diff --git a/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonTileEntity.java b/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonTileEntity.java index 081ddf8e8..b09beee26 100644 --- a/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/schematics/block/SchematicannonTileEntity.java @@ -11,6 +11,10 @@ import com.simibubi.create.config.AllConfigs; import com.simibubi.create.config.CSchematics; import com.simibubi.create.foundation.behaviour.base.SmartTileEntity; import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; +import com.simibubi.create.foundation.item.ItemHelper; +import com.simibubi.create.foundation.item.ItemHelper.ExtractionCountMode; +import com.simibubi.create.modules.schematics.ItemRequirement; +import com.simibubi.create.modules.schematics.ItemRequirement.ItemUseType; import com.simibubi.create.modules.schematics.MaterialChecklist; import com.simibubi.create.modules.schematics.SchematicWorld; import com.simibubi.create.modules.schematics.item.SchematicItem; @@ -18,6 +22,7 @@ import com.simibubi.create.modules.schematics.item.SchematicItem; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.block.PistonHeadBlock; +import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; @@ -33,7 +38,6 @@ import net.minecraft.network.PacketBuffer; import net.minecraft.state.properties.BedPart; import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.state.properties.DoubleBlockHalf; -import net.minecraft.state.properties.SlabType; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.Direction; @@ -48,6 +52,7 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.ItemHandlerHelper; public class SchematicannonTileEntity extends SmartTileEntity implements INamedContainerProvider { @@ -71,17 +76,18 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC public BlockPos currentPos; public BlockPos schematicAnchor; public boolean schematicLoaded; - public BlockState missingBlock; - public boolean blockNotLoaded; + public ItemStack missingItem; + public boolean positionNotLoaded; public boolean hasCreativeCrate; private int printerCooldown; private int skipsLeft; private boolean blockSkipped; + private int printingEntityIndex; public BlockPos target; public BlockPos previousTarget; public List attachedInventories; - public List flyingBlocks; + public List flyingBlocks; public MaterialChecklist checklist; // Gui information @@ -124,6 +130,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC inventory = new SchematicannonInventory(this); statusMsg = "idle"; state = State.STOPPED; + printingEntityIndex = -1; replaceMode = 2; neighbourCheckCooldown = NEIGHBOUR_CHECKING; checklist = new MaterialChecklist(); @@ -143,8 +150,8 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC TileEntity tileEntity = world.getTileEntity(pos.offset(facing)); if (tileEntity != null) { - LazyOptional capability = tileEntity - .getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing.getOpposite()); + LazyOptional capability = + tileEntity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing.getOpposite()); if (capability.isPresent()) { attachedInventories.add(capability.orElse(null)); } @@ -174,11 +181,11 @@ 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"); - if (compound.contains("MissingBlock")) - missingBlock = NBTUtil.readBlockState(compound.getCompound("MissingBlock")); - else - missingBlock = null; + missingItem = null; + if (compound.contains("MissingItem")) + missingItem = ItemStack.read(compound.getCompound("MissingItem")); // Settings CompoundNBT options = compound.getCompound("Options"); @@ -203,15 +210,12 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC for (int i = 0; i < tagBlocks.size(); i++) { CompoundNBT c = tagBlocks.getCompound(i); - - BlockPos readBlockPos = NBTUtil.readBlockPos(c.getCompound("Target")); - BlockState readBlockState = NBTUtil.readBlockState(c.getCompound("Block")); - int int1 = c.getInt("TicksLeft"); - int int2 = c.getInt("TotalTicks"); + LaunchedItem launched = LaunchedItem.fromNBT(c); + BlockPos readBlockPos = launched.target; // Always write to Server tile if (world == null || !world.isRemote) { - flyingBlocks.add(new LaunchedBlock(this, readBlockPos, readBlockState, int1, int2)); + flyingBlocks.add(launched); continue; } @@ -224,7 +228,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC // Add new server side blocks if (i >= flyingBlocks.size()) { - flyingBlocks.add(new LaunchedBlock(this, readBlockPos, readBlockState, int1, int2)); + flyingBlocks.add(launched); continue; } @@ -256,9 +260,10 @@ 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 (missingBlock != null) - compound.put("MissingBlock", NBTUtil.writeBlockState(missingBlock)); + if (missingItem != null) + compound.put("MissingItem", missingItem.serializeNBT()); // Settings CompoundNBT options = new CompoundNBT(); @@ -271,14 +276,8 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC if (target != null) compound.put("Target", NBTUtil.writeBlockPos(target)); ListNBT tagBlocks = new ListNBT(); - for (LaunchedBlock b : flyingBlocks) { - CompoundNBT c = new CompoundNBT(); - c.putInt("TotalTicks", b.totalTicks); - c.putInt("TicksLeft", b.ticksRemaining); - c.put("Target", NBTUtil.writeBlockPos(b.target)); - c.put("Block", NBTUtil.writeBlockState(b.state)); - tagBlocks.add(c); - } + for (LaunchedItem b : flyingBlocks) + tagBlocks.add(b.serializeNBT()); compound.put("FlyingBlocks", tagBlocks); return compound; @@ -287,7 +286,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC @Override public void tick() { super.tick(); - + if (neighbourCheckCooldown-- <= 0) { neighbourCheckCooldown = NEIGHBOUR_CHECKING; findInventories(); @@ -344,7 +343,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC return; } - if (state == State.PAUSED && !blockNotLoaded && missingBlock == null && fuelLevel > getFuelUsageRate()) + if (state == State.PAUSED && !positionNotLoaded && missingItem == null && fuelLevel > getFuelUsageRate()) return; // Initialize Printer @@ -370,13 +369,13 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC // Update Target if (hasCreativeCrate) { - if (missingBlock != null) { - missingBlock = null; + if (missingItem != null) { + missingItem = null; state = State.RUNNING; } } - if (missingBlock == null && !blockNotLoaded) { + if (missingItem == null && !positionNotLoaded) { advanceCurrentPos(); // End reached @@ -387,61 +386,83 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC target = schematicAnchor.add(currentPos); } + boolean entityMode = printingEntityIndex >= 0; + // Check block if (!getWorld().isAreaLoaded(target, 0)) { - blockNotLoaded = true; + positionNotLoaded = true; statusMsg = "targetNotLoaded"; state = State.PAUSED; return; } else { - if (blockNotLoaded) { - blockNotLoaded = false; + if (positionNotLoaded) { + positionNotLoaded = false; state = State.RUNNING; } } - BlockState blockState = blockReader.getBlockState(target); - ItemStack requiredItem = getItemForBlock(blockState); + boolean shouldSkip = false; + BlockState blockState = Blocks.AIR.getDefaultState(); + ItemRequirement requirement; - if (!shouldPlace(target, blockState) || requiredItem.isEmpty()) { + if (entityMode) { + requirement = ItemRequirement.of(blockReader.getEntities().get(printingEntityIndex)); + + } else { + blockState = blockReader.getBlockState(target); + requirement = ItemRequirement.of(blockState); + shouldSkip = !shouldPlace(target, blockState); + } + + if (shouldSkip || requirement.isInvalid()) { statusMsg = "searching"; blockSkipped = true; return; } // Find item - if (blockState.has(BlockStateProperties.SLAB_TYPE) - && blockState.get(BlockStateProperties.SLAB_TYPE) == SlabType.DOUBLE) - requiredItem.setCount(2); + List requiredItems = requirement.getRequiredItems(); + if (!requirement.isEmpty()) { + for (ItemStack required : requiredItems) { + if (!grabItemsFromAttachedInventories(required, requirement.getUsage(), true)) { + if (skipMissing) { + statusMsg = "skipping"; + blockSkipped = true; + if (missingItem != null) { + missingItem = null; + state = State.RUNNING; + } + return; + } - if (!findItemInAttachedInventories(requiredItem)) { - if (skipMissing) { - statusMsg = "skipping"; - blockSkipped = true; - if (missingBlock != null) { - missingBlock = null; - state = State.RUNNING; + missingItem = required; + state = State.PAUSED; + statusMsg = "missingBlock"; + return; } - return; } - missingBlock = blockState; - state = State.PAUSED; - statusMsg = "missingBlock"; - return; + for (ItemStack required : requiredItems) + grabItemsFromAttachedInventories(required, requirement.getUsage(), false); } // Success state = State.RUNNING; - if (blockState.getBlock() != Blocks.AIR) + if (blockState.getBlock() != Blocks.AIR || entityMode) statusMsg = "placing"; else statusMsg = "clearing"; - launchBlock(target, blockState); + + ItemStack icon = requirement.isEmpty() || requiredItems.isEmpty() ? ItemStack.EMPTY : requiredItems.get(0); + if (entityMode) + launchEntity(target, icon, blockReader.getEntities().get(printingEntityIndex)); + else + launchBlock(target, icon, blockState); + printerCooldown = config().schematicannonDelay.get(); fuelLevel -= getFuelUsageRate(); sendUpdate = true; - missingBlock = null; + missingItem = null; } public double getFuelUsageRate() { @@ -487,6 +508,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC schematicLoaded = true; state = State.PAUSED; statusMsg = "ready"; + printingEntityIndex = -1; updateChecklist(); sendUpdate = true; blocksToPlace += blocksPlaced; @@ -498,44 +520,82 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC return item == Items.AIR ? ItemStack.EMPTY : new ItemStack(item); } - protected boolean findItemInAttachedInventories(ItemStack requiredItem) { + protected boolean grabItemsFromAttachedInventories(ItemStack required, ItemUseType usage, boolean simulate) { if (hasCreativeCrate) return true; - boolean two = requiredItem.getCount() == 2; - int lastSlot = -1; + // Find and apply damage + if (usage == ItemUseType.DAMAGE) { + for (IItemHandler iItemHandler : attachedInventories) { + for (int slot = 0; slot < iItemHandler.getSlots(); slot++) { + ItemStack extractItem = iItemHandler.extractItem(slot, 1, true); + if (!ItemRequirement.validate(required, extractItem)) + continue; + if (!extractItem.isDamageable()) + continue; - for (IItemHandler iItemHandler : attachedInventories) { - for (int slot = 0; slot < iItemHandler.getSlots(); slot++) { - ItemStack stackInSlot = iItemHandler.getStackInSlot(slot); - if (!stackInSlot.isItemEqual(requiredItem)) - continue; - if (!two && !iItemHandler.extractItem(slot, 1, false).isEmpty()) - return true; - - // Two Items required (Double slabs) - if (two) { - int count = iItemHandler.extractItem(slot, 2, true).getCount(); - if (count == 2) { - iItemHandler.extractItem(slot, 2, false); - return true; - } else if (count == 1) { - if (lastSlot == -1) - lastSlot = slot; - else { - iItemHandler.extractItem(lastSlot, 1, false); - iItemHandler.extractItem(slot, 1, false); - return true; + if (!simulate) { + ItemStack stack = iItemHandler.extractItem(slot, 1, false); + stack.setDamage(stack.getDamage() + 1); + if (stack.getDamage() <= stack.getMaxDamage()) { + if (iItemHandler.getStackInSlot(slot).isEmpty()) + iItemHandler.insertItem(slot, stack, false); + else + ItemHandlerHelper.insertItem(iItemHandler, stack, false); } } - } + return true; + } } } - return false; + + // Find and remove + boolean success = false; + if (usage == ItemUseType.CONSUME) { + int amountFound = 0; + for (IItemHandler iItemHandler : attachedInventories) { + + amountFound += ItemHelper.extract(iItemHandler, s -> ItemRequirement.validate(required, s), + ExtractionCountMode.UPTO, required.getCount(), true).getCount(); + + if (amountFound < required.getCount()) + continue; + + success = true; + break; + } + } + + if (!simulate && success) { + int amountFound = 0; + for (IItemHandler iItemHandler : attachedInventories) { + amountFound += ItemHelper.extract(iItemHandler, s -> ItemRequirement.validate(required, s), + ExtractionCountMode.UPTO, required.getCount(), false).getCount(); + if (amountFound < required.getCount()) + continue; + break; + } + } + + return success; } protected void advanceCurrentPos() { + List entities = blockReader.getEntities(); + if (printingEntityIndex != -1) { + printingEntityIndex++; + + // End of entities reached + if (printingEntityIndex >= entities.size()) { + finishedPrinting(); + return; + } + + currentPos = entities.get(printingEntityIndex).getPosition().subtract(schematicAnchor); + return; + } + BlockPos size = blockReader.getBounds().getSize(); currentPos = currentPos.offset(Direction.EAST); BlockPos posInBounds = currentPos.subtract(blockReader.getBounds().getOrigin()); @@ -545,29 +605,38 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC if (posInBounds.getZ() > size.getZ()) currentPos = new BlockPos(currentPos.getX(), currentPos.getY() + 1, blockReader.getBounds().z).west(); - // End reached + // End of blocks reached if (currentPos.getY() > size.getY()) { - inventory.setStackInSlot(0, ItemStack.EMPTY); - inventory.setStackInSlot(1, - new ItemStack(AllItems.EMPTY_BLUEPRINT.get(), inventory.getStackInSlot(1).getCount() + 1)); - state = State.STOPPED; - statusMsg = "finished"; - resetPrinter(); - target = getPos().add(1, 0, 0); - world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), AllSoundEvents.SCHEMATICANNON_FINISH.get(), - SoundCategory.BLOCKS, 1, .7f); - sendUpdate = true; - return; + printingEntityIndex = 0; + if (entities.isEmpty()) { + finishedPrinting(); + return; + } + currentPos = entities.get(0).getPosition().subtract(schematicAnchor); } } + public void finishedPrinting() { + inventory.setStackInSlot(0, ItemStack.EMPTY); + inventory.setStackInSlot(1, + new ItemStack(AllItems.EMPTY_BLUEPRINT.get(), inventory.getStackInSlot(1).getCount() + 1)); + state = State.STOPPED; + statusMsg = "finished"; + resetPrinter(); + target = getPos().add(1, 0, 0); + world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), AllSoundEvents.SCHEMATICANNON_FINISH.get(), + SoundCategory.BLOCKS, 1, .7f); + sendUpdate = true; + } + protected void resetPrinter() { schematicLoaded = false; schematicAnchor = null; currentPos = null; blockReader = null; - missingBlock = null; + missingItem = null; sendUpdate = true; + printingEntityIndex = -1; schematicProgress = 0; blocksPlaced = 0; blocksToPlace = 0; @@ -577,6 +646,10 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC BlockState toReplace = world.getBlockState(pos); boolean placingAir = state.getBlock() == Blocks.AIR; + if (!world.isBlockPresent(pos)) + return false; + if (!world.getWorldBorder().contains(pos)) + return false; if (toReplace == state) return false; if (toReplace.getBlockHardness(world, pos) == -1) @@ -622,21 +695,10 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC } protected void tickFlyingBlocks() { - List toRemove = new LinkedList<>(); - for (LaunchedBlock b : flyingBlocks) { - b.update(); - if (b.ticksRemaining <= 0 && !world.isRemote) { - - // Piston - if (b.state.has(BlockStateProperties.EXTENDED)) { - b.state = b.state.with(BlockStateProperties.EXTENDED, false); - } - - world.setBlockState(b.target, b.state, 18); - b.state.getBlock().onBlockPlacedBy(world, b.target, b.state, null, getItemForBlock(b.state)); + List toRemove = new LinkedList<>(); + for (LaunchedItem b : flyingBlocks) + if (b.update(world)) toRemove.add(b); - } - } flyingBlocks.removeAll(toRemove); } @@ -696,10 +758,20 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC sendUpdate = true; } - protected void launchBlock(BlockPos target, BlockState state) { + protected void launchBlock(BlockPos target, ItemStack stack, BlockState state) { if (state.getBlock() != Blocks.AIR) blocksPlaced++; - flyingBlocks.add(new LaunchedBlock(this, target, state)); + flyingBlocks.add(new LaunchedItem.ForBlockState(this.getPos(), target, stack, state)); + playFiringSound(); + } + + protected void launchEntity(BlockPos target, ItemStack stack, Entity entity) { + blocksPlaced++; + flyingBlocks.add(new LaunchedItem.ForEntity(this.getPos(), target, stack, entity)); + playFiringSound(); + } + + public void playFiringSound() { world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), AllSoundEvents.SCHEMATICANNON_LAUNCH_BLOCK.get(), SoundCategory.BLOCKS, .1f, 1.1f); } @@ -721,6 +793,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC public void updateChecklist() { checklist.required.clear(); + checklist.damageRequired.clear(); checklist.blocksNotLoaded = false; if (schematicLoaded) { @@ -734,18 +807,22 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC } if (!shouldPlace(pos.add(schematicAnchor), required)) continue; - ItemStack requiredItem = getItemForBlock(required); - if (requiredItem.isEmpty()) + ItemRequirement requirement = ItemRequirement.of(required); + if (requirement.isEmpty()) continue; - - // Two items for double slabs - if (required.has(BlockStateProperties.SLAB_TYPE) - && required.get(BlockStateProperties.SLAB_TYPE) == SlabType.DOUBLE) - checklist.require(requiredItem.getItem()); - - checklist.require(requiredItem.getItem()); + if (requirement.isInvalid()) + continue; + checklist.require(requirement); blocksToPlace++; } + for (Entity entity : blockReader.getEntities()) { + ItemRequirement requirement = ItemRequirement.of(entity); + if (requirement.isEmpty()) + continue; + if (requirement.isInvalid()) + continue; + checklist.require(requirement); + } } checklist.gathered.clear(); for (IItemHandler inventory : attachedInventories) { @@ -762,7 +839,7 @@ public class SchematicannonTileEntity extends SmartTileEntity implements INamedC @Override public void addBehaviours(List behaviours) { } - + @Override public void lazyTick() { super.lazyTick(); diff --git a/src/main/java/com/simibubi/create/modules/schematics/client/SchematicAndQuillHandler.java b/src/main/java/com/simibubi/create/modules/schematics/client/SchematicAndQuillHandler.java index c0cec4880..aabb4e709 100644 --- a/src/main/java/com/simibubi/create/modules/schematics/client/SchematicAndQuillHandler.java +++ b/src/main/java/com/simibubi/create/modules/schematics/client/SchematicAndQuillHandler.java @@ -225,7 +225,7 @@ public class SchematicAndQuillHandler { Template t = new Template(); MutableBoundingBox bb = new MutableBoundingBox(firstPos, secondPos); t.takeBlocksFromWorld(Minecraft.getInstance().world, new BlockPos(bb.minX, bb.minY, bb.minZ), - new BlockPos(bb.getXSize(), bb.getYSize(), bb.getZSize()), false, Blocks.AIR); + new BlockPos(bb.getXSize(), bb.getYSize(), bb.getZSize()), true, Blocks.AIR); if (string.isEmpty()) string = Lang.translate("schematicAndQuill.fallbackName"); diff --git a/src/main/java/com/simibubi/create/modules/schematics/packet/SchematicPlacePacket.java b/src/main/java/com/simibubi/create/modules/schematics/packet/SchematicPlacePacket.java index 82db762ef..a62e6e2f1 100644 --- a/src/main/java/com/simibubi/create/modules/schematics/packet/SchematicPlacePacket.java +++ b/src/main/java/com/simibubi/create/modules/schematics/packet/SchematicPlacePacket.java @@ -9,6 +9,7 @@ import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTUtil; import net.minecraft.network.PacketBuffer; +import net.minecraft.world.gen.feature.template.PlacementSettings; import net.minecraft.world.gen.feature.template.Template; import net.minecraftforge.fml.network.NetworkEvent.Context; @@ -32,8 +33,10 @@ public class SchematicPlacePacket extends SimplePacketBase { context.get().enqueueWork(() -> { ServerPlayerEntity player = context.get().getSender(); Template t = SchematicItem.loadSchematic(stack); + PlacementSettings settings = SchematicItem.getSettings(stack); + settings.setIgnoreEntities(false); t.addBlocksToWorld(player.getServerWorld(), NBTUtil.readBlockPos(stack.getTag().getCompound("Anchor")), - SchematicItem.getSettings(stack)); + settings); }); context.get().setPacketHandled(true); } diff --git a/src/main/resources/assets/create/lang/en_us.json b/src/main/resources/assets/create/lang/en_us.json index 566681264..9afcf6b02 100644 --- a/src/main/resources/assets/create/lang/en_us.json +++ b/src/main/resources/assets/create/lang/en_us.json @@ -534,11 +534,11 @@ "create.schematicannon.status.paused": "Paused", "create.schematicannon.status.stopped": "Stopped", "create.schematicannon.status.noGunpowder": "Out of Gunpowder", - "create.schematicannon.status.targetNotLoaded": "Block is Not Loaded", - "create.schematicannon.status.targetOutsideRange": "Target too Far Away", + "create.schematicannon.status.targetNotLoaded": "Target is not loaded", + "create.schematicannon.status.targetOutsideRange": "Target too far away", "create.schematicannon.status.searching": "Searching", "create.schematicannon.status.skipping": "Skipping", - "create.schematicannon.status.missingBlock": "Missing Block:", + "create.schematicannon.status.missingBlock": "Missing Item:", "create.schematicannon.status.placing": "Placing", "create.schematicannon.status.clearing": "Clearing Blocks", "create.schematicannon.status.schematicInvalid": "Schematic Invalid", diff --git a/src/main/resources/assets/create/textures/item/belt.png b/src/main/resources/assets/create/textures/item/belt.png index 772436f7e58dbe0c41f7e493ecfd201428bc6a47..31fec49cfa5770f5bdde5012c46126b6c35b47f7 100644 GIT binary patch delta 351 zcmV-l0igbZ0+9odBnkm@Qb$4nuFf3kks%v@zyJUazyWI3i3tDz0VYXAK~y+T#ge~j z!!QuWFWzmL%&kpdpv45T_7AjMAw${Qq2yLuD-bn zAXOM+{4=O)O(Mq7(1x~=P3-H)1+M|bFmZusF`w~*DcZQYxif5#ua@+h=XO|YzAP2E zCJ=46HLuO4q_48z{Q%BLuqg*pJ|bR9l5-y&O#S2itx1n33?qJkTR?IwxQlgin(&Y0 xFNrG9%7&oZ$|-Ui$#%D=V@uaM^+*tx!`>j_^ZAI zzevj#v_OfyLqFyM1TAdNFQ)<%2y57!Urq%E!Wx+M6e0?h-_h;?8Rn91kQ;=w00000 LNkvXXu0mjfA%ajd diff --git a/src/main/resources/assets/create/textures/item/brass_ingot.png b/src/main/resources/assets/create/textures/item/brass_ingot.png index c7c3ab97ffdbb596f9151f048540793680ad2f2b..ea33f846b0b2544775a54c086094658816e27864 100644 GIT binary patch delta 380 zcmV-?0fYYh1H1!}Nq@iq01m(bYSxJf0003|NklG${NGvpRd1b-XC0ulge20Dx3-=AY( z1;_xV2?So>oX&9e=s7U^?!$Lr8f*XvT-n>hpyONuHUwQ02!J&1J9HY%77>>K(;xsh z00dx$AZr2vn8vpsJ~9Xk@PpYfzyzfkE}Vxbh5?w3APmw7!XSWbI|zVX@b&voFb4!+ z{)A}+0hp~I&2{T{T?4a^pMM071-KWUy#@vf&=m#@Mo>3)GboJ@oPyE_ zK+u5nNb~<7dgk(zj9>v2a0%*YCcB#SkZT1T%uzbet-YM@a5$r zhW~&60Nu*OP}{T!%@8C5(j#;K10@;${W-?)@9%F0MrKxq?|<)JF#G`;^8epopx7UV z(}zwmEZKCLVabLIKtmwm!2~9u;L6?}1|}9Opr)@3e|~&nU}9!rVBzEeVr~Xul~e|g zq}dFU`|}xM0u10@WP#Ff@cQO-22u4)h94icF>nd{K}8uDzP;VVaA@OZhBxoNGTeRm z4l0V~f(z%*fq!X`OE?6)7(m_w8MtF!D#N<@D;O@_e9Z9dH9|88zzz8N{U?|{dlX^_ z2<)6chhgXD!(dxM8sEHp0mjGx#RbTwgKUOrglT^J;UgF$1GoX8kiB&C1=yJ&ue^MM zu=VE6n_!LC9{vU6{JoX#{D20XAffuxP}Z5-kno;L(Jt2^ruq n00J;G4mJQZ0B#u>;1mM@S?%OFiw+{x00000NkvXXu0mjfbSKDN