diff options
| author | Raven Szewczyk <git@eigenraven.me> | 2024-05-30 18:26:10 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-30 19:26:10 +0200 |
| commit | 337594e83a74c432c140b3df3287575b81bce467 (patch) | |
| tree | abe57b3390d3dd037ea1442f83c4519ebcb9de07 /src/main/java/kubatech/api | |
| parent | 752f262ccd545bdb785ef0e9ce922bf1117d23d6 (diff) | |
| download | GT5-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.java | 510 | ||||
| -rw-r--r-- | src/main/java/kubatech/api/IBlockStemAccesor.java | 8 | ||||
| -rw-r--r-- | src/main/java/kubatech/api/eig/EIGBucket.java | 247 | ||||
| -rw-r--r-- | src/main/java/kubatech/api/eig/EIGDropTable.java | 224 | ||||
| -rw-r--r-- | src/main/java/kubatech/api/eig/EIGMode.java | 154 | ||||
| -rw-r--r-- | src/main/java/kubatech/api/eig/IEIGBucketFactory.java | 15 | ||||
| -rw-r--r-- | src/main/java/kubatech/api/enums/EIGModes.java | 42 | ||||
| -rw-r--r-- | src/main/java/kubatech/api/enums/EIGSetupPhase.java | 16 | ||||
| -rw-r--r-- | src/main/java/kubatech/api/gui/AutoScalingStackSizeText.java | 72 | ||||
| -rw-r--r-- | src/main/java/kubatech/api/implementations/KubaTechGTMultiBlockBase.java | 6 | ||||
| -rw-r--r-- | src/main/java/kubatech/api/utils/StringUtils.java | 6 |
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/src/main/java/kubatech/api/eig/EIGDropTable.java @@ -0,0 +1,224 @@ +package kubatech.api.eig; + +import static kubatech.api.utils.ItemUtils.readItemStackFromNBT; +import static kubatech.api.utils.ItemUtils.writeItemStackToNBT; + +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; + +import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; + +public class EIGDropTable { + + private static final String NBT_DROP_TABLE_ITEM_KEY = "item"; + private static final String NBT_DROP_TABLE_COUNT_KEY = "count"; + + private final ItemStackMap<Double> dropTable; + + /** + * Initialises a new empty drop table. + */ + public EIGDropTable() { + this.dropTable = new ItemStackMap<>(true); + } + + /** + * Loads a serialised drop table from nbt. + * + * @param nbt The nbt tag that contains the key for a drop table + * @param key The name of the key name for the drop table. + */ + public EIGDropTable(NBTTagCompound nbt, String key) { + // should create an empty table if no drops are found. + this(nbt.getTagList(key, 10)); + } + + /** + * Loads a serialised drop table from nbt. + * + * @param nbt The nbt tag that contains the key for a drop table + */ + public EIGDropTable(NBTTagList nbt) { + this(); + for (int i = 0; i < nbt.tagCount(); i++) { + NBTTagCompound drop = nbt.getCompoundTagAt(i); + dropTable.merge( + readItemStackFromNBT(drop.getCompoundTag(NBT_DROP_TABLE_ITEM_KEY)), + drop.getDouble(NBT_DROP_TABLE_COUNT_KEY), + Double::sum); + } + } + + /** + * Serialises the drop table to nbt + * + * @return The serialised drop table. + */ + public NBTTagList save() { + NBTTagList nbt = new NBTTagList(); + for (Map.Entry<ItemStack, Double> entry : this.dropTable.entrySet()) { + NBTTagCompound entryNBT = new NBTTagCompound(); + entryNBT.setTag(NBT_DROP_TABLE_ITEM_KEY, writeItemStackToNBT(entry.getKey())); + entryNBT.setDouble(NBT_DROP_TABLE_COUNT_KEY, entry.getValue()); + nbt.appendTag(entryNBT); + } + return nbt; + } + + /** + * Adds a drop to the drop table + * + * @param itemStack The item to add to the table. + * @param amount The amount to add to the table. + */ + public void addDrop(ItemStack itemStack, double amount) { + ItemStack key = itemStack.copy(); + key.stackSize = 1; + this.dropTable.merge(key, amount, Double::sum); + } + + /** + * Adds the values from this drop table to another, but multiplies the amount by a random amount bound by variance. + * + * @param target The drop table that you want to add the value to. + * @param variance How much to vary the amounts of this drop table to, 0 < x < 1 plz + * @param rand The random source for the variance. + */ + public void addTo(EIGDropTable target, double variance, Random rand) { + this.addTo(target, 1.0, variance, rand); + } + + /** + * Adds the values from this drop table to another, but multiplies the amount by a multiplier and a random amount + * bound by variance. + * + * @param target The drop table that you want to add the value to. + * @param multiplier A multiplier to apply to all amounts from this drop table. + * @param variance How much to vary the amounts of this drop table to, 0 < x < 1 plz. + * @param rand The random source for the variance. + */ + public void addTo(EIGDropTable target, double multiplier, double variance, Random rand) { + this.addTo(target, variance * (rand.nextDouble() - 0.5) * multiplier); + } + + /** + * Adds the values from this drop table to another. + * + * @param target The drop table that you want to add the value to. + */ + public void addTo(EIGDropTable target) { + this.addTo(target, 1.0); + } + + /** + * Adds the values from this drop table to another but multiplies the values by a multiplier. + * + * @param target The drop table that you want to add the value to. + * @param multiplier A multiplier to apply to all amounts from this drop table. + */ + public void addTo(EIGDropTable target, double multiplier) { + for (Map.Entry<ItemStack, Double> entry : this.dropTable.entrySet()) { + target.dropTable.merge(entry.getKey(), entry.getValue() * multiplier, Double::sum); + } + } + + /** + * Checks if the drop table is empty; + * + * @return true if empty. + */ + public boolean isEmpty() { + return this.dropTable.isEmpty(); + } + + /** + * Returns the entry set for this drop table. + * + * @return ItemStack -> amount + */ + public Set<Map.Entry<ItemStack, Double>> entrySet() { + return this.dropTable.entrySet(); + } + + /** + * Gets the amount for a specific item. + * + * @param item The item to look for. + * @return 0 if nothing is found else a positive value. + */ + public double getItemAmount(ItemStack item) { + if (this.dropTable.containsKey(item)) { + return this.dropTable.get(item); + } + return 0; + } + + /** + * Sets the amount for a specific item. + * + * @param item The item to look for. + */ + public void setItemAmount(ItemStack item, double value) { + this.dropTable.put(item, value); + } + + /** + * Removes an item from the drop table + * + * @param item The item to remove from the drop table. + */ + public void removeItem(ItemStack item) { + this.dropTable.remove(item); + } + + /** + * Creates a new drop table that is the intersection of this drop table and another. + * + * + * @param with The drop table to intersect with. + * @return The result of the intersection. + */ + public EIGDropTable intersect(EIGDropTable with) { + EIGDropTable ret = new EIGDropTable(); + for (ItemStack key : with.dropTable.keySet()) { + if (this.dropTable.containsKey(key)) { + ret.addDrop(key, this.dropTable.get(key)); + } + } + return ret; + } + + /** + * Consumes drops with drop counts above 1 and returns a list of the consumed item stacks. + * + * @return The list of consumed items; + */ + public ItemStack[] getDrops() { + // doesn't need to filter for less than 0 so that the EIG displays the progress of incomplete items. + return this.dropTable.entrySet() + .parallelStream() + .map(EIGDropTable::computeDrops) + .toArray(ItemStack[]::new); + } + + /** + * Consumes the items in the entry and returns the consumed item without removing partial items. + * + * @param entry The entry to consume from + * @return The item tha twas removed. + */ + private static ItemStack computeDrops(Map.Entry<ItemStack, Double> entry) { + ItemStack copied = entry.getKey() + .copy(); + copied.stackSize = (int) Math.floor(entry.getValue()); + if (entry.getValue() >= 1.0d) { + entry.setValue(entry.getValue() % 1); + } + return copied; + } +} diff --git a/src/main/java/kubatech/api/eig/EIGMode.java b/src/main/java/kubatech/api/eig/EIGMode.java new file mode 100644 index 0000000000..68ad633773 --- /dev/null +++ b/src/main/java/kubatech/api/eig/EIGMode.java @@ -0,0 +1,154 @@ +package kubatech.api.eig; + +import static kubatech.kubatech.error; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; + +import gregtech.api.util.GT_Multiblock_Tooltip_Builder; +import kubatech.tileentity.gregtech.multiblock.GT_MetaTileEntity_ExtremeIndustrialGreenhouse; + +public abstract class EIGMode { + + public abstract int getUIIndex(); + + public abstract String getName(); + + public abstract int getMinVoltageTier(); + + public abstract int getMinGlassTier(); + + public abstract int getStartingSlotCount(); + + public abstract int getSlotPerTierMultiplier(); + + public abstract int getSlotCount(int machineTier); + + public abstract int getSeedCapacityPerSlot(); + + public abstract int getWeedEXMultiplier(); + + public abstract int getMaxFertilizerUsagePerSeed(); + + public abstract double getFertilizerBoost(); + + public abstract GT_Multiblock_Tooltip_Builder addTooltipInfo(GT_Multiblock_Tooltip_Builder builder); + + /** + * Used to resolve factory type to an identifier. + */ + private final HashMap<String, IEIGBucketFactory> factories; + /** + * A way to have other mods submit custom buckets that can be prioritized over our default buckets + */ + private final LinkedList<IEIGBucketFactory> orderedFactories; + + public EIGMode() { + this.factories = new HashMap<>(); + this.orderedFactories = new LinkedList<>(); + } + + /** + * Adds a bucket factory to the EIG mode and gives it a low priority. Factories with using existing IDs will + * overwrite each other. + * + * @param factory The bucket factory to add. + */ + public void addLowPriorityFactory(IEIGBucketFactory factory) { + String factoryId = factory.getNBTIdentifier(); + dealWithDuplicateFactoryId(factoryId); + // add factory as lowest priority + this.factories.put(factoryId, factory); + this.orderedFactories.addLast(factory); + } + + /** + * Adds a bucket factory to the EIG mode and gives it a high priority. Factories with using existing IDs will + * overwrite each other. + * + * @param factory The bucket factory to add. + */ + public void addHighPriorityFactory(IEIGBucketFactory factory) { + String factoryId = factory.getNBTIdentifier(); + dealWithDuplicateFactoryId(factoryId); + // add factory as lowest priority + this.factories.put(factoryId, factory); + this.orderedFactories.addFirst(factory); + } + + /** + * A standardized way to deal with duplicate factory type identifiers. + * + * @param factoryId The ID of the factory + */ + private void dealWithDuplicateFactoryId(String factoryId) { + if (this.factories.containsKey(factoryId)) { + // TODO: Check with devs to see if they want a throw instead. + error("Duplicate EIG bucket index detected!!!: " + factoryId); + // remove duplicate from ordered list + this.orderedFactories.remove(this.factories.get(factoryId)); + } + } + + /** + * Attempts to create a new bucket from a given item. Returns if the item cannot be inserted into the EIG. + * + * @see IEIGBucketFactory#tryCreateBucket(GT_MetaTileEntity_ExtremeIndustrialGreenhouse, ItemStack) + * @param greenhouse The {@link GT_MetaTileEntity_ExtremeIndustrialGreenhouse} that will contain the seed. + * @param input The {@link ItemStack} for the input item. + * @param maxConsume The maximum amount of items to consume. + * @param simulate Whether to actually consume the seed. + * @return Null if no bucket could be created from the item. + */ + public EIGBucket tryCreateNewBucket(GT_MetaTileEntity_ExtremeIndustrialGreenhouse greenhouse, ItemStack input, + int maxConsume, boolean simulate) { + // Validate inputs + if (input == null) return null; + maxConsume = Math.min(input.stackSize, maxConsume); + if (maxConsume <= 0) return null; + for (IEIGBucketFactory factory : this.orderedFactories) { + EIGBucket bucket = factory.tryCreateBucket(greenhouse, input); + if (bucket == null || !bucket.isValid()) continue; + if (!simulate) input.stackSize--; + maxConsume--; + bucket.tryAddSeed(greenhouse, input, maxConsume, simulate); + return bucket; + } + return null; + } + + /** + * Restores the buckets of an EIG for the given mode. + * + * @see IEIGBucketFactory#restore(NBTTagCompound) + * @param bucketNBTList The + */ + public void restoreBuckets(NBTTagList bucketNBTList, List<EIGBucket> loadTo) { + for (int i = 0; i < bucketNBTList.tagCount(); i++) { + // validate nbt + NBTTagCompound bucketNBT = bucketNBTList.getCompoundTagAt(i); + if (bucketNBT.hasNoTags()) { + error("Empty nbt bucket found in EIG nbt."); + continue; + } + if (!bucketNBT.hasKey("type", 8)) { + error("Failed to identify bucket type in EIG nbt."); + continue; + } + // identify bucket type + String bucketType = bucketNBT.getString("type"); + IEIGBucketFactory factory = factories.getOrDefault(bucketType, null); + if (factory == null) { + error("failed to find EIG bucket factory for type: " + bucketType); + continue; + } + // restore bucket + loadTo.add(factory.restore(bucketNBT)); + } + } +} diff --git a/src/main/java/kubatech/api/eig/IEIGBucketFactory.java b/src/main/java/kubatech/api/eig/IEIGBucketFactory.java new file mode 100644 index 0000000000..647e544573 --- /dev/null +++ b/src/main/java/kubatech/api/eig/IEIGBucketFactory.java @@ -0,0 +1,15 @@ +package kubatech.api.eig; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import kubatech.tileentity.gregtech.multiblock.GT_MetaTileEntity_ExtremeIndustrialGreenhouse; + +public interface IEIGBucketFactory { + + String getNBTIdentifier(); + + EIGBucket tryCreateBucket(GT_MetaTileEntity_ExtremeIndustrialGreenhouse greenhouse, ItemStack stack); + + EIGBucket restore(NBTTagCompound nbt); +} diff --git a/src/main/java/kubatech/api/enums/EIGModes.java b/src/main/java/kubatech/api/enums/EIGModes.java new file mode 100644 index 0000000000..a81de2b8c5 --- /dev/null +++ b/src/main/java/kubatech/api/enums/EIGModes.java @@ -0,0 +1,42 @@ +package kubatech.api.enums; + +import java.util.HashMap; + +import gregtech.api.util.GT_Multiblock_Tooltip_Builder; +import kubatech.api.eig.EIGMode; +import kubatech.tileentity.gregtech.multiblock.eigmodes.EIGIC2Mode; +import kubatech.tileentity.gregtech.multiblock.eigmodes.EIGNormalMode; + +public class EIGModes { + + private static final HashMap<String, EIGMode> modes = new HashMap<>(); + + public static final EIGMode Normal = addMode(EIGNormalMode.instance); + public static final EIGMode IC2 = addMode(EIGIC2Mode.instance); + + // this is basically a fake enum, plz don't instantiate + private EIGModes() {} + + private static EIGMode addMode(EIGMode mode) { + modes.put(mode.getName(), mode); + return mode; + } + + public static EIGMode getModeFromName(String name) { + return modes.get(name); + } + + public static EIGMode getNextMode(EIGMode from) { + int id = (from.getUIIndex() + 1) % modes.size(); + for (EIGMode mode : modes.values()) { + if (mode.getUIIndex() == id) return mode; + } + return Normal; + } + + public static void addTooltipInfo(GT_Multiblock_Tooltip_Builder tt) { + // maybe make this use the mods list instead + EIGModes.Normal.addTooltipInfo(tt); + EIGModes.IC2.addTooltipInfo(tt); + } +} diff --git a/src/main/java/kubatech/api/enums/EIGSetupPhase.java b/src/main/java/kubatech/api/enums/EIGSetupPhase.java new file mode 100644 index 0000000000..95e8854347 --- /dev/null +++ b/src/main/java/kubatech/api/enums/EIGSetupPhase.java @@ -0,0 +1,16 @@ +package kubatech.api.enums; + +public enum EIGSetupPhase { + + Operation(0, "Operation"), + Input(1, "Input"), + Output(2, "Output"); + + public final int id; + public final String name; + + private EIGSetupPhase(int id, String name) { + this.id = id; + this.name = name; + } +} diff --git a/src/main/java/kubatech/api/gui/AutoScalingStackSizeText.java b/src/main/java/kubatech/api/gui/AutoScalingStackSizeText.java new file mode 100644 index 0000000000..313610de07 --- /dev/null +++ b/src/main/java/kubatech/api/gui/AutoScalingStackSizeText.java @@ -0,0 +1,72 @@ +package kubatech.api.gui; + +import java.util.Collections; + +import com.gtnewhorizons.modularui.api.NumberFormatMUI; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.TextRenderer; +import com.gtnewhorizons.modularui.api.math.Alignment; +import com.gtnewhorizons.modularui.api.math.Color; +import com.gtnewhorizons.modularui.common.internal.Theme; + +public class AutoScalingStackSizeText implements IDrawable { + + private static final TextRenderer measuringRenderer = new TextRenderer(); + private static final NumberFormatMUI muiNumberFormat = new NumberFormatMUI(); + private static final TextRenderer renderer = new TextRenderer(); + private Alignment alignment = Alignment.Center; + private final String text; + private int simWidth; + + private int color; + private boolean shadow = false; + + public AutoScalingStackSizeText(long stackSize) { + this.text = muiNumberFormat.formatWithSuffix(stackSize); + this.color = Theme.INSTANCE.getText(); + this.measure(); + } + + public AutoScalingStackSizeText color(int color) { + this.color = Color.withAlpha(color, 255); + return this; + } + + public AutoScalingStackSizeText shadow(boolean shadow) { + this.shadow = shadow; + return this; + } + + public AutoScalingStackSizeText shadow() { + return shadow(true); + } + + public AutoScalingStackSizeText alignment(Alignment alignment) { + this.alignment = alignment; + return this; + } + + public AutoScalingStackSizeText measure() { + this.simWidth = measuringRenderer.getMaxWidth(Collections.singletonList(this.text)); + return this; + } + + public boolean hasColor() { + return Color.getAlpha(color) > 0; + } + + @Override + public void applyThemeColor(int color) { + renderer.setColor(hasColor() ? this.color : Theme.INSTANCE.getText()); + } + + @Override + public void draw(float x, float y, float width, float height, float partialTicks) { + renderer.setPos((int) (x - 0.5), (int) (y - 0.5)); + renderer.setShadow(this.shadow); + renderer.setAlignment(alignment, width, height); + renderer.setColor(this.color); + renderer.setScale(this.simWidth <= 16.0f ? 1.0f : 16.0f / this.simWidth); + renderer.draw(this.text); + } +} diff --git a/src/main/java/kubatech/api/implementations/KubaTechGTMultiBlockBase.java b/src/main/java/kubatech/api/implementations/KubaTechGTMultiBlockBase.java index bf74df9154..4451e3f401 100644 --- a/src/main/java/kubatech/api/implementations/KubaTechGTMultiBlockBase.java +++ b/src/main/java/kubatech/api/implementations/KubaTechGTMultiBlockBase.java @@ -208,7 +208,7 @@ public abstract class KubaTechGTMultiBlockBase<T extends GT_MetaTileEntity_Exten return tryOutputAll(list, l -> Collections.singletonList((ItemStack) l)); } - protected boolean tryOutputAll(List<?> list, Function<Object, List<ItemStack>> mappingFunction) { + protected <Y> boolean tryOutputAll(List<Y> list, Function<Y, List<ItemStack>> mappingFunction) { if (list == null || list.isEmpty() || mappingFunction == null) return false; int emptySlots = 0; boolean ignoreEmptiness = false; @@ -224,6 +224,10 @@ public abstract class KubaTechGTMultiBlockBase<T extends GT_MetaTileEntity_Exten boolean wasSomethingRemoved = false; while (!list.isEmpty()) { List<ItemStack> toOutputNow = mappingFunction.apply(list.get(0)); + if (toOutputNow == null) { + list.remove(0); + continue; + } if (!ignoreEmptiness && emptySlots < toOutputNow.size()) break; emptySlots -= toOutputNow.size(); list.remove(0); diff --git a/src/main/java/kubatech/api/utils/StringUtils.java b/src/main/java/kubatech/api/utils/StringUtils.java index c60da71b14..68f6c8249f 100644 --- a/src/main/java/kubatech/api/utils/StringUtils.java +++ b/src/main/java/kubatech/api/utils/StringUtils.java @@ -22,6 +22,8 @@ package kubatech.api.utils; import net.minecraft.util.EnumChatFormatting; +import gregtech.api.enums.GT_Values; + public class StringUtils { private static final String[] rainbow = new String[] { EnumChatFormatting.DARK_RED.toString(), @@ -49,4 +51,8 @@ public class StringUtils { public static String applyRainbow(String str) { return applyRainbow(str, 0, ""); } + + public static String voltageTooltipFormatted(int tier) { + return GT_Values.TIER_COLORS[tier] + GT_Values.VN[tier] + EnumChatFormatting.GRAY; + } } |
