From 40e9a1ee41097e697a88e10df96f55fd57fc8683 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Sun, 19 Jan 2020 19:29:39 +0100 Subject: [PATCH] The Filter Brothers - Torque generators no longer show stress impact levels - Added Filters for matching outputs against a group of items / nested filters - Added Attribute Filters for matching outputs against a collection of item properties - Extraction count on Extractors and Transposers can now be adjusted through scrolling on the value box --- .../com/simibubi/create/AllContainers.java | 9 + .../java/com/simibubi/create/AllItems.java | 4 +- .../java/com/simibubi/create/AllPackets.java | 4 + .../com/simibubi/create/ClientEvents.java | 4 +- .../com/simibubi/create/ScreenResources.java | 12 + .../create/foundation/behaviour/ValueBox.java | 5 +- .../behaviour/ValueBoxRenderer.java | 24 +- .../behaviour/base/SmartTileEntity.java | 6 + .../behaviour/base/TileEntityBehaviour.java | 4 + .../filtering/FilteringBehaviour.java | 64 +++- .../filtering/FilteringCountUpdatePacket.java | 44 +++ .../behaviour/filtering/FilteringHandler.java | 55 +++- .../filtering/FilteringRenderer.java | 9 +- .../inventory/ExtractingBehaviour.java | 12 +- .../gui/AbstractSimiContainerScreen.java | 4 +- .../create/foundation/gui/widgets/Label.java | 40 ++- .../foundation/item/ItemDescription.java | 11 +- .../modules/contraptions/base/IRotate.java | 8 + .../bearing/MechanicalBearingBlock.java | 7 +- .../components/fan/EncasedFanBlock.java | 5 + .../components/motor/MotorBlock.java | 8 +- .../waterwheel/WaterWheelBlock.java | 5 + .../belts/BeltAttachableLogisticalBlock.java | 6 +- .../transposer/TransposerTileEntity.java | 2 +- .../modules/logistics/item/FilterItem.java | 11 - .../item/filter/AbstractFilterContainer.java | 142 +++++++++ .../item/filter/AbstractFilterScreen.java | 154 ++++++++++ .../item/filter/AttributeFilterContainer.java | 138 +++++++++ .../item/filter/AttributeFilterScreen.java | 257 ++++++++++++++++ .../item/filter/FilterContainer.java | 59 ++++ .../logistics/item/filter/FilterItem.java | 222 ++++++++++++++ .../logistics/item/filter/FilterScreen.java | 132 +++++++++ .../item/filter/FilterScreenPacket.java | 83 ++++++ .../logistics/item/filter/ItemAttribute.java | 274 ++++++++++++++++++ .../resources/assets/create/lang/en_us.json | 51 +++- .../assets/create/models/item/filter.json | 60 ++-- .../create/models/item/logistical_filter.json | 7 + .../create/models/item/property_filter.json | 7 + .../create/textures/block/copper_casing.png | Bin 0 -> 554 bytes .../assets/create/textures/gui/filter.pdn | Bin 9152 -> 0 bytes .../assets/create/textures/gui/filter.png | Bin 0 -> 15775 bytes .../assets/create/textures/gui/icons.png | Bin 2394 -> 2833 bytes .../assets/create/textures/item/filter.png | Bin 278 -> 337 bytes .../assets/create/textures/item/filter_1.png | Bin 164 -> 0 bytes .../assets/create/textures/item/filter_2.png | Bin 173 -> 0 bytes .../textures/item/logistical_filter.png | Bin 0 -> 376 bytes .../create/textures/item/property_filter.png | Bin 0 -> 329 bytes 47 files changed, 1870 insertions(+), 79 deletions(-) create mode 100644 src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringCountUpdatePacket.java delete mode 100644 src/main/java/com/simibubi/create/modules/logistics/item/FilterItem.java create mode 100644 src/main/java/com/simibubi/create/modules/logistics/item/filter/AbstractFilterContainer.java create mode 100644 src/main/java/com/simibubi/create/modules/logistics/item/filter/AbstractFilterScreen.java create mode 100644 src/main/java/com/simibubi/create/modules/logistics/item/filter/AttributeFilterContainer.java create mode 100644 src/main/java/com/simibubi/create/modules/logistics/item/filter/AttributeFilterScreen.java create mode 100644 src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterContainer.java create mode 100644 src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterItem.java create mode 100644 src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterScreen.java create mode 100644 src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterScreenPacket.java create mode 100644 src/main/java/com/simibubi/create/modules/logistics/item/filter/ItemAttribute.java create mode 100644 src/main/resources/assets/create/models/item/logistical_filter.json create mode 100644 src/main/resources/assets/create/models/item/property_filter.json create mode 100644 src/main/resources/assets/create/textures/block/copper_casing.png delete mode 100644 src/main/resources/assets/create/textures/gui/filter.pdn create mode 100644 src/main/resources/assets/create/textures/gui/filter.png delete mode 100644 src/main/resources/assets/create/textures/item/filter_1.png delete mode 100644 src/main/resources/assets/create/textures/item/filter_2.png create mode 100644 src/main/resources/assets/create/textures/item/logistical_filter.png create mode 100644 src/main/resources/assets/create/textures/item/property_filter.png diff --git a/src/main/java/com/simibubi/create/AllContainers.java b/src/main/java/com/simibubi/create/AllContainers.java index d14325d20..bfd6f7be3 100644 --- a/src/main/java/com/simibubi/create/AllContainers.java +++ b/src/main/java/com/simibubi/create/AllContainers.java @@ -3,6 +3,10 @@ package com.simibubi.create; import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.modules.logistics.block.inventories.FlexcrateContainer; import com.simibubi.create.modules.logistics.block.inventories.FlexcrateScreen; +import com.simibubi.create.modules.logistics.item.filter.AttributeFilterContainer; +import com.simibubi.create.modules.logistics.item.filter.AttributeFilterScreen; +import com.simibubi.create.modules.logistics.item.filter.FilterContainer; +import com.simibubi.create.modules.logistics.item.filter.FilterScreen; import com.simibubi.create.modules.logistics.management.controller.LogisticalInventoryControllerContainer; import com.simibubi.create.modules.logistics.management.controller.LogisticalInventoryControllerScreen; import com.simibubi.create.modules.logistics.management.index.LogisticalIndexContainer; @@ -33,6 +37,9 @@ public enum AllContainers { LOGISTICAL_INDEX(LogisticalIndexContainer::new), LOGISTICAL_CONTROLLER(LogisticalInventoryControllerContainer::new), + FILTER(FilterContainer::new), + ATTRIBUTE_FILTER(AttributeFilterContainer::new), + ; public ContainerType type; @@ -57,6 +64,8 @@ public enum AllContainers { bind(FLEXCRATE, FlexcrateScreen::new); bind(LOGISTICAL_INDEX, LogisticalIndexScreen::new); bind(LOGISTICAL_CONTROLLER, LogisticalInventoryControllerScreen::new); + bind(FILTER, FilterScreen::new); + bind(ATTRIBUTE_FILTER, AttributeFilterScreen::new); } @OnlyIn(Dist.CLIENT) diff --git a/src/main/java/com/simibubi/create/AllItems.java b/src/main/java/com/simibubi/create/AllItems.java index fa7f61a4e..d43b46d27 100644 --- a/src/main/java/com/simibubi/create/AllItems.java +++ b/src/main/java/com/simibubi/create/AllItems.java @@ -17,7 +17,7 @@ import com.simibubi.create.modules.curiosities.symmetry.SymmetryWandItem; import com.simibubi.create.modules.curiosities.symmetry.client.SymmetryWandItemRenderer; import com.simibubi.create.modules.gardens.TreeFertilizerItem; import com.simibubi.create.modules.logistics.item.CardboardBoxItem; -import com.simibubi.create.modules.logistics.item.FilterItem; +import com.simibubi.create.modules.logistics.item.filter.FilterItem; import com.simibubi.create.modules.logistics.management.LogisticalDialItem; import com.simibubi.create.modules.logistics.management.base.LogisticalControllerBlock.Type; import com.simibubi.create.modules.logistics.management.base.LogisticalControllerItem; @@ -108,6 +108,8 @@ public enum AllItems { CARDBOARD_BOX_1410(new CardboardBoxItem(standardItemProperties())), FILTER(new FilterItem(standardItemProperties()), true), + PROPERTY_FILTER(new FilterItem(standardItemProperties()), true), + LOGISTICAL_FILTER(new FilterItem(standardItemProperties())), LOGISTICAL_DIAL(new LogisticalDialItem(standardItemProperties())), LOGISTICAL_CONTROLLER_SUPPLY(new LogisticalControllerItem(standardItemProperties(), Type.SUPPLY)), LOGISTICAL_CONTROLLER_REQUEST(new LogisticalControllerItem(standardItemProperties(), Type.REQUEST)), diff --git a/src/main/java/com/simibubi/create/AllPackets.java b/src/main/java/com/simibubi/create/AllPackets.java index d3046b799..00c498277 100644 --- a/src/main/java/com/simibubi/create/AllPackets.java +++ b/src/main/java/com/simibubi/create/AllPackets.java @@ -4,6 +4,7 @@ import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; +import com.simibubi.create.foundation.behaviour.filtering.FilteringCountUpdatePacket; import com.simibubi.create.foundation.packet.NbtPacket; import com.simibubi.create.foundation.packet.SimplePacketBase; import com.simibubi.create.modules.contraptions.components.contraptions.chassis.ConfigureChassisPacket; @@ -12,6 +13,7 @@ import com.simibubi.create.modules.contraptions.components.motor.ConfigureMotorP import com.simibubi.create.modules.curiosities.placementHandgun.BuilderGunBeamPacket; import com.simibubi.create.modules.curiosities.symmetry.SymmetryEffectPacket; import com.simibubi.create.modules.logistics.block.diodes.ConfigureFlexpeaterPacket; +import com.simibubi.create.modules.logistics.item.filter.FilterScreenPacket; import com.simibubi.create.modules.logistics.management.controller.LogisticalControllerConfigurationPacket; import com.simibubi.create.modules.logistics.management.index.IndexContainerUpdatePacket; import com.simibubi.create.modules.logistics.management.index.IndexOrderRequest; @@ -43,6 +45,8 @@ public enum AllPackets { PLACE_SCHEMATIC(SchematicPlacePacket.class, SchematicPlacePacket::new), UPLOAD_SCHEMATIC(SchematicUploadPacket.class, SchematicUploadPacket::new), INDEX_ORDER_REQUEST(IndexOrderRequest.class, IndexOrderRequest::new), + CONFIGURE_FILTER(FilterScreenPacket.class, FilterScreenPacket::new), + CONFIGURE_FILTERING_AMOUNT(FilteringCountUpdatePacket.class, FilteringCountUpdatePacket::new), // Server to Client SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new), diff --git a/src/main/java/com/simibubi/create/ClientEvents.java b/src/main/java/com/simibubi/create/ClientEvents.java index eed93ddf1..093d08585 100644 --- a/src/main/java/com/simibubi/create/ClientEvents.java +++ b/src/main/java/com/simibubi/create/ClientEvents.java @@ -3,6 +3,7 @@ package com.simibubi.create; import java.util.ArrayList; import java.util.List; +import com.simibubi.create.foundation.behaviour.filtering.FilteringHandler; import com.simibubi.create.foundation.block.IHaveScrollableValue; import com.simibubi.create.foundation.gui.ScreenOpener; import com.simibubi.create.foundation.item.TooltipHelper; @@ -100,7 +101,8 @@ public class ClientEvents { boolean cancelled = CreateClient.schematicHandler.mouseScrolled(delta) || CreateClient.schematicAndQuillHandler.mouseScrolled(delta) - || IHaveScrollableValue.onScroll(delta); + || IHaveScrollableValue.onScroll(delta) + || FilteringHandler.onScroll(delta); event.setCanceled(cancelled); } diff --git a/src/main/java/com/simibubi/create/ScreenResources.java b/src/main/java/com/simibubi/create/ScreenResources.java index 6be7adbe8..4c83da215 100644 --- a/src/main/java/com/simibubi/create/ScreenResources.java +++ b/src/main/java/com/simibubi/create/ScreenResources.java @@ -33,6 +33,9 @@ public enum ScreenResources { STOCKSWITCH_BOUND_LEFT("flex_crate_and_stockpile_switch.png", 234, 129, 7, 21), STOCKSWITCH_BOUND_RIGHT("flex_crate_and_stockpile_switch.png", 241, 129, 7, 21), + FILTER("filter.png", 200, 100), + ATTRIBUTE_FILTER("filter.png", 0, 100, 200, 86), + // Logistical Index INDEX_TOP("index.png", 41, 0, 174, 22), INDEX_TOP_TRIM("index.png", 41, 22, 174, 6), @@ -130,6 +133,15 @@ public enum ScreenResources { I_PRIORITY_VERY_HIGH(112, 0), I_ACTIVE(64, 16), I_PASSIVE(80, 16), + + I_BLACKLIST(128, 0), + I_WHITELIST(144, 0), + I_WHITELIST_OR(160, 0), + I_WHITELIST_AND(176, 0), + I_WHITELIST_NOT(192, 0), + + I_RESPECT_NBT(208, 0), + I_IGNORE_NBT(224, 0), ; diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/ValueBox.java b/src/main/java/com/simibubi/create/foundation/behaviour/ValueBox.java index 858861aad..225d9d0d4 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/ValueBox.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/ValueBox.java @@ -1,5 +1,6 @@ package com.simibubi.create.foundation.behaviour; +import net.minecraft.item.ItemStack; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.Vec3d; @@ -28,10 +29,12 @@ public class ValueBox { } public static class ItemValueBox extends ValueBox { + ItemStack stack; int count; - public ItemValueBox(String label, AxisAlignedBB bb, int count) { + public ItemValueBox(String label, AxisAlignedBB bb, ItemStack stack, int count) { super(label, bb); + this.stack = stack; this.count = count; } diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/ValueBoxRenderer.java b/src/main/java/com/simibubi/create/foundation/behaviour/ValueBoxRenderer.java index d5fd3b5f4..2369e40c8 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/ValueBoxRenderer.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/ValueBoxRenderer.java @@ -1,11 +1,11 @@ package com.simibubi.create.foundation.behaviour; import com.mojang.blaze3d.platform.GlStateManager; -import com.simibubi.create.AllItems; import com.simibubi.create.foundation.behaviour.ValueBox.ItemValueBox; import com.simibubi.create.foundation.utility.ColorHelper; import com.simibubi.create.foundation.utility.TessellatorHelper; import com.simibubi.create.modules.contraptions.relays.elementary.CogWheelBlock; +import com.simibubi.create.modules.logistics.item.filter.FilterItem; import net.minecraft.block.Block; import net.minecraft.block.Blocks; @@ -67,12 +67,20 @@ public class ValueBoxRenderer { } if (box instanceof ItemValueBox) { - String count = ((ItemValueBox) box).count + ""; - 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); + ItemValueBox itemValueBox = (ItemValueBox) box; + String count = itemValueBox.count == 0 ? "*" : itemValueBox.count + ""; + + boolean isFilter = itemValueBox.stack.getItem() instanceof FilterItem; + if (isFilter) + GlStateManager.translated(3, 8, 7.25f); + else + GlStateManager.translated(-7 - font.getStringWidth(count), 10, 10 + 1 / 4f); + + double scale = 1.5; + GlStateManager.scaled(scale, scale, scale); + font.drawString(count, 0, 0, isFilter ? 0xFFFFFF : 0xEDEDED); + GlStateManager.translated(0, 0, -1 / 16f); + font.drawString(count, 1 - 1 / 8f, 1 - 1 / 8f, 0x4F4F4F); } } @@ -89,7 +97,7 @@ public class ValueBoxRenderer { private static float customZOffset(Item item) { float NUDGE = -.1f; - if (AllItems.FILTER.get() == item) + if (item instanceof FilterItem) return NUDGE; if (item instanceof BlockItem) { Block block = ((BlockItem) item).getBlock(); diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/base/SmartTileEntity.java b/src/main/java/com/simibubi/create/foundation/behaviour/base/SmartTileEntity.java index e16da9cd5..297a1255d 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/base/SmartTileEntity.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/base/SmartTileEntity.java @@ -69,6 +69,12 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka behaviours.values().forEach(tb -> tb.writeNBT(compound)); return super.write(compound); } + + @Override + public CompoundNBT writeToClient(CompoundNBT compound) { + behaviours.values().forEach(tb -> tb.writeToClient(compound)); + return super.writeToClient(compound); + } @Override public void read(CompoundNBT compound) { diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/base/TileEntityBehaviour.java b/src/main/java/com/simibubi/create/foundation/behaviour/base/TileEntityBehaviour.java index 1451339db..d9128d533 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/base/TileEntityBehaviour.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/base/TileEntityBehaviour.java @@ -98,4 +98,8 @@ public abstract class TileEntityBehaviour { return ste.getBehaviour(type); } + public CompoundNBT writeToClient(CompoundNBT compound) { + return compound; + } + } diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringBehaviour.java b/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringBehaviour.java index fa9fdac41..ebaaf0c94 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringBehaviour.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringBehaviour.java @@ -3,9 +3,11 @@ package com.simibubi.create.foundation.behaviour.filtering; import java.util.function.Consumer; import java.util.function.Function; +import com.simibubi.create.AllPackets; import com.simibubi.create.foundation.behaviour.base.IBehaviourType; import com.simibubi.create.foundation.behaviour.base.SmartTileEntity; import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; +import com.simibubi.create.modules.logistics.item.filter.FilterItem; import net.minecraft.block.BlockState; import net.minecraft.item.ItemStack; @@ -22,8 +24,13 @@ public class FilteringBehaviour extends TileEntityBehaviour { Vec3d textShift; private ItemStack filter; + public int count; private Consumer callback; + int scrollableValue; + int ticksUntilScrollPacket; + boolean forceClientState; + public FilteringBehaviour(SmartTileEntity te) { super(te); filter = ItemStack.EMPTY; @@ -32,20 +39,54 @@ public class FilteringBehaviour extends TileEntityBehaviour { callback = stack -> { }; textShift = Vec3d.ZERO; + count = -1; + ticksUntilScrollPacket = -1; } @Override public void writeNBT(CompoundNBT nbt) { nbt.put("Filter", getFilter().serializeNBT()); + nbt.putInt("FilterAmount", count); super.writeNBT(nbt); } @Override public void readNBT(CompoundNBT nbt) { filter = ItemStack.read(nbt.getCompound("Filter")); + count = nbt.getInt("FilterAmount"); + if (nbt.contains("ForceScrollable")) { + scrollableValue = count; + ticksUntilScrollPacket = -1; + } super.readNBT(nbt); } + @Override + public CompoundNBT writeToClient(CompoundNBT compound) { + if (forceClientState) { + compound.putBoolean("ForceScrollable", true); + forceClientState = false; + } + return super.writeToClient(compound); + } + + @Override + public void tick() { + super.tick(); + + if (!getWorld().isRemote) + return; + if (ticksUntilScrollPacket == -1) + return; + if (ticksUntilScrollPacket > 0) { + ticksUntilScrollPacket--; + return; + } + + AllPackets.channel.sendToServer(new FilteringCountUpdatePacket(getPos(), scrollableValue)); + ticksUntilScrollPacket = -1; + } + public FilteringBehaviour withCallback(Consumer filterCallback) { callback = filterCallback; return this; @@ -65,10 +106,23 @@ public class FilteringBehaviour extends TileEntityBehaviour { textShift = shift; return this; } + + @Override + public void initialize() { + super.initialize(); + scrollableValue = count; + } public void setFilter(ItemStack stack) { filter = stack.copy(); callback.accept(filter); + + if (filter.getItem() instanceof FilterItem) + count = 0; + else + count = stack.getCount(); + forceClientState = true; + tileEntity.markDirty(); tileEntity.sendData(); } @@ -82,7 +136,7 @@ public class FilteringBehaviour extends TileEntityBehaviour { } public boolean test(ItemStack stack) { - return filter.isEmpty() || ItemStack.areItemsEqual(filter, stack); + return filter.isEmpty() || FilterItem.test(stack, filter); } @Override @@ -98,6 +152,14 @@ public class FilteringBehaviour extends TileEntityBehaviour { Vec3d localHit = hit.subtract(new Vec3d(tileEntity.getPos())); return localHit.distanceTo(offset) < slotPositioning.scale / 2; } + + public int getAmount() { + return count; + } + + public boolean anyAmount() { + return count == 0; + } public static class SlotPositioning { Function offset; diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringCountUpdatePacket.java b/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringCountUpdatePacket.java new file mode 100644 index 000000000..5b8eaa568 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringCountUpdatePacket.java @@ -0,0 +1,44 @@ +package com.simibubi.create.foundation.behaviour.filtering; + +import com.simibubi.create.foundation.behaviour.base.SmartTileEntity; +import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; +import com.simibubi.create.foundation.packet.TileEntityConfigurationPacket; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.math.BlockPos; + +public class FilteringCountUpdatePacket extends TileEntityConfigurationPacket { + + int amount; + + public FilteringCountUpdatePacket(PacketBuffer buffer) { + super(buffer); + } + + public FilteringCountUpdatePacket(BlockPos pos, int amount) { + super(pos); + this.amount = amount; + } + + @Override + protected void writeSettings(PacketBuffer buffer) { + buffer.writeInt(amount); + } + + @Override + protected void readSettings(PacketBuffer buffer) { + amount = buffer.readInt(); + } + + @Override + protected void applySettings(SmartTileEntity te) { + FilteringBehaviour behaviour = TileEntityBehaviour.get(te, FilteringBehaviour.TYPE); + if (behaviour == null) + return; + behaviour.forceClientState = true; + behaviour.count = amount; + te.markDirty(); + te.sendData(); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringHandler.java b/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringHandler.java index f210c1ce9..1fa219809 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringHandler.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringHandler.java @@ -1,16 +1,25 @@ package com.simibubi.create.foundation.behaviour.filtering; +import com.simibubi.create.AllKeys; import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; import com.simibubi.create.foundation.utility.RaycastHelper; +import com.simibubi.create.modules.logistics.item.filter.FilterItem; +import net.minecraft.client.Minecraft; +import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; import net.minecraft.util.ActionResultType; import net.minecraft.util.Hand; import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundEvents; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockRayTraceResult; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RayTraceResult; import net.minecraft.world.World; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.LogicalSide; @@ -38,12 +47,54 @@ public class FilteringHandler { return; if (behaviour.testHit(ray.getHitVec())) { - if (event.getSide() != LogicalSide.CLIENT) - behaviour.setFilter(player.getHeldItem(hand)); + if (event.getSide() != LogicalSide.CLIENT) { + ItemStack heldItem = player.getHeldItem(hand).copy(); + if (!player.isCreative()) { + if (behaviour.getFilter().getItem() instanceof FilterItem) + player.inventory.placeItemBackInInventory(world, behaviour.getFilter()); + if (heldItem.getItem() instanceof FilterItem) + player.getHeldItem(hand).shrink(1); + } + if (heldItem.getItem() instanceof FilterItem) + heldItem.setCount(1); + behaviour.setFilter(heldItem); + } event.setCanceled(true); event.setCancellationResult(ActionResultType.SUCCESS); world.playSound(null, pos, SoundEvents.ENTITY_ITEM_FRAME_ADD_ITEM, SoundCategory.BLOCKS, .25f, .1f); } } + @OnlyIn(Dist.CLIENT) + public static boolean onScroll(double delta) { + RayTraceResult objectMouseOver = Minecraft.getInstance().objectMouseOver; + if (!(objectMouseOver instanceof BlockRayTraceResult)) + return false; + + BlockRayTraceResult result = (BlockRayTraceResult) objectMouseOver; + Minecraft mc = Minecraft.getInstance(); + ClientWorld world = mc.world; + BlockPos blockPos = result.getPos(); + + FilteringBehaviour filtering = TileEntityBehaviour.get(world, blockPos, FilteringBehaviour.TYPE); + if (filtering == null) + return false; + if (mc.player.isSneaking()) + return false; + if (!mc.player.isAllowEdit()) + return false; + if (!filtering.isCountVisible()) + return false; + if (!filtering.testHit(objectMouseOver.getHitVec())) + return false; + if (filtering.getFilter().isEmpty()) + return false; + + filtering.ticksUntilScrollPacket = 10; + filtering.scrollableValue = (int) MathHelper + .clamp(filtering.scrollableValue + delta * (AllKeys.ctrlDown() ? 16 : 1), 0, 64); + + return true; + } + } diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringRenderer.java b/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringRenderer.java index f54046877..ff73ab21c 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringRenderer.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/filtering/FilteringRenderer.java @@ -10,10 +10,12 @@ import com.simibubi.create.foundation.behaviour.filtering.FilteringBehaviour.Slo import com.simibubi.create.foundation.utility.GlHelper; import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.TessellatorHelper; +import com.simibubi.create.modules.logistics.item.filter.FilterItem; import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.world.ClientWorld; +import net.minecraft.item.ItemStack; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockRayTraceResult; @@ -41,6 +43,8 @@ public class FilteringRenderer { FilteringBehaviour behaviour = TileEntityBehaviour.get(world, pos, FilteringBehaviour.TYPE); if (behaviour == null) return; + if (Minecraft.getInstance().player.isSneaking()) + return; TessellatorHelper.prepareForDrawing(); GlStateManager.translated(pos.getX(), pos.getY(), pos.getZ()); @@ -50,7 +54,10 @@ public class FilteringRenderer { AxisAlignedBB bb = new AxisAlignedBB(Vec3d.ZERO, Vec3d.ZERO).grow(.25f); String label = Lang.translate("logistics.filter"); - ValueBox box = behaviour.isCountVisible() ? new ItemValueBox(label, bb, behaviour.getFilter().getCount()) + ItemStack filter = behaviour.getFilter(); + if (filter.getItem() instanceof FilterItem) + label = ""; + ValueBox box = behaviour.isCountVisible() ? new ItemValueBox(label, bb, filter, behaviour.scrollableValue) : new ValueBox(label, bb); box.offsetLabel(behaviour.textShift).withColors(0x7777BB, 0xCCBBFF); ValueBoxRenderer.renderBox(box, behaviour.testHit(target.getHitVec())); diff --git a/src/main/java/com/simibubi/create/foundation/behaviour/inventory/ExtractingBehaviour.java b/src/main/java/com/simibubi/create/foundation/behaviour/inventory/ExtractingBehaviour.java index 9a2a5714a..6340f8c78 100644 --- a/src/main/java/com/simibubi/create/foundation/behaviour/inventory/ExtractingBehaviour.java +++ b/src/main/java/com/simibubi/create/foundation/behaviour/inventory/ExtractingBehaviour.java @@ -39,19 +39,17 @@ public class ExtractingBehaviour extends InventoryManagementBehaviour { this.customAmountFilter = filter; return this; } - + public ExtractingBehaviour withAdditionalFilter(Predicate filter) { this.customFilter = filter; return this; } - + public boolean extract() { int amount = -1; FilteringBehaviour filter = get(tileEntity, FilteringBehaviour.TYPE); - if (filter != null) { - ItemStack filterItem = filter.getFilter(); - amount = filterItem.isEmpty() ? -1 : filterItem.getCount(); - } + if (filter != null && !filter.anyAmount()) + amount = filter.getAmount(); return extract(amount); } @@ -70,7 +68,7 @@ public class ExtractingBehaviour extends InventoryManagementBehaviour { extract = ItemHelper.extract(inv, test, exactAmount, false); else extract = ItemHelper.extract(inv, test, customAmountFilter, false); - + if (!extract.isEmpty()) { callback.accept(extract); return true; diff --git a/src/main/java/com/simibubi/create/foundation/gui/AbstractSimiContainerScreen.java b/src/main/java/com/simibubi/create/foundation/gui/AbstractSimiContainerScreen.java index 33b6d644f..1a1f5e3d1 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/AbstractSimiContainerScreen.java +++ b/src/main/java/com/simibubi/create/foundation/gui/AbstractSimiContainerScreen.java @@ -29,7 +29,7 @@ public abstract class AbstractSimiContainerScreen extends C protected List widgets; - protected AbstractSimiContainerScreen(T container, PlayerInventory inv, ITextComponent title) { + public AbstractSimiContainerScreen(T container, PlayerInventory inv, ITextComponent title) { super(container, inv, title); widgets = new ArrayList<>(); } @@ -126,7 +126,7 @@ public abstract class AbstractSimiContainerScreen extends C } protected void renderWindowForeground(int mouseX, int mouseY, float partialTicks) { - super.renderHoveredToolTip(mouseX, mouseY); + renderHoveredToolTip(mouseX, mouseY); for (Widget widget : widgets) { if (!widget.isHovered()) continue; diff --git a/src/main/java/com/simibubi/create/foundation/gui/widgets/Label.java b/src/main/java/com/simibubi/create/foundation/gui/widgets/Label.java index 3d58e5dac..5f55bfbe8 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/widgets/Label.java +++ b/src/main/java/com/simibubi/create/foundation/gui/widgets/Label.java @@ -12,7 +12,7 @@ public class Label extends AbstractSimiWidget { protected boolean hasShadow; protected int color; protected FontRenderer font; - + public Label(int x, int y, String text) { super(x, y, Minecraft.getInstance().fontRenderer.getStringWidth(text), 10); font = Minecraft.getInstance().fontRenderer; @@ -21,34 +21,60 @@ public class Label extends AbstractSimiWidget { hasShadow = false; suffix = ""; } - + public Label colored(int color) { this.color = color; return this; } - + public Label withShadow() { this.hasShadow = true; return this; } - + public Label withSuffix(String s) { suffix = s; return this; } - + + public void setTextAndTrim(String newText, boolean trimFront, int maxWidthPx) { + FontRenderer fontRenderer = Minecraft.getInstance().fontRenderer; + + if (fontRenderer.getStringWidth(newText) <= maxWidthPx) { + text = newText; + return; + } + + String trim = "..."; + int trimWidth = fontRenderer.getStringWidth(trim); + + StringBuilder builder = new StringBuilder(newText); + int startIndex = trimFront ? 0 : newText.length() - 1; + int endIndex = !trimFront ? 0 : newText.length() - 1; + int step = (int) Math.signum(endIndex - startIndex); + + for (int i = startIndex; i != endIndex; i += step) { + String sub = builder.substring(trimFront ? i : startIndex, trimFront ? endIndex + 1 : i + 1); + if (fontRenderer.getStringWidth(sub) + trimWidth <= maxWidthPx) { + text = trimFront ? trim + sub : sub + trim; + return; + } + } + + } + @Override public void render(int mouseX, int mouseY, float partialTicks) { if (!visible) return; if (text == null || text.isEmpty()) return; - + GlStateManager.color4f(1, 1, 1, 1); if (hasShadow) font.drawStringWithShadow(text + suffix, x, y, color); else font.drawString(text + suffix, x, y, color); } - + } diff --git a/src/main/java/com/simibubi/create/foundation/item/ItemDescription.java b/src/main/java/com/simibubi/create/foundation/item/ItemDescription.java index 2eeb26366..8b8d524bb 100644 --- a/src/main/java/com/simibubi/create/foundation/item/ItemDescription.java +++ b/src/main/java/com/simibubi/create/foundation/item/ItemDescription.java @@ -69,7 +69,7 @@ public class ItemDescription { linesOnShift = new ArrayList<>(); linesOnCtrl = new ArrayList<>(); } - + public ItemDescription withSummary(String summary) { add(linesOnShift, cutString(summary, palette.color, palette.hColor)); add(linesOnShift, ""); @@ -86,11 +86,12 @@ public class ItemDescription { if (hasSpeedRequirement) { List speedLevels = Lang.translatedOptions("tooltip.speedRequirement", "none", "medium", "high"); int index = minimumRequiredSpeedLevel.ordinal(); - String level = minimumRequiredSpeedLevel.getTextColor() + makeProgressBar(3, index) + speedLevels.get(index); + String level = minimumRequiredSpeedLevel.getTextColor() + makeProgressBar(3, index) + + speedLevels.get(index); add(linesOnShift, GRAY + Lang.translate("tooltip.speedRequirement")); add(linesOnShift, level); } - if (hasStressImpact) { + if (hasStressImpact && !block.hideStressImpact()) { List stressLevels = Lang.translatedOptions("tooltip.stressImpact", "low", "medium", "high"); double impact = parameters.stressEntries.get(id).get(); StressImpact impactId = impact >= parameters.highStressImpact.get() ? StressImpact.HIGH @@ -109,6 +110,10 @@ public class ItemDescription { : (capacity >= parameters.mediumCapacity.get() ? StressImpact.MEDIUM : StressImpact.HIGH); int index = StressImpact.values().length - 1 - impactId.ordinal(); String level = impactId.getColor() + makeProgressBar(3, index) + stressCapacityLevels.get(index); + if (block.showCapacityWithAnnotation()) + level += " " + DARK_GRAY + TextFormatting.ITALIC + + Lang.translate("tooltip.capacityProvided.asGenerator"); + add(linesOnShift, GRAY + Lang.translate("tooltip.capacityProvided")); add(linesOnShift, level); } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/base/IRotate.java b/src/main/java/com/simibubi/create/modules/contraptions/base/IRotate.java index d7da35144..3e4bcb1eb 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/base/IRotate.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/base/IRotate.java @@ -57,5 +57,13 @@ public interface IRotate extends IWrenchable { public default SpeedLevel getMinimumRequiredSpeedLevel() { return SpeedLevel.NONE; } + + public default boolean hideStressImpact() { + return false; + } + + public default boolean showCapacityWithAnnotation() { + return false; + } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/bearing/MechanicalBearingBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/bearing/MechanicalBearingBlock.java index 5687166d2..1d237fe5c 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/bearing/MechanicalBearingBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/bearing/MechanicalBearingBlock.java @@ -43,10 +43,15 @@ public class MechanicalBearingBlock extends DirectionalKineticBlock protected boolean hasStaticPart() { return true; } - + @Override public Axis getRotationAxis(BlockState state) { return state.get(FACING).getAxis(); } + @Override + public boolean showCapacityWithAnnotation() { + return true; + } + } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/fan/EncasedFanBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/components/fan/EncasedFanBlock.java index c55fdcb9c..4e501d553 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/fan/EncasedFanBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/fan/EncasedFanBlock.java @@ -77,5 +77,10 @@ public class EncasedFanBlock extends DirectionalKineticBlock implements IWithTil public boolean hasShaftTowards(World world, BlockPos pos, BlockState state, Direction face) { return face == state.get(FACING).getOpposite(); } + + @Override + public boolean showCapacityWithAnnotation() { + return true; + } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/motor/MotorBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/components/motor/MotorBlock.java index 4605e6717..2aa49755c 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/motor/MotorBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/motor/MotorBlock.java @@ -20,7 +20,8 @@ import net.minecraft.world.IBlockReader; import net.minecraft.world.IWorld; import net.minecraft.world.World; -public class MotorBlock extends HorizontalKineticBlock implements IWithTileEntity, IHaveScrollableValue { +public class MotorBlock extends HorizontalKineticBlock + implements IWithTileEntity, IHaveScrollableValue { private static final Vec3d valuePos = new Vec3d(15 / 16f, 5 / 16f, 5 / 16f); @@ -91,4 +92,9 @@ public class MotorBlock extends HorizontalKineticBlock implements IWithTileEntit public Direction getValueBoxDirection(BlockState state, IWorld world, BlockPos pos) { return state.get(HORIZONTAL_FACING).getOpposite(); } + + @Override + public boolean hideStressImpact() { + return true; + } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/waterwheel/WaterWheelBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/components/waterwheel/WaterWheelBlock.java index b27ec550d..b7f73cf1e 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/waterwheel/WaterWheelBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/waterwheel/WaterWheelBlock.java @@ -144,4 +144,9 @@ public class WaterWheelBlock extends HorizontalKineticBlock { return 1f; } + @Override + public boolean hideStressImpact() { + return true; + } + } diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/belts/BeltAttachableLogisticalBlock.java b/src/main/java/com/simibubi/create/modules/logistics/block/belts/BeltAttachableLogisticalBlock.java index df8d652f3..10233a838 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/belts/BeltAttachableLogisticalBlock.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/belts/BeltAttachableLogisticalBlock.java @@ -56,7 +56,7 @@ public abstract class BeltAttachableLogisticalBlock extends AttachedLogisticalBl if (extracting == null) return false; - if (filtering != null && (!filtering.test(stack) || stack.getCount() < filtering.getFilter().getCount())) + if (filtering != null && (!filtering.test(stack) || stack.getCount() < filtering.getAmount())) return false; return true; @@ -76,10 +76,10 @@ public abstract class BeltAttachableLogisticalBlock extends AttachedLogisticalBl return false; FilteringBehaviour filtering = TileEntityBehaviour.get(world, pos, FilteringBehaviour.TYPE); - if (filtering != null && (!filtering.test(stack) || stack.getCount() < filtering.getFilter().getCount())) + if (filtering != null && (!filtering.test(stack) || stack.getCount() < filtering.getAmount())) return false; if (!extracting.getShouldExtract().get()) - return !filtering.getFilter().isEmpty(); + return !filtering.anyAmount(); return !extracting.extractFromInventory(); } diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/transposer/TransposerTileEntity.java b/src/main/java/com/simibubi/create/modules/logistics/block/transposer/TransposerTileEntity.java index 2bce19482..92d8a97d1 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/transposer/TransposerTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/transposer/TransposerTileEntity.java @@ -57,7 +57,7 @@ public class TransposerTileEntity extends ExtractorTileEntity { return te.tryInsertingFromSide(facing, stack, true); } - if (filtering.getFilter().isEmpty()) + if (filtering.anyAmount()) return true; return inserting.insert(stack, true).isEmpty(); } diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/FilterItem.java b/src/main/java/com/simibubi/create/modules/logistics/item/FilterItem.java deleted file mode 100644 index d10d2c612..000000000 --- a/src/main/java/com/simibubi/create/modules/logistics/item/FilterItem.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.simibubi.create.modules.logistics.item; - -import net.minecraft.item.Item; - -public class FilterItem extends Item { - - public FilterItem(Properties properties) { - super(properties); - } - -} diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/filter/AbstractFilterContainer.java b/src/main/java/com/simibubi/create/modules/logistics/item/filter/AbstractFilterContainer.java new file mode 100644 index 000000000..6a01c3db5 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/logistics/item/filter/AbstractFilterContainer.java @@ -0,0 +1,142 @@ +package com.simibubi.create.modules.logistics.item.filter; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.container.ClickType; +import net.minecraft.inventory.container.Container; +import net.minecraft.inventory.container.ContainerType; +import net.minecraft.inventory.container.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.items.ItemHandlerHelper; +import net.minecraftforge.items.ItemStackHandler; + +public abstract class AbstractFilterContainer extends Container { + + public PlayerEntity player; + protected PlayerInventory playerInventory; + public ItemStack filterItem; + public ItemStackHandler filterInventory; + + protected AbstractFilterContainer(ContainerType type, int id, PlayerInventory inv, PacketBuffer extraData) { + this(type, id, inv, extraData.readItemStack()); + } + + protected AbstractFilterContainer(ContainerType type, int id, PlayerInventory inv, ItemStack filterItem) { + super(type, id); + player = inv.player; + playerInventory = inv; + this.filterItem = filterItem; + init(); + } + + protected void init() { + this.filterInventory = createFilterInventory(); + readData(filterItem); + addPlayerSlots(); + addFilterSlots(); + detectAndSendChanges(); + } + + protected void clearContents() { + for (int i = 0; i < filterInventory.getSlots(); i++) + filterInventory.setStackInSlot(i, ItemStack.EMPTY); + } + + protected abstract int getInventoryOffset(); + + protected abstract void addFilterSlots(); + + protected abstract ItemStackHandler createFilterInventory(); + + protected abstract void readData(ItemStack filterItem); + + protected abstract void saveData(ItemStack filterItem); + + protected void addPlayerSlots() { + int x = 58; + int y = 28 + getInventoryOffset(); + + for (int hotbarSlot = 0; hotbarSlot < 9; ++hotbarSlot) + this.addSlot(new Slot(playerInventory, hotbarSlot, x + hotbarSlot * 18, y + 58)); + for (int row = 0; row < 3; ++row) + for (int col = 0; col < 9; ++col) + this.addSlot(new Slot(playerInventory, col + row * 9 + 9, x + col * 18, y + row * 18)); + } + + @Override + public boolean canMergeSlot(ItemStack stack, Slot slotIn) { + return canDragIntoSlot(slotIn); + } + + @Override + public boolean canDragIntoSlot(Slot slotIn) { + return slotIn.inventory == playerInventory; + } + + @Override + public boolean canInteractWith(PlayerEntity playerIn) { + return true; + } + + @Override + public ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, PlayerEntity player) { + if (slotId == playerInventory.currentItem && clickTypeIn != ClickType.THROW) + return ItemStack.EMPTY; + + ItemStack held = playerInventory.getItemStack(); + if (slotId < 36) + return super.slotClick(slotId, dragType, clickTypeIn, player); + if (clickTypeIn == ClickType.THROW) + return ItemStack.EMPTY; + + int slot = slotId - 36; + if (clickTypeIn == ClickType.CLONE) { + if (player.isCreative() && held.isEmpty()) { + ItemStack stackInSlot = filterInventory.getStackInSlot(slot).copy(); + stackInSlot.setCount(64); + playerInventory.setItemStack(stackInSlot); + return ItemStack.EMPTY; + } + return ItemStack.EMPTY; + } + + if (held.isEmpty()) { + filterInventory.setStackInSlot(slot, ItemStack.EMPTY); + return ItemStack.EMPTY; + } + + ItemStack insert = held.copy(); + insert.setCount(1); + filterInventory.setStackInSlot(slot, insert); + return held; + } + + @Override + public ItemStack transferStackInSlot(PlayerEntity playerIn, int index) { + if (index < 36) { + ItemStack stackToInsert = playerInventory.getStackInSlot(index); + for (int i = 0; i < filterInventory.getSlots(); i++) { + ItemStack stack = filterInventory.getStackInSlot(i); + if (ItemHandlerHelper.canItemStacksStack(stack, stackToInsert)) + break; + if (stack.isEmpty()) { + ItemStack copy = stackToInsert.copy(); + copy.setCount(1); + filterInventory.insertItem(i, copy, false); + break; + } + } + } else + filterInventory.extractItem(index - 36, 1, false); + return ItemStack.EMPTY; + } + + @Override + public void onContainerClosed(PlayerEntity playerIn) { + super.onContainerClosed(playerIn); + filterItem.getOrCreateTag().put("Items", filterInventory.serializeNBT()); + saveData(filterItem); + } + +} diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/filter/AbstractFilterScreen.java b/src/main/java/com/simibubi/create/modules/logistics/item/filter/AbstractFilterScreen.java new file mode 100644 index 000000000..564c00211 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/logistics/item/filter/AbstractFilterScreen.java @@ -0,0 +1,154 @@ +package com.simibubi.create.modules.logistics.item.filter; + +import static com.simibubi.create.ScreenResources.PLAYER_INVENTORY; +import static net.minecraft.util.text.TextFormatting.GRAY; + +import java.util.Collections; +import java.util.List; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.simibubi.create.AllPackets; +import com.simibubi.create.ScreenResources; +import com.simibubi.create.foundation.gui.AbstractSimiContainerScreen; +import com.simibubi.create.foundation.gui.widgets.IconButton; +import com.simibubi.create.foundation.gui.widgets.Indicator; +import com.simibubi.create.foundation.gui.widgets.Indicator.State; +import com.simibubi.create.foundation.item.ItemDescription.Palette; +import com.simibubi.create.foundation.item.TooltipHelper; +import com.simibubi.create.modules.logistics.item.filter.FilterScreenPacket.Option; + +import net.minecraft.client.gui.widget.Widget; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.util.text.ITextComponent; + +public abstract class AbstractFilterScreen extends AbstractSimiContainerScreen { + + protected ScreenResources background; + + private IconButton resetButton; + private IconButton confirmButton; + + protected AbstractFilterScreen(F container, PlayerInventory inv, ITextComponent title, ScreenResources background) { + super(container, inv, title); + this.background = background; + } + + @Override + protected void init() { + setWindowSize(background.width + 80, background.height + PLAYER_INVENTORY.height + 20); + super.init(); + widgets.clear(); + + resetButton = new IconButton(guiLeft + 15, guiTop + background.height - 30, ScreenResources.I_TRASH); + confirmButton = new IconButton(guiLeft + 159, guiTop + background.height - 30, ScreenResources.I_CONFIRM); + + widgets.add(resetButton); + widgets.add(confirmButton); + } + + @Override + protected void renderWindow(int mouseX, int mouseY, float partialTicks) { + int x = guiLeft; + int y = guiTop; + background.draw(this, x, y); + + int invX = x + 50; + int invY = y + background.height + 10; + PLAYER_INVENTORY.draw(this, invX, invY); + + font.drawString(playerInventory.getDisplayName().getFormattedText(), invX + 7, invY + 6, 0x666666); + font.drawString(I18n.format(container.filterItem.getTranslationKey()), x + 15, y + 9, 0x5B5037); + + RenderHelper.enableGUIStandardItemLighting(); + GlStateManager.pushMatrix(); + GlStateManager.translated(guiLeft + background.width + 0, guiTop + background.height - 60, 0); + GlStateManager.scaled(5, 5, 5); + itemRenderer.renderItemIntoGUI(container.filterItem, 0, 0); + GlStateManager.popMatrix(); + } + + @Override + public void tick() { + handleTooltips(); + super.tick(); + handleIndicators(); + + if (!container.player.getHeldItemMainhand().equals(container.filterItem, false)) + minecraft.player.closeScreen(); + } + + public void handleIndicators() { + List tooltipButtons = getTooltipButtons(); + for (IconButton button : tooltipButtons) + button.active = isButtonEnabled(button); + for (Widget w : widgets) + if (w instanceof Indicator) + ((Indicator) w).state = isIndicatorOn((Indicator) w) ? State.ON : State.OFF; + } + + protected abstract boolean isButtonEnabled(IconButton button); + + protected abstract boolean isIndicatorOn(Indicator indicator); + + protected void handleTooltips() { + List tooltipButtons = getTooltipButtons(); + + for (IconButton button : tooltipButtons) { + if (!button.getToolTip().isEmpty()) { + button.setToolTip(button.getToolTip().get(0)); + button.getToolTip().add(TooltipHelper.holdShift(Palette.Yellow, hasShiftDown())); + } + } + + if (hasShiftDown()) { + List tooltipDescriptions = getTooltipDescriptions(); + for (int i = 0; i < tooltipButtons.size(); i++) + fillToolTip(tooltipButtons.get(i), tooltipDescriptions.get(i)); + } + } + + protected List getTooltipButtons() { + return Collections.emptyList(); + } + + protected List getTooltipDescriptions() { + return Collections.emptyList(); + } + + private void fillToolTip(IconButton button, String tooltip) { + if (!button.isHovered()) + return; + List tip = button.getToolTip(); + tip.addAll(TooltipHelper.cutString(tooltip, GRAY, GRAY)); + } + + @Override + public boolean mouseClicked(double x, double y, int button) { + boolean mouseClicked = super.mouseClicked(x, y, button); + + if (button == 0) { + if (confirmButton.isHovered()) { + minecraft.player.closeScreen(); + return true; + } + if (resetButton.isHovered()) { + container.clearContents(); + contentsCleared(); + sendOptionUpdate(Option.CLEAR); + return true; + } + } + + return mouseClicked; + } + + protected void contentsCleared() { + } + + protected void sendOptionUpdate(Option option) { + AllPackets.channel.sendToServer(new FilterScreenPacket(option)); + } + +} diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/filter/AttributeFilterContainer.java b/src/main/java/com/simibubi/create/modules/logistics/item/filter/AttributeFilterContainer.java new file mode 100644 index 000000000..819678412 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/logistics/item/filter/AttributeFilterContainer.java @@ -0,0 +1,138 @@ +package com.simibubi.create.modules.logistics.item.filter; + +import java.util.ArrayList; +import java.util.List; + +import com.simibubi.create.AllContainers; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.container.ClickType; +import net.minecraft.inventory.container.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.items.SlotItemHandler; + +public class AttributeFilterContainer extends AbstractFilterContainer { + + public enum WhitelistMode { + WHITELIST_DISJ, WHITELIST_CONJ, BLACKLIST; + } + + WhitelistMode whitelistMode; + List selectedAttributes; + + public AttributeFilterContainer(int id, PlayerInventory inv, PacketBuffer extraData) { + super(AllContainers.ATTRIBUTE_FILTER.type, id, inv, extraData); + } + + public AttributeFilterContainer(int id, PlayerInventory inv, ItemStack stack) { + super(AllContainers.ATTRIBUTE_FILTER.type, id, inv, stack); + } + + public void appendSelectedAttribute(ItemAttribute itemAttribute) { + selectedAttributes.add(itemAttribute); + } + + @Override + protected void clearContents() { + selectedAttributes.clear(); + } + + @Override + protected void init() { + super.init(); + ItemStack stack = new ItemStack(Items.NAME_TAG); + stack.setDisplayName( + new StringTextComponent("Selected Tags").applyTextStyles(TextFormatting.RESET, TextFormatting.BLUE)); + filterInventory.setStackInSlot(1, stack); + } + + @Override + protected ItemStackHandler createFilterInventory() { + return new ItemStackHandler(2); + } + + protected void addFilterSlots() { + this.addSlot(new SlotItemHandler(filterInventory, 0, 16, 23)); + this.addSlot(new SlotItemHandler(filterInventory, 1, 59, 56) { + @Override + public boolean canTakeStack(PlayerEntity playerIn) { + return false; + } + }); + } + + @Override + public ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, PlayerEntity player) { + if (slotId == 37) + return ItemStack.EMPTY; + return super.slotClick(slotId, dragType, clickTypeIn, player); + } + + @Override + public boolean canDragIntoSlot(Slot slotIn) { + if (slotIn.slotNumber == 37) + return false; + return super.canDragIntoSlot(slotIn); + } + + @Override + public boolean canMergeSlot(ItemStack stack, Slot slotIn) { + if (slotIn.slotNumber == 37) + return false; + return super.canMergeSlot(stack, slotIn); + } + + @Override + public ItemStack transferStackInSlot(PlayerEntity playerIn, int index) { + if (index == 37) + return ItemStack.EMPTY; + if (index == 36) { + filterInventory.setStackInSlot(37, ItemStack.EMPTY); + return ItemStack.EMPTY; + } + if (index < 36) { + ItemStack stackToInsert = playerInventory.getStackInSlot(index); + ItemStack copy = stackToInsert.copy(); + copy.setCount(1); + filterInventory.setStackInSlot(0, copy); + } + return ItemStack.EMPTY; + } + + @Override + protected int getInventoryOffset() { + return 86; + } + + @Override + protected void readData(ItemStack filterItem) { + selectedAttributes = new ArrayList<>(); + whitelistMode = WhitelistMode.values()[filterItem.getOrCreateTag().getInt("WhitelistMode")]; + ListNBT attributes = filterItem.getOrCreateTag().getList("MatchedAttributes", NBT.TAG_COMPOUND); + attributes.forEach(inbt -> selectedAttributes.add(ItemAttribute.fromNBT((CompoundNBT) inbt))); + } + + @Override + protected void saveData(ItemStack filterItem) { + filterItem.getOrCreateTag().putInt("WhitelistMode", whitelistMode.ordinal()); + ListNBT attributes = new ListNBT(); + selectedAttributes.forEach(at -> { + if (at == null) + return; + CompoundNBT compoundNBT = new CompoundNBT(); + at.serializeNBT(compoundNBT); + attributes.add(compoundNBT); + }); + filterItem.getOrCreateTag().put("MatchedAttributes", attributes); + } + +} diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/filter/AttributeFilterScreen.java b/src/main/java/com/simibubi/create/modules/logistics/item/filter/AttributeFilterScreen.java new file mode 100644 index 000000000..f5b32a1f7 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/logistics/item/filter/AttributeFilterScreen.java @@ -0,0 +1,257 @@ +package com.simibubi.create.modules.logistics.item.filter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.simibubi.create.AllPackets; +import com.simibubi.create.ScreenResources; +import com.simibubi.create.foundation.gui.widgets.IconButton; +import com.simibubi.create.foundation.gui.widgets.Indicator; +import com.simibubi.create.foundation.gui.widgets.Label; +import com.simibubi.create.foundation.gui.widgets.SelectionScrollInput; +import com.simibubi.create.foundation.utility.Lang; +import com.simibubi.create.modules.logistics.item.filter.AttributeFilterContainer.WhitelistMode; +import com.simibubi.create.modules.logistics.item.filter.FilterScreenPacket.Option; + +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextFormatting; + +public class AttributeFilterScreen extends AbstractFilterScreen { + + private static final String PREFIX = "gui.attribute_filter."; + + private IconButton whitelistDis, whitelistCon, blacklist; + private Indicator whitelistDisIndicator, whitelistConIndicator, blacklistIndicator; + private IconButton add; + + private String whitelistDisN = Lang.translate(PREFIX + "whitelist_disjunctive"); + private String whitelistDisDESC = Lang.translate(PREFIX + "whitelist_disjunctive.description"); + private String whitelistConN = Lang.translate(PREFIX + "whitelist_conjunctive"); + private String whitelistConDESC = Lang.translate(PREFIX + "whitelist_conjunctive.description"); + private String blacklistN = Lang.translate(PREFIX + "blacklist"); + private String blacklistDESC = Lang.translate(PREFIX + "blacklist.description"); + + private String referenceH = Lang.translate(PREFIX + "add_reference_item"); + private String noSelectedT = Lang.translate(PREFIX + "no_selected_attributes"); + private String selectedT = Lang.translate(PREFIX + "selected_attributes"); + + private ItemStack lastItemScanned = ItemStack.EMPTY; + private List attributesOfItem = new ArrayList<>(); + private List selectedAttributes = new ArrayList<>(); + private SelectionScrollInput attributeSelector; + private Label attributeSelectorLabel; + + public AttributeFilterScreen(AttributeFilterContainer container, PlayerInventory inv, ITextComponent title) { + super(container, inv, title, ScreenResources.ATTRIBUTE_FILTER); + } + + @Override + protected void init() { + super.init(); + int x = guiLeft; + int y = guiTop; + + whitelistDis = new IconButton(x + 84, y + 58, ScreenResources.I_WHITELIST_OR); + whitelistDis.setToolTip(whitelistDisN); + whitelistCon = new IconButton(x + 102, y + 58, ScreenResources.I_WHITELIST_AND); + whitelistCon.setToolTip(whitelistConN); + blacklist = new IconButton(x + 120, y + 58, ScreenResources.I_WHITELIST_NOT); + blacklist.setToolTip(blacklistN); + + whitelistDisIndicator = new Indicator(x + 84, y + 53, ""); + whitelistConIndicator = new Indicator(x + 102, y + 53, ""); + blacklistIndicator = new Indicator(x + 120, y + 53, ""); + + widgets.addAll(Arrays.asList(blacklist, whitelistCon, whitelistDis, blacklistIndicator, whitelistConIndicator, + whitelistDisIndicator)); + + add = new IconButton(x + 159, y + 22, ScreenResources.I_ADD); + widgets.add(add); + handleIndicators(); + + attributeSelectorLabel = new Label(x + 40, y + 27, "").colored(0xF3EBDE).withShadow(); + attributeSelector = new SelectionScrollInput(x + 37, y + 24, 118, 14); + attributeSelector.forOptions(Arrays.asList("")); + attributeSelector.calling(s -> { + }); + referenceItemChanged(container.filterInventory.getStackInSlot(0)); + + widgets.add(attributeSelector); + widgets.add(attributeSelectorLabel); + + selectedAttributes.clear(); + selectedAttributes + .add(TextFormatting.YELLOW + (container.selectedAttributes.isEmpty() ? noSelectedT : selectedT)); + container.selectedAttributes.forEach(at -> selectedAttributes.add(TextFormatting.GRAY + "- " + at.format())); + + } + + private void referenceItemChanged(ItemStack stack) { + lastItemScanned = stack; + + if (stack.isEmpty()) { + attributeSelector.active = false; + attributeSelector.visible = false; + attributeSelectorLabel.text = TextFormatting.ITALIC + referenceH; + add.active = false; + attributeSelector.calling(s -> { + }); + return; + } + + add.active = true; + attributeSelector.titled(stack.getDisplayName().getFormattedText() + "..."); + attributesOfItem.clear(); + for (ItemAttribute itemAttribute : ItemAttribute.types) + attributesOfItem.addAll(itemAttribute.listAttributesOf(stack)); + List options = attributesOfItem.stream().map(ItemAttribute::format).collect(Collectors.toList()); + attributeSelector.forOptions(options); + attributeSelector.active = true; + attributeSelector.visible = true; + attributeSelector.setState(0); + attributeSelector.calling(i -> { + attributeSelectorLabel.setTextAndTrim(options.get(i), true, 112); + ItemAttribute selected = attributesOfItem.get(i); + for (ItemAttribute existing : container.selectedAttributes) { + CompoundNBT testTag = new CompoundNBT(); + CompoundNBT testTag2 = new CompoundNBT(); + existing.serializeNBT(testTag); + selected.serializeNBT(testTag2); + if (testTag.equals(testTag2)) { + add.active = false; + return; + } + } + add.active = true; + }); + attributeSelector.onChanged(); + } + + @Override + public void renderWindowForeground(int mouseX, int mouseY, float partialTicks) { + ItemStack stack = container.filterInventory.getStackInSlot(1); + GlStateManager.pushMatrix(); + GlStateManager.translatef(0.0F, 0.0F, 32.0F); + this.blitOffset = 200; + this.itemRenderer.zLevel = 200.0F; + this.itemRenderer.renderItemOverlayIntoGUI(font, stack, guiLeft + 59, guiTop + 56, + String.valueOf(selectedAttributes.size() - 1)); + this.blitOffset = 0; + this.itemRenderer.zLevel = 0.0F; + GlStateManager.popMatrix(); + + super.renderWindowForeground(mouseX, mouseY, partialTicks); + } + + @Override + public void tick() { + super.tick(); + ItemStack stackInSlot = container.filterInventory.getStackInSlot(0); + if (!stackInSlot.equals(lastItemScanned, false)) + referenceItemChanged(stackInSlot); + } + + @Override + protected void renderHoveredToolTip(int mouseX, int mouseY) { + if (this.minecraft.player.inventory.getItemStack().isEmpty() && this.hoveredSlot != null + && this.hoveredSlot.getHasStack()) { + if (this.hoveredSlot.slotNumber == 37) { + renderTooltip(selectedAttributes, mouseX, mouseY, font); + return; + } + this.renderTooltip(this.hoveredSlot.getStack(), mouseX, mouseY); + } + super.renderHoveredToolTip(mouseX, mouseY); + } + + @Override + protected List getTooltipButtons() { + return Arrays.asList(blacklist, whitelistCon, whitelistDis); + } + + @Override + protected List getTooltipDescriptions() { + return Arrays.asList(blacklistDESC, whitelistConDESC, whitelistDisDESC); + } + + @Override + public boolean mouseClicked(double x, double y, int button) { + boolean mouseClicked = super.mouseClicked(x, y, button); + + if (button != 0) + return mouseClicked; + + if (blacklist.isHovered()) { + container.whitelistMode = WhitelistMode.BLACKLIST; + sendOptionUpdate(Option.BLACKLIST); + return true; + } + + if (whitelistCon.isHovered()) { + container.whitelistMode = WhitelistMode.WHITELIST_CONJ; + sendOptionUpdate(Option.WHITELIST2); + return true; + } + + if (whitelistDis.isHovered()) { + container.whitelistMode = WhitelistMode.WHITELIST_DISJ; + sendOptionUpdate(Option.WHITELIST); + return true; + } + + if (add.isHovered() && add.active) { + int index = attributeSelector.getState(); + if (index < attributesOfItem.size()) { + add.active = false; + CompoundNBT tag = new CompoundNBT(); + ItemAttribute itemAttribute = attributesOfItem.get(index); + itemAttribute.serializeNBT(tag); + AllPackets.channel.sendToServer(new FilterScreenPacket(Option.ADD_TAG, tag)); + container.selectedAttributes.add(itemAttribute); + if (container.selectedAttributes.size() == 1) + selectedAttributes.set(0, TextFormatting.YELLOW + selectedT); + selectedAttributes.add(TextFormatting.GRAY + "- " + itemAttribute.format()); + return true; + } + } + + return mouseClicked; + } + + @Override + protected void contentsCleared() { + selectedAttributes.clear(); + selectedAttributes.add(TextFormatting.YELLOW + noSelectedT); + if (!lastItemScanned.isEmpty()) + add.active = true; + } + + @Override + protected boolean isButtonEnabled(IconButton button) { + if (button == blacklist) + return container.whitelistMode != WhitelistMode.BLACKLIST; + if (button == whitelistCon) + return container.whitelistMode != WhitelistMode.WHITELIST_CONJ; + if (button == whitelistDis) + return container.whitelistMode != WhitelistMode.WHITELIST_DISJ; + return true; + } + + @Override + protected boolean isIndicatorOn(Indicator indicator) { + if (indicator == blacklistIndicator) + return container.whitelistMode == WhitelistMode.BLACKLIST; + if (indicator == whitelistConIndicator) + return container.whitelistMode == WhitelistMode.WHITELIST_CONJ; + if (indicator == whitelistDisIndicator) + return container.whitelistMode == WhitelistMode.WHITELIST_DISJ; + return false; + } + +} diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterContainer.java b/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterContainer.java new file mode 100644 index 000000000..e3ed993ec --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterContainer.java @@ -0,0 +1,59 @@ +package com.simibubi.create.modules.logistics.item.filter; + +import com.simibubi.create.AllContainers; + +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.items.SlotItemHandler; + +public class FilterContainer extends AbstractFilterContainer { + + boolean respectNBT; + boolean blacklist; + + public FilterContainer(int id, PlayerInventory inv, PacketBuffer extraData) { + super(AllContainers.FILTER.type, id, inv, extraData); + } + + public FilterContainer(int id, PlayerInventory inv, ItemStack stack) { + super(AllContainers.FILTER.type, id, inv, stack); + } + + @Override + protected void addFilterSlots() { + int x = 16; + int y = 21; + + for (int row = 0; row < 2; ++row) + for (int col = 0; col < 9; ++col) + this.addSlot(new SlotItemHandler(filterInventory, col + row * 9, x + col * 18, y + row * 18)); + } + + @Override + protected ItemStackHandler createFilterInventory() { + return FilterItem.getFilterItems(filterItem); + } + + @Override + protected int getInventoryOffset() { + return 100; + } + + @Override + protected void readData(ItemStack filterItem) { + CompoundNBT tag = filterItem.getOrCreateTag(); + respectNBT = tag.getBoolean("RespectNBT"); + blacklist = tag.getBoolean("Blacklist"); + } + + @Override + protected void saveData(ItemStack filterItem) { + CompoundNBT tag = filterItem.getOrCreateTag(); + tag.putBoolean("RespectNBT", respectNBT); + tag.putBoolean("Blacklist", blacklist); + } + +} diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterItem.java b/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterItem.java new file mode 100644 index 000000000..0ac7aba75 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterItem.java @@ -0,0 +1,222 @@ +package com.simibubi.create.modules.logistics.item.filter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.simibubi.create.AllItems; +import com.simibubi.create.AllKeys; +import com.simibubi.create.foundation.item.ItemDescription; +import com.simibubi.create.foundation.utility.Lang; +import com.simibubi.create.modules.logistics.item.filter.AttributeFilterContainer.WhitelistMode; + +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.inventory.container.Container; +import net.minecraft.inventory.container.INamedContainerProvider; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUseContext; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.INBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.util.ActionResult; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.Hand; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.world.World; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.fml.network.NetworkHooks; +import net.minecraftforge.items.ItemHandlerHelper; +import net.minecraftforge.items.ItemStackHandler; + +public class FilterItem extends Item implements INamedContainerProvider { + + public FilterItem(Properties properties) { + super(properties); + } + + @Override + public ActionResultType onItemUse(ItemUseContext context) { + return onItemRightClick(context.getWorld(), context.getPlayer(), context.getHand()).getType(); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void addInformation(ItemStack stack, World worldIn, List tooltip, ITooltipFlag flagIn) { + if (!AllKeys.shiftDown()) { + List makeSummary = makeSummary(stack); + if (makeSummary.isEmpty()) + return; + ItemDescription.add(tooltip, " "); + ItemDescription.add(tooltip, makeSummary); + } + } + + private List makeSummary(ItemStack filter) { + List list = new ArrayList<>(); + + if (AllItems.FILTER.typeOf(filter)) { + ItemStackHandler filterItems = getFilterItems(filter); + boolean blacklist = filter.getOrCreateTag().getBoolean("Blacklist"); + + list.add(TextFormatting.GOLD + + (blacklist ? Lang.translate("gui.filter.blacklist") : Lang.translate("gui.filter.whitelist"))); + int count = 0; + for (int i = 0; i < filterItems.getSlots(); i++) { + if (count > 3) { + list.add(TextFormatting.DARK_GRAY + "- ..."); + break; + } + + ItemStack filterStack = filterItems.getStackInSlot(i); + if (filterStack.isEmpty()) + continue; + list.add(TextFormatting.GRAY + "- " + filterStack.getDisplayName().getFormattedText()); + count++; + } + + if (count == 0) + return Collections.emptyList(); + } + + if (AllItems.PROPERTY_FILTER.typeOf(filter)) { + WhitelistMode whitelistMode = WhitelistMode.values()[filter.getOrCreateTag().getInt("WhitelistMode")]; + list.add(TextFormatting.GOLD + (whitelistMode == WhitelistMode.WHITELIST_CONJ + ? Lang.translate("gui.attribute_filter.whitelist_conjunctive") + : whitelistMode == WhitelistMode.WHITELIST_DISJ + ? Lang.translate("gui.attribute_filter.whitelist_disjunctive") + : Lang.translate("gui.attribute_filter.blacklist"))); + + int count = 0; + ListNBT attributes = filter.getOrCreateTag().getList("MatchedAttributes", NBT.TAG_COMPOUND); + for (INBT inbt : attributes) { + ItemAttribute attribute = ItemAttribute.fromNBT((CompoundNBT) inbt); + if (count > 3) { + list.add(TextFormatting.DARK_GRAY + "- ..."); + break; + } + list.add(TextFormatting.GRAY + "- " + attribute.format()); + count++; + } + + if (count == 0) + return Collections.emptyList(); + } + + return list; + } + + @Override + public ActionResult onItemRightClick(World world, PlayerEntity player, Hand hand) { + ItemStack heldItem = player.getHeldItem(hand); + + if (!player.isSneaking() && hand == Hand.MAIN_HAND) { + if (AllItems.LOGISTICAL_FILTER.typeOf(heldItem)) + return ActionResult.newResult(ActionResultType.FAIL, heldItem); + if (!world.isRemote && player instanceof ServerPlayerEntity) + NetworkHooks.openGui((ServerPlayerEntity) player, this, buf -> { + buf.writeItemStack(heldItem); + }); + return ActionResult.newResult(ActionResultType.SUCCESS, heldItem); + } + return ActionResult.newResult(ActionResultType.PASS, heldItem); + } + + @Override + public Container createMenu(int id, PlayerInventory inv, PlayerEntity player) { + ItemStack heldItem = player.getHeldItemMainhand(); + if (AllItems.FILTER.typeOf(heldItem)) + return new FilterContainer(id, inv, heldItem); + if (AllItems.PROPERTY_FILTER.typeOf(heldItem)) + return new AttributeFilterContainer(id, inv, heldItem); + return null; + } + + @Override + public ITextComponent getDisplayName() { + return new StringTextComponent(getTranslationKey()); + } + + public static ItemStackHandler getFilterItems(ItemStack stack) { + ItemStackHandler newInv = new ItemStackHandler(18); + if (!AllItems.FILTER.typeOf(stack)) + throw new IllegalArgumentException("Cannot get filter items from non-filter: " + stack); + CompoundNBT invNBT = stack.getOrCreateChildTag("Items"); + if (!invNBT.isEmpty()) + newInv.deserializeNBT(invNBT); + return newInv; + } + + public static boolean test(ItemStack stack, ItemStack filter) { + return test(stack, filter, false); + } + + private static boolean test(ItemStack stack, ItemStack filter, boolean matchNBT) { + if (!(filter.getItem() instanceof FilterItem)) + return (matchNBT ? ItemHandlerHelper.canItemStacksStack(filter, stack) + : ItemStack.areItemsEqual(filter, stack)); + + if (AllItems.FILTER.typeOf(filter)) { + ItemStackHandler filterItems = getFilterItems(filter); + boolean respectNBT = filter.getOrCreateTag().getBoolean("RespectNBT"); + boolean blacklist = filter.getOrCreateTag().getBoolean("Blacklist"); + for (int slot = 0; slot < filterItems.getSlots(); slot++) { + ItemStack stackInSlot = filterItems.getStackInSlot(slot); + if (stackInSlot.isEmpty()) + continue; + boolean matches = test(stack, stackInSlot, respectNBT); + if (matches) + return !blacklist; + } + return blacklist; + } + + if (AllItems.PROPERTY_FILTER.typeOf(filter)) { + WhitelistMode whitelistMode = WhitelistMode.values()[filter.getOrCreateTag().getInt("WhitelistMode")]; + ListNBT attributes = filter.getOrCreateTag().getList("MatchedAttributes", NBT.TAG_COMPOUND); + for (INBT inbt : attributes) { + ItemAttribute attribute = ItemAttribute.fromNBT((CompoundNBT) inbt); + boolean matches = attribute.appliesTo(stack); + + if (matches) { + switch (whitelistMode) { + case BLACKLIST: + return false; + case WHITELIST_CONJ: + continue; + case WHITELIST_DISJ: + return true; + } + } else { + switch (whitelistMode) { + case BLACKLIST: + continue; + case WHITELIST_CONJ: + return false; + case WHITELIST_DISJ: + continue; + } + } + } + + switch (whitelistMode) { + case BLACKLIST: + return true; + case WHITELIST_CONJ: + return true; + case WHITELIST_DISJ: + return false; + } + } + + return false; + } + +} diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterScreen.java b/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterScreen.java new file mode 100644 index 000000000..d25a87f48 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterScreen.java @@ -0,0 +1,132 @@ +package com.simibubi.create.modules.logistics.item.filter; + +import java.util.Arrays; +import java.util.List; + +import com.simibubi.create.ScreenResources; +import com.simibubi.create.foundation.gui.widgets.IconButton; +import com.simibubi.create.foundation.gui.widgets.Indicator; +import com.simibubi.create.foundation.utility.Lang; +import com.simibubi.create.modules.logistics.item.filter.FilterScreenPacket.Option; + +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.util.text.ITextComponent; + +public class FilterScreen extends AbstractFilterScreen { + + private static final String PREFIX = "gui.filter."; + + private String whitelistN = Lang.translate(PREFIX + "whitelist"); + private String whitelistDESC = Lang.translate(PREFIX + "whitelist.description"); + private String blacklistN = Lang.translate(PREFIX + "blacklist"); + private String blacklistDESC = Lang.translate(PREFIX + "blacklist.description"); + + private String respectDataN = Lang.translate(PREFIX + "respect_data"); + private String respectDataDESC = Lang.translate(PREFIX + "respect_data.description"); + private String ignoreDataN = Lang.translate(PREFIX + "ignore_data"); + private String ignoreDataDESC = Lang.translate(PREFIX + "ignore_data.description"); + + private IconButton whitelist, blacklist; + private IconButton respectNBT, ignoreNBT; + private Indicator whitelistIndicator, blacklistIndicator; + private Indicator respectNBTIndicator, ignoreNBTIndicator; + + public FilterScreen(FilterContainer container, PlayerInventory inv, ITextComponent title) { + super(container, inv, title, ScreenResources.FILTER); + } + + @Override + protected void init() { + super.init(); + int x = guiLeft; + int y = guiTop; + + blacklist = new IconButton(x + 58, y + 72, ScreenResources.I_BLACKLIST); + blacklist.setToolTip(blacklistN); + whitelist = new IconButton(x + 76, y + 72, ScreenResources.I_WHITELIST); + whitelist.setToolTip(whitelistN); + blacklistIndicator = new Indicator(x + 58, y + 67, ""); + whitelistIndicator = new Indicator(x + 76, y + 67, ""); + widgets.addAll(Arrays.asList(blacklist, whitelist, blacklistIndicator, whitelistIndicator)); + + respectNBT = new IconButton(x + 98, y + 72, ScreenResources.I_RESPECT_NBT); + respectNBT.setToolTip(respectDataN); + ignoreNBT = new IconButton(x + 116, y + 72, ScreenResources.I_IGNORE_NBT); + ignoreNBT.setToolTip(ignoreDataN); + respectNBTIndicator = new Indicator(x + 98, y + 67, ""); + ignoreNBTIndicator = new Indicator(x + 116, y + 67, ""); + widgets.addAll(Arrays.asList(respectNBT, ignoreNBT, respectNBTIndicator, ignoreNBTIndicator)); + handleIndicators(); + } + + @Override + public boolean mouseClicked(double x, double y, int button) { + boolean mouseClicked = super.mouseClicked(x, y, button); + + if (button != 0) + return mouseClicked; + + if (blacklist.isHovered()) { + container.blacklist = true; + sendOptionUpdate(Option.BLACKLIST); + return true; + } + + if (whitelist.isHovered()) { + container.blacklist = false; + sendOptionUpdate(Option.WHITELIST); + return true; + } + + if (respectNBT.isHovered()) { + container.respectNBT = true; + sendOptionUpdate(Option.RESPECT_DATA); + return true; + } + + if (ignoreNBT.isHovered()) { + container.respectNBT = false; + sendOptionUpdate(Option.IGNORE_DATA); + return true; + } + + return mouseClicked; + } + + @Override + protected List getTooltipButtons() { + return Arrays.asList(blacklist, whitelist, respectNBT, ignoreNBT); + } + + @Override + protected List getTooltipDescriptions() { + return Arrays.asList(blacklistDESC, whitelistDESC, respectDataDESC, ignoreDataDESC); + } + + @Override + protected boolean isButtonEnabled(IconButton button) { + if (button == blacklist) + return !container.blacklist; + if (button == whitelist) + return container.blacklist; + if (button == respectNBT) + return !container.respectNBT; + if (button == ignoreNBT) + return container.respectNBT; + return true; + } + + @Override + protected boolean isIndicatorOn(Indicator indicator) { + if (indicator == blacklistIndicator) + return container.blacklist; + if (indicator == whitelistIndicator) + return !container.blacklist; + if (indicator == respectNBTIndicator) + return container.respectNBT; + if (indicator == ignoreNBTIndicator) + return !container.respectNBT; + return false; + } + +} diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterScreenPacket.java b/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterScreenPacket.java new file mode 100644 index 000000000..0ce993235 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterScreenPacket.java @@ -0,0 +1,83 @@ +package com.simibubi.create.modules.logistics.item.filter; + +import java.util.function.Supplier; + +import com.simibubi.create.foundation.packet.SimplePacketBase; +import com.simibubi.create.modules.logistics.item.filter.AttributeFilterContainer.WhitelistMode; + +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.network.NetworkEvent.Context; + +public class FilterScreenPacket extends SimplePacketBase { + + enum Option { + CLEAR, WHITELIST, WHITELIST2, BLACKLIST, RESPECT_DATA, IGNORE_DATA, ADD_TAG; + } + + private Option option; + private CompoundNBT data; + + public FilterScreenPacket(Option option) { + this(option, new CompoundNBT()); + } + + public FilterScreenPacket(Option option, CompoundNBT data) { + this.option = option; + this.data = data; + } + + public FilterScreenPacket(PacketBuffer buffer) { + option = Option.values()[buffer.readInt()]; + data = buffer.readCompoundTag(); + } + + @Override + public void write(PacketBuffer buffer) { + buffer.writeInt(option.ordinal()); + buffer.writeCompoundTag(data); + } + + @Override + public void handle(Supplier context) { + context.get().enqueueWork(() -> { + ServerPlayerEntity player = context.get().getSender(); + + if (player.openContainer instanceof AbstractFilterContainer) { + AbstractFilterContainer c = (AbstractFilterContainer) player.openContainer; + if (option == Option.CLEAR) { + c.clearContents(); + return; + } + } + + if (player.openContainer instanceof FilterContainer) { + FilterContainer c = (FilterContainer) player.openContainer; + if (option == Option.WHITELIST) + c.blacklist = false; + if (option == Option.BLACKLIST) + c.blacklist = true; + if (option == Option.RESPECT_DATA) + c.respectNBT = true; + if (option == Option.IGNORE_DATA) + c.respectNBT = false; + } + + if (player.openContainer instanceof AttributeFilterContainer) { + AttributeFilterContainer c = (AttributeFilterContainer) player.openContainer; + if (option == Option.WHITELIST) + c.whitelistMode = WhitelistMode.WHITELIST_DISJ; + if (option == Option.WHITELIST2) + c.whitelistMode = WhitelistMode.WHITELIST_CONJ; + if (option == Option.BLACKLIST) + c.whitelistMode = WhitelistMode.BLACKLIST; + if (option == Option.ADD_TAG) + c.appendSelectedAttribute(ItemAttribute.fromNBT(data)); + } + + }); + context.get().setPacketHandled(true); + } + +} diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/filter/ItemAttribute.java b/src/main/java/com/simibubi/create/modules/logistics/item/filter/ItemAttribute.java new file mode 100644 index 000000000..11f17fa82 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/logistics/item/filter/ItemAttribute.java @@ -0,0 +1,274 @@ +package com.simibubi.create.modules.logistics.item.filter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.base.Predicates; +import com.simibubi.create.foundation.utility.Lang; + +import net.minecraft.client.resources.I18n; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemGroup; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.tileentity.AbstractFurnaceTileEntity; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.forgespi.language.IModInfo; + +public interface ItemAttribute { + + static List types = new ArrayList<>(); + + static ItemAttribute standard = register(StandardTraits.DUMMY); + static ItemAttribute inTag = register(new InTag(new ResourceLocation("dummy"))); + static ItemAttribute inItemGroup = register(new InItemGroup(ItemGroup.MISC)); + static ItemAttribute addedBy = register(new AddedBy("dummy")); + + static ItemAttribute register(ItemAttribute attributeType) { + types.add(attributeType); + return attributeType; + } + + public boolean appliesTo(ItemStack stack); + + public List listAttributesOf(ItemStack stack); + + public String getTranslationKey(); + + void writeNBT(CompoundNBT nbt); + + ItemAttribute readNBT(CompoundNBT nbt); + + public default void serializeNBT(CompoundNBT nbt) { + CompoundNBT compound = new CompoundNBT(); + writeNBT(compound); + nbt.put(getNBTKey(), compound); + } + + public static ItemAttribute fromNBT(CompoundNBT nbt) { + for (ItemAttribute itemAttribute : types) { + if (!itemAttribute.canRead(nbt)) + continue; + return itemAttribute.readNBT(nbt.getCompound(itemAttribute.getNBTKey())); + } + return null; + } + + default Object[] getTranslationParameters() { + return new String[0]; + } + + default boolean canRead(CompoundNBT nbt) { + return nbt.contains(getNBTKey()); + } + + default String getNBTKey() { + return getTranslationKey(); + } + + @OnlyIn(value = Dist.CLIENT) + default String format() { + return Lang.translate("item_attributes." + getTranslationKey(), getTranslationParameters()); + } + + public static enum StandardTraits implements ItemAttribute { + + DUMMY(s -> false), + PLACEABLE(s -> s.getItem() instanceof BlockItem), + CONSUMABLE(ItemStack::isFood), + ENCHANTED(ItemStack::isEnchanted), + DAMAGED(ItemStack::isDamaged), + BADLY_DAMAGED(s -> s.isDamaged() && s.getDamage() / s.getMaxDamage() > 3 / 4f), + NOT_STACKABLE(Predicates.not(ItemStack::isStackable)), + EQUIPABLE(s -> s.getEquipmentSlot() != null), + FURNACE_FUEL(AbstractFurnaceTileEntity::isFuel); + + private Predicate test; + + private StandardTraits(Predicate test) { + this.test = test; + } + + @Override + public boolean appliesTo(ItemStack stack) { + return test.test(stack); + } + + @Override + public List listAttributesOf(ItemStack stack) { + List attributes = new ArrayList<>(); + for (StandardTraits trait : values()) + if (trait.test.test(stack)) + attributes.add(trait); + return attributes; + } + + @Override + public String getTranslationKey() { + return Lang.asId(name()); + } + + @Override + public String getNBTKey() { + return "standard_trait"; + } + + @Override + public void writeNBT(CompoundNBT nbt) { + nbt.putBoolean(name(), true); + } + + @Override + public ItemAttribute readNBT(CompoundNBT nbt) { + for (StandardTraits trait : values()) + if (nbt.contains(trait.name())) + return trait; + return null; + } + + } + + public static class InTag implements ItemAttribute { + + ResourceLocation tagName; + + public InTag(ResourceLocation tagName) { + this.tagName = tagName; + } + + @Override + public boolean appliesTo(ItemStack stack) { + return stack.getItem().getTags().contains(tagName); + } + + @Override + public List listAttributesOf(ItemStack stack) { + return stack.getItem().getTags().stream().map(InTag::new).collect(Collectors.toList()); + } + + @Override + public String getTranslationKey() { + return "in_tag"; + } + + @Override + public Object[] getTranslationParameters() { + return new Object[] { "#" + tagName.toString() }; + } + + @Override + public void writeNBT(CompoundNBT nbt) { + nbt.putString("space", tagName.getNamespace()); + nbt.putString("path", tagName.getPath()); + } + + @Override + public ItemAttribute readNBT(CompoundNBT nbt) { + return new InTag(new ResourceLocation(nbt.getString("space"), nbt.getString("path"))); + } + + } + + public static class InItemGroup implements ItemAttribute { + + private ItemGroup group; + + public InItemGroup(ItemGroup group) { + this.group = group; + } + + @Override + public boolean appliesTo(ItemStack stack) { + Item item = stack.getItem(); + return item.getGroup() == group; + } + + @Override + public List listAttributesOf(ItemStack stack) { + ItemGroup group = stack.getItem().getGroup(); + return group == null ? Collections.emptyList() : Arrays.asList(new InItemGroup(group)); + } + + @Override + public String getTranslationKey() { + return "in_item_group"; + } + + @OnlyIn(value = Dist.CLIENT) + public String format() { + return Lang.translate("item_attributes." + getTranslationKey(), I18n.format(group.getTranslationKey())); + } + + @Override + public void writeNBT(CompoundNBT nbt) { + nbt.putString("path", group.getPath()); + } + + @Override + public ItemAttribute readNBT(CompoundNBT nbt) { + String readPath = nbt.getString("path"); + for (ItemGroup group : ItemGroup.GROUPS) + if (group.getPath().equals(readPath)) + return new InItemGroup(group); + return null; + } + + } + + public static class AddedBy implements ItemAttribute { + + private String modId; + + public AddedBy(String modId) { + this.modId = modId; + } + + @Override + public boolean appliesTo(ItemStack stack) { + return modId.equals(stack.getItem().getCreatorModId(stack)); + } + + @Override + public List listAttributesOf(ItemStack stack) { + String id = stack.getItem().getCreatorModId(stack); + return id == null ? Collections.emptyList() : Arrays.asList(new AddedBy(id)); + } + + @Override + public String getTranslationKey() { + return "added_by"; + } + + @Override + public Object[] getTranslationParameters() { + Optional modContainerById = ModList.get().getModContainerById(modId); + String name = modContainerById.map(ModContainer::getModInfo).map(IModInfo::getDisplayName) + .orElse(StringUtils.capitalize(modId)); + return new Object[] { name }; + } + + @Override + public void writeNBT(CompoundNBT nbt) { + nbt.putString("id", modId); + } + + @Override + public ItemAttribute readNBT(CompoundNBT nbt) { + return new AddedBy(nbt.getString("id")); + } + + } + +} diff --git a/src/main/resources/assets/create/lang/en_us.json b/src/main/resources/assets/create/lang/en_us.json index 70c83ceda..bf6e7114d 100644 --- a/src/main/resources/assets/create/lang/en_us.json +++ b/src/main/resources/assets/create/lang/en_us.json @@ -16,6 +16,8 @@ "item.create.belt_connector": "Mechanical Belt", "item.create.goggles": "Engineer's Goggles", "item.create.filter": "Filter", + "item.create.property_filter": "Attribute Filter", + "item.create.logistical_filter": "Address Filter", "item.create.rose_quartz": "Rose Quartz", "item.create.refined_rose_quartz": "Refined Rose Quartz", "item.create.refined_radiance_cube": "Refined Radiance", @@ -406,6 +408,37 @@ "create.gui.requester.requestedItemCount": "Requested Amount", "create.gui.storage.passiveModeOnly": "Item Storage is Passive only", + "create.gui.filter.blacklist": "Blacklist", + "create.gui.filter.blacklist.description": "Items pass if they do NOT match any of the above. An empty Blacklist accepts everything.", + "create.gui.filter.whitelist": "Whitelist", + "create.gui.filter.whitelist.description": "Items pass if they match any of the above. An empty Whitelist rejects anything.", + "create.gui.filter.respect_data": "Respect Data", + "create.gui.filter.respect_data.description": "Items only match if their durability, enchantments and other attributes match as well.", + "create.gui.filter.ignore_data": "Ignore Data", + "create.gui.filter.ignore_data.description": "Items match regardless of their attributes.", + + "create.item_attributes.placeable": "is placeable", + "create.item_attributes.consumable": "can be eaten", + "create.item_attributes.enchanted": "is enchanted", + "create.item_attributes.damaged": "is damaged", + "create.item_attributes.badly_damaged": "is heavily damaged", + "create.item_attributes.not_stackable": "cannot stack", + "create.item_attributes.equipable": "can be equipped", + "create.item_attributes.furnace_fuel": "is furnace fuel", + "create.item_attributes.in_tag": "is tagged %1$s", + "create.item_attributes.in_item_group": "belongs to %1$s", + "create.item_attributes.added_by": "was added by %1$s", + + "create.gui.attribute_filter.no_selected_attributes": "No attributes selected", + "create.gui.attribute_filter.selected_attributes": "Selected attributes:", + "create.gui.attribute_filter.whitelist_disjunctive": "Whitelist (Any)", + "create.gui.attribute_filter.whitelist_disjunctive.description": "Items pass if they have at least one of the selected attributes.", + "create.gui.attribute_filter.whitelist_conjunctive": "Whitelist (All)", + "create.gui.attribute_filter.whitelist_conjunctive.description": "Items pass only if they have ALL of the selected attributes.", + "create.gui.attribute_filter.blacklist": "Blacklist", + "create.gui.attribute_filter.blacklist.description": "Items pass if they do NOT have any of the selected attributes.", + "create.gui.attribute_filter.add_reference_item": "Add Reference Item", + "create.tooltip.holdKey": "Hold [%1$s]", "create.tooltip.holdKeyOrKey": "Hold [%1$s] or [%2$s]", "create.tooltip.keyShift": "Shift", @@ -425,6 +458,7 @@ "create.tooltip.capacityProvided.low": "Small", "create.tooltip.capacityProvided.medium": "Medium", "create.tooltip.capacityProvided.high": "Large", + "create.tooltip.capacityProvided.asGenerator": "(As Generator)", "create.tooltip.wip": "WIP", "create.tooltip.workInProgress": "Work in progress!", @@ -484,7 +518,21 @@ "item.create.tree_fertilizer.tooltip.summary": "A powerful combination of minerals suitable for common tree types", "item.create.tree_fertilizer.tooltip.condition1": "When used on Sapling", "item.create.tree_fertilizer.tooltip.behaviour1": "Grows Trees regardless of their spacing Conditions", - + + "item.create.filter.tooltip": "FILTER", + "item.create.filter.tooltip.summary": "_Controls_ _outputs_ and inputs of logistical devices with more _precision,_ matching them against a _set_ _of_ _items_ or several _nested_ _filters._", + "item.create.filter.tooltip.condition1": "When in filter slot", + "item.create.filter.tooltip.behaviour1": "_Controls_ item flow according to its _configuration._", + "item.create.filter.tooltip.condition2": "When R-Clicked", + "item.create.filter.tooltip.behaviour2": "Opens the _configuration_ _interface._", + + "item.create.property_filter.tooltip": "ATTRIBUTE FILTER", + "item.create.property_filter.tooltip.summary": "_Controls_ _outputs_ and inputs of logistical devices with more _precision,_ matching them against a _set_ _of_ item _attributes_ and _categories._", + "item.create.property_filter.tooltip.condition1": "When in filter slot", + "item.create.property_filter.tooltip.behaviour1": "_Controls_ item flow according to its _configuration._", + "item.create.property_filter.tooltip.condition2": "When R-Clicked", + "item.create.property_filter.tooltip.behaviour2": "Opens the _configuration_ _interface._", + "block.create.cocoa_log.tooltip": "COCOA LOG", "block.create.cocoa_log.tooltip.summary": "An augmented jungle log allowing for easier automation of _Cocoa_ _Beans_", "block.create.cocoa_log.tooltip.condition1": "When Mature", @@ -734,6 +782,7 @@ "block.create.package_funnel.tooltip": "WIP", "block.create.logisticians_table.tooltip": "WIP", "item.create.logistical_dial.tooltip": "WIP", + "item.create.logistical_filter.tooltip": "WIP", "itemGroup.create": "Create" } diff --git a/src/main/resources/assets/create/models/item/filter.json b/src/main/resources/assets/create/models/item/filter.json index 1f0cf52d6..40fb9ed09 100644 --- a/src/main/resources/assets/create/models/item/filter.json +++ b/src/main/resources/assets/create/models/item/filter.json @@ -2,10 +2,8 @@ "credit": "Made with Blockbench", "parent": "block/block", "textures": { - "0": "create:block/net", - "1": "create:block/brass_casing", - "2": "create:block/clutch_off", - "particle": "create:block/net" + "0": "create:item/filter", + "particle": "create:item/filter" }, "elements": [ { @@ -13,12 +11,12 @@ "from": [13, 0, 3], "to": [15, 2, 13], "faces": { - "north": {"uv": [0, 6, 2, 8], "texture": "#1"}, - "east": {"uv": [3, 0, 13, 2], "texture": "#1"}, - "south": {"uv": [14, 6, 16, 8], "texture": "#1"}, - "west": {"uv": [3, 0, 13, 2], "texture": "#1"}, - "up": {"uv": [0, 3, 2, 13], "rotation": 180, "texture": "#1"}, - "down": {"uv": [14, 3, 16, 13], "texture": "#1"} + "north": {"uv": [14, 0, 16, 2], "texture": "#0"}, + "east": {"uv": [0, 0, 2, 10], "rotation": 90, "texture": "#0"}, + "south": {"uv": [14, 0, 16, 2], "rotation": 180, "texture": "#0"}, + "west": {"uv": [14, 0, 16, 10], "rotation": 270, "texture": "#0"}, + "up": {"uv": [14, 0, 16, 10], "rotation": 180, "texture": "#0"}, + "down": {"uv": [14, 0, 16, 10], "rotation": 180, "texture": "#0"} } }, { @@ -26,12 +24,12 @@ "from": [1, 0, 3], "to": [3, 2, 13], "faces": { - "north": {"uv": [2, 6, 0, 8], "texture": "#1"}, - "east": {"uv": [13, 0, 3, 2], "texture": "#1"}, - "south": {"uv": [16, 6, 14, 8], "texture": "#1"}, - "west": {"uv": [13, 0, 3, 2], "texture": "#1"}, - "up": {"uv": [2, 3, 0, 13], "rotation": 180, "texture": "#1"}, - "down": {"uv": [16, 3, 14, 13], "texture": "#1"} + "north": {"uv": [16, 0, 14, 2], "texture": "#0"}, + "east": {"uv": [14, 10, 16, 0], "rotation": 270, "texture": "#0"}, + "south": {"uv": [16, 0, 14, 2], "rotation": 180, "texture": "#0"}, + "west": {"uv": [0, 10, 2, 0], "rotation": 90, "texture": "#0"}, + "up": {"uv": [16, 0, 14, 10], "rotation": 180, "texture": "#0"}, + "down": {"uv": [16, 0, 14, 10], "rotation": 180, "texture": "#0"} } }, { @@ -39,8 +37,12 @@ "from": [3, 1, 4], "to": [13, 1.1, 12], "faces": { - "up": {"uv": [3.5, 4.5, 13.5, 12.5], "texture": "#0"}, - "down": {"uv": [3.5, 4.5, 13.5, 12.5], "texture": "#0"} + "north": {"uv": [0, 0, 0, 0], "texture": "#0"}, + "east": {"uv": [0, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [0, 0, 0, 0], "texture": "#0"}, + "west": {"uv": [0, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [2.5, 0.5, 12.5, 8.5], "texture": "#0"}, + "down": {"uv": [2.5, 0.5, 12.5, 8.5], "texture": "#0"} } }, { @@ -48,10 +50,12 @@ "from": [3, 0.5, 3], "to": [13, 1.5, 4], "faces": { - "north": {"uv": [2, 7, 12, 8], "texture": "#2"}, - "south": {"uv": [3, 7, 13, 8], "texture": "#2"}, - "up": {"uv": [3, 7, 13, 8], "texture": "#2"}, - "down": {"uv": [3, 7, 13, 8], "texture": "#2"} + "north": {"uv": [3, 9, 13, 10], "texture": "#0"}, + "east": {"uv": [0, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [3, 9, 13, 10], "texture": "#0"}, + "west": {"uv": [0, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [3, 9, 13, 10], "texture": "#0"}, + "down": {"uv": [3, 9, 13, 10], "texture": "#0"} } }, { @@ -59,10 +63,12 @@ "from": [3, 0.5, 12], "to": [13, 1.5, 13], "faces": { - "north": {"uv": [13, 7, 3, 8], "texture": "#2"}, - "south": {"uv": [14, 7, 4, 8], "texture": "#2"}, - "up": {"uv": [3, 8, 13, 7], "texture": "#2"}, - "down": {"uv": [3, 8, 13, 7], "texture": "#2"} + "north": {"uv": [3, 9, 13, 10], "texture": "#0"}, + "east": {"uv": [0, 0, 0, 0], "texture": "#0"}, + "south": {"uv": [3, 9, 13, 10], "texture": "#0"}, + "west": {"uv": [0, 0, 0, 0], "texture": "#0"}, + "up": {"uv": [3, 9, 13, 10], "texture": "#0"}, + "down": {"uv": [3, 9, 13, 10], "texture": "#0"} } } ], @@ -88,7 +94,7 @@ "scale": [0.5, 0.5, 0.5] }, "ground": { - "translation": [0, 0.75, 0], + "translation": [0, 3, 0], "scale": [0.5, 0.5, 0.5] }, "gui": { diff --git a/src/main/resources/assets/create/models/item/logistical_filter.json b/src/main/resources/assets/create/models/item/logistical_filter.json new file mode 100644 index 000000000..709b0ab34 --- /dev/null +++ b/src/main/resources/assets/create/models/item/logistical_filter.json @@ -0,0 +1,7 @@ +{ + "parent": "create:item/filter", + "textures": { + "0": "create:item/logistical_filter", + "particle": "create:item/logistical_filter" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/create/models/item/property_filter.json b/src/main/resources/assets/create/models/item/property_filter.json new file mode 100644 index 000000000..93cc3e8e0 --- /dev/null +++ b/src/main/resources/assets/create/models/item/property_filter.json @@ -0,0 +1,7 @@ +{ + "parent": "create:item/filter", + "textures": { + "0": "create:item/property_filter", + "particle": "create:item/property_filter" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/create/textures/block/copper_casing.png b/src/main/resources/assets/create/textures/block/copper_casing.png new file mode 100644 index 0000000000000000000000000000000000000000..a42508803759344be4066812c98c593ac2cd73bb GIT binary patch literal 554 zcmV+_0@eMAP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0l!H^K~y+TRgyhU z!$1&)*XwocWZ8s-8VUpj4Wgi+phw~i6x@It&~Xn64gq(dMnXbCAS47rB*B>2*iQI1 zgDiN8UuI`#X6G+B-dwfI{dQuMQfWGcO%%bR0n@yyio(QkHXd>G+T)`GI0(Zqiozr@ z{NhAMEj6E4#k9<_^6LwOx+u;st_ z7SC#)3tKI&t+md%!C+-H$wcnD9vp_6qohF2T4^11I-CW0?#loh>uVy|j_dc9d545K zYQD%?&Rw@O==YMOji#Pe-c~6zjZAsYwKboKy^$c)<|F+in!!+7=38^abc;VgPucy>N8yO3NuFA5? zbJ{FML?x@|muGML-;*K2zUVstE#NQ6Q0k%4k`g0d%z;PC5+;~j2!p1EyWt}&3*sH- zbj?3kSQ|k{T48EiNDjsJr;zKGekd@AfR07*qoM6N<$g5~@3vH$=8 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/create/textures/gui/filter.pdn b/src/main/resources/assets/create/textures/gui/filter.pdn deleted file mode 100644 index 31cc0e7587e890715f360c8c8eb61163a46ee6d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9152 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 E2NTHBpa1{> literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/create/textures/gui/icons.png b/src/main/resources/assets/create/textures/gui/icons.png index e451bcdad08a8ecb742e3e915f20cea4cd9c8e60..5f8a79ec84b14e358ca2a09e2f36dd4c27a69af0 100644 GIT binary patch delta 2665 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 JPDHLkV1n%Rcvk=b delta 200 zcmV;(05|{90+s@hR)4EWL_t(IPhE7#IMvrWU-SS%C-u0000+aC%MVb>lsd$Dk8c za8ZL{He-(iPf$;{8{>|chVud<4jZ}Mf*&1_K5&46!An|9R7#VdA7}`Jr>mdKI;Vst E0H=T}kN^Mx diff --git a/src/main/resources/assets/create/textures/item/filter_2.png b/src/main/resources/assets/create/textures/item/filter_2.png deleted file mode 100644 index bf8d0a23ea6a9ec17265b005bd16dedd3a4318be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucK_^ca#}Etu+aC%MVb>lsdcwnk_ z_<_LYZa2mS87{}B>&LSN?Z_zU diff --git a/src/main/resources/assets/create/textures/item/logistical_filter.png b/src/main/resources/assets/create/textures/item/logistical_filter.png new file mode 100644 index 0000000000000000000000000000000000000000..a2989b1dbbd688c5f466babad98d01710fc949a7 GIT binary patch literal 376 zcmV-;0f+vHP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T+ z`=1t}@*fBou3S0zf8B~nSlNdUu7a~cG)T|(xhemdK7aYd0K!+V9{PXl%0-62@LWc0 zY#X~!I2#0*m>C(qegDnC1m*z&vWE9>Ut(p$)IhZ`{P^{Ufk{|MfB}SY*@n!9sR3zb zWr5hu#LUdhz{to*&vo^AS)up3ykTJivQWz*cc`)xWVY4CH!B2 zla1l;zyH911)2^bpnwq+=s>&PeEP|7_|`kdyabK^no7D1j4&6F3*ZTfTrg??0|Nl8 W(J;=`&GxAP0000vj&@M6iV(fuK=Q`i8UW#F0>*;yY&UoVK|GNK4 z2~0;1bWhK3bXv0e!TIy@JU(+|bOQ7;^%!3M{`{DoSy@A|F)8ub&+H!e-`xk7Z7wJ> z+Q6{5V`eY&oAdwc>zZ?a|Ng9g*j{0q){De523LN~pD20g0EbuT6ow@ZD*QcC2M#bW YRP8+mdKI;Vst0EQliUH||9 literal 0 HcmV?d00001