aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/kubatech/api
diff options
context:
space:
mode:
authorRaven Szewczyk <git@eigenraven.me>2024-05-30 18:26:10 +0100
committerGitHub <noreply@github.com>2024-05-30 19:26:10 +0200
commit337594e83a74c432c140b3df3287575b81bce467 (patch)
treeabe57b3390d3dd037ea1442f83c4519ebcb9de07 /src/main/java/kubatech/api
parent752f262ccd545bdb785ef0e9ce922bf1117d23d6 (diff)
downloadGT5-Unofficial-337594e83a74c432c140b3df3287575b81bce467.tar.gz
GT5-Unofficial-337594e83a74c432c140b3df3287575b81bce467.tar.bz2
GT5-Unofficial-337594e83a74c432c140b3df3287575b81bce467.zip
Complete backend rework of the EIG (#2616)
* Complete backend rework of the EIG * Mergening Related Updates Also some loader references refactoring * fix (cherry picked from commit 7fd5d7417bddfb6e49ede3986d9a547f15b21289) * More Mergening fixes Updates the declaration of the stem mixin to match the new format. * Inline EIG IC2 bucket constants addresses: https://github.com/GTNewHorizons/GT5-Unofficial/pull/2616#discussion_r1620596497 * Fix Seed Removal in regular seed simulations Should address https://github.com/GTNewHorizons/GT5-Unofficial/pull/2616#discussion_r1620583338 --------- Co-authored-by: Guillaume Mercier <10gui-gui10@live.ca> Co-authored-by: Martin Robertz <dream-master@gmx.net>
Diffstat (limited to 'src/main/java/kubatech/api')
-rw-r--r--src/main/java/kubatech/api/EIGDynamicInventory.java510
-rw-r--r--src/main/java/kubatech/api/IBlockStemAccesor.java8
-rw-r--r--src/main/java/kubatech/api/eig/EIGBucket.java247
-rw-r--r--src/main/java/kubatech/api/eig/EIGDropTable.java224
-rw-r--r--src/main/java/kubatech/api/eig/EIGMode.java154
-rw-r--r--src/main/java/kubatech/api/eig/IEIGBucketFactory.java15
-rw-r--r--src/main/java/kubatech/api/enums/EIGModes.java42
-rw-r--r--src/main/java/kubatech/api/enums/EIGSetupPhase.java16
-rw-r--r--src/main/java/kubatech/api/gui/AutoScalingStackSizeText.java72
-rw-r--r--src/main/java/kubatech/api/implementations/KubaTechGTMultiBlockBase.java6
-rw-r--r--src/main/java/kubatech/api/utils/StringUtils.java6
11 files changed, 1299 insertions, 1 deletions
diff --git a/src/main/java/kubatech/api/EIGDynamicInventory.java b/src/main/java/kubatech/api/EIGDynamicInventory.java
new file mode 100644
index 0000000000..1c703fe2fa
--- /dev/null
+++ b/src/main/java/kubatech/api/EIGDynamicInventory.java
@@ -0,0 +1,510 @@
+package kubatech.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+
+import org.lwjgl.opengl.GL11;
+
+import com.gtnewhorizons.modularui.api.GlStateManager;
+import com.gtnewhorizons.modularui.api.ModularUITextures;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.ItemDrawable;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.internal.Theme;
+import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.ChangeableWidget;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedRow;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.Scrollable;
+import com.kuba6000.mobsinfo.api.utils.ItemID;
+
+import kubatech.api.gui.AutoScalingStackSizeText;
+import kubatech.api.helpers.GTHelper;
+import kubatech.api.utils.ModUtils;
+
+public class EIGDynamicInventory<T> {
+
+ int width, height;
+ Supplier<Integer> maxSeedCountGetter;
+ Supplier<Integer> maxSeedTypeGetter;
+ Supplier<Integer> usedSeedCountGetter;
+ Supplier<Integer> usedSeedTypesGetter;
+ private int maxSeedTypes = 0;
+ private int maxSeedCount = 0;
+ private int usedSeedTypes = 0;
+ private int usedSeedCount = 0;
+ List<T> inventory;
+ TInventoryGetter<T> inventoryGetter;
+ TInventoryInjector inventoryInjector = null;
+ TInventoryExtractor<T> inventoryExtractor = null;
+ TInventoryReplacerOrMerger inventoryReplacer = null;
+ Supplier<Boolean> isEnabledGetter = null;
+ boolean isEnabled = true;
+
+ public EIGDynamicInventory(int width, int height, Supplier<Integer> maxSeedTypeGetter,
+ Supplier<Integer> maxSeedCountGetter, Supplier<Integer> usedSeedTypesGetter,
+ Supplier<Integer> usedSeedCountGetter, List<T> inventory, TInventoryGetter<T> inventoryGetter) {
+ this.width = width;
+ this.height = height;
+ this.maxSeedTypeGetter = maxSeedTypeGetter;
+ this.maxSeedCountGetter = maxSeedCountGetter;
+ this.usedSeedTypesGetter = usedSeedTypesGetter;
+ this.usedSeedCountGetter = usedSeedCountGetter;
+ this.inventory = inventory;
+ this.inventoryGetter = inventoryGetter;
+ }
+
+ public EIGDynamicInventory<T> allowInventoryInjection(TInventoryInjector inventoryInjector) {
+ this.inventoryInjector = inventoryInjector;
+ return this;
+ }
+
+ public EIGDynamicInventory<T> allowInventoryExtraction(TInventoryExtractor<T> inventoryExtractor) {
+ this.inventoryExtractor = inventoryExtractor;
+ return this;
+ }
+
+ public EIGDynamicInventory<T> allowInventoryReplace(TInventoryReplacerOrMerger inventoryReplacer) {
+ this.inventoryReplacer = inventoryReplacer;
+ return this;
+ }
+
+ public EIGDynamicInventory<T> setEnabled(Supplier<Boolean> isEnabled) {
+ this.isEnabledGetter = isEnabled;
+ return this;
+ }
+
+ public UITexture getItemSlot() {
+ return ModularUITextures.ITEM_SLOT;
+ }
+
+ @SuppressWarnings("UnstableApiUsage")
+ public Widget asWidget(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ ChangeableWidget container = new ChangeableWidget(() -> createWidget(buildContext.getPlayer()));
+ // TODO: Only reset the widget when there are more slot stacks, otherwise just refresh them somehow
+
+ container
+ // max seed types
+ .attachSyncer(new FakeSyncWidget.IntegerSyncer(() -> {
+ int i = this.maxSeedTypeGetter.get();
+ if (this.maxSeedTypes != i) {
+ this.maxSeedTypes = i;
+ container.notifyChangeNoSync();
+ }
+ return i;
+ }, i -> {
+ if (this.maxSeedTypes != i) {
+ this.maxSeedTypes = i;
+ container.notifyChangeNoSync();
+ }
+ }), builder)
+ // used seed types
+ .attachSyncer(new FakeSyncWidget.IntegerSyncer(() -> {
+ int i = this.usedSeedTypesGetter.get();
+ if (this.usedSeedTypes != i) {
+ this.usedSeedTypes = i;
+ container.notifyChangeNoSync();
+ }
+ return i;
+ }, i -> {
+ if (this.usedSeedTypes != i) {
+ this.usedSeedTypes = i;
+ container.notifyChangeNoSync();
+ }
+ }), builder)
+ // max seed count
+ .attachSyncer(new FakeSyncWidget.IntegerSyncer(() -> {
+ int i = this.maxSeedCountGetter.get();
+ if (this.maxSeedCount != i) {
+ this.maxSeedCount = i;
+ container.notifyChangeNoSync();
+ }
+ return i;
+ }, i -> {
+ if (this.maxSeedCount != i) {
+ this.maxSeedCount = i;
+ container.notifyChangeNoSync();
+ }
+ }), builder)
+ // used seed count
+ .attachSyncer(new FakeSyncWidget.IntegerSyncer(() -> {
+ int i = this.usedSeedCountGetter.get();
+ if (this.usedSeedCount != i) {
+ this.usedSeedCount = i;
+ container.notifyChangeNoSync();
+ }
+ return i;
+ }, i -> {
+ if (this.usedSeedCount != i) {
+ this.usedSeedCount = i;
+ container.notifyChangeNoSync();
+ }
+ }), builder)
+
+ .attachSyncer(new FakeSyncWidget.ListSyncer<>(() -> {
+ List<GTHelper.StackableItemSlot> newDrawables = new ArrayList<>();
+ for (int i = 0, mStorageSize = inventory.size(); i < mStorageSize; i++) {
+ T slot = inventory.get(i);
+ if (slot == null) {
+ continue;
+ }
+ ItemStack stack = inventoryGetter.get(slot);
+ newDrawables
+ .add(new GTHelper.StackableItemSlot(1, stack, new ArrayList<>(Collections.singletonList(i))));
+ }
+ if (!Objects.equals(newDrawables, drawables)) {
+ drawables = newDrawables;
+ container.notifyChangeNoSync();
+ }
+ return drawables;
+ }, l -> {
+ drawables.clear();
+ drawables.addAll(l);
+ container.notifyChangeNoSync();
+ }, (buffer, i) -> {
+ try {
+ i.write(buffer);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }, buffer -> {
+ try {
+ return GTHelper.StackableItemSlot.read(buffer);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }), builder);
+ if (isEnabledGetter != null) {
+ container.attachSyncer(new FakeSyncWidget.BooleanSyncer(isEnabledGetter, i -> isEnabled = i), builder);
+ }
+ return container;
+ }
+
+ List<GTHelper.StackableItemSlot> drawables = new ArrayList<>();
+
+ private Widget createWidget(EntityPlayer player) {
+ Scrollable dynamicInventoryWidget = new Scrollable().setVerticalScroll();
+
+ ArrayList<Widget> buttons = new ArrayList<>();
+
+ if (!ModUtils.isClientThreaded()) {
+ HashMap<ItemID, Integer> itemMap = new HashMap<>();
+ HashMap<ItemID, ItemStack> stackMap = new HashMap<>();
+ HashMap<ItemID, ArrayList<Integer>> realSlotMap = new HashMap<>();
+ drawables = new ArrayList<>();
+ for (int i = 0, inventorySize = inventory.size(); i < inventorySize; i++) {
+ T slot = inventory.get(i);
+ if (slot == null) {
+ continue;
+ }
+ ItemStack stack = inventoryGetter.get(slot);
+ drawables
+ .add(new GTHelper.StackableItemSlot(1, stack, new ArrayList<Integer>(Collections.singleton(i))));
+ }
+ }
+
+ for (int ID = 0; ID < drawables.size(); ID++) {
+ final int finalID = ID;
+
+ buttons.add(new ButtonWidget() {
+
+ @Override
+ public void drawBackground(float partialTicks) {
+ super.drawBackground(partialTicks);
+ if (!isEnabled) {
+ GL11.glDisable(GL11.GL_LIGHTING);
+ GL11.glEnable(GL11.GL_BLEND);
+ GlStateManager.colorMask(true, true, true, false);
+ ModularGui.drawSolidRect(1, 1, 16, 16, Color.withAlpha(Color.BLACK.normal, 0x80));
+ GlStateManager.colorMask(true, true, true, true);
+ GL11.glDisable(GL11.GL_BLEND);
+ }
+ // Copied from SlotWidget#draw
+ else if (isHovering() && !getContext().getCursor()
+ .hasDraggable()) {
+ GL11.glDisable(GL11.GL_LIGHTING);
+ GL11.glEnable(GL11.GL_BLEND);
+ GlStateManager.colorMask(true, true, true, false);
+ ModularGui.drawSolidRect(1, 1, 16, 16, Theme.INSTANCE.getSlotHighlight());
+ GlStateManager.colorMask(true, true, true, true);
+ GL11.glDisable(GL11.GL_BLEND);
+ }
+ }
+ }.setPlayClickSound(false)
+ .setOnClick((clickData, widget) -> {
+ if (!(player instanceof EntityPlayerMP)) return;
+ if (!isEnabledGetter.get()) return;
+
+ if (clickData.mouseButton == 2) {
+ // special button handler goes here
+ if (drawables.size() <= finalID) return;
+ if (player.capabilities.isCreativeMode && player.inventory.getItemStack() == null) {
+ int realID = drawables.get(finalID).realSlots.get(0);
+ ItemStack stack = inventoryGetter.get(inventory.get(realID))
+ .copy();
+ stack.stackSize = stack.getMaxStackSize();
+ player.inventory.setItemStack(stack);
+ ((EntityPlayerMP) player).isChangingQuantityOnly = false;
+ ((EntityPlayerMP) player).updateHeldItem();
+ return;
+ }
+ } else if (clickData.shift) {
+ if (inventoryExtractor == null) return;
+ if (drawables.size() <= finalID) return;
+ int realID = drawables.get(finalID).realSlots.get(0);
+ T toRemoveFrom = this.inventory.get(realID);
+ ItemStack removed = this.inventoryExtractor.extract(toRemoveFrom, (EntityPlayerMP) player);
+ if (removed != null) {
+ if (player.inventory.addItemStackToInventory(removed))
+ player.inventoryContainer.detectAndSendChanges();
+ else player.entityDropItem(removed, 0.f);
+ return;
+ }
+ } else {
+ ItemStack input = player.inventory.getItemStack();
+ if (input != null) {
+ if (inventoryInjector == null) return;
+ if (clickData.mouseButton == 1) {
+ ItemStack copy = input.copy();
+ copy.stackSize = 1;
+ inventoryInjector.inject(copy);
+ if (copy.stackSize == 1) return;
+ input.stackSize--;
+ if (input.stackSize > 0) {
+ // clearing and updating the held item value like this is the only
+ // way i found to be able to reliably update the item count in the UI.
+ player.inventory.setItemStack(null);
+ ((EntityPlayerMP) player).updateHeldItem();
+ player.inventory.setItemStack(input);
+ ((EntityPlayerMP) player).updateHeldItem();
+ return;
+ } else player.inventory.setItemStack(null);
+ } else {
+ inventoryInjector.inject(input);
+ if (input.stackSize > 0) {
+ // clearing and updating the held item value like this is the only
+ // way i found to be able to reliably update the item count in the UI.
+ player.inventory.setItemStack(null);
+ ((EntityPlayerMP) player).updateHeldItem();
+ player.inventory.setItemStack(input);
+ ((EntityPlayerMP) player).updateHeldItem();
+ return;
+ } else player.inventory.setItemStack(null);
+ }
+ ((EntityPlayerMP) player).isChangingQuantityOnly = false;
+ ((EntityPlayerMP) player).updateHeldItem();
+ return;
+ }
+ if (drawables.size() > finalID) {
+ if (inventoryExtractor == null) return;
+ int realID = drawables.get(finalID).realSlots.get(0);
+ T toRemoveFrom = this.inventory.get(realID);
+ ItemStack removed = this.inventoryExtractor.extract(toRemoveFrom, (EntityPlayerMP) player);
+ if (removed != null) {
+ player.inventory.setItemStack(removed);
+ ((EntityPlayerMP) player).isChangingQuantityOnly = false;
+ ((EntityPlayerMP) player).updateHeldItem();
+ return;
+ }
+ }
+ }
+ })
+ .setBackground(() -> {
+ ItemStack stack = drawables.get(finalID).stack;
+ float slotSize = 16.0f;
+ IDrawable itemDrawable = new ItemDrawable(stack).withFixedSize(slotSize, slotSize, 1, 1);
+ IDrawable stackSizeText = new AutoScalingStackSizeText(stack.stackSize).color(Color.WHITE.normal)
+ .shadow()
+ .alignment(Alignment.BottomRight)
+ .measure();
+
+ return new IDrawable[] { getItemSlot(), itemDrawable, stackSizeText };
+ })
+ .dynamicTooltip(() -> {
+ if (drawables.size() > finalID) {
+ ItemStack stack = drawables.get(finalID).stack;
+ List<String> tip = new LinkedList<>();
+ for (Object o : stack.getTooltip(player, false)) {
+ tip.add(o.toString());
+ }
+ if (tip.size() >= 1 && tip.get(0) != null) {
+ tip.set(0, stack.stackSize + " x " + tip.get(0));
+ }
+ return tip;
+ }
+ return Collections.emptyList();
+ })
+ .setSize(18, 18));
+ }
+
+ // only add the extra slot if we are still able to insert
+ if (this.usedSeedCount < this.maxSeedCount) {
+ buttons.add(new ButtonWidget() {
+
+ @Override
+ public void drawBackground(float partialTicks) {
+ super.drawBackground(partialTicks);
+ if (!isEnabled) {
+ GL11.glDisable(GL11.GL_LIGHTING);
+ GL11.glEnable(GL11.GL_BLEND);
+ GlStateManager.colorMask(true, true, true, false);
+ ModularGui.drawSolidRect(1, 1, 16, 16, Color.withAlpha(Color.BLACK.normal, 0x80));
+ GlStateManager.colorMask(true, true, true, true);
+ GL11.glDisable(GL11.GL_BLEND);
+ }
+ // Copied from SlotWidget#draw
+ else if (isHovering() && !getContext().getCursor()
+ .hasDraggable()) {
+ GL11.glDisable(GL11.GL_LIGHTING);
+ GL11.glEnable(GL11.GL_BLEND);
+ GlStateManager.colorMask(true, true, true, false);
+ ModularGui.drawSolidRect(1, 1, 16, 16, Theme.INSTANCE.getSlotHighlight());
+ GlStateManager.colorMask(true, true, true, true);
+ GL11.glDisable(GL11.GL_BLEND);
+ }
+ }
+ }.setPlayClickSound(false)
+ .setOnClick((clickData, widget) -> {
+ if (!(player instanceof EntityPlayerMP)) return;
+ if (!isEnabledGetter.get()) return;
+ ItemStack input = player.inventory.getItemStack();
+ if (input != null) {
+ if (clickData.mouseButton == 1) {
+ ItemStack copy = input.copy();
+ copy.stackSize = 1;
+ inventoryInjector.inject(copy);
+ if (copy.stackSize == 1) return;
+
+ input.stackSize--;
+ if (input.stackSize > 0) {
+ // clearing and updating the held item value like this is the only
+ // way i found to be able to reliably update the item count in the UI.
+ player.inventory.setItemStack(null);
+ ((EntityPlayerMP) player).updateHeldItem();
+ player.inventory.setItemStack(input);
+ ((EntityPlayerMP) player).updateHeldItem();
+ return;
+ } else player.inventory.setItemStack(null);
+ } else {
+ inventoryInjector.inject(input);
+ if (input.stackSize > 0) {
+ // clearing and updating the held item value like this is the only
+ // way i found to be able to reliably update the item count in the UI.
+ player.inventory.setItemStack(null);
+ ((EntityPlayerMP) player).updateHeldItem();
+ player.inventory.setItemStack(input);
+ ((EntityPlayerMP) player).updateHeldItem();
+ return;
+ } else player.inventory.setItemStack(null);
+ }
+ ((EntityPlayerMP) player).isChangingQuantityOnly = false;
+ ((EntityPlayerMP) player).updateHeldItem();
+ return;
+ }
+ })
+ .setBackground(() -> {
+ IDrawable itemSlot = getItemSlot();
+
+ IDrawable stackSizeText = new AutoScalingStackSizeText(this.maxSeedCount - this.usedSeedCount)
+ .color(Color.WHITE.normal)
+ .shadow()
+ .alignment(Alignment.BottomRight)
+ .measure();
+
+ return new IDrawable[] { itemSlot, stackSizeText };
+ })
+ .dynamicTooltip(() -> {
+ // TODO: all l10n for insertion slot tooltip.
+ List<String> tip = new ArrayList<>();
+ tip.add(
+ EnumChatFormatting.DARK_PURPLE + "Remaining seed types: "
+ + (this.maxSeedTypes - this.usedSeedTypes));
+ tip.add(
+ EnumChatFormatting.DARK_GREEN + "Remaining seed capacity: "
+ + (this.maxSeedCount - this.usedSeedCount));
+ return tip;
+ })
+ .setSize(18, 18));
+ }
+
+ final int perRow = width / 18;
+ for (int i = 0, imax = ((buttons.size() - 1) / perRow); i <= imax; i++) {
+ DynamicPositionedRow row = new DynamicPositionedRow().setSynced(false);
+ for (int j = 0, jmax = (i == imax ? (buttons.size() - 1) % perRow : (perRow - 1)); j <= jmax; j++) {
+ final int finalI = i * perRow;
+ final int finalJ = j;
+ final int ID = finalI + finalJ;
+ row.widget(buttons.get(ID));
+ }
+ dynamicInventoryWidget.widget(row.setPos(0, i * 18));
+ }
+ dynamicInventoryWidget.setSize(width, height);
+ return dynamicInventoryWidget;
+ }
+
+ @FunctionalInterface
+ public interface TInventoryGetter<T> {
+
+ /**
+ * Allows to get an ItemStack from the dynamic inventory
+ *
+ * @param from Dynamic inventory item from which we want to take an item out
+ * @return ItemStack or null if inaccessible
+ */
+ ItemStack get(T from);
+ }
+
+ @FunctionalInterface
+ public interface TInventoryInjector {
+
+ /**
+ * Allows to insert an item to the dynamic inventory
+ *
+ * @param what ItemStack which we are trying to insert
+ * @return Leftover ItemStack (stackSize == 0 if everything has been inserted) or null
+ */
+ ItemStack inject(ItemStack what);
+ }
+
+ @FunctionalInterface
+ public interface TInventoryExtractor<T> {
+
+ /**
+ * Allows to extract an item from the dynamic inventory
+ *
+ * @return Item that we took out or null
+ */
+ ItemStack extract(T container, EntityPlayerMP player);
+ }
+
+ @FunctionalInterface
+ public interface TInventoryReplacerOrMerger {
+
+ /**
+ * Allows to replace an item in Dynamic Inventory
+ *
+ * @param where which index we want to replace
+ * @param stack what stack we want to replace it with
+ * @return Stack that we are left with or null
+ */
+ ItemStack replaceOrMerge(int where, ItemStack stack);
+ }
+
+}
diff --git a/src/main/java/kubatech/api/IBlockStemAccesor.java b/src/main/java/kubatech/api/IBlockStemAccesor.java
new file mode 100644
index 0000000000..8f2c37e15f
--- /dev/null
+++ b/src/main/java/kubatech/api/IBlockStemAccesor.java
@@ -0,0 +1,8 @@
+package kubatech.api;
+
+import net.minecraft.block.Block;
+
+public interface IBlockStemAccesor {
+
+ Block getCropBlock();
+}
diff --git a/src/main/java/kubatech/api/eig/EIGBucket.java b/src/main/java/kubatech/api/eig/EIGBucket.java
new file mode 100644
index 0000000000..6a3dbdb642
--- /dev/null
+++ b/src/main/java/kubatech/api/eig/EIGBucket.java
@@ -0,0 +1,247 @@
+package kubatech.api.eig;
+
+import static kubatech.api.utils.ItemUtils.readItemStackFromNBT;
+import static kubatech.api.utils.ItemUtils.writeItemStackToNBT;
+
+import java.util.LinkedList;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.util.EnumChatFormatting;
+
+import gregtech.api.util.GT_Utility;
+import kubatech.tileentity.gregtech.multiblock.GT_MetaTileEntity_ExtremeIndustrialGreenhouse;
+
+public abstract class EIGBucket {
+
+ protected ItemStack seed;
+ protected int seedCount;
+ protected ItemStack[] supportItems;
+
+ public EIGBucket(ItemStack seed, int seedCount, ItemStack[] supportItem) {
+ this.seed = seed.copy();
+ this.seed.stackSize = 1;
+ this.seedCount = seedCount;
+ this.supportItems = supportItem;
+ }
+
+ public EIGBucket(NBTTagCompound nbt) {
+ this.seed = readItemStackFromNBT(nbt.getCompoundTag("seed"));
+ this.seedCount = nbt.getInteger("count");
+
+ // parse support items
+ if (nbt.hasKey("supportItems", 9)) {
+ NBTTagList supportItemsNBTList = nbt.getTagList("supportItems", 10);
+ if (supportItemsNBTList.tagCount() > 0) {
+ this.supportItems = new ItemStack[supportItemsNBTList.tagCount()];
+ for (int i = 0; i < supportItemsNBTList.tagCount(); i++) {
+ this.supportItems[i] = readItemStackFromNBT(supportItemsNBTList.getCompoundTagAt(i));
+ }
+ } else {
+ supportItems = null;
+ }
+ } else {
+ supportItems = null;
+ }
+ }
+
+ /**
+ * Creates a persistent save of the bucket's current data.
+ *
+ * @return The nbt data for this bucket.
+ */
+ public NBTTagCompound save() {
+ NBTTagCompound nbt = new NBTTagCompound();
+ nbt.setString("type", this.getNBTIdentifier());
+ nbt.setTag("seed", writeItemStackToNBT(this.seed));
+ nbt.setInteger("count", this.seedCount);
+ if (this.supportItems != null && this.supportItems.length > 0) {
+ NBTTagList supportItemNBT = new NBTTagList();
+ for (ItemStack supportItem : this.supportItems) {
+ supportItemNBT.appendTag(writeItemStackToNBT(supportItem));
+ }
+ nbt.setTag("supportItems", supportItemNBT);
+ }
+ return nbt;
+ }
+
+ /**
+ * Gets an item stack representing the seeds in this bucket
+ *
+ * @return an item stack representing the seeds in this bucket.
+ */
+ public ItemStack getSeedStack() {
+ ItemStack copied = this.seed.copy();
+ copied.stackSize = this.seedCount;
+ return copied;
+ }
+
+ /**
+ * Gets the number of seeds in this bucket
+ *
+ * @return gets the number of seeds in this bucket.
+ */
+ public int getSeedCount() {
+ return this.seedCount;
+ }
+
+ /**
+ * Gets the display name of the seed in this bucket
+ *
+ * @return The display name of the seed.
+ */
+ public String getDisplayName() {
+ return this.seed.getDisplayName();
+ }
+
+ public String getInfoData() {
+ StringBuilder sb = new StringBuilder();
+ // display invalid buckets, we don't want people to think they lost their seeds or something.
+ sb.append(this.isValid() ? EnumChatFormatting.GREEN : EnumChatFormatting.RED);
+ sb.append("x");
+ sb.append(this.getSeedCount());
+ sb.append(" ");
+ sb.append(this.getDisplayName());
+ this.getAdditionalInfoData(sb);
+ sb.append(EnumChatFormatting.RESET);
+ return sb.toString();
+ }
+
+ protected void getAdditionalInfoData(StringBuilder sb) {}
+
+ /**
+ * Attempts to add seeds to tbe bucket if the input is compatible
+ *
+ * @param input A stack of an item that may be able to be added to our current bucket.
+ * @param maxConsume The maximum amount of seeds to add to this bucket.
+ * @param simulate True if you want to see if you can add more seeds (useful for support item checks)
+ * @return number of seeds consumed, 0 for wrong item, -1 if it missed the support items, -2 if you tried to consume
+ * 0 or less items;
+ */
+ public int tryAddSeed(GT_MetaTileEntity_ExtremeIndustrialGreenhouse greenhouse, ItemStack input, int maxConsume,
+ boolean simulate) {
+ // Abort is input if empty
+ if (input == null || input.stackSize <= 0) return -2;
+ // Cap max to input count
+ maxConsume = Math.min(maxConsume, input.stackSize);
+ // Abort if item isn't an identical seed.
+ if (!GT_Utility.areStacksEqual(this.seed, input, false)) return 0;
+
+ // no support items, consume and exit early.
+ if (this.supportItems == null || this.supportItems.length <= 0) {
+ if (!simulate) {
+ input.stackSize -= maxConsume;
+ this.seedCount += maxConsume;
+ }
+ return maxConsume;
+ }
+
+ // Check if the item is found
+ LinkedList<ItemStack> toConsumeFrom = new LinkedList<>();
+ supportLoop: for (ItemStack supportItem : this.supportItems) {
+ for (ItemStack otherInput : greenhouse.getStoredInputs()) {
+ // filter usable inputs
+ if (otherInput == null || otherInput.stackSize <= 0) continue;
+ if (!GT_Utility.areStacksEqual(supportItem, otherInput, false)) continue;
+ // update max consume again
+ maxConsume = Math.min(maxConsume, otherInput.stackSize);
+ toConsumeFrom.addLast(otherInput);
+ continue supportLoop;
+ }
+ // no support found, no seeds added
+ return -1;
+ }
+
+ // consume items
+ if (!simulate) {
+ input.stackSize -= maxConsume;
+ for (ItemStack stack : toConsumeFrom) {
+ stack.stackSize -= maxConsume;
+ }
+ this.seedCount += maxConsume;
+ }
+ return maxConsume;
+ }
+
+ /**
+ * Attempts to remove a seed from the bucket
+ *
+ * @param toRemove The maximum amount of items to remove.
+ * @return The items that were removed from the bucket. Null if the bucket is empty.
+ */
+ public ItemStack[] tryRemoveSeed(int toRemove, boolean simulate) {
+ // validate inputs
+ toRemove = Math.min(this.seedCount, toRemove);
+ if (toRemove <= 0) return null;
+
+ // consume and return output
+ ItemStack[] ret = new ItemStack[1 + (this.supportItems == null ? 0 : this.supportItems.length)];
+ ret[0] = this.seed.copy();
+ ret[0].stackSize = toRemove;
+ if (this.supportItems != null) {
+ for (int i = 0; i < this.supportItems.length; i++) {
+ ret[i + 1] = this.supportItems[i].copy();
+ ret[i + 1].stackSize = toRemove;
+ }
+ }
+ if (!simulate) {
+ this.seedCount -= toRemove;
+ }
+ return ret;
+ }
+
+ /**
+ * Sets the seed count to 0 and returns item stacks representing every item in this bucket.
+ *
+ * @return The contents of the bucket
+ */
+ public ItemStack[] emptyBucket() {
+ if (this.seedCount <= 0) return null;
+ ItemStack[] ret = new ItemStack[1 + (this.supportItems == null ? 0 : this.supportItems.length)];
+ ret[0] = this.seed.copy();
+ ret[0].stackSize = this.seedCount;
+ if (this.supportItems != null) {
+ for (int i = 0; i < this.supportItems.length; i++) {
+ ret[i + 1] = this.supportItems[i].copy();
+ ret[i + 1].stackSize = this.seedCount;
+ }
+ }
+ this.seedCount = 0;
+ return ret;
+ }
+
+ /**
+ * Returns true if the bucket can output items.
+ *
+ * @return true if the bucket is valid.
+ */
+ public boolean isValid() {
+ return this.seed != null && this.seedCount > 0;
+ }
+
+ /**
+ * Gets the identifier used to identify this class during reconstruction
+ *
+ * @return the identifier for this bucket type.
+ */
+ protected abstract String getNBTIdentifier();
+
+ /**
+ * Adds item drops to the item tracker.
+ *
+ * @param multiplier A multiplier to apply to the output.
+ * @param tracker The item drop tracker
+ */
+ public abstract void addProgress(double multiplier, EIGDropTable tracker);
+
+ /**
+ * Attempts to revalidate a seed bucket. If it returns false, attempt to seed and support items and delete the
+ * bucket.
+ *
+ * @param greenhouse The greenhouse that contains the bucket.
+ * @return True if the bucket was successfully validated. {@link EIGBucket#isValid()} should also return true.
+ */
+ public abstract boolean revalidate(GT_MetaTileEntity_ExtremeIndustrialGreenhouse greenhouse);
+
+}
diff --git a/src/main/java/kubatech/api/eig/EIGDropTable.java b/src/main/java/kubatech/api/eig/EIGDropTable.java
new file mode 100644
index 0000000000..bb5bbe6456
--- /dev/null
+++ b/