From 5c8206030cb7a54b4ff58422521ebbd8f1d06566 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Tue, 17 Sep 2019 14:57:29 +0200 Subject: [PATCH] Bug Busting 0.1 Part I - Washing recipes can now have stochastic outputs - Splashing fans no longer spam the extinguish sound - Mechanical Press now properly registers as a belt attachment - Improved animations for Mechanical Press - Fixed Pulse Repeaters staying powered under certain conditions - Fixed Belt observer state sync issues - Stopped Filters on tileentities from disappearing - Fixed Item count not showing on filtered Extractors - Fixed Filters not being properly handled by Extractors - Fixed Extractors no longer being redstone locked on world reload - Fixed Redstone Links being powered inconsistently - Added a few more processing recipes --- .../com/simibubi/create/ScreenResources.java | 1 + .../create/compat/jei/SplashingCategory.java | 39 +++++++- .../block/IBlockWithScrollableValue.java | 10 +-- .../receivers/EncasedFanTileEntity.java | 9 +- .../receivers/MechanicalPressBlock.java | 62 ++++++++----- .../receivers/MechanicalPressTileEntity.java | 53 ++++++++--- .../relays/belt/AllBeltAttachments.java | 13 ++- .../relays/belt/BeltTileEntity.java | 16 +--- .../modules/logistics/FrequencyHandler.java | 2 +- .../modules/logistics/InWorldProcessing.java | 39 ++++++-- .../logistics/block/BeltFunnelTileEntity.java | 6 +- .../logistics/block/EntityDetectorBlock.java | 7 +- .../block/EntityDetectorTileEntity.java | 5 +- .../logistics/block/ExtractorBlock.java | 11 +-- .../logistics/block/ExtractorTileEntity.java | 14 ++- .../logistics/block/IBlockWithFilter.java | 50 ++++++++--- .../modules/logistics/block/IExtractor.java | 85 ++++++++++++------ .../logistics/block/LinkedExtractorBlock.java | 2 +- .../block/LinkedExtractorTileEntity.java | 26 +++++- .../logistics/block/RedstoneBridgeBlock.java | 19 ++-- .../block/diodes/PulseRepeaterBlock.java | 2 +- .../resources/assets/create/lang/en_us.json | 2 +- .../assets/create/models/item/extractor.json | 3 +- .../create/models/item/linked_extractor.json | 3 +- .../create/models/item/mechanical_press.json | 3 +- .../assets/create/textures/gui/recipes3.png | Bin 0 -> 15269 bytes .../create/recipes/crushing/coal_ore.json | 26 ++++++ .../create/recipes/crushing/diamond_ore.json | 26 ++++++ .../create/recipes/crushing/emerald_ore.json | 26 ++++++ .../create/recipes/crushing/gold_ore.json | 26 ++++++ .../create/recipes/crushing/iron_ore.json | 26 ++++++ .../create/recipes/crushing/lapis_ore.json | 26 ++++++ .../recipes/crushing/nether_quartz_ore.json | 26 ++++++ .../create/recipes/crushing/redstone_ore.json | 26 ++++++ .../create/recipes/crushing/terracotta.json | 16 ++++ .../splashing/black_concrete_powder.json | 16 ++++ .../splashing/blue_concrete_powder.json | 16 ++++ .../splashing/brown_concrete_powder.json | 16 ++++ .../data/create/recipes/splashing/bucket.json | 16 ++++ .../splashing/cyan_concrete_powder.json | 16 ++++ .../data/create/recipes/splashing/gravel.json | 22 +++++ .../splashing/gray_concrete_powder.json | 16 ++++ .../splashing/green_concrete_powder.json | 16 ++++ .../data/create/recipes/splashing/ice.json | 16 ++++ .../splashing/light_blue_concrete_powder.json | 16 ++++ .../splashing/light_gray_concrete_powder.json | 16 ++++ .../splashing/lime_concrete_powder.json | 16 ++++ .../splashing/magenta_concrete_powder.json | 16 ++++ .../create/recipes/splashing/magma_block.json | 16 ++++ .../splashing/orange_concrete_powder.json | 16 ++++ .../splashing/pink_concrete_powder.json | 16 ++++ .../splashing/purple_concrete_powder.json | 16 ++++ .../splashing/red_concrete_powder.json | 16 ++++ .../create/recipes/splashing/red_sand.json | 22 +++++ .../data/create/recipes/splashing/sand.json | 16 ++++ .../create/recipes/splashing/soul_sand.json | 22 +++++ .../splashing/white_concrete_powder.json | 16 ++++ .../splashing/yellow_concrete_powder.json | 16 ++++ 58 files changed, 957 insertions(+), 135 deletions(-) create mode 100644 src/main/resources/assets/create/textures/gui/recipes3.png create mode 100644 src/main/resources/data/create/recipes/crushing/coal_ore.json create mode 100644 src/main/resources/data/create/recipes/crushing/diamond_ore.json create mode 100644 src/main/resources/data/create/recipes/crushing/emerald_ore.json create mode 100644 src/main/resources/data/create/recipes/crushing/gold_ore.json create mode 100644 src/main/resources/data/create/recipes/crushing/iron_ore.json create mode 100644 src/main/resources/data/create/recipes/crushing/lapis_ore.json create mode 100644 src/main/resources/data/create/recipes/crushing/nether_quartz_ore.json create mode 100644 src/main/resources/data/create/recipes/crushing/redstone_ore.json create mode 100644 src/main/resources/data/create/recipes/crushing/terracotta.json create mode 100644 src/main/resources/data/create/recipes/splashing/black_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/blue_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/brown_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/bucket.json create mode 100644 src/main/resources/data/create/recipes/splashing/cyan_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/gravel.json create mode 100644 src/main/resources/data/create/recipes/splashing/gray_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/green_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/ice.json create mode 100644 src/main/resources/data/create/recipes/splashing/light_blue_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/light_gray_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/lime_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/magenta_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/magma_block.json create mode 100644 src/main/resources/data/create/recipes/splashing/orange_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/pink_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/purple_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/red_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/red_sand.json create mode 100644 src/main/resources/data/create/recipes/splashing/sand.json create mode 100644 src/main/resources/data/create/recipes/splashing/soul_sand.json create mode 100644 src/main/resources/data/create/recipes/splashing/white_concrete_powder.json create mode 100644 src/main/resources/data/create/recipes/splashing/yellow_concrete_powder.json diff --git a/src/main/java/com/simibubi/create/ScreenResources.java b/src/main/java/com/simibubi/create/ScreenResources.java index a62fbedc1..ede2380d6 100644 --- a/src/main/java/com/simibubi/create/ScreenResources.java +++ b/src/main/java/com/simibubi/create/ScreenResources.java @@ -37,6 +37,7 @@ public enum ScreenResources { FAN_RECIPE("recipes1.png", 0, 128, 177, 109), BLOCKZAPPER_UPGRADE_RECIPE("recipes2.png", 144, 66), PRESSER_RECIPE("recipes2.png", 0, 108, 177, 109), + WASHING_RECIPE("recipes3.png", 177, 109), // Widgets PALETTE_BUTTON("palette_picker.png", 0, 236, 20, 20), diff --git a/src/main/java/com/simibubi/create/compat/jei/SplashingCategory.java b/src/main/java/com/simibubi/create/compat/jei/SplashingCategory.java index 24eb86065..fe04f13a2 100644 --- a/src/main/java/com/simibubi/create/compat/jei/SplashingCategory.java +++ b/src/main/java/com/simibubi/create/compat/jei/SplashingCategory.java @@ -1,20 +1,29 @@ package com.simibubi.create.compat.jei; +import java.util.Arrays; +import java.util.List; + import com.mojang.blaze3d.platform.GlStateManager; import com.simibubi.create.AllItems; import com.simibubi.create.Create; import com.simibubi.create.ScreenResources; import com.simibubi.create.foundation.gui.ScreenElementRenderer; import com.simibubi.create.foundation.utility.Lang; +import com.simibubi.create.modules.contraptions.base.StochasticOutput; import com.simibubi.create.modules.contraptions.receivers.SplashingRecipe; +import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.gui.IRecipeLayout; import mezz.jei.api.gui.drawable.IDrawable; +import mezz.jei.api.gui.ingredient.IGuiItemStackGroup; +import mezz.jei.api.ingredients.IIngredients; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.block.FlowingFluidBlock; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.TextFormatting; public class SplashingCategory extends ProcessingViaFanCategory { @@ -45,10 +54,38 @@ public class SplashingCategory extends ProcessingViaFanCategory public String getTitle() { return Lang.translate("recipe.splashing"); } + + @Override + public void setIngredients(SplashingRecipe recipe, IIngredients ingredients) { + ingredients.setInputIngredients(recipe.getIngredients()); + ingredients.setOutputs(VanillaTypes.ITEM, recipe.getPossibleOutputs()); + } + + @Override + public void setRecipe(IRecipeLayout recipeLayout, SplashingRecipe recipe, IIngredients ingredients) { + IGuiItemStackGroup itemStacks = recipeLayout.getItemStacks(); + itemStacks.init(0, true, 20, 67); + itemStacks.set(0, Arrays.asList(recipe.getIngredients().get(0).getMatchingStacks())); + + List results = recipe.getRollableResults(); + for (int outputIndex = 0; outputIndex < results.size(); outputIndex++) { + itemStacks.init(outputIndex + 1, false, 139, 58 + 19 * outputIndex); + itemStacks.set(outputIndex + 1, results.get(outputIndex).getStack()); + } + + itemStacks.addTooltipCallback((slotIndex, input, ingredient, tooltip) -> { + if (input) + return; + StochasticOutput output = results.get(slotIndex - 1); + if (output.getChance() != 1) + tooltip.add(1, TextFormatting.GOLD + + Lang.translate("recipe.processing.chance", (int) (output.getChance() * 100))); + }); + } @Override public IDrawable getBackground() { - return new ScreenResourceWrapper(ScreenResources.FAN_RECIPE); + return new ScreenResourceWrapper(ScreenResources.WASHING_RECIPE); } @Override diff --git a/src/main/java/com/simibubi/create/foundation/block/IBlockWithScrollableValue.java b/src/main/java/com/simibubi/create/foundation/block/IBlockWithScrollableValue.java index eef2e3afb..62fac0142 100644 --- a/src/main/java/com/simibubi/create/foundation/block/IBlockWithScrollableValue.java +++ b/src/main/java/com/simibubi/create/foundation/block/IBlockWithScrollableValue.java @@ -100,12 +100,12 @@ public interface IBlockWithScrollableValue { if (contains) { GlStateManager.lineWidth(2); - WorldRenderer.drawBoundingBox(bufferbuilder, bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ, .5f, 1, + WorldRenderer.drawBoundingBox(bufferbuilder, bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ, 1, .5f, .75f, 1f); } else { GlStateManager.lineWidth(2); - WorldRenderer.drawBoundingBox(bufferbuilder, bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ, .25f, - .5f, .35f, 1f); + WorldRenderer.drawBoundingBox(bufferbuilder, bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ, .5f, + .25f, .35f, 1f); } tessellator.draw(); @@ -127,13 +127,13 @@ public interface IBlockWithScrollableValue { GlStateManager.scaled(textScale, -textScale, textScale); String text = block.getValueName(state, world, blockPos); - mc.fontRenderer.drawString(text, 0, 0, 0x88FFBB); + mc.fontRenderer.drawString(text, 0, 0, 0xFF88BB); GlStateManager.translated(0, 0, -1 / 4f); mc.fontRenderer.drawString(text, 1, 1, 0x224433); GlStateManager.translated(0, 0, 1 / 4f); text = TextFormatting.ITALIC + "<" + Lang.translate("action.scroll") + ">"; - mc.fontRenderer.drawString(text, 0, 10, 0xBBBBCC); + mc.fontRenderer.drawString(text, 0, 10, 0xCCBBCC); GlStateManager.translated(0, 0, -1 / 4f); mc.fontRenderer.drawString(text, 1, 11, 0x111111); GlStateManager.translated(0, 0, 1 / 4f); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/receivers/EncasedFanTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/receivers/EncasedFanTileEntity.java index ccdc1f5d1..b0bd5c4b6 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/receivers/EncasedFanTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/receivers/EncasedFanTileEntity.java @@ -243,9 +243,12 @@ public class EncasedFanTileEntity extends KineticTileEntity implements ITickable entity.attackEntityFrom(damageSourceLava, 8); } if (getProcessingType() == Type.SPLASHING) { - entity.extinguish(); - world.playSound(null, entity.getPosition(), SoundEvents.ENTITY_GENERIC_EXTINGUISH_FIRE, - SoundCategory.NEUTRAL, 0.7F, 1.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.4F); + if (entity.isBurning()) { + entity.extinguish(); + world.playSound(null, entity.getPosition(), SoundEvents.ENTITY_GENERIC_EXTINGUISH_FIRE, + SoundCategory.NEUTRAL, 0.7F, + 1.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.4F); + } } } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/receivers/MechanicalPressBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/receivers/MechanicalPressBlock.java index c89ab4b66..ed3529f17 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/receivers/MechanicalPressBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/receivers/MechanicalPressBlock.java @@ -20,6 +20,7 @@ import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.block.HorizontalBlock; import net.minecraft.entity.Entity; +import net.minecraft.entity.item.ItemEntity; import net.minecraft.item.BlockItemUseContext; import net.minecraft.state.StateContainer.Builder; import net.minecraft.tileentity.TileEntity; @@ -56,12 +57,12 @@ public class MechanicalPressBlock extends HorizontalKineticBlock return; if (worldIn.isBlockPowered(pos)) { - if (!te.finished && !te.running) + if (!te.finished && !te.running && te.getSpeed() != 0) te.start(false); } else { te.finished = false; } - + } @Override @@ -92,17 +93,9 @@ public class MechanicalPressBlock extends HorizontalKineticBlock return true; } - public static class Head extends HorizontalBlock implements IRenderUtilityBlock { - - public Head() { - super(Properties.from(Blocks.AIR)); - } - - @Override - protected void fillStateContainer(Builder builder) { - builder.add(HORIZONTAL_FACING); - super.fillStateContainer(builder); - } + @Override + public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) { + onAttachmentPlaced(worldIn, pos, state); } @Override @@ -110,6 +103,14 @@ public class MechanicalPressBlock extends HorizontalKineticBlock return Arrays.asList(te.getPos().up(2)); } + @Override + public void onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) { + onAttachmentRemoved(worldIn, pos, state); + if (state.hasTileEntity() && state.getBlock() != newState.getBlock()) { + worldIn.removeTileEntity(pos); + } + } + @Override public Optional getValidBeltPositionFor(IWorld world, BlockPos pos, BlockState state) { BlockState blockState = world.getBlockState(pos.down(2)); @@ -122,11 +123,15 @@ public class MechanicalPressBlock extends HorizontalKineticBlock public boolean handleEntity(BeltTileEntity te, Entity entity, BeltAttachmentState state) { MechanicalPressTileEntity pressTe = (MechanicalPressTileEntity) te.getWorld() .getTileEntity(state.attachmentPos); - + // Not powered if (pressTe == null || pressTe.getSpeed() == 0) return false; + // Not an Item + if (!(entity instanceof ItemEntity)) + return false; + // Running if (pressTe.running) { double distanceTo = entity.getPositionVec().distanceTo(VecHelper.getCenterOf(te.getPos())); @@ -138,26 +143,43 @@ public class MechanicalPressBlock extends HorizontalKineticBlock } return false; } - + // Start process if (state.processingEntity != entity) { state.processingEntity = entity; - state.processingDuration = 1; - pressTe.start(true); + if (!pressTe.getRecipe((ItemEntity) entity).isPresent()) { + state.processingDuration = -1; + } else { + state.processingDuration = 1; + pressTe.start(true); + } return false; } - + // Already processed if (state.processingDuration == -1) return false; - + // Just Finished if (pressTe.finished) { state.processingDuration = -1; return false; } - + return false; } + public static class Head extends HorizontalBlock implements IRenderUtilityBlock { + + public Head() { + super(Properties.from(Blocks.AIR)); + } + + @Override + protected void fillStateContainer(Builder builder) { + builder.add(HORIZONTAL_FACING); + super.fillStateContainer(builder); + } + } + } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/receivers/MechanicalPressTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/receivers/MechanicalPressTileEntity.java index 0d368ff76..c4f5d0918 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/receivers/MechanicalPressTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/receivers/MechanicalPressTileEntity.java @@ -4,15 +4,21 @@ import java.util.Optional; import com.simibubi.create.AllRecipes; import com.simibubi.create.AllTileEntities; +import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.modules.contraptions.base.KineticTileEntity; import com.simibubi.create.modules.logistics.InWorldProcessing; import net.minecraft.entity.Entity; import net.minecraft.entity.item.ItemEntity; import net.minecraft.nbt.CompoundNBT; +import net.minecraft.particles.ItemParticleData; +import net.minecraft.particles.ParticleTypes; import net.minecraft.tileentity.ITickableTileEntity; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvents; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; import net.minecraftforge.items.ItemStackHandler; import net.minecraftforge.items.wrapper.RecipeWrapper; @@ -59,11 +65,12 @@ public class MechanicalPressTileEntity extends KineticTileEntity implements ITic public float getRenderedHeadOffset(float partialTicks) { if (running) { - if (runningTicks < 50) { - return MathHelper.clamp((runningTicks - 1 + partialTicks) / 20f, 0, beltMode ? 1 + 3 / 16f : 1); + if (runningTicks < 40) { + float num = (runningTicks - 1 + partialTicks) / 30f; + return MathHelper.clamp(num * num * num, 0, beltMode ? 1 + 3 / 16f : 1); } - if (runningTicks >= 50) { - return MathHelper.clamp(((100 - runningTicks) + 1 - partialTicks) / 20f, 0, beltMode ? 1 + 3 / 16f : 1); + if (runningTicks >= 40) { + return MathHelper.clamp(((60 - runningTicks) + 1 - partialTicks) / 20f, 0, beltMode ? 1 + 3 / 16f : 1); } } return 0; @@ -81,21 +88,34 @@ public class MechanicalPressTileEntity extends KineticTileEntity implements ITic if (!running) return; - if (!world.isRemote && runningTicks > 100) { - + if (runningTicks == 30) { AxisAlignedBB bb = new AxisAlignedBB(pos.down(beltMode ? 2 : 1)); for (Entity entity : world.getEntitiesWithinAABBExcludingEntity(null, bb)) { if (!(entity instanceof ItemEntity)) continue; - ItemEntity itemEntity = (ItemEntity) entity; - pressingInv.setInventorySlotContents(0, itemEntity.getItem()); - Optional recipe = world.getRecipeManager().getRecipe(AllRecipes.Types.PRESSING, - pressingInv, world); - if (recipe.isPresent()) - InWorldProcessing.applyRecipeOn(itemEntity, recipe.get()); - } + if (world.isRemote) { + for (int i = 0; i < 20; i++) { + Vec3d motion = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .25f).mul(1, 0, 1); + world.addParticle(new ItemParticleData(ParticleTypes.ITEM, itemEntity.getItem()), entity.posX, + entity.posY, entity.posZ, motion.x, motion.y, motion.z); + } + } + + if (!world.isRemote) { + Optional recipe = getRecipe(itemEntity); + if (recipe.isPresent()) + InWorldProcessing.applyRecipeOn(itemEntity, recipe.get()); + } + } + if (!world.isRemote) { + world.playSound(null, getPos(), SoundEvents.ENTITY_ITEM_BREAK, SoundCategory.BLOCKS, .5f, 1f); + world.playSound(null, getPos(), SoundEvents.BLOCK_ANVIL_LAND, SoundCategory.BLOCKS, .125f, 1f); + } + } + + if (!world.isRemote && runningTicks > 60) { finished = true; if (!beltMode) finished = world.isBlockPowered(pos); @@ -107,4 +127,11 @@ public class MechanicalPressTileEntity extends KineticTileEntity implements ITic runningTicks++; } + public Optional getRecipe(ItemEntity itemEntity) { + pressingInv.setInventorySlotContents(0, itemEntity.getItem()); + Optional recipe = world.getRecipeManager().getRecipe(AllRecipes.Types.PRESSING, pressingInv, + world); + return recipe; + } + } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/AllBeltAttachments.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/AllBeltAttachments.java index cb9336693..80e7e331e 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/AllBeltAttachments.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/AllBeltAttachments.java @@ -6,6 +6,7 @@ import java.util.Optional; import java.util.function.Consumer; import com.simibubi.create.AllBlocks; +import com.simibubi.create.Create; import net.minecraft.block.BlockState; import net.minecraft.entity.Entity; @@ -76,9 +77,11 @@ public enum AllBeltAttachments { public static class Tracker { public List attachments; + private BeltTileEntity te; - public Tracker() { + public Tracker(BeltTileEntity te) { attachments = new ArrayList<>(0); + this.te = te; } public void findAttachments(BeltTileEntity belt) { @@ -103,8 +106,13 @@ public enum AllBeltAttachments { public BeltAttachmentState addAttachment(IWorld world, BlockPos pos) { BlockState state = world.getBlockState(pos); removeAttachment(pos); + if (!(state.getBlock() instanceof IBeltAttachment)) { + Create.logger.warn("Missing belt attachment for Belt at " + pos.toString()); + return null; + } BeltAttachmentState newAttachmentState = new BeltAttachmentState((IBeltAttachment) state.getBlock(), pos); attachments.add(newAttachmentState); + te.markDirty(); return newAttachmentState; } @@ -115,6 +123,7 @@ public enum AllBeltAttachments { toRemove = atState; if (toRemove != null) attachments.remove(toRemove); + te.markDirty(); } public void forEachAttachment(Consumer consumer) { @@ -131,6 +140,8 @@ public enum AllBeltAttachments { CompoundNBT stateNBT = (CompoundNBT) data; BlockPos attachmentPos = NBTUtil.readBlockPos(stateNBT.getCompound("Position")); BeltAttachmentState atState = addAttachment(belt.getWorld(), attachmentPos); + if (atState == null) + continue; atState.processingDuration = stateNBT.getInt("Duration"); } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntity.java index 3d72955aa..3251ab5a1 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/relays/belt/BeltTileEntity.java @@ -66,7 +66,7 @@ public class BeltTileEntity extends KineticTileEntity implements ITickableTileEn public BeltTileEntity() { super(AllTileEntities.BELT.type); controller = BlockPos.ZERO; - attachmentTracker = new Tracker(); + attachmentTracker = new Tracker(this); color = -1; } @@ -187,15 +187,6 @@ public class BeltTileEntity extends KineticTileEntity implements ITickableTileEn return; } - if (entityIn instanceof ItemEntity) { - if (speed == 0) { - ((ItemEntity) entityIn).setAgeToCreativeDespawnTime(); - } else { - if (((ItemEntity) entityIn).getAge() > 0) - ((ItemEntity) entityIn).setNoDespawn(); - } - } - if (speed == 0) return; @@ -227,7 +218,8 @@ public class BeltTileEntity extends KineticTileEntity implements ITickableTileEn Vec3d movement = new Vec3d(movementDirection.getDirectionVec()).scale(movementSpeed); double diffCenter = axis == Axis.Z ? (pos.getX() + .5f - entityIn.posX) : (pos.getZ() + .5f - entityIn.posZ); - if (Math.abs(diffCenter) > 48 / 64f) + float maxDiffCenter = (entityIn instanceof ItemEntity)? 32 / 64f : 48 / 64f; + if (Math.abs(diffCenter) > maxDiffCenter) return; Part part = blockState.get(BeltBlock.PART); @@ -257,7 +249,7 @@ public class BeltTileEntity extends KineticTileEntity implements ITickableTileEn entityIn.stepHeight = 1; if (Math.abs(movementSpeed) < .5f) { - Vec3d checkDistance = movement.scale(2f).add(movement.normalize().scale(.5)); + Vec3d checkDistance = movement.scale(2f).add(movement.normalize()); AxisAlignedBB bb = entityIn.getBoundingBox(); AxisAlignedBB checkBB = new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); if (!world diff --git a/src/main/java/com/simibubi/create/modules/logistics/FrequencyHandler.java b/src/main/java/com/simibubi/create/modules/logistics/FrequencyHandler.java index febbde892..7f06a89dd 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/FrequencyHandler.java +++ b/src/main/java/com/simibubi/create/modules/logistics/FrequencyHandler.java @@ -32,7 +32,7 @@ public class FrequencyHandler { } public ItemStack getStack() { - return stack; + return stack.copy(); } @Override diff --git a/src/main/java/com/simibubi/create/modules/logistics/InWorldProcessing.java b/src/main/java/com/simibubi/create/modules/logistics/InWorldProcessing.java index eb6dfa91d..0da160b30 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/InWorldProcessing.java +++ b/src/main/java/com/simibubi/create/modules/logistics/InWorldProcessing.java @@ -1,5 +1,6 @@ package com.simibubi.create.modules.logistics; +import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -10,7 +11,6 @@ import com.simibubi.create.modules.contraptions.receivers.SplashingRecipe; import net.minecraft.entity.item.ItemEntity; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; import net.minecraft.item.crafting.BlastingRecipe; import net.minecraft.item.crafting.FurnaceRecipe; import net.minecraft.item.crafting.IRecipe; @@ -21,6 +21,7 @@ import net.minecraft.tileentity.BlastFurnaceTileEntity; import net.minecraft.tileentity.FurnaceTileEntity; import net.minecraft.tileentity.SmokerTileEntity; import net.minecraft.world.World; +import net.minecraftforge.items.ItemHandlerHelper; import net.minecraftforge.items.ItemStackHandler; import net.minecraftforge.items.wrapper.RecipeWrapper; @@ -111,7 +112,7 @@ public class InWorldProcessing { } } - entity.setItem(new ItemStack(Items.GUNPOWDER, entity.getItem().getCount())); + entity.remove(); return; } @@ -144,10 +145,38 @@ public class InWorldProcessing { } public static void applyRecipeOn(ItemEntity entity, IRecipe recipe) { - ItemStack out = recipe.getRecipeOutput().copy(); - List stacks = ItemHelper.multipliedOutput(entity.getItem(), out); - if (stacks.isEmpty()) + List stacks; + + if (recipe instanceof SplashingRecipe) { + stacks = new ArrayList<>(); + for (int i = 0; i < entity.getItem().getCount(); i++) { + for (ItemStack stack : ((SplashingRecipe) recipe).rollResults()) { + for (ItemStack previouslyRolled : stacks) { + if (stack.isEmpty()) + continue; + if (!ItemHandlerHelper.canItemStacksStack(stack, previouslyRolled)) + continue; + int amount = Math.min(previouslyRolled.getMaxStackSize() - previouslyRolled.getCount(), + stack.getCount()); + previouslyRolled.grow(amount); + stack.shrink(amount); + } + + if (stack.isEmpty()) + continue; + + stacks.add(stack); + } + } + } else { + ItemStack out = recipe.getRecipeOutput().copy(); + stacks = ItemHelper.multipliedOutput(entity.getItem(), out); + } + + if (stacks.isEmpty()) { + entity.remove(); return; + } entity.setItem(stacks.remove(0)); for (ItemStack additional : stacks) entity.world.addEntity(new ItemEntity(entity.world, entity.posX, entity.posY, entity.posZ, additional)); diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/BeltFunnelTileEntity.java b/src/main/java/com/simibubi/create/modules/logistics/block/BeltFunnelTileEntity.java index 3d597e5c4..dbc55795f 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/BeltFunnelTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/BeltFunnelTileEntity.java @@ -10,6 +10,8 @@ import net.minecraft.particles.ItemParticleData; import net.minecraft.particles.ParticleTypes; import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.tileentity.ITickableTileEntity; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvents; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3i; import net.minecraftforge.common.util.LazyOptional; @@ -92,8 +94,10 @@ public class BeltFunnelTileEntity extends SyncedTileEntity implements ITickableT for (int slot = 0; slot < inv.getSlots(); slot++) { stack = inv.insertItem(slot, stack, world.isRemote); if (stack.isEmpty()) { - if (!world.isRemote) + if (!world.isRemote) { entity.remove(); + world.playSound(null, pos, SoundEvents.ENTITY_GENERIC_EAT, SoundCategory.BLOCKS, .125f, 1f); + } else { Vec3i directionVec = getBlockState().get(BlockStateProperties.HORIZONTAL_FACING).getDirectionVec(); float xSpeed = directionVec.getX() * 1/8f; diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/EntityDetectorBlock.java b/src/main/java/com/simibubi/create/modules/logistics/block/EntityDetectorBlock.java index c10fde58c..d6b9e8d7f 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/EntityDetectorBlock.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/EntityDetectorBlock.java @@ -179,6 +179,9 @@ public class EntityDetectorBlock extends HorizontalBlock @Override public boolean handleEntity(BeltTileEntity te, Entity entity, BeltAttachmentState state) { + if (te.getWorld().isRemote) + return false; + if (state.processingEntity != entity) { state.processingEntity = entity; state.processingDuration = 0; @@ -207,7 +210,7 @@ public class EntityDetectorBlock extends HorizontalBlock state.processingDuration = -1; world.setBlockState(state.attachmentPos, blockState.with(POWERED, true)); - world.getPendingBlockTicks().scheduleTick(state.attachmentPos, this, 4); + world.getPendingBlockTicks().scheduleTick(state.attachmentPos, this, 6); world.notifyNeighborsOfStateChange(state.attachmentPos, this); return false; @@ -215,7 +218,7 @@ public class EntityDetectorBlock extends HorizontalBlock @Override public void tick(BlockState state, World worldIn, BlockPos pos, Random random) { - worldIn.setBlockState(pos, state.with(POWERED, false)); + worldIn.setBlockState(pos, state.with(POWERED, false), 2); worldIn.notifyNeighborsOfStateChange(pos, this); } diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/EntityDetectorTileEntity.java b/src/main/java/com/simibubi/create/modules/logistics/block/EntityDetectorTileEntity.java index d09902dc2..5866141b6 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/EntityDetectorTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/EntityDetectorTileEntity.java @@ -29,13 +29,14 @@ public class EntityDetectorTileEntity extends SyncedTileEntity implements IHaveF @Override public void setFilter(ItemStack stack) { - filter = stack; + filter = stack.copy(); + markDirty(); sendData(); } @Override public ItemStack getFilter() { - return filter; + return filter.copy(); } } diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/ExtractorBlock.java b/src/main/java/com/simibubi/create/modules/logistics/block/ExtractorBlock.java index 47e5aa606..18635e0b7 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/ExtractorBlock.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/ExtractorBlock.java @@ -16,8 +16,8 @@ import net.minecraft.state.StateContainer.Builder; import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; -import net.minecraft.util.Hand; import net.minecraft.util.Direction.Axis; +import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.Vec3d; @@ -48,6 +48,11 @@ public class ExtractorBlock extends HorizontalBlock implements IBlockWithFilter super.fillStateContainer(builder); } + @Override + public boolean showsCount() { + return true; + } + @Override public boolean hasTileEntity(BlockState state) { return true; @@ -80,8 +85,6 @@ public class ExtractorBlock extends HorizontalBlock implements IBlockWithFilter @Override public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) { updateObservedInventory(state, worldIn, pos); - - cacheItemPositions(); } @Override @@ -137,8 +140,6 @@ public class ExtractorBlock extends HorizontalBlock implements IBlockWithFilter } private void cacheItemPositions() { -// if (!itemPositions.isEmpty()) -// return; itemPositions.clear(); Vec3d position = Vec3d.ZERO; diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/ExtractorTileEntity.java b/src/main/java/com/simibubi/create/modules/logistics/block/ExtractorTileEntity.java index eb960626a..4d59e6d4a 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/ExtractorTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/ExtractorTileEntity.java @@ -22,7 +22,8 @@ public class ExtractorTileEntity extends SyncedTileEntity implements IExtractor, public ExtractorTileEntity() { super(AllTileEntities.EXTRACTOR.type); - state = State.WAITING_FOR_INVENTORY; + state = State.ON_COOLDOWN; + cooldown = CreateConfig.parameters.extractorDelay.get(); inventory = LazyOptional.empty(); filter = ItemStack.EMPTY; } @@ -35,12 +36,15 @@ public class ExtractorTileEntity extends SyncedTileEntity implements IExtractor, @Override public void read(CompoundNBT compound) { filter = ItemStack.read(compound.getCompound("Filter")); + if (compound.getBoolean("Locked")) + setState(State.LOCKED); super.read(compound); } @Override public CompoundNBT write(CompoundNBT compound) { compound.put("Filter", filter.serializeNBT()); + compound.putBoolean("Locked", getState() == State.LOCKED); return super.write(compound); } @@ -52,6 +56,8 @@ public class ExtractorTileEntity extends SyncedTileEntity implements IExtractor, @Override public void tick() { if (initialize && hasWorld()) { + if (world.isBlockPowered(pos)) + state = State.LOCKED; neighborChanged(); initialize = false; } @@ -87,13 +93,15 @@ public class ExtractorTileEntity extends SyncedTileEntity implements IExtractor, @Override public void setFilter(ItemStack stack) { - filter = stack; + filter = stack.copy(); + markDirty(); sendData(); + neighborChanged(); } @Override public ItemStack getFilter() { - return filter; + return filter.copy(); } } diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/IBlockWithFilter.java b/src/main/java/com/simibubi/create/modules/logistics/block/IBlockWithFilter.java index 475443ac1..1ff2435ee 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/IBlockWithFilter.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/IBlockWithFilter.java @@ -6,6 +6,7 @@ import com.simibubi.create.foundation.utility.TessellatorHelper; import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.WorldRenderer; @@ -40,13 +41,17 @@ public interface IBlockWithFilter { return 2 / 16f; } - public default boolean handleActivatedFilterSlots(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, - Hand handIn, BlockRayTraceResult hit) { + public default boolean showsCount() { + return false; + } + + public default boolean handleActivatedFilterSlots(BlockState state, World worldIn, BlockPos pos, + PlayerEntity player, Hand handIn, BlockRayTraceResult hit) { TileEntity te = worldIn.getTileEntity(pos); if (te == null || !(te instanceof IHaveFilter)) return false; - IHaveFilter actor = (IHaveFilter) te; + Vec3d vec = new Vec3d(pos); Vec3d position = vec.add(getFilterPosition(state)); ItemStack stack = player.getHeldItem(handIn); @@ -75,13 +80,18 @@ public interface IBlockWithFilter { if (!(state.getBlock() instanceof IBlockWithFilter)) return; + TileEntity te = world.getTileEntity(pos); + if (te == null || !(te instanceof IHaveFilter)) + return; + IHaveFilter actor = (IHaveFilter) te; IBlockWithFilter filterBlock = (IBlockWithFilter) state.getBlock(); Vec3d vec = new Vec3d(pos); Vec3d position = filterBlock.getFilterPosition(state).add(vec); float scale = filterBlock.getItemHitboxScale(); - AxisAlignedBB bb = new AxisAlignedBB(position, position).grow(scale, scale / 1.25f, scale).offset(0, -scale / 16f, 0); + AxisAlignedBB bb = new AxisAlignedBB(position, position).grow(scale, scale / 1.25f, scale).offset(0, + -scale / 16f, 0); boolean contains = bb.grow(scale).contains(result.getHitVec()); TessellatorHelper.prepareForDrawing(); @@ -108,30 +118,42 @@ public interface IBlockWithFilter { if (contains) { GlStateManager.lineWidth(2); - WorldRenderer.drawBoundingBox(bufferbuilder, bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ, .5f, 1, .75f, 1f); + WorldRenderer.drawBoundingBox(bufferbuilder, bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ, .5f, 1, + .75f, 1f); } else { GlStateManager.lineWidth(2); - WorldRenderer.drawBoundingBox(bufferbuilder, bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ, .25f, .5f, .35f, 1f); + WorldRenderer.drawBoundingBox(bufferbuilder, bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ, .25f, + .5f, .35f, 1f); } - + tessellator.draw(); - + GlStateManager.popMatrix(); GlStateManager.enableTexture(); GlStateManager.depthMask(true); - + if (contains) { - float textScale = 1/128f; + float textScale = 1 / 128f; GlStateManager.translated(position.x, position.y, position.z); GlStateManager.rotated(facing.getHorizontalAngle() * (facing.getAxis() == Axis.X ? -1 : 1), 0, 1, 0); GlStateManager.scaled(textScale, -textScale, textScale); GlStateManager.translated(17.5f, -5f, -5f); GlStateManager.rotated(67.5f, 1, 0, 0); - + String text = Lang.translate("logistics.filter"); - Minecraft.getInstance().fontRenderer.drawString(text, 0, 0, 0x88FFBB); - GlStateManager.translated(0, 0, -1/4f); - Minecraft.getInstance().fontRenderer.drawString(text, 1, 1, 0x224433); + FontRenderer font = Minecraft.getInstance().fontRenderer; + font.drawString(text, 0, 0, 0x88FFBB); + GlStateManager.translated(0, 0, -1 / 4f); + font.drawString(text, 1, 1, 0x224433); + + if (filterBlock.showsCount() && !actor.getFilter().isEmpty()) { + String count = actor.getFilter().getCount() + ""; + GlStateManager.translated(-7 - font.getStringWidth(count), 10, 10 + 1 / 4f); + GlStateManager.scaled(1.5,1.5, 1.5); + font.drawString(count, 0, 0, 0xEDEDED); + GlStateManager.translated(0, 0, -1 / 4f); + font.drawString(count, 1, 1, 0x4F4F4F); + } } GlStateManager.disableBlend(); diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/IExtractor.java b/src/main/java/com/simibubi/create/modules/logistics/block/IExtractor.java index 8e15d3ef6..074ebd75f 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/IExtractor.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/IExtractor.java @@ -6,6 +6,8 @@ import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.entity.item.ItemEntity; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.ITickableTileEntity; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvents; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; @@ -28,7 +30,7 @@ public interface IExtractor extends ITickableTileEntity, IInventoryManipulator { default void tick() { if (isFrozen()) return; - + State state = getState(); if (state == State.LOCKED) @@ -36,8 +38,11 @@ public interface IExtractor extends ITickableTileEntity, IInventoryManipulator { if (state == State.ON_COOLDOWN) { int cooldown = tickCooldown(); - if (cooldown <= 0) + if (cooldown <= 0) { setState(State.RUNNING); + if (!getInventory().isPresent()) + findNewInventory(); + } return; } @@ -48,9 +53,8 @@ public interface IExtractor extends ITickableTileEntity, IInventoryManipulator { if (hasSpace && hasInventory) { toExtract = extract(true); - ItemStack filter = (this instanceof IHaveFilter) ? filter = ((IHaveFilter) this).getFilter() - : ItemStack.EMPTY; - if (!filter.isEmpty() && !ItemStack.areItemsEqual(toExtract, filter)) + ItemStack filterItem = (this instanceof IHaveFilter) ? ((IHaveFilter) this).getFilter() : ItemStack.EMPTY; + if (!filterItem.isEmpty() && !ItemStack.areItemsEqual(toExtract, filterItem)) toExtract = ItemStack.EMPTY; } @@ -84,16 +88,15 @@ public interface IExtractor extends ITickableTileEntity, IInventoryManipulator { public default void neighborChanged() { if (isFrozen()) return; - + boolean hasSpace = hasSpaceForExtracting(); boolean hasInventory = getInventory().isPresent(); ItemStack toExtract = ItemStack.EMPTY; if (hasSpace && hasInventory) { toExtract = extract(true); - ItemStack filter = (this instanceof IHaveFilter) ? filter = ((IHaveFilter) this).getFilter() - : ItemStack.EMPTY; - if (!filter.isEmpty() && !ItemStack.areItemsEqual(toExtract, filter)) + ItemStack filterItem = (this instanceof IHaveFilter) ? ((IHaveFilter) this).getFilter() : ItemStack.EMPTY; + if (!filterItem.isEmpty() && !ItemStack.areItemsEqual(toExtract, filterItem)) toExtract = ItemStack.EMPTY; } @@ -116,38 +119,64 @@ public interface IExtractor extends ITickableTileEntity, IInventoryManipulator { default ItemStack extract(boolean simulate) { IItemHandler inv = getInventory().orElse(null); ItemStack extracting = ItemStack.EMPTY; - ItemStack filter = (this instanceof IHaveFilter) ? filter = ((IHaveFilter) this).getFilter() : ItemStack.EMPTY; - int extractionCount = filter.isEmpty() ? CreateConfig.parameters.extractorAmount.get() : filter.getCount(); + ItemStack filterItem = (this instanceof IHaveFilter) ? ((IHaveFilter) this).getFilter() : ItemStack.EMPTY; + int extractionCount = filterItem.isEmpty() ? CreateConfig.parameters.extractorAmount.get() + : filterItem.getCount(); + boolean checkHasEnoughItems = !filterItem.isEmpty(); + boolean hasEnoughItems = !checkHasEnoughItems; - for (int slot = 0; slot < inv.getSlots(); slot++) { - ItemStack stack = inv.extractItem(slot, extractionCount - extracting.getCount(), true); - ItemStack compare = stack.copy(); - compare.setCount(extracting.getCount()); - if (!extracting.isEmpty() && !extracting.equals(compare, false)) - continue; + Extraction: do { + extracting = ItemStack.EMPTY; - if (extracting.isEmpty()) - extracting = stack.copy(); + for (int slot = 0; slot < inv.getSlots(); slot++) { + ItemStack stack = inv.extractItem(slot, extractionCount - extracting.getCount(), true); + ItemStack compare = stack.copy(); + + compare.setCount(filterItem.getCount()); + if (!filterItem.isEmpty() && !filterItem.equals(compare, false)) + continue; + + compare.setCount(extracting.getCount()); + if (!extracting.isEmpty() && !extracting.equals(compare, false)) + continue; + + if (extracting.isEmpty()) + extracting = stack.copy(); + else + extracting.grow(stack.getCount()); + + if (!simulate && hasEnoughItems) + inv.extractItem(slot, stack.getCount(), false); + + if (extracting.getCount() >= extractionCount) { + if (checkHasEnoughItems) { + hasEnoughItems = true; + checkHasEnoughItems = false; + continue Extraction; + } else { + break Extraction; + } + } + } + + if (checkHasEnoughItems) + checkHasEnoughItems = false; else - extracting.grow(stack.getCount()); + break Extraction; + } while (true); - if (!simulate) - inv.extractItem(slot, stack.getCount(), false); - if (extracting.getCount() >= extractionCount) - break; - } - - if (!simulate) { + if (!simulate && hasEnoughItems) { World world = getWorld(); Vec3d pos = VecHelper.getCenterOf(getPos()).add(0, -0.5f, 0); ItemEntity entityIn = new ItemEntity(world, pos.x, pos.y, pos.z, extracting); entityIn.setMotion(Vec3d.ZERO); world.addEntity(entityIn); + world.playSound(null, getPos(), SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.BLOCKS, .125f, .1f); } return extracting; } - + public static boolean isFrozen() { return CreateConfig.parameters.freezeExtractors.get(); } diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/LinkedExtractorBlock.java b/src/main/java/com/simibubi/create/modules/logistics/block/LinkedExtractorBlock.java index 40ba35221..6774d518d 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/LinkedExtractorBlock.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/LinkedExtractorBlock.java @@ -40,7 +40,7 @@ public class LinkedExtractorBlock extends ExtractorBlock implements IBlockWithFr public boolean hasTileEntity(BlockState state) { return true; } - + @Override public TileEntity createTileEntity(BlockState state, IBlockReader world) { return new LinkedExtractorTileEntity(); diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/LinkedExtractorTileEntity.java b/src/main/java/com/simibubi/create/modules/logistics/block/LinkedExtractorTileEntity.java index e65f35963..bcd28b14c 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/LinkedExtractorTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/LinkedExtractorTileEntity.java @@ -23,13 +23,20 @@ public class LinkedExtractorTileEntity extends LinkedTileEntity private ItemStack filter; private int cooldown; private LazyOptional inventory; + private boolean initialize; public LinkedExtractorTileEntity() { super(AllTileEntities.LINKED_EXTRACTOR.type); - state = State.WAITING_FOR_INVENTORY; + setState(State.ON_COOLDOWN); inventory = LazyOptional.empty(); filter = ItemStack.EMPTY; } + + @Override + public void onLoad() { + super.onLoad(); + initialize = true; + } @Override public void setSignal(boolean powered) { @@ -39,18 +46,29 @@ public class LinkedExtractorTileEntity extends LinkedTileEntity @Override public void read(CompoundNBT compound) { filter = ItemStack.read(compound.getCompound("Filter")); + if (compound.getBoolean("Locked")) + setState(State.LOCKED); super.read(compound); } @Override public CompoundNBT write(CompoundNBT compound) { compound.put("Filter", filter.serializeNBT()); + compound.putBoolean("Locked", getState() == State.LOCKED); return super.write(compound); } @Override public void tick() { + if (initialize && hasWorld()) { + if (world.isBlockPowered(pos)) + state = State.LOCKED; + neighborChanged(); + initialize = false; + } + IExtractor.super.tick(); + if (world.isRemote) return; if (receivedSignal != getBlockState().get(POWERED)) { @@ -94,13 +112,15 @@ public class LinkedExtractorTileEntity extends LinkedTileEntity @Override public void setFilter(ItemStack stack) { - filter = stack; + filter = stack.copy(); + markDirty(); sendData(); + neighborChanged(); } @Override public ItemStack getFilter() { - return filter; + return filter.copy(); } } diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/RedstoneBridgeBlock.java b/src/main/java/com/simibubi/create/modules/logistics/block/RedstoneBridgeBlock.java index 95ce3f1a2..a3fa142de 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/RedstoneBridgeBlock.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/RedstoneBridgeBlock.java @@ -64,25 +64,24 @@ public class RedstoneBridgeBlock extends ProperDirectionalBlock implements IBloc updateTransmittedSignal(state, worldIn, pos, blockFacing); } - + @Override public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) { updateTransmittedSignal(state, worldIn, pos, state.get(FACING)); } - + private void updateTransmittedSignal(BlockState state, World worldIn, BlockPos pos, Direction blockFacing) { if (worldIn.isRemote) return; if (state.get(RECEIVER)) return; - - boolean shouldPower = worldIn.getWorld().isBlockPowered(pos.offset(blockFacing.getOpposite())) - || worldIn.getWorld().isBlockPowered(pos); + + boolean shouldPower = worldIn.getWorld().isBlockPowered(pos); boolean previouslyPowered = state.get(POWERED); - + if (previouslyPowered != shouldPower) { worldIn.setBlockState(pos, state.cycle(POWERED), 2); - + RedstoneBridgeTileEntity te = (RedstoneBridgeTileEntity) worldIn.getTileEntity(pos); if (te == null) return; @@ -133,7 +132,7 @@ public class RedstoneBridgeBlock extends ProperDirectionalBlock implements IBloc RedstoneBridgeTileEntity te = (RedstoneBridgeTileEntity) worldIn.getTileEntity(pos); if (te == null) return false; - + if (!worldIn.isRemote) { Boolean wasReceiver = state.get(RECEIVER); boolean blockPowered = worldIn.isBlockPowered(pos); @@ -151,7 +150,7 @@ public class RedstoneBridgeBlock extends ProperDirectionalBlock implements IBloc @Override public boolean canConnectRedstone(BlockState state, IBlockReader world, BlockPos pos, Direction side) { - return state.get(FACING) == Direction.UP; + return state.get(FACING) == Direction.UP && side != null; } @Override @@ -233,7 +232,7 @@ public class RedstoneBridgeBlock extends ProperDirectionalBlock implements IBloc Direction facing = state.get(FACING); return itemPositions.get(facing.getIndex()); } - + @Override public Direction getFrequencyItemFacing(BlockState state) { return state.get(FACING); diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/diodes/PulseRepeaterBlock.java b/src/main/java/com/simibubi/create/modules/logistics/block/diodes/PulseRepeaterBlock.java index a7b9e6f0a..bb7531914 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/diodes/PulseRepeaterBlock.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/diodes/PulseRepeaterBlock.java @@ -42,7 +42,7 @@ public class PulseRepeaterBlock extends RedstoneDiodeBlock { boolean shouldPower = shouldBePowered(worldIn, pos, state); if (pulsing) { - worldIn.setBlockState(pos, state.with(POWERED, true).with(PULSING, false), 2); + worldIn.setBlockState(pos, state.with(POWERED, shouldPower).with(PULSING, false), 2); } else if (powered && !shouldPower) { worldIn.setBlockState(pos, state.with(POWERED, false).with(PULSING, false), 2); } else if (!powered) { diff --git a/src/main/resources/assets/create/lang/en_us.json b/src/main/resources/assets/create/lang/en_us.json index 221ab9de8..f8a41473f 100644 --- a/src/main/resources/assets/create/lang/en_us.json +++ b/src/main/resources/assets/create/lang/en_us.json @@ -546,7 +546,7 @@ "block.create.stockswitch.tooltip.action1": "Opens the _Configuration_ _Interface_", "block.create.redstone_bridge.tooltip": "REDSTONE LINK", - "block.create.redstone_bridge.tooltip.summary": "Endpoints for _Wireless_ _Redstone_ connections. Can be assigned _Frequencies_ using any item. Signal can travel distances up to _128m_", + "block.create.redstone_bridge.tooltip.summary": "Endpoints for _Wireless_ _Redstone_ connections. Can be assigned _Frequencies_ using any item. Signal range is limited, but reasonably far.", "block.create.redstone_bridge.tooltip.condition1": "When Powered", "block.create.redstone_bridge.tooltip.behaviour1": "Receiving Links of the same _Frequency_ will provide a Redstone signal.", "block.create.redstone_bridge.tooltip.control1": "When R-Clicked with an Item", diff --git a/src/main/resources/assets/create/models/item/extractor.json b/src/main/resources/assets/create/models/item/extractor.json index 4738b69ac..0a39d9155 100644 --- a/src/main/resources/assets/create/models/item/extractor.json +++ b/src/main/resources/assets/create/models/item/extractor.json @@ -13,7 +13,8 @@ } }, "textures": { - "extractor": "create:block/extractor" + "extractor": "create:block/extractor", + "particle": "create:block/extractor" }, "elements": [ { diff --git a/src/main/resources/assets/create/models/item/linked_extractor.json b/src/main/resources/assets/create/models/item/linked_extractor.json index 119fdddc3..7ad05603f 100644 --- a/src/main/resources/assets/create/models/item/linked_extractor.json +++ b/src/main/resources/assets/create/models/item/linked_extractor.json @@ -14,7 +14,8 @@ }, "textures": { "redstone_antenna": "create:block/redstone_antenna", - "extractor": "create:block/extractor" + "extractor": "create:block/extractor", + "particle": "create:block/extractor" }, "elements": [ { diff --git a/src/main/resources/assets/create/models/item/mechanical_press.json b/src/main/resources/assets/create/models/item/mechanical_press.json index 0a69efb81..20baae057 100644 --- a/src/main/resources/assets/create/models/item/mechanical_press.json +++ b/src/main/resources/assets/create/models/item/mechanical_press.json @@ -7,7 +7,8 @@ "mechanical_press_pole": "create:block/mechanical_press_pole", "gearbox": "create:block/gearbox", "mechanical_press_top": "create:block/mechanical_press_top", - "mechanical_press_bottom": "create:block/mechanical_press_bottom" + "mechanical_press_bottom": "create:block/mechanical_press_bottom", + "particle": "create:block/gearbox_top" }, "display": { "gui": { diff --git a/src/main/resources/assets/create/textures/gui/recipes3.png b/src/main/resources/assets/create/textures/gui/recipes3.png new file mode 100644 index 0000000000000000000000000000000000000000..c299dda80a54b94ea3b31e8d7248f056c4bf1399 GIT binary patch literal 15269 zcmeIZ=UY?F7d9G-5CnlxBq$I_s7gngL~3Xc0tzZ1y$MJY5J;p;OC$mjrASlY5tZJh z*C&MDlopDRP^9-3%E9+{&WH0qydU zm;eA&m$y^^Al>DUmG9SQe_MVgx|)FEex5&<4O$lsLk$3+EP?UV{>o*W;f0=s9{|AI z{`aEl^#1Au0I1y6M`%1k+N@9ORtTE?lKeBpnE$E9@#iLL< z8ajk@%N70%R(@8^Ha#HBK1v)Sab!Hy!^j2|IHSoKjn@o;_VuOvy4M&tCQr z8Fos7Opmr&detuczWgIFa7JyyC?T`-Z&&BPy~8M$V>1>bbz$en17TS&roR{Y9QV4Y z`bqOfdNr%mwT|w8`xzQm|Me_?<)Hg^$=ak!Cdn&HjwS4<9a86s54o!=GPQK_XWaNp z^^3OcndGzU0UdDaqp{}mU%bHcos{O@^Sd4E-kZagKCvoO_6K7ECu7F?vXoVF&}Qgi zeIPMb?R0K;^}Qmji`>c*xEFxoT4vl}DZ*y|2HM)uN7QDzP|s{iy@Z@wDyx;YJN z`fI(V12)%--Y2)H!Dtlii-HPeMA#r@iSncK-i8cil{Jm5t$y`rVjy&s0l=2_Q|;n- zfL8_;ciB_Zw0yeyG}HXBfz&$TtbX>)YX|EBck~@GR5Z*wsLc1LyH10^ZiZb@GRq9_ z1qaTSH`UsxyytdxG2mYaK3lcT4yF)ivw}%CXP3L3en^B~4rbu|uBsE3OQSj2e!{%) z;|1_g_LMB5_NRK0%cQw>ISzLQgs311T716mSj*GWK0W{G`$4=gTF_1rZR(A(D^0P# zQEj*pP*dM`U^J8eQ^E~J5*Ukjfo$vAd%!NK zu;ZRC8pu&ge^O+ztgydf`+!j%IspySQvq8ToB~=m{Y!!}$r1TIiN(zbLrO{W`AJta zmyOc#eo5Fp)S4qirS{@%Q?1_DO1cSuV$Tv}u2@~rksn>_phbHi8Ccu1wk+_`myPN@ zaP88_tnb9N3<64LZ-r%xp8<0q@7NNN<}=-$9k=K)eA{T<-eUNp)5ijy5IDV0*&SEP ze>5O4P9x`a^64k*nkip{!2Vg)WRA&1*E#%6?+ojZ+x@DPH6t)u)Uo+!KCWH}IG^z0 z)NZC6Av;@^c3#cWsqHIqO!R#wy2Cx~>CoOQNe*~@YDBrDU5&Bcj*n^_GvtzUVpC&GI4`&3JW1m6T(4M>a#@IW)*V$>7rh8)y3v=x3$?M}iP?be5&I>5Jy-Q!TL`v(HBz-0 z2k!z~jD!W;3!4CYhhv{Jm1y1Xsb}5bPZC!q@k?Vk65rCZIs=C+`JJ*mxU61i)CjT` z*Q6o4S`M2I8?BG^l@8uM;0+lXw0SMSC6Hhj)FqVi((|7uH^sSt;pG&*+qkwbgddyK z>(l$^{Y*0}Q6`s*g@G^aEp|9rHj zNuH=etFg>Zy7Y<=v%_4z{jU?a_J7}2*Cn$eIe^f2e*Mb%lpr(lo#58;g(1mQoZIH$ zIT@LldarUlUd!UA@ff$+l2wZPxUOG_Hh@MZ0l?~L$-)vREH$17@8GbZ$+=x(3?HH{ z>7#MaPYF3;fHEj1FoVWm%Fs{ZV+A~g|n5U_q7qHJ;<>qBSw+dthmc8>|K-u z64a$S_j#~BI)L#I!!UekizvU&=~gKvF1C4g84k%gagr-JkgitsC;>5U8oIW<1*tI9 z&ij10^Uh$b>dSz=t!<7?@6+mVz7jwQ&x5FnW z#SvK0+j%1Q{s`_wQn)`%wL*8cPtR&NqgP8p_X|W)tOF+ChpY%}rCP|KnT<`<+;q5l z2tr!F$B+O?80{=3$6D)8Wsew^ifa%xU*BOc%}a1(NUO;H*FMNbHW|2VTI>S3$8k*W zkVW%b;IlVV+WSx5pVBfuWoO7?dJ=s&ZLbDEgDi!j_K;+WfM2wD9Bb;^A7=Bz`|50UsQEe_P*i#yg zFzEE;Q~)V&T7udA*_P1wx|8?o52Zk^+C{t-6Xq(V(*O>m7C1Gl7xyFfs%s~7CiCuyLIS&GqyqCm=s`t& zVit&g@>$0F-OH8kztd$LS`OPBYdWavACY)-U!k=)>*fh{12bsLy5U^mHIq$u`J;bs zDtZ#o6~c=YfK|jpnt2&; z_?G3%#C5Z#oez=@iT)xyoz3;mw1StGPDKAO1BC0{4w%pYKWDIoxPmM=fQJamSDvg1YC^xY@T8Kbgi*aA}9G z(cOM$qe_i@JcPv>TSKqrHgWFbk7EtbHXbw9w2d!?F!-TMJCvF2(*PF2gc9+Jo4a4I zX@h38(yY%u-$alHVC7B2CA-(pq8@}95?>7lOf#V|drdj%Rgl+jn1er<`O417FHv4J zI)|_B{IZOh&-HL<^^@*iEv_#%xLd6c-xoDooXu@M0Ogi*TQ;p2*eQUj%Cwq<5;UWC z1wuUp_?jS|A7MilInAn2fZU_Y_wLTp_TDZU2%6cgK5*3=dPAq-sz02ybdMGfiCQY-@QSV z8pX7=T&>E`+X=%p^yZ`Vy9S>mh?DM<+bdO7&~U0$T>!zmuX>%bIykU$AW}cQncahk z!3qa*%W${n90*a{^UI#E@|%qRn@#Dc`}&-d+YAS(7E<%j3LT7bCet$up*Z~FdlREP ziS&MatXmCk6E<28OkzkCdc#*Zsh9w_*XV|NBtknJhzJ(x#ryrxN|5#}QY*?TH`KjT zL2NAN!b`a_r)_yMpPC9+3TOSe;zAiVw!e^ODRR^sZxXfmnDIurhU~s1cUWb7WxQKF zVoF&mj3b~OPF+eTLcKnTbX#hI})v!`$*^7k~iuoZKjTt`)AL~Sx%PTpk> z!FtX}M0TIX5G8du_cpZ`o%Ns(zHou5jP1&CNE|((Dnz`X^JBI&;Yuai{@Q<6rK)6- zxi5s5gC-0UrNTWU4Nvr=h==mS3Ys29No2WAPy||&!uGTSuW;~F^<)K5q|tHY3z6a#f1bVb!I7I=Wd1xcCDvR28t4MymmE;5>fmVbX!ts% z;)GMy3Z;uh(T4BYG^uMcXpj}T3kF#%EZgPEig0_d*9SBAn>J@u`jzfi=Tn6kUkoLL zR^CpJ^|7|h587*wQ(tks6DmmC04ZG#q`hvRHblSLPh-plvyBQUs{h)6)kxfCH#!`+ zB7jsG?HC4IoNsmXSNT)CB11@yP3Z4Vw_NPj-c~i`-S4byT^)+ApEa;*O8*?}iR|~w zk_$`GQf{aQnx=>R#2w0?7TJ{exQqDSj^+KDlu9huUH=(Kk*og-wBx_Dmu=-Xr&&qg zN2^F|YEJ{h-q>$X@N;OzL1ZYBxFI6AC;RSINFjwg)fb%V2s!;tUth8TEnDi{R-$rT zKt&zH>k|lgv!Kjfrn?;v10WdWd!ia-SlS$)wmbRjb&A$q|D`G?(r-RBl|^r;K}Mx@ z?Fm@aHY}69^~V5AeshF^lJJzXIf=YeM0j+nx(QAeTb(MpwjrmhWxni_HO8&-jMtMf;A&@Yl( z;`YnzRbCetxur*-7WLjRkn4?VUhtzN=OR2K>y@nP5I6j>;n;U z0KJy+_x3xuBpGFVIPy+O+TXw(5_h%V_(>dHe%!p18GP0KM6aYZlX68L@v%fj~oABTTKjiN-y zyCt|SofMjjM?&XwZPta~%pm%Hfa$A~wtbsds|aVqgghjCsN7${?S07h z|1ct2#geh+Iq!VxY}Gv0MLw!u&3Y>hW*6LD?ZEO`{OU}t>N&rDc+anIB6g3@wT=c8gr5x(jh8P zcd)AVZYkvh_8}o*Dve%2Q*e!4o^;cMoxPUNI2+6O&>=R>cr5M*`m2KS1}HbcaI@gA zxfSATw({W78xH6{{5nzP!})js`7UoKOfh<*4Ec7-u_QH)#2cN-z& z;5;Gg>!bz6{{v4PR5gK$uqhR*#mhZ&D)TK?CGDar~Vi{ z`mP)H>l!|3oF6VD6N{(zF^!inY2Nv8$u?bCdGu_pJ$Sj5xK}^j+-)63rS3!=h_(BW3PaMu_SqljjRiV>W6EiX#~yQNpZ~=fBLh^A<=uqa zT56oEXzAuYqG@wwhp1o*W$EFdG$;SgqpFC9+u-x;ixw_|-^QND>hIHrhK)2rUpY}@4}_iR+2k&DM%a&j_|Lr-2(k7*13N^o6aa`>%r zFirAyNG!_c-CeCi7i9zo$+>SJ{YiS^ICg#2^X&xVnM*D!kZfdG$@%SFkNT~=QAmyx zGbW=NDr&-JMp>MKuQq$AB_yQ!N}c;%C#{Q2dScMK2qP5Yr)*2e;^2>OX4zFer*6$V zm1@-l$+wZtw#h=$5+xPHKlund-Kn|E&ETbrbT1axczQW*;TW6+^OiX_{Gs$G){j2* z%v8u5OGpIX*~EH4$$G$?x5j&CO~t6oI)xAc8XuswEZF%m31xZ~^~m-nw`Z}bV(z0@ zS?YD+TN|V@BvPj%k=MoUPs+)MloJx>tPhAX$Ln+a)V}}T9a53^w z%1zRE)Mwz)bt*c0G~oi7T)o?=&5X}fQc;y4WsZE7+F@y700Fe*xe2YJqIcV=_QCp{_0+K>gq{28P5K5qW0BM97MwD*U#IYve&4^Rc+u9)zlo>Y=*tTjk=&S<8G%`^B+U+ zmF~sIC_9q(>{-|RDy+Ofw;5X_J%!qhs@$0C$`r}L!1o|1+pX7JFByz>|0wN*UQ&$d z4VRQ~YFuZm>fYzBo3lsw;)F@P_f`WFM-h+7qvuFuh_nv=(HWuz%&LF(VDbNr4W=;F z#E>s5!r3xVxrEjq*A#s3O5V~l?^b#eF&2lT|BgLWljMg)&U1q3zU^yMz&A4#fEk9z=?5t;_QfIK|$pU(d@OgF-2e13$6{nkBpv`Zw>rhTo zWFeGlUHI;aZpU7>D1TX%nz1pK>~R|Cp3L11QdaU3e_x`8zeQ?^A5TBXykzmGmD^g+ zPX`P^{tMq?Gqe0hE&d0mmk+Lom&vqP1M8nNlmb_inW((~HN=L;I&4jLy7F)-v-09& zItX!Agn7`&zbQ78XCI|CNDL@GGFrNJRJk-ApZ;JTB$NHD{?+ z?8nqC^klBM(@*0=xflDau+GG8pR6%ikp+u-7f&@<)F!Fj@(dNGB1QQ{}J z%T2RR5>-Lyox}kpp<%W;0THT`tyr}wZPt^o)*S?-Tgfhex%z$0`5Ws$T_EeQ8wj)# zNNnC7MXC{@`ok^&5+iA3?A<2ap8rQu&l=?JZ1j)}31{~OOyjukKU=L*ydwMY4&KS# zR$0?kr-z`;j%9X>D(ZSB_k?aiHBZ}Br(0`VuLu|O2%EL!Yhw;6d-LKCeeS~N#4I17 zhn+{BrYyeJKL2t08TSFI;zWdQVY$9lCFJ{swOR9Ve6uPEThZ28!0%p7sZz}8SjIu! zEpf@dNWISep7I`)ptKRzwvk&ZCr6b$Dn*(6wux`~QNZ%2%93!6koR^)==#aRNi{sj zX-%X4!|`R=p!z0c7hQ_6w>!EBOyel-Op07bMS1>}AB zb@-x!EJ(aETO7Ky_I$WhSJw6M9(vWSh%R?q%H#%kxdtVpiU%pN z9{vgR1>)PzmoR z$Qa9AQ^qVNC$*t9B_2^JLe{-;czZS&yopW^cmv+k3M=Zu-4#^G>XujQxc2RrK?nMq z!qp25_2ye_!DZpk=%(7)ALBrWZ??X?cwJ?D?CzilUjiX;kg||*9N1Xv_TX=Z z0t5ULM;5r}_*>&0<{6o=@k3s?d<#hEarMx8niK~YeoJ4uc_N5SWJeVhIVbEAaAhDa zazN&*_XT{1u(GPc)j|fG&GCxQJ(xo!_HCJKJz5YW5%V^xpHT`&mxeRUMj=4zeDZf^ zsJGGFpJ5)|`;P?dDvRwa@r)W8=5bMa5kK|ZGxl|r-Uc^(q$EdJ11f{P*La^BM1y_te`+ zU@L{jm&#WN(He~U_$g#@w-FyLielh3_^7Btmu0AafZCu3=KJPC)${mjICeFf)&|lbrmbE7n(P+ zz;3)EM%av4|Hkr)gU3>zAMoWa;@*10^PF|{`g7TZNX`uyS(!mws1DQHvFfOw#?TnO zxQ$;ag45`%X4K0?hjjUSxGnv>k+!N@<~2XrVtKymH6fB%N{-#TQH^!I^*pJj`D}gqqXmWAk1dw))sQNZjfy{iX(`LVJ#vAi zobB3D70B%#C%SVu6qMk*->&p60r)hA{~k`$288i>pq5Y+Y{_TQ45HJ=;zX$s*FT+6 z4c9(X2oquTG>a;&KIz>RnM%;pw5)0Fi&mE1#E&((d!pRNmXnH5_j%naC_i(v<0o!) z&elG=^5*m}!S#i25m1ZbcMrl3q@teU-+-|YFb!A15|F0VnSI3@ z(`pKpV=0o%o99N-cA{*Yp3++X)=&?^y`z83GV}tgXW`|_nETIs|5GK9UYk>nurH`g zsTi(IJ!3;bI$7pDR-BjpeGTg`kVv15QW*?;t&YZ;74HWca8R$9hcPQe1VlA(2U1qv zNE64*&H}6#XSRW0R(S{dNt%#3TUyTtG47_xbvuD9S05dD(b)cwr?y)j{fPb8`D_hd z7m^SU(5*FS7 zX`TtV9v)t$nFf1uA6rg?3Lno8w(W214lTmPT$iq~PNs5~B&V(fWEYVZ1$+KABo68# zpyL4zCu4_*;!9t7))WVb+JzTN3?!HnT1G5YOkdhDVQmArx4{gus0l$lK0MqmHMj?T zDgoE^?O}Wvvt^Bfm+u$Lf2Qz?sK#&iUY0{TA_WT6FoJWrQ&CYkSGq0?1|2TLV6fk5 zN(G`$7)V7>hYWo4{FES|7IjL|%s#biU@aBmEAz*?9$OxHs9E+fkzA~>Y1^ho-4B{V zdKgHc9TmC_f1|LF-t_I>W9w4a4OXSWFWQ9dCUa5a4yEX>r@?;qu*(29W6ROvH~EyA zHBy|>gB!QH8wyBxvj@-LEw4g!!0uoqh;V96*}RkoFPmwfluuzF{TMiUpTZD0C4)CY zBuALn3OgX+QHQi0O;J3KBGl!J9XimYd|P304S2{bc)cd3w%2^DHH45Du=P_l{3lch zA@cEnJ((B?DKB1WFJ6Co#reyHR*#T4{8h4oXBzFbaSbR!L2s%TbDZfA#(Ps>Q90br zJUCn(E|!21Z1Zw@_nVZ4>qg~O-&YT?;lUW%Fr{%>b4ucoqYxPu*jL<5l z`ff(QOGB=PBnh-Shm$alSV~I{?SQOn%F-0PX8-t>2Cj-LD^8lsG${`^zs@Q&FSHgm zOPNtb5s5RS3{=vR_#bH3N%5<8Sk>Z+?1NQCmgwI2!?B;GMoLfY71VgEg(hUhp^C1} z$2LOMafv%v#}hY$26bA;Q{3eBZ*Nlw5(bifxL246ABBT{m8P6c3io^5CFOKqt>)<@ z+mH84HC_tJ0)tJUNRwDXWSQPLhBmQONC{Sfz#Z*QWh*jh+Tc2LRd;Y3Lz$}|B-#h; zNcLn9IDIPo#mKw1$uU)s$&;+tJj@dgnqSs+BSKQ9rC9!82fm-ixbqJg7Ava83yHaV zRJ@dRp|!X5H2X48dEch>)gM>7VA@M+V0z~2184Rh?ubu(d2BQ5)O7nBfz9Z(l;HG%8@Axh;}BnVzU_s7g(!sVF-#*Zx$3F+aIMj6 z)7QgE@wrjw*W^#Nouz|2tHp@-@Hgy%Vr_WS-QQjM>w8Vc~zE8EWu4w^IfEJO3-`JC3 zRolX&C(8{BZzJk{IgC56LX`NdP}}2#0=N_N@vq$A1aRZ~s182fSB;~x;RN99?uo>JWI^^S zCTbzvIl|XU_kQ7Ogf1eSbmt(dH@$5i^qm!q>Wd?_`BLm4$67m{D-*OcQv6?|qDu>i zJcI#O9?RDxONl}8>(75%(+Fb!n1ZyZLeGB^!d=u3$c)^b!x=<`9NF_8cb_)dI|j8A1Mr+ zk;IOpgvk*JsLBw?hrj4VNOYauq9-SjD{VTRbO0)`%{tcs+s)>Aw+fG=>xL6NYc@XU zIXebfz5csSE?wl_KGpEkAXrgJ-qm{ctlg;@VCCq6s(LlNn1M53x0H|`xPUV;_bT^9 zEjL6ZNin1J(Vb72LECWKqka;<8p*=xRLkcPbqgV+()Q>rS&zrKC=3xl#_@J*@g)CV zJ^Zoq_s~82riQ0|J^51g;Y%(eISr4M?(}HkH3qlFMb4H&0L;EmGR2`z#?`eOZEDmf0zTgz+dbcb;x~7#UqFK)56dU#O4fF!g z3k(1An{P`U&i~?j zYK?r6i)hhm6*yu&5umm^ok%_YZKd>qOhbN&E-3TP)L8Lrrl3pm=6h|P`_lcL%l3Mj z6)Y`W&6nI)LyBTJu%}H&uun2>3~};3O~w3#>!Ex7WcVTWDj6I0$3B|&5>%kDrA9N^f>49hva1F&n>8*nAU_C%s(0DePvp)2TG-3A%pL2})*7 zL;f@H8zF+05Pu(4ez1s1%mDo8-#;JqnwcsWB-3Zbj=4hAIFA)$|9zMctkSyE4HviE z0EO&!()G=LyvuEf3j?rGO@-drxJUr<&lan{n+Vx3Lq-t6s zRpNgZv!H7F!|+_23Qcko8{StOn}^T@Mu43I{;e9H638eJXECaqeFT#w>M%JYiHn_a@1Rb-_&Y6 zdPal}$T&Hi`YZjm29cahgx&RT(6GzWtU91$g02{UyjaaU3oO{P#Z8A{Ky$1ve%v(#G*ybRg2QyjX}U0%I=83&&pLrj&($z zva%RNgdhN;7c+EKeI??snv}?f?LWs&Bnzlgv#^=zC7*!+u++}KxgF}fMPdL_SH<|d zdueY$nY^>@bF9>7!JSNP*xp6@i9Rl9#C9^z2e7;s-e*y0tgW#JA>h};y>^?L-w3K# zd!?3d;5eef4OeHjr-KH+Zd}Ra8HP^6JHuRKobb_nK( zG0bzcAZpRyS`(Ho=~u$zXZO?YA=u=m;GnOn zy-dO;5BZPYiheCHb;kzQcUA<1I2`y!Ie;zi!N| z;n&gvAENpvAt}rGL2T$v%VP#ouEgZ05~A1iDlWSujtD{jpRH2~1_j`YRNbGrdypNM zHyoY%-5|aKx~gJhXXgRNSmJ0|@Ox0sEs;(Qiw%E>(#8$v@F43XO6zgr^zr)?$KS7< z@3JUozsmIPiF!ZMtZPZ;Ow8slq?v<~p+HaRFN-PkB6K-dckeSvpggSqw{!0%=PP)! zQ@KVe)YioIT@@AYaJxw*I$MQ~wX#_Kr(xTq;q#d(sAx#)Mt-8g3&V@Ly|AI`XkUcv zOmR}=@BNzX20iiTWor{l#;+&tR!g9nK&fx-*QzfIC>hBhK>03RL!*0Ro`b|~6kZ8I z&6LdwXpbv%OQ}8)H9Ka4&rJG9MY{MGk&g3tm6t~NOuy8}o2zlZlUR)`#v&Yp!Vgmp&o1JvBwO{s+OMEOe)NnqtK*wNOFdQ=;w zP6-47#+Rnv=mFJpRQ$N+ra+f4&<^y>Z;)RWsMA(3!F(n1ciE2Ka}bXim+`mIs0!@^ zqz>(tq7>;w+j-$A$4??T)1^gkNrrp)Sks`PG*n4sd!e@B01U%#ZWcy2vL_sAb+PC< zXG%?U?!ua@Z_+zfNKDrL8vD$Tp5z$t{v!(zb}rq$AqQ;fj~stR%|2y|(Zin>b z4f$#?o}dKe)Ka_3&r()KcVN1>!>H#y%jH-3->7kBP%VB`hMbbU<=#W+hr#{`lcHYo zug%bP8&@}S7F*--XOrJA3Kktq_js?Qa|5nDEt-n{Fs?+64&=mNEs9Q)k<8LSFZ1lY z_Nxz`FM|ok^NQ@@j+V1um=#lP<7U9-xv+8s>>Y@@7HBMfsu!K8IyUy?y{}grb~`p7yN32o{F3vi=1Xi)Wx^*gNWqE)Y0gNX7X*%fWv#lKmT{S$9l?@qeq z%iT=oG4fA6^w<2XM(dMpjO_JRsVR%*Ewv?qxfhM8zGQ9i^IcPpDmxJRxt zseMG|4d|FZLteAwXK@h~S!VsoGv)U$hU$pz;#+ur#g*n_!Zm^hx zE9I_l27|c}b(*E}+mI%bfXSr4*F;*t{dF0T`u_lUn-#-tvI|qg)U5{|oAGe~q``u; zAkjH1Y(yf#vA!rm@V?MfrnjkGpm8>tP6kG;gE%cVKQ?CpN@|%A?P=YhDhyyutq@T+ z)rn#CKIl@o@s}Z94+79-+4Gyd`$yHw4~|cCq)bWDL~Ka{v=ULeUNQRU`8i%=L0B-C z_10D#G&Q%Qt|>!ud@Ssm@b}#$yQo0Wd(Yxv=Z|`+?$8fHc_C`$%+hW@??``5q2t;TV2cXh z@qosMfA5Es?gK|gty29qeSY9oeMIOusn%HBVc{eIta`(RmJr{7ZBbBWxTsPZX4H0` z#&9~?3sL}|m95zVB@ToDtEDACer14k$)pj-Nojw=IQoY8kgQtr_ce#QjJWCK|D&cK zT<4Vm-xlJ^VjNju6}*9)RW*^L%gX$WTxrRbqZ`Zgnn8i$HC6s7wr zC;Vvb`5WRL-+S!RqSP-+5~|Yl<-}~<8_!Qx>upnUX3`d;<0Lymf4mZV8+42$mN52J!Yx@F%J|?C+g*x~F9SOtQYB8tB6s4WG`qK2 z!Z;G(epJ@1c)|Fb2!nBeJNd_I>-G7K`B> zy5c~#i(o`6I2RA~k>|*F1cz{-U8XrV6ld99Sj@5q1`K8g_Ne<~*eJ{`!Fp55l2{kD z(G8oEZT3F|uQcQgYRTV!@sQ;p{C_#uw>k)!UOwPTD*^V+RHn;c*uj!Y-61g-6sn~* zwaCOHmvSCpouR>~ujP*HQSYbe!T^n>>)VbTrYJ5>obu+ z($H$T_q2d=Ny1M;PXWx&4%K4gv?{c?W_fx11~_-`4Afs>{N(}&YzF?nYK_Z^HFr3? zoBfL=bo;1>?K!Z&hn;rpnZ9$TkrFxrqTFR*G1EgZu*~W-U^19Ge)8`Lx<^Ke>k`_c z-vEsK_@&vdwu2@oL(*b(O?KBCnG7nm-Ge(0XVV3$d>cd8aZ}1m8FFEA13sImtt}Pd z&FYwdpcMpm&qB@R@&w2whRf$pup}_X&KGb8!sb~%G3i>lv?T~RDeVH)aX_+ z!|(qfm^#579yNSvC^P8o`BR&t2r%`p)8xMx@FA#0jCs|UK_x4BdpBHS2t+H!5Uv}@ zR@7Me|0$lF2)+7UXi&r zR06E*L)6K8F#C|^=6*_*vx6YpR$XmqM$ks>WjPpe`H2A5i8>w&q2YH}OWHYUO{Z(# z=ZRd(iIa_4H9;f&J(e?>H68@_Ndz^faQj#6{Z8J|)yVUcC)ww0rJ*yL>1Q74yT$Bj z$n2J|qK!^X$-3p7slQ3f8$dj$&C=a1ioM^9%?75D`b8VTDM>MJ4?3oEEBqzYdph_{ zHy~>^XnIt&3`E7F_|xE1aqZ&A4L52N1HtMSHWW>C!h4;aJcUd&jwrIO6|(`g1Qdg;iJ*alM}uJ zB|GILt~TUZrAB>alBiqrwm=M`$5${<#Dgs?=Y}(?eXuV3lRQJ;<;vfKg-?ziyqvxs z5dPm{d8<{`*+s>AdDmA@&ji2A6L}WY99N@3IZZ}pqFAsHAi53xkov*@z5Ty-_`i$b h|L+X0GHIs}2!}*!-?**d<