diff --git a/dependencies.gradle b/dependencies.gradle
index 26b5a599..01284063 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -10,12 +10,16 @@ dependencies {
compileOnly("com.github.GTNewHorizons:BloodMagic:1.8.5") {transitive = false}
compileOnly("com.github.GTNewHorizons:CraftTweaker:3.4.2:dev") {transitive = false}
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.11:dev") {transitive = false}
- compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.97:dev") {transitive = false}
+ compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.97:dev") {
+ exclude module: "Avaritia"
+ }
+ compileOnly("com.github.GTNewHorizons:ModularUI2:2.3.25-1.7.10:dev")
+ compileOnly("com.github.GTNewHorizons:GTNHLib:0.9.0:dev")
compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") {transitive = false}
compileOnly("curse.maven:cofh-lib-220333:2388748") {transitive = false}
compileOnly("curse.maven:cofh-core-69162:2388751") {transitive = false}
compileOnly("curse.maven:witchery-69673:2234410") {transitive = false}
- runtimeOnly("com.github.GTNewHorizons:GTNHLib:0.7.7:dev")
+ runtimeOnly("com.github.GTNewHorizons:GTNHLib:0.9.0:dev")
}
diff --git a/src/main/java/fox/spiteful/avaritia/Mods.java b/src/main/java/fox/spiteful/avaritia/Mods.java
new file mode 100644
index 00000000..77320166
--- /dev/null
+++ b/src/main/java/fox/spiteful/avaritia/Mods.java
@@ -0,0 +1,78 @@
+package fox.spiteful.avaritia;
+
+import java.util.Locale;
+
+import net.minecraft.util.ResourceLocation;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.gtnhlib.util.data.IMod;
+import com.gtnewhorizon.gtnhmixins.builders.ITargetMod;
+import com.gtnewhorizon.gtnhmixins.builders.TargetModBuilder;
+
+import cpw.mods.fml.common.Loader;
+
+@SuppressWarnings("unused")
+public enum Mods implements IMod, ITargetMod {
+
+ AE2FluidCraft(Names.A_E2_FLUID_CRAFT),
+ ModularUI2(Names.MODULAR_U_I_2),
+
+ ;
+
+ public static class Names {
+
+ // spotless:off
+
+ public static final String A_E2_FLUID_CRAFT = "ae2fc";
+ public static final String MODULAR_U_I_2 = "modularui2";
+
+ // spotless:on
+ }
+
+ public final String ID;
+ public final String resourceDomain;
+ protected boolean checkedMod, modLoaded;
+ protected final TargetModBuilder builder;
+
+ Mods(String ID) {
+ this.ID = ID;
+ this.resourceDomain = ID.toLowerCase(Locale.ENGLISH);
+ this.builder = new TargetModBuilder().setModId(getEffectiveModID());
+ }
+
+ @Override
+ public @NotNull TargetModBuilder getBuilder() {
+ return builder;
+ }
+
+ @Override
+ public String getID() {
+ return ID;
+ }
+
+ protected String getEffectiveModID() {
+ return ID;
+ }
+
+ public boolean isModLoaded() {
+ if (!checkedMod) {
+ this.modLoaded = Loader.isModLoaded(getEffectiveModID());
+ checkedMod = true;
+ }
+ return this.modLoaded;
+ }
+
+ @Override
+ public String getResourceLocation() {
+ return resourceDomain;
+ }
+
+ public String getResourcePath(String... path) {
+ return this.getResourceLocation(path).toString();
+ }
+
+ public ResourceLocation getResourceLocation(String... path) {
+ return new ResourceLocation(this.resourceDomain, String.join("/", path));
+ }
+}
diff --git a/src/main/java/fox/spiteful/avaritia/blocks/BlockMatterClusterOpener.java b/src/main/java/fox/spiteful/avaritia/blocks/BlockMatterClusterOpener.java
new file mode 100644
index 00000000..790a3670
--- /dev/null
+++ b/src/main/java/fox/spiteful/avaritia/blocks/BlockMatterClusterOpener.java
@@ -0,0 +1,115 @@
+package fox.spiteful.avaritia.blocks;
+
+import net.minecraft.block.BlockContainer;
+import net.minecraft.block.material.Material;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.IIcon;
+import net.minecraft.util.MathHelper;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.cleanroommc.modularui.factory.GuiManager;
+import com.cleanroommc.modularui.factory.PosGuiData;
+import com.cleanroommc.modularui.factory.TileEntityGuiFactory;
+
+import cpw.mods.fml.common.registry.GameRegistry;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import fox.spiteful.avaritia.items.ItemMatterClusterOpener;
+import fox.spiteful.avaritia.tile.TileMatterClusterOpener;
+
+public class BlockMatterClusterOpener extends BlockContainer {
+
+ public BlockMatterClusterOpener() {
+ super(Material.iron);
+
+ setBlockName("cluster_opener");
+ setHardness(2f);
+ setResistance(2f);
+ }
+
+ public static void register() {
+ LudicrousBlocks.clusterOpener = new BlockMatterClusterOpener();
+ GameRegistry.registerBlock(LudicrousBlocks.clusterOpener, ItemMatterClusterOpener.class, "cluster_opener");
+ GameRegistry.registerTileEntity(TileMatterClusterOpener.class, "cluster_opener");
+ }
+
+ @Override
+ public TileEntity createNewTileEntity(World worldIn, int meta) {
+ return new TileMatterClusterOpener();
+ }
+
+ @Override
+ public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer player, int side, float subX,
+ float subY, float subZ) {
+ if (!worldIn.isRemote) {
+ PosGuiData data = new PosGuiData(player, x, y, z);
+ GuiManager.open(TileEntityGuiFactory.INSTANCE, data, (EntityPlayerMP) player);
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase player, ItemStack stack) {
+ int l = MathHelper.floor_double((double) (player.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3;
+
+ world.setBlockMetadataWithNotify(x, y, z, l, 2);
+ }
+
+ @Override
+ public void onBlockPreDestroy(World worldIn, int x, int y, int z, int meta) {
+ super.onBlockPreDestroy(worldIn, x, y, z, meta);
+
+ ((TileMatterClusterOpener) worldIn.getTileEntity(x, y, z)).dropContents();
+ }
+
+ @SideOnly(Side.CLIENT)
+ private IIcon top, side, front;
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void registerBlockIcons(IIconRegister reg) {
+ top = reg.registerIcon("avaritia:cluster_opener/top");
+ side = reg.registerIcon("avaritia:cluster_opener/side");
+ front = reg.registerIcon("avaritia:cluster_opener/front");
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public IIcon getIcon(IBlockAccess worldIn, int x, int y, int z, int side) {
+ // In-world rendering
+ ForgeDirection dir = ForgeDirection.getOrientation(side);
+
+ if (dir == ForgeDirection.UP) return top;
+ if (dir == ForgeDirection.DOWN) return top;
+
+ ForgeDirection front = switch (worldIn.getBlockMetadata(x, y, z)) {
+ case 0 -> ForgeDirection.NORTH;
+ case 1 -> ForgeDirection.EAST;
+ case 2 -> ForgeDirection.SOUTH;
+ case 3 -> ForgeDirection.WEST;
+ default -> ForgeDirection.UNKNOWN;
+ };
+
+ return dir == front ? this.front : this.side;
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public IIcon getIcon(int side, int meta) {
+ // In-hand rendering
+ ForgeDirection dir = ForgeDirection.getOrientation(side);
+
+ if (dir == ForgeDirection.UP) return top;
+ if (dir == ForgeDirection.DOWN) return top;
+
+ return dir == ForgeDirection.SOUTH ? this.front : this.side;
+ }
+}
diff --git a/src/main/java/fox/spiteful/avaritia/blocks/LudicrousBlocks.java b/src/main/java/fox/spiteful/avaritia/blocks/LudicrousBlocks.java
index f0cf0577..c21753ce 100644
--- a/src/main/java/fox/spiteful/avaritia/blocks/LudicrousBlocks.java
+++ b/src/main/java/fox/spiteful/avaritia/blocks/LudicrousBlocks.java
@@ -2,6 +2,7 @@
import net.minecraft.block.Block;
+import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.registry.GameRegistry;
import fox.spiteful.avaritia.Config;
import fox.spiteful.avaritia.tile.TileEntityCompressor;
@@ -23,6 +24,8 @@ public class LudicrousBlocks {
public static Block infinitato;
+ public static Block clusterOpener;
+
public static void voxelize() {
double_craft = GameRegistry.registerBlock(new BlockDoubleCraft(), "Double_Craft");
triple_craft = GameRegistry.registerBlock(new BlockTripleCraft(), "Triple_Craft");
@@ -37,5 +40,9 @@ public static void voxelize() {
GameRegistry.registerTileEntity(TileEntityNeutron.class, "Avaritia_Neutron");
compressor = GameRegistry.registerBlock(new BlockCompressor(), "Neutronium_Compressor");
GameRegistry.registerTileEntity(TileEntityCompressor.class, "Avaritia_Compressor");
+
+ if (Loader.isModLoaded("gtnhlib") && Loader.isModLoaded("modularui2")) {
+ BlockMatterClusterOpener.register();
+ }
}
}
diff --git a/src/main/java/fox/spiteful/avaritia/crafting/Grinder.java b/src/main/java/fox/spiteful/avaritia/crafting/Grinder.java
index 6a7c06bd..e21e45d7 100644
--- a/src/main/java/fox/spiteful/avaritia/crafting/Grinder.java
+++ b/src/main/java/fox/spiteful/avaritia/crafting/Grinder.java
@@ -483,6 +483,31 @@ public static void artsAndCrafts() {
new ItemStack(Blocks.redstone_block, 1),
'O',
new ItemStack(LudicrousBlocks.resource_block, 1, 0));
+
+ if (LudicrousBlocks.clusterOpener != null) {
+ ExtremeCraftingManager.getInstance().addRecipe(
+ new ItemStack(LudicrousBlocks.clusterOpener),
+ "BBBIBIBBB",
+ "I I",
+ "E X E",
+ "IIIIXIIII",
+ "E X E",
+ "I I",
+ "E IXXXI E",
+ "I I I I",
+ "BBBHHHBBB",
+ 'X',
+ // Crystal matrix ingot
+ new ItemStack(LudicrousItems.resource, 1, 1),
+ 'B',
+ new ItemStack(Blocks.iron_block, 1),
+ 'I',
+ new ItemStack(Items.iron_ingot, 1),
+ 'H',
+ new ItemStack(Blocks.hopper, 1),
+ 'E',
+ new ItemStack(Blocks.emerald_block, 1));
+ }
}
}
diff --git a/src/main/java/fox/spiteful/avaritia/items/ItemMatterCluster.java b/src/main/java/fox/spiteful/avaritia/items/ItemMatterCluster.java
index 2d95e3d7..5baf4dd7 100644
--- a/src/main/java/fox/spiteful/avaritia/items/ItemMatterCluster.java
+++ b/src/main/java/fox/spiteful/avaritia/items/ItemMatterCluster.java
@@ -33,7 +33,14 @@ public class ItemMatterCluster extends Item implements ICosmicRenderItem {
public static final String COUNTTAG = "count";
public static final String MAINCOUNTTAG = "total";
- public static final int MAX_CAPACITY = 64 * 256;
+ /// The max number of items a normal cluster will store. Avaritia tools cannot generate clusters bigger than this,
+ /// and this is the maximum that clusters will automatically combine into.
+ ///
+ /// It's possible to generate a super-critical (>max capacity) cluster via [#makeCluster(ItemStack)] for situations
+ /// where this limit is too low, but care should be taken to avoid allowing the player to automate those clusters
+ /// since they can easily be used for near-infinite item storage.
+ public static final int MAX_NORMAL_CAPACITY = 64 * 256;
+ public static final int MAX_SUPER_CRITICAL_CAPACITY = Integer.MAX_VALUE;
public IIcon iconFull;
public IIcon cosmicIcon;
@@ -70,7 +77,9 @@ public void addInformation(ItemStack stack, EntityPlayer player, List to
tooltip.add(
clustertag.getInteger(MAINCOUNTTAG) + "/"
- + Math.max(MAX_CAPACITY, clustertag.getInteger(MAINCOUNTTAG))
+ + (clustertag.getInteger(MAINCOUNTTAG) > MAX_SUPER_CRITICAL_CAPACITY
+ ? MAX_SUPER_CRITICAL_CAPACITY
+ : MAX_NORMAL_CAPACITY)
+ " "
+ StatCollector.translateToLocal("tooltip.matter_cluster.counter"));
tooltip.add("");
@@ -113,7 +122,7 @@ public static List makeClusters(List input) {
ItemStackWrapper wrap = e.getKey();
int wrapcount = e.getValue();
- int count = Math.min(MAX_CAPACITY - currentTotal, wrapcount);
+ int count = Math.min(MAX_NORMAL_CAPACITY - currentTotal, wrapcount);
if (!currentItems.containsKey(e.getKey())) {
currentItems.put(wrap, count);
@@ -127,7 +136,7 @@ public static List makeClusters(List input) {
itemlist.remove(0);
}
- if (currentTotal >= MAX_CAPACITY) {
+ if (currentTotal == MAX_NORMAL_CAPACITY) {
ItemStack cluster = makeCluster(currentItems);
clusters.add(cluster);
@@ -199,7 +208,7 @@ public static int getClusterSize(ItemStack cluster) {
}
public static boolean isClusterFull(ItemStack cluster) {
- return getClusterSize(cluster) >= MAX_CAPACITY;
+ return getClusterSize(cluster) >= MAX_NORMAL_CAPACITY;
}
public static void setClusterData(ItemStack stack, Map data, int count) {
@@ -225,7 +234,7 @@ public static void mergeClusters(ItemStack donor, ItemStack recipient) {
int donorcount = getClusterSize(donor);
int recipientcount = getClusterSize(recipient);
- if (donorcount == 0 || donorcount >= MAX_CAPACITY || recipientcount >= MAX_CAPACITY) {
+ if (donorcount == 0 || donorcount >= MAX_NORMAL_CAPACITY || recipientcount >= MAX_NORMAL_CAPACITY) {
return;
}
@@ -233,12 +242,12 @@ public static void mergeClusters(ItemStack donor, ItemStack recipient) {
Map recipientdata = getClusterData(recipient);
List> datalist = new ArrayList<>(donordata.entrySet());
- while (recipientcount < MAX_CAPACITY && donorcount > 0) {
+ while (recipientcount < MAX_NORMAL_CAPACITY && donorcount > 0) {
Entry e = datalist.get(0);
ItemStackWrapper wrap = e.getKey();
int wrapcount = e.getValue();
- int count = Math.min(MAX_CAPACITY - recipientcount, wrapcount);
+ int count = Math.min(MAX_NORMAL_CAPACITY - recipientcount, wrapcount);
if (!recipientdata.containsKey(wrap)) {
recipientdata.put(wrap, count);
@@ -269,7 +278,7 @@ public static void mergeClusters(ItemStack donor, ItemStack recipient) {
@Override
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
// Do nothing for super critical clusters
- if (getClusterSize(stack) > MAX_CAPACITY) return stack;
+ if (getClusterSize(stack) > MAX_NORMAL_CAPACITY) return stack;
if (!world.isRemote) {
List drops = ToolHelper.collateMatterClusterContents(ItemMatterCluster.getClusterData(stack));
@@ -291,7 +300,7 @@ public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer pla
@Override
public IIcon getMaskTexture(ItemStack stack, EntityPlayer player) {
int count = getClusterSize(stack);
- if (count >= MAX_CAPACITY) {
+ if (count >= MAX_NORMAL_CAPACITY) {
return cosmicIconFull;
}
return cosmicIcon;
@@ -300,13 +309,13 @@ public IIcon getMaskTexture(ItemStack stack, EntityPlayer player) {
@Override
public float getMaskMultiplier(ItemStack stack, EntityPlayer player) {
int count = getClusterSize(stack);
- return Math.min(1f, count / (float) MAX_CAPACITY);
+ return Math.min(1f, count / (float) MAX_NORMAL_CAPACITY);
}
@Override
public IIcon getIcon(ItemStack stack, int pass) {
int count = getClusterSize(stack);
- if (count >= MAX_CAPACITY) {
+ if (count >= MAX_NORMAL_CAPACITY) {
return iconFull;
}
return super.getIcon(stack, pass);
@@ -320,10 +329,10 @@ public IIcon getIconIndex(ItemStack stack) {
@Override
public String getUnlocalizedName(ItemStack stack) {
int count = getClusterSize(stack);
- if (count == MAX_CAPACITY) {
+ if (count == MAX_NORMAL_CAPACITY) {
return super.getUnlocalizedName(stack) + ".full";
}
- if (count > MAX_CAPACITY) {
+ if (count > MAX_NORMAL_CAPACITY) {
return super.getUnlocalizedName(stack) + ".veryfull";
}
return super.getUnlocalizedName(stack);
diff --git a/src/main/java/fox/spiteful/avaritia/items/ItemMatterClusterOpener.java b/src/main/java/fox/spiteful/avaritia/items/ItemMatterClusterOpener.java
new file mode 100644
index 00000000..9f5f565e
--- /dev/null
+++ b/src/main/java/fox/spiteful/avaritia/items/ItemMatterClusterOpener.java
@@ -0,0 +1,29 @@
+package fox.spiteful.avaritia.items;
+
+import java.util.List;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemBlock;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.StatCollector;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+public class ItemMatterClusterOpener extends ItemBlock {
+
+ public ItemMatterClusterOpener(Block block) {
+ super(block);
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean advanced) {
+ super.addInformation(stack, player, tooltip, advanced);
+
+ tooltip.add(StatCollector.translateToLocal("tooltip.matter-cluster-decompressor.line1"));
+ tooltip.add(StatCollector.translateToLocal("tooltip.matter-cluster-decompressor.line2"));
+ tooltip.add(StatCollector.translateToLocal("tooltip.matter-cluster-decompressor.line3"));
+ }
+}
diff --git a/src/main/java/fox/spiteful/avaritia/tile/TileMatterClusterOpener.java b/src/main/java/fox/spiteful/avaritia/tile/TileMatterClusterOpener.java
new file mode 100644
index 00000000..510f913e
--- /dev/null
+++ b/src/main/java/fox/spiteful/avaritia/tile/TileMatterClusterOpener.java
@@ -0,0 +1,401 @@
+package fox.spiteful.avaritia.tile;
+
+import java.util.Map;
+
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.ISidedInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTank;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidHandler;
+import net.minecraftforge.oredict.OreDictionary;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import com.cleanroommc.modularui.api.IGuiHolder;
+import com.cleanroommc.modularui.drawable.AdaptableUITexture;
+import com.cleanroommc.modularui.drawable.GuiTextures;
+import com.cleanroommc.modularui.drawable.UITexture;
+import com.cleanroommc.modularui.factory.PosGuiData;
+import com.cleanroommc.modularui.screen.ModularPanel;
+import com.cleanroommc.modularui.screen.UISettings;
+import com.cleanroommc.modularui.utils.Alignment;
+import com.cleanroommc.modularui.utils.item.IItemHandlerModifiable;
+import com.cleanroommc.modularui.utils.item.InvWrapper;
+import com.cleanroommc.modularui.value.sync.PanelSyncManager;
+import com.cleanroommc.modularui.widgets.SlotGroupWidget;
+import com.cleanroommc.modularui.widgets.TextWidget;
+import com.cleanroommc.modularui.widgets.layout.Column;
+import com.cleanroommc.modularui.widgets.layout.Row;
+import com.cleanroommc.modularui.widgets.slot.FluidSlot;
+import com.cleanroommc.modularui.widgets.slot.ItemSlot;
+import com.cleanroommc.modularui.widgets.slot.ModularSlot;
+import com.glodblock.github.common.item.ItemFluidDrop;
+import com.gtnewhorizon.gtnhlib.capability.CapabilityProvider;
+import com.gtnewhorizon.gtnhlib.capability.item.ItemSink;
+import com.gtnewhorizon.gtnhlib.capability.item.ItemSource;
+import com.gtnewhorizon.gtnhlib.item.InventoryItemSink;
+import com.gtnewhorizon.gtnhlib.item.InventoryItemSource;
+import com.gtnewhorizon.gtnhlib.util.ItemUtil;
+import com.gtnewhorizon.gtnhlib.util.data.LazyItem;
+
+import fox.spiteful.avaritia.Mods;
+import fox.spiteful.avaritia.items.ItemMatterCluster;
+import fox.spiteful.avaritia.items.ItemStackWrapper;
+
+public class TileMatterClusterOpener extends TileEntity
+ implements ISidedInventory, CapabilityProvider, IGuiHolder, IFluidHandler {
+
+ private ItemStack clusterInput, itemOutput;
+ private final FluidTank fluidOutput = new FluidTank(Integer.MAX_VALUE);
+
+ private static final LazyItem FLUID_DROPS = new LazyItem(
+ Mods.AE2FluidCraft,
+ "fluid_drop",
+ OreDictionary.WILDCARD_VALUE);
+
+ private static final int[] INPUT_SLOTS = { 0 };
+ private static final int[] OUTPUT_SLOTS = { 1 };
+
+ @Override
+ public void updateEntity() {
+ super.updateEntity();
+
+ if (worldObj.isRemote) return;
+
+ if (clusterInput != null && clusterInput.stackSize <= 0) {
+ clusterInput = null;
+ markDirty();
+ }
+
+ if (itemOutput != null && itemOutput.stackSize <= 0) {
+ itemOutput = null;
+ markDirty();
+ }
+
+ if (fluidOutput.getFluid() != null && fluidOutput.getFluidAmount() <= 0) {
+ fluidOutput.setFluid(null);
+ markDirty();
+ }
+
+ if (clusterInput == null) return;
+ if (itemOutput != null || fluidOutput.getFluid() != null) return;
+
+ Map contents = ItemMatterCluster.getClusterData(clusterInput);
+
+ if (contents.isEmpty()) return;
+
+ var iter = contents.entrySet().iterator();
+
+ var e = iter.next();
+ iter.remove();
+
+ if (contents.isEmpty()) {
+ clusterInput = null;
+ } else {
+ ItemMatterCluster.setClusterData(clusterInput, contents, contents.values().stream().mapToInt(i -> i).sum());
+ }
+
+ itemOutput = ItemUtil.copyAmount(e.getValue(), e.getKey().stack);
+
+ if (FLUID_DROPS.isLoaded() && FLUID_DROPS.matches(itemOutput)) {
+ fluidOutput.setFluid(ItemFluidDrop.getFluidStack(itemOutput));
+ itemOutput = null;
+ }
+
+ markDirty();
+ }
+
+ @Override
+ public void writeToNBT(NBTTagCompound compound) {
+ super.writeToNBT(compound);
+
+ if (clusterInput != null) {
+ compound.setTag("clusterInput", clusterInput.writeToNBT(new NBTTagCompound()));
+ }
+
+ if (itemOutput != null) {
+ compound.setTag("itemOutput", itemOutput.writeToNBT(new NBTTagCompound()));
+ }
+
+ if (fluidOutput.getFluid() != null) {
+ compound.setTag("fluidOutput", fluidOutput.getFluid().writeToNBT(new NBTTagCompound()));
+ }
+ }
+
+ @Override
+ public void readFromNBT(NBTTagCompound compound) {
+ super.readFromNBT(compound);
+
+ clusterInput = null;
+ itemOutput = null;
+ fluidOutput.setFluid(null);
+
+ if (compound.hasKey("clusterInput")) {
+ clusterInput = ItemStack.loadItemStackFromNBT(compound.getCompoundTag("clusterInput"));
+ }
+
+ if (compound.hasKey("itemOutput")) {
+ itemOutput = ItemStack.loadItemStackFromNBT(compound.getCompoundTag("itemOutput"));
+ }
+
+ if (compound.hasKey("fluidOutput")) {
+ fluidOutput.setFluid(FluidStack.loadFluidStackFromNBT(compound.getCompoundTag("fluidOutput")));
+ }
+ }
+
+ public void dropContents() {
+ if (clusterInput != null) {
+ EntityItem item = new EntityItem(worldObj, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5, clusterInput);
+ worldObj.spawnEntityInWorld(item);
+ clusterInput = null;
+ }
+
+ if (itemOutput != null) {
+ ItemStack cluster = ItemMatterCluster.makeCluster(itemOutput);
+ itemOutput = null;
+
+ EntityItem item = new EntityItem(worldObj, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5, cluster);
+ worldObj.spawnEntityInWorld(item);
+ }
+
+ if (FLUID_DROPS.isLoaded() && fluidOutput.getFluidAmount() > 0) {
+ ItemStack drops = ItemFluidDrop.newStack(fluidOutput.getFluid());
+ fluidOutput.setFluid(null);
+
+ assert drops != null;
+ ItemStack cluster = ItemMatterCluster.makeCluster(drops);
+
+ EntityItem item = new EntityItem(worldObj, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5, cluster);
+ worldObj.spawnEntityInWorld(item);
+ }
+ }
+
+ @Override
+ public @Nullable T getCapability(@NotNull Class capability, @NotNull ForgeDirection side) {
+ if (capability == ItemSource.class) {
+ return capability.cast(new OutputSlotSource());
+ }
+
+ if (capability == ItemSink.class) {
+ return capability.cast(new InventoryItemSink(this, side));
+ }
+
+ return null;
+ }
+
+ private class OutputSlotSource extends InventoryItemSource {
+
+ public OutputSlotSource() {
+ super(TileMatterClusterOpener.this, ForgeDirection.UNKNOWN);
+ }
+
+ @Override
+ protected int[] getSlots() {
+ return OUTPUT_SLOTS;
+ }
+ }
+
+ @Override
+ public int[] getAccessibleSlotsFromSide(int side) {
+ return INPUT_SLOTS;
+ }
+
+ @Override
+ public boolean canInsertItem(int slotIndex, ItemStack stack, int side) {
+ return slotIndex == 0;
+ }
+
+ @Override
+ public boolean canExtractItem(int slotIndex, ItemStack stack, int side) {
+ return slotIndex == 0;
+ }
+
+ @Override
+ public int getSizeInventory() {
+ return 2;
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int slotIn) {
+ if (slotIn == 0) return clusterInput;
+ if (slotIn == 1) return itemOutput;
+
+ return null;
+ }
+
+ @Override
+ public ItemStack decrStackSize(int index, int count) {
+ if (index != 0) return null;
+ if (clusterInput == null) return null;
+
+ int removable = Math.min(count, clusterInput.stackSize);
+
+ ItemStack out = clusterInput.splitStack(removable);
+
+ if (ItemUtil.isStackEmpty(clusterInput)) clusterInput = null;
+
+ markDirty();
+
+ return out;
+ }
+
+ @Override
+ public ItemStack getStackInSlotOnClosing(int index) {
+ return decrStackSize(index, Integer.MAX_VALUE);
+ }
+
+ @Override
+ public void setInventorySlotContents(int index, ItemStack stack) {
+ if (index == 0) {
+ clusterInput = stack;
+ }
+
+ if (index == 1) {
+ itemOutput = stack;
+ }
+ }
+
+ @Override
+ public String getInventoryName() {
+ return getBlockType().getLocalizedName();
+ }
+
+ @Override
+ public boolean hasCustomInventoryName() {
+ return false;
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+ return 1;
+ }
+
+ @Override
+ public boolean isUseableByPlayer(EntityPlayer player) {
+ return true;
+ }
+
+ @Override
+ public void openInventory() {
+
+ }
+
+ @Override
+ public void closeInventory() {
+
+ }
+
+ @Override
+ public boolean isItemValidForSlot(int index, ItemStack stack) {
+ if (index == 0) {
+ return stack.getItem() instanceof ItemMatterCluster;
+ } else if (index == 1) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int fill(ForgeDirection from, FluidStack resource, boolean doFill) {
+ return 0;
+ }
+
+ @Override
+ public boolean canFill(ForgeDirection from, Fluid fluid) {
+ return false;
+ }
+
+ @Override
+ public boolean canDrain(ForgeDirection from, Fluid fluid) {
+ return fluidOutput.getFluid() != null && fluidOutput.getFluid().getFluid() == fluid;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) {
+ FluidStack stored = fluidOutput.getFluid();
+
+ if (stored == null || stored.amount <= 0) return null;
+ if (stored.getFluid() != resource.getFluid()) return null;
+
+ return drain(from, resource.amount, doDrain);
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) {
+ FluidStack stored = fluidOutput.getFluid();
+
+ if (stored == null || stored.amount <= 0) return null;
+
+ int removable = Math.min(stored.amount, maxDrain);
+
+ FluidStack out = new FluidStack(stored.getFluid(), removable);
+
+ if (doDrain) {
+ stored.amount -= removable;
+
+ if (stored.amount <= 0) {
+ fluidOutput.setFluid(null);
+ }
+
+ markDirty();
+ }
+
+ return out;
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection from) {
+ return new FluidTankInfo[] { fluidOutput.getInfo() };
+ }
+
+ public static final UITexture CLUSTER_SLOT_BACKGROUND = AdaptableUITexture.builder()
+ .location("avaritia", "gui/slot/cluster_background.png").imageSize(16, 16).build();
+
+ @Override
+ public ModularPanel buildUI(PosGuiData data, PanelSyncManager syncManager, UISettings settings) {
+ IItemHandlerModifiable inv = new InvWrapper(this);
+
+ syncManager.registerSlotGroup("input", 1, 150);
+ syncManager.registerSlotGroup("output", 1, false);
+
+ // spotless:off
+ return new ModularPanel("ClusterOpener")
+ .size(178, 152)
+ .padding(8)
+ .child(new Column()
+ .sizeRel(1)
+ .expanded()
+ .child(new TextWidget<>(getInventoryName()).height(16).alignX(0.5f).align(Alignment.TopCenter))
+ .child(new Row().widthRel(1)
+ .alignX(0.5f)
+ .expanded()
+ .child(new ItemSlot()
+ .alignY(0.5f)
+ .leftRel(0.5f, -18, 0.5f)
+ .slot(new ModularSlot(inv, 0).slotGroup("input"))
+ .background(GuiTextures.SLOT_ITEM, CLUSTER_SLOT_BACKGROUND))
+ .child(new ItemSlot()
+ .alignY(0.5f)
+ .leftRel(0.5f, 18, 0.5f)
+ .slot(new ModularSlot(inv, 1).slotGroup("output").accessibility(false, false)))
+ .child(new FluidSlot()
+ .alignY(0.5f)
+ .leftRel(0.5f, 36, 0.5f)
+ .syncHandler(fluidOutput)))
+ .child(new Row()
+ .widthRel(1)
+ .height(76)
+ .alignX(0.5f)
+ .child(SlotGroupWidget.playerInventory(false))));
+ // spotless:on
+ }
+}
diff --git a/src/main/resources/assets/avaritia/lang/en_US.lang b/src/main/resources/assets/avaritia/lang/en_US.lang
index 701a6e09..59d9fe5b 100644
--- a/src/main/resources/assets/avaritia/lang/en_US.lang
+++ b/src/main/resources/assets/avaritia/lang/en_US.lang
@@ -68,6 +68,7 @@ tile.alfheim_deadrock1.name=Deadrock Brick
tile.alfheim_deadrock2.name=Mossy Deadrock Brick
tile.alfheim_deadrock3.name=Cracked Deadrock Brick
tile.alfheim_deadrock4.name=Chiselled Deadrock
+tile.cluster_opener.name=Matter Cluster Decompressor
#
# Tooltips
tooltip.armok.desc=Always contains maximum Life Essence
@@ -89,6 +90,9 @@ tooltip.matter_cluster.counter=items
tooltip.morvinabox.desc=Instant housing
tooltip.morvinabox.subdesc=In Canada, Morvy comes in bags.
tooltip.claybalance.desc=UNLIMITED BALANCE!!!!
+tooltip.matter-cluster-decompressor.line1=Extracts items from Matter Clusters.
+tooltip.matter-cluster-decompressor.line2=Only GT and AE can pull items from decompressors.
+tooltip.matter-cluster-decompressor.line3=Fluid drops will be converted to fluids automatically.
#
# Achievements
achievement.avaritia:crystal_matrix=The First Seal
diff --git a/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/front.png b/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/front.png
new file mode 100644
index 00000000..acf2cfcf
Binary files /dev/null and b/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/front.png differ
diff --git a/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/front.png.mcmeta b/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/front.png.mcmeta
new file mode 100644
index 00000000..96c0529b
--- /dev/null
+++ b/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/front.png.mcmeta
@@ -0,0 +1,17 @@
+{
+ "animation": {
+ "frametime": 2,
+ "frames": [
+ 0,
+ 0,
+ 1,
+ 2,
+ 3,
+ 3,
+ 2,
+ 2,
+ 1,
+ 1
+ ]
+ }
+}
diff --git a/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/side.png b/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/side.png
new file mode 100644
index 00000000..2576c387
Binary files /dev/null and b/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/side.png differ
diff --git a/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/top.png b/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/top.png
new file mode 100644
index 00000000..83c9e094
Binary files /dev/null and b/src/main/resources/assets/avaritia/textures/blocks/cluster_opener/top.png differ
diff --git a/src/main/resources/assets/avaritia/textures/gui/slot/cluster_background.png b/src/main/resources/assets/avaritia/textures/gui/slot/cluster_background.png
new file mode 100644
index 00000000..85d5503c
Binary files /dev/null and b/src/main/resources/assets/avaritia/textures/gui/slot/cluster_background.png differ