/*
* Copyright (C) 2022 NotEnoughUpdates contributors
*
* This file is part of NotEnoughUpdates.
*
* NotEnoughUpdates is free software: you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* NotEnoughUpdates is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with NotEnoughUpdates. If not, see .
*/
package io.github.moulberry.notenoughupdates.miscfeatures;
import com.google.gson.JsonObject;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe;
import io.github.moulberry.notenoughupdates.events.SlotClickEvent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiChest;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.ContainerChest;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;
@NEUAutoSubscribe
public class BetterContainers {
private static final ResourceLocation TOGGLE_OFF = new ResourceLocation("notenoughupdates:dynamic_54/toggle_off.png");
private static final ResourceLocation TOGGLE_ON = new ResourceLocation("notenoughupdates:dynamic_54/toggle_on.png");
private static final ResourceLocation DYNAMIC_54_BASE = new ResourceLocation(
"notenoughupdates:dynamic_54/style1/dynamic_54.png");
private static final ResourceLocation DYNAMIC_54_SLOT = new ResourceLocation(
"notenoughupdates:dynamic_54/style1/dynamic_54_slot_ctm.png");
private static final ResourceLocation DYNAMIC_54_BUTTON = new ResourceLocation(
"notenoughupdates:dynamic_54/style1/dynamic_54_button_ctm.png");
private static final ResourceLocation rl = new ResourceLocation("notenoughupdates:dynamic_chest_inventory.png");
private static boolean loaded = false;
private static DynamicTexture texture = null;
private static int textColour = 4210752;
private static int lastClickedSlot = 0;
private static int clickedSlot = 0;
private static long clickedSlotMillis = 0;
public static long lastRenderMillis = 0;
private static int lastInvHashcode = 0;
private static final int lastHashcodeCheck = 0;
public static void clickSlot(int slot) {
clickedSlotMillis = System.currentTimeMillis();
clickedSlot = slot;
}
public static int getClickedSlot() {
if (System.currentTimeMillis() - clickedSlotMillis < 500) {
return clickedSlot;
}
return -1;
}
public static void bindHook(TextureManager textureManager, ResourceLocation location) {
long currentMillis = System.currentTimeMillis();
if (isChestOpen()) {
int invHashcode = lastInvHashcode;
if (currentMillis - lastHashcodeCheck > 50) {
Container container = ((GuiChest) Minecraft.getMinecraft().currentScreen).inventorySlots;
invHashcode = container.getInventory().hashCode();
}
if ((texture != null && lastClickedSlot != getClickedSlot()) || !loaded || lastInvHashcode != invHashcode) {
lastInvHashcode = invHashcode;
lastClickedSlot = getClickedSlot();
generateTex();
}
if (texture != null && loaded) {
lastRenderMillis = currentMillis;
GlStateManager.color(1, 1, 1, 1);
textureManager.loadTexture(rl, texture);
textureManager.bindTexture(rl);
return;
}
} else if (currentMillis - lastRenderMillis < 200 && texture != null) {
GlStateManager.color(1, 1, 1, 1);
textureManager.loadTexture(rl, texture);
textureManager.bindTexture(rl);
return;
}
GlStateManager.enableBlend();
textureManager.bindTexture(location);
}
public static boolean isBlacklistedInventory() {
if (!isChestOpen()) return false;
GuiChest eventGui = (GuiChest) Minecraft.getMinecraft().currentScreen;
ContainerChest cc = (ContainerChest) eventGui.inventorySlots;
String containerName = cc.getLowerChestInventory().getDisplayName().getUnformattedText();
return containerName.toLowerCase(Locale.ROOT).trim().startsWith("navigate the maze");
}
public static boolean isOverriding() {
return isChestOpen() && ((loaded && texture != null) || System.currentTimeMillis() - lastRenderMillis < 200) &&
!isBlacklistedInventory();
}
public static boolean isBlankStack(int index, ItemStack stack) {
return stack != null && stack.getItem() == Item.getItemFromBlock(Blocks.stained_glass_pane) &&
stack.getItemDamage() == 15 &&
stack.getDisplayName() != null && stack.getDisplayName().trim().isEmpty();
}
public static boolean shouldRenderStack(int index, ItemStack stack) {
return !isBlankStack(index, stack) && !isToggleOff(stack) && !isToggleOn(stack);
}
public static boolean isButtonStack(int index, ItemStack stack) {
return stack != null && stack.getItem() != Item.getItemFromBlock(Blocks.stained_glass_pane)
&& NotEnoughUpdates.INSTANCE.manager.getInternalNameForItem(stack) == null && !isToggleOn(stack) && !isToggleOff(
stack);
}
public static int getTextColour() {
return textColour;
}
public static boolean isToggleOn(ItemStack stack) {
if (stack != null && stack.getTagCompound() != null && stack.getTagCompound().hasKey("display", 10) &&
stack.getTagCompound().getCompoundTag("display").hasKey("Lore", 9)) {
NBTTagList lore = stack.getTagCompound().getCompoundTag("display").getTagList("Lore", 8);
return lore.tagCount() == 1 && lore.getStringTagAt(0).equalsIgnoreCase(
EnumChatFormatting.GRAY + "click to disable!");
}
return false;
}
public static boolean isToggleOff(ItemStack stack) {
if (stack != null && stack.getTagCompound() != null && stack.getTagCompound().hasKey("display", 10) &&
stack.getTagCompound().getCompoundTag("display").hasKey("Lore", 9)) {
NBTTagList lore = stack.getTagCompound().getCompoundTag("display").getTagList("Lore", 8);
return lore.tagCount() == 1 && lore.getStringTagAt(0).equalsIgnoreCase(
EnumChatFormatting.GRAY + "click to enable!");
}
return false;
}
static BufferedImage bufferedImageOn = null;
static BufferedImage bufferedImageOff = null;
static BufferedImage bufferedImageBase = null;
static BufferedImage bufferedImageSlot = null;
static BufferedImage bufferedImageButton = null;
static List lastSlots = null;
private static void generateBufferedImages() {
try {
int backgroundStyle = NotEnoughUpdates.INSTANCE.config.improvedSBMenu.backgroundStyle + 1;
backgroundStyle = Math.max(1, Math.min(10, backgroundStyle));
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(Minecraft
.getMinecraft()
.getResourceManager()
.getResource(
new ResourceLocation("notenoughupdates:dynamic_54/style" + backgroundStyle + "/dynamic_config.json"))
.getInputStream(), StandardCharsets.UTF_8))
) {
JsonObject json = NotEnoughUpdates.INSTANCE.manager.gson.fromJson(reader, JsonObject.class);
String textColourS = json.get("text-colour").getAsString();
textColour = (int) Long.parseLong(textColourS, 16);
} catch (Exception e) {
textColour = 4210752;
}
bufferedImageOn = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(TOGGLE_ON).getInputStream());
bufferedImageOff = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(TOGGLE_OFF).getInputStream());
bufferedImageBase = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(DYNAMIC_54_BASE).getInputStream());
bufferedImageSlot = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(DYNAMIC_54_SLOT).getInputStream());
bufferedImageButton = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(DYNAMIC_54_BUTTON).getInputStream());
try {
bufferedImageBase = ImageIO.read(Minecraft
.getMinecraft()
.getResourceManager()
.getResource(
new ResourceLocation("notenoughupdates:dynamic_54/style" + backgroundStyle + "/dynamic_54.png"))
.getInputStream());
} catch (Exception ignored) {
}
try {
int buttonStyle = NotEnoughUpdates.INSTANCE.config.improvedSBMenu.buttonStyle + 1;
buttonStyle = Math.max(1, Math.min(10, buttonStyle));
bufferedImageSlot = ImageIO.read(Minecraft
.getMinecraft()
.getResourceManager()
.getResource(
new ResourceLocation("notenoughupdates:dynamic_54/style" + buttonStyle + "/dynamic_54_slot_ctm.png"))
.getInputStream());
} catch (Exception ignored) {
}
try {
int buttonStyle = NotEnoughUpdates.INSTANCE.config.improvedSBMenu.buttonStyle + 1;
buttonStyle = Math.max(1, Math.min(10, buttonStyle));
bufferedImageButton = ImageIO.read(Minecraft
.getMinecraft()
.getResourceManager()
.getResource(
new ResourceLocation("notenoughupdates:dynamic_54/style" + buttonStyle + "/dynamic_54_button_ctm.png"))
.getInputStream());
} catch (Exception ignored) {
}
} catch (Exception e) {
}
}
private static void generateTex() {
if (!hasItem()) return;
loaded = true;
Container container = ((GuiChest) Minecraft.getMinecraft().currentScreen).inventorySlots;
List inventorySlots = ((GuiChest) Minecraft.getMinecraft().currentScreen).inventorySlots.inventorySlots;
if (hasNullPane() && container instanceof ContainerChest) {
if (lastSlots != inventorySlots) {
generateBufferedImages();
lastSlots = inventorySlots;
}
try {
int horzTexMult = bufferedImageBase.getWidth() / 256;
int vertTexMult = bufferedImageBase.getWidth() / 256;
BufferedImage bufferedImageNew = new BufferedImage(
bufferedImageBase.getColorModel(),
bufferedImageBase.copyData(null),
bufferedImageBase.isAlphaPremultiplied(),
null
);
IInventory lower = ((ContainerChest) container).getLowerChestInventory();
int size = lower.getSizeInventory();
boolean[][] slots = new boolean[9][size / 9];
boolean[][] buttons = new boolean[9][size / 9];
boolean ultrasequencer = lower.getDisplayName().getUnformattedText().startsWith("Ultrasequencer") &&
!lower.getDisplayName().getUnformattedText().contains("Stakes");
boolean superpairs = lower.getDisplayName().getUnformattedText().startsWith("Superpairs") &&
!lower.getDisplayName().getUnformattedText().contains("Stakes");
for (int index = 0; index < size; index++) {
ItemStack stack = getStackFromInvetory(lower, index);
buttons[index % 9][index / 9] = isButtonStack(index, stack);
if (ultrasequencer && stack.getItem() == Items.dye) {
buttons[index % 9][index / 9] = false;
}
if (superpairs && index > 9 && index < size - 9) {
buttons[index % 9][index / 9] = false;
}
if (buttons[index % 9][index / 9] && lastClickedSlot == index) {
//buttons[index%9][index/9] = false;
//slots[index%9][index/9] = true;
} else {
slots[index % 9][index / 9] = !isBlankStack(index, stack) && !buttons[index % 9][index / 9];
}
}
for (int index = 0; index < size; index++) {
ItemStack stack = getStackFromInvetory(lower, index);
int xi = index % 9;
int yi = index / 9;
if (slots[xi][yi] || buttons[xi][yi]) {
int x = 7 * horzTexMult + xi * 18 * horzTexMult;
int y = 17 * vertTexMult + yi * 18 * vertTexMult;
boolean on = isToggleOn(stack);
boolean off = isToggleOff(stack);
if (on || off) {
for (int x2 = 0; x2 < 18; x2++) {
for (int y2 = 0; y2 < 18; y2++) {
BufferedImage toggle = on ? bufferedImageOn : bufferedImageOff;
Color c = new Color(toggle.getRGB(x2, y2), true);
if (c.getAlpha() < 10) {
continue;
}
bufferedImageNew.setRGB(x + x2, y + y2, c.getRGB());
}
}
continue;
}
if (buttons[xi][yi]) {
boolean up = yi > 0 && buttons[xi][yi - 1];
boolean right = xi < buttons.length - 1 && buttons[xi + 1][yi];
boolean down = yi < buttons[xi].length - 1 && buttons[xi][yi + 1];
boolean left = xi > 0 && buttons[xi - 1][yi];
boolean upleft = yi > 0 && xi > 0 && buttons[xi - 1][yi - 1];
boolean upright = yi > 0 && xi < buttons.length - 1 && buttons[xi + 1][yi - 1];
boolean downright = xi < buttons.length - 1 && yi < buttons[xi + 1].length - 1 && buttons[xi + 1][yi + 1];
boolean downleft = xi > 0 && yi < buttons[xi - 1].length - 1 && buttons[xi - 1][yi + 1];
int ctmIndex = getCTMIndex(up, right, down, left, upleft, upright, downright, downleft);
int[] rgbs = bufferedImageButton.getRGB(
(ctmIndex % 12) * 19 * horzTexMult,
(ctmIndex / 12) * 19 * vertTexMult,
18 * horzTexMult,
18 * vertTexMult,
null,
0,
18 * vertTexMult
);
bufferedImageNew.setRGB(x, y, 18 * horzTexMult, 18 * vertTexMult, rgbs, 0, 18 * vertTexMult);
} else {
boolean up = yi > 0 && slots[xi][yi - 1];
boolean right = xi < slots.length - 1 && slots[xi + 1][yi];
boolean down = yi < slots[xi].length - 1 && slots[xi][yi + 1];
boolean left = xi > 0 && slots[xi - 1][yi];
boolean upleft = yi > 0 && xi > 0 && slots[xi - 1][yi - 1];
boolean upright = yi > 0 && xi < slots.length - 1 && slots[xi + 1][yi - 1];
boolean downright = xi < slots.length - 1 && yi < slots[xi + 1].length - 1 && slots[xi + 1][yi + 1];
boolean downleft = xi > 0 && yi < slots[xi - 1].length - 1 && slots[xi - 1][yi + 1];
int ctmIndex = getCTMIndex(up, right, down, left, upleft, upright, downright, downleft);
int[] rgbs = bufferedImageSlot.getRGB(
(ctmIndex % 12) * 19 * horzTexMult,
(ctmIndex / 12) * 19 * vertTexMult,
18 * horzTexMult,
18 * vertTexMult,
null,
0,
18 * vertTexMult
);
bufferedImageNew.setRGB(x, y, 18 * horzTexMult, 18 * vertTexMult, rgbs, 0, 18 * vertTexMult);
}
}
}
if (texture != null) {
bufferedImageNew.getRGB(0, 0, bufferedImageNew.getWidth(), bufferedImageNew.getHeight(),
texture.getTextureData(), 0, bufferedImageNew.getWidth()
);
texture.updateDynamicTexture();
} else {
texture = new DynamicTexture(bufferedImageNew);
}
return;
} catch (Exception e) {
e.printStackTrace();
}
}
texture = null;
}
public static void reset() {
loaded = false;
clickedSlot = -1;
clickedSlotMillis = 0;
}
private static boolean isChestOpen() {
return Minecraft.getMinecraft().currentScreen instanceof GuiChest &&
NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard() &&
NotEnoughUpdates.INSTANCE.config.improvedSBMenu.enableSbMenus;
}
private static boolean hasItem() {
if (!isChestOpen()) return false;
Container container = ((GuiChest) Minecraft.getMinecraft().currentScreen).inventorySlots;
if (container instanceof ContainerChest) {
IInventory lower = ((ContainerChest) container).getLowerChestInventory();
int size = lower.getSizeInventory();
for (int index = 0; index < size; index++) {
if (getStackFromInvetory(lower, index) != null) return true;
}
}
return false;
}
private static ItemStack getStackFromInvetory(IInventory lower, int index) {
return lower.getStackInSlot(index);
}
private static boolean hasNullPane() {
if (!isChestOpen()) return false;
Container container = ((GuiChest) Minecraft.getMinecraft().currentScreen).inventorySlots;
if (container instanceof ContainerChest) {
IInventory lower = ((ContainerChest) container).getLowerChestInventory();
int size = lower.getSizeInventory();
for (int index = 0; index < size; index++) {
if (isBlankStack(index, getStackFromInvetory(lower, index))) return true;
}
}
return false;
}
public static int getCTMIndex(
boolean up,
boolean right,
boolean down,
boolean left,
boolean upleft,
boolean upright,
boolean downright,
boolean downleft
) {
if (up && right && down && left) {
if (upleft && upright && downright && downleft) {
return 26;
} else if (upleft && upright && downright && !downleft) {
return 33;
} else if (upleft && upright && !downright && downleft) {
return 32;
} else if (upleft && upright && !downright && !downleft) {
return 11;
} else if (upleft && !upright && downright && downleft) {
return 44;
} else if (upleft && !upright && downright && !downleft) {
return 35;
} else if (upleft && !upright && !downright && downleft) {
return 10;
} else if (upleft && !upright && !downright && !downleft) {
return 20;
} else if (!upleft && upright && downright && downleft) {
return 45;
} else if (!upleft && upright && downright && !downleft) {
return 23;
} else if (!upleft && upright && !downright && downleft) {
return 34;
} else if (!upleft && upright && !downright && !downleft) {
return 8;
} else if (!upleft && !upright && downright && downleft) {
return 22;
} else if (!upleft && !upright && downright && !downleft) {
return 9;
} else if (!upleft && !upright && !downright && downleft) {
return 21;
} else {
return 46;
}
} else if (up && right && down && !left) {
if (!upright && !downright) {
return 6;
} else if (!upright) {
return 28;
} else if (!downright) {
return 30;
} else {
return 25;
}
} else if (up && right && !down && left) {
if (!upleft && !upright) {
return 18;
} else if (!upleft) {
return 40;
} else if (!upright) {
return 42;
} else {
return 38;
}
} else if (up && right && !down && !left) {
if (!upright) {
return 16;
} else {
return 37;
}
} else if (up && !right && down && left) {
if (!upleft && !downleft) {
return 19;
} else if (!upleft) {
return 43;
} else if (!downleft) {
return 41;
} else {
return 27;
}
} else if (up && !right && down && !left) {
return 24;
} else if (up && !right && !down && left) {
if (!upleft) {
return 17;
} else {
return 39;
}
} else if (up && !right && !down && !left) {
return 36;
} else if (!up && right && down && left) {
if (!downleft && !downright) {
return 7;
} else if (!downleft) {
return 31;
} else if (!downright) {
return 29;
} else {
return 14;
}
} else if (!up && right && down && !left) {
if (!downright) {
return 4;
} else {
return 13;
}
} else if (!up && right && !down && left) {
return 2;
} else if (!up && right && !down && !left) {
return 1;
} else if (!up && !right && down && left) {
if (!downleft) {
return 5;
} else {
return 15;
}
} else if (!up && !right && down && !left) {
return 12;
} else if (!up && !right && !down && left) {
return 3;
} else {
return 0;
}
}
@SubscribeEvent
public void onMouseClick(SlotClickEvent event) {
if (!isOverriding()) return;
boolean isBlankStack = BetterContainers.isBlankStack(event.slot.slotNumber, event.slot.getStack());
if (!(isBlankStack ||
BetterContainers.isButtonStack(event.slot.slotNumber, event.slot.getStack()))) return;
clickSlot(event.slotId);
if (isBlankStack) {
event.usePickblockInstead();
}
}
}