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
This commit is contained in:
simibubi 2020-05-05 20:21:22 +02:00
parent 8bfc4445b0
commit c4513020df
23 changed files with 696 additions and 242 deletions

View File

@ -127,17 +127,27 @@ public class ItemHelper {
return false;
}
public static enum ExtractionCountMode {
EXACTLY, UPTO
}
public static ItemStack extract(IItemHandler inv, Predicate<ItemStack> 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<ItemStack> test, int exactAmount, boolean simulate) {
return extract(inv, test, ExtractionCountMode.EXACTLY, exactAmount, simulate);
}
public static ItemStack extract(IItemHandler inv, Predicate<ItemStack> 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;

View File

@ -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;

View File

@ -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) {
context.get().enqueueWork(() -> DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
Minecraft mc = Minecraft.getInstance();

View File

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

View File

@ -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<BeltTileEntity>, IHaveColorHandler {
implements IHaveNoBlockItem, ITE<BeltTileEntity>, IHaveColorHandler, ISpecialBlockItemRequirement {
public static final IProperty<Slope> SLOPE = EnumProperty.create("slope", Slope.class);
public static final IProperty<Part> PART = EnumProperty.create("part", Part.class);
@ -603,5 +607,19 @@ public class BeltBlock extends HorizontalKineticBlock
public Class<BeltTileEntity> getTileEntityClass() {
return BeltTileEntity.class;
}
@Override
public ItemRequirement getRequiredItems(BlockState state) {
List<ItemStack> 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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,9 @@
package com.simibubi.create.modules.schematics;
public interface ISpecialEntityItemRequirement {
default ItemRequirement getRequiredItems() {
return ItemRequirement.INVALID;
}
}

View File

@ -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<ItemStack> 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<ItemStack> 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<ItemStack> 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<ItemStack> getRequiredItems() {
return requiredItems;
}
public ItemUseType getUsage() {
return usage;
}
public static boolean validate(ItemStack required, ItemStack present) {
return required.isEmpty() || required.getItem() == present.getItem();
}
}

View File

@ -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<Item, Integer> gathered;
public Map<Item, Integer> required;
public Map<Item, Integer> 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<Item, Integer> 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<Item> keys = new ArrayList<>(required.keySet());
List<Item> 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<Item> 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;

View File

@ -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<BlockPos, BlockState> blocks;
private Map<BlockPos, TileEntity> tileEntities;
private List<Entity> 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<BlockPos> 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<Entity> getEntities() {
return entities;
}
@Override
public TileEntity getTileEntity(BlockPos pos) {

View File

@ -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) {

View File

@ -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--;
}
}

View File

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

View File

@ -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() {

View File

@ -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<SchematicannonTile
return AllShapes.SCHEMATICANNON_SHAPE;
}
@Override
public void onNeighborChange(BlockState state, IWorldReader world, BlockPos pos, BlockPos neighbor) {
withTileEntityDo(world, pos, SchematicannonTileEntity::findInventories);
}
@Override
public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn,
BlockRayTraceResult hit) {

View File

@ -7,11 +7,14 @@ import com.simibubi.create.AllBlockPartials;
import com.simibubi.create.foundation.block.SafeTileEntityRenderer;
import com.simibubi.create.foundation.utility.SuperByteBuffer;
import com.simibubi.create.foundation.utility.TessellatorHelper;
import com.simibubi.create.modules.schematics.block.LaunchedItem.ForBlockState;
import com.simibubi.create.modules.schematics.block.LaunchedItem.ForEntity;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.particles.ParticleTypes;
@ -20,6 +23,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
@SuppressWarnings("deprecation")
public class SchematicannonRenderer extends SafeTileEntityRenderer<SchematicannonTileEntity> {
@Override
@ -56,14 +60,14 @@ public class SchematicannonRenderer extends SafeTileEntityRenderer<Schematicanno
}
if (!tileEntityIn.flyingBlocks.isEmpty()) {
for (LaunchedBlock block : tileEntityIn.flyingBlocks) {
for (LaunchedItem launched : tileEntityIn.flyingBlocks) {
if (block.ticksRemaining == 0)
if (launched.ticksRemaining == 0)
continue;
// Calculate position of flying block
Vec3d start = new Vec3d(tileEntityIn.getPos().add(.5f, 1, .5f));
Vec3d target = new Vec3d(block.target).add(-.5, 0, 1);
Vec3d target = new Vec3d(launched.target).add(-.5, 0, 1);
Vec3d distance = target.subtract(start);
double targetY = target.y - start.y;
@ -71,10 +75,10 @@ public class SchematicannonRenderer extends SafeTileEntityRenderer<Schematicanno
Vec3d cannonOffset = distance.add(0, throwHeight, 0).normalize().scale(2);
start = start.add(cannonOffset);
double progress = ((double) block.totalTicks - (block.ticksRemaining + 1 - partialTicks))
/ block.totalTicks;
Vec3d blockLocationXZ = new Vec3d(x + .5, y + .5, z + .5)
.add(target.subtract(start).scale(progress).mul(1, 0, 1));
double progress =
((double) launched.totalTicks - (launched.ticksRemaining + 1 - partialTicks)) / launched.totalTicks;
Vec3d blockLocationXZ =
new Vec3d(x + .5, y + .5, z + .5).add(target.subtract(start).scale(progress).mul(1, 0, 1));
// Height is determined through a bezier curve
double t = progress;
@ -86,21 +90,32 @@ public class SchematicannonRenderer extends SafeTileEntityRenderer<Schematicanno
GlStateManager.translated(blockLocation.x, blockLocation.y, blockLocation.z);
// Rotation and Scaling effects
double scale = .3f;
GlStateManager.rotated(360 * t * 2, 1, 1, 0);
GlStateManager.scaled(scale, scale, scale);
// Render the Block
Minecraft.getInstance().getBlockRendererDispatcher().renderBlockBrightness(block.state, 1);
if (launched instanceof ForBlockState) {
double scale = .3f;
GlStateManager.scaled(scale, scale, scale);
Minecraft.getInstance().getBlockRendererDispatcher()
.renderBlockBrightness(((ForBlockState) launched).state, 1);
}
// Render the item
if (launched instanceof ForEntity) {
double scale = 1.2f;
GlStateManager.scaled(scale, scale, scale);
Minecraft.getInstance().getItemRenderer().renderItem(launched.stack, TransformType.GROUND);
}
GlStateManager.popMatrix();
// Apply Recoil if block was just launched
if ((block.ticksRemaining + 1 - partialTicks) > 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();

View File

@ -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<Schematica
// Replace settings
replaceLevelButtons = new Vector<>(4);
replaceLevelIndicators = new Vector<>(4);
List<ScreenResources> icons = ImmutableList.of(ScreenResources.I_DONT_REPLACE,
ScreenResources.I_REPLACE_SOLID, ScreenResources.I_REPLACE_ANY,
ScreenResources.I_REPLACE_EMPTY);
List<ScreenResources> icons = ImmutableList.of(ScreenResources.I_DONT_REPLACE, ScreenResources.I_REPLACE_SOLID,
ScreenResources.I_REPLACE_ANY, ScreenResources.I_REPLACE_EMPTY);
List<String> 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<Schematica
String msg = Lang.translate("schematicannon.status." + te.statusMsg);
int stringWidth = font.getStringWidth(msg);
if (te.missingBlock != null) {
if (te.missingItem != null) {
stringWidth += 15;
itemRenderer.renderItemIntoGUI(new ItemStack(BlockItem.BLOCK_TO_ITEM.get(te.missingBlock.getBlock())),
guiLeft + 145, guiTop + 25);
itemRenderer.renderItemIntoGUI(te.missingItem, guiLeft + 145, guiTop + 25);
}
font.drawStringWithShadow(msg, guiLeft + 20 + 96 - stringWidth / 2, guiTop + 30, 0xCCDDFF);
@ -227,9 +224,10 @@ public class SchematicannonScreen extends AbstractSimiContainerScreen<Schematica
font.drawString(playerInventory.getDisplayName().getFormattedText(), guiLeft - 10 + 7, guiTop + 145 + 6,
0x666666);
//to see or debug the bounds of the extra area uncomment the following lines
//Rectangle2d r = extraAreas.get(0);
//fill(r.getX() + r.getWidth(), r.getY() + r.getHeight(), r.getX(), r.getY(), 0xd3d3d3d3);
// to see or debug the bounds of the extra area uncomment the following lines
// Rectangle2d r = extraAreas.get(0);
// fill(r.getX() + r.getWidth(), r.getY() + r.getHeight(), r.getX(), r.getY(),
// 0xd3d3d3d3);
}
protected void renderCannon() {
@ -282,21 +280,27 @@ public class SchematicannonScreen extends AbstractSimiContainerScreen<Schematica
if (mouseX >= 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<String> 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);
}
}

View File

@ -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<IItemHandler> attachedInventories;
public List<LaunchedBlock> flyingBlocks;
public List<LaunchedItem> 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<IItemHandler> capability = tileEntity
.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing.getOpposite());
LazyOptional<IItemHandler> 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<ItemStack> 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<Entity> 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<LaunchedBlock> 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<LaunchedItem> 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<TileEntityBehaviour> behaviours) {
}
@Override
public void lazyTick() {
super.lazyTick();

View File

@ -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");

View File

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

View File

@ -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",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 B

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 B

After

Width:  |  Height:  |  Size: 444 B