package com.thatgravyboat.skyblockhud.config; import static com.thatgravyboat.skyblockhud.GuiTextures.*; import com.google.common.collect.Lists; import com.thatgravyboat.skyblockhud.core.GlScissorStack; import com.thatgravyboat.skyblockhud.core.GuiElement; import com.thatgravyboat.skyblockhud.core.config.Config; import com.thatgravyboat.skyblockhud.core.config.gui.GuiOptionEditor; import com.thatgravyboat.skyblockhud.core.config.gui.GuiOptionEditorAccordion; import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor; import com.thatgravyboat.skyblockhud.core.util.lerp.LerpUtils; import com.thatgravyboat.skyblockhud.core.util.lerp.LerpingInteger; import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils; import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils; import java.awt.*; import java.net.URI; import java.util.*; import java.util.List; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.ResourceLocation; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; public class SBHConfigEditor extends GuiElement { private static final ResourceLocation[] socialsIco = new ResourceLocation[] { DISCORD, TWITTER }; private static final String[] socialsLink = new String[] { "https://discord.gg/moulberry", "https://twitter.com/thatgravytboat/" }; private final long openedMillis; private String selectedCategory = null; private final LerpingInteger optionsScroll = new LerpingInteger(0, 150); private final LerpingInteger categoryScroll = new LerpingInteger(0, 150); private LinkedHashMap processedConfig; private HashMap categoryForOption = new HashMap<>(); public SBHConfigEditor(Config config) { this(config, null); } public SBHConfigEditor(Config config, String categoryOpen) { this.openedMillis = System.currentTimeMillis(); this.processedConfig = ConfigProcessor.create(config); for (ConfigProcessor.ProcessedCategory category : processedConfig.values()) { for (ConfigProcessor.ProcessedOption option : category.options.values()) { categoryForOption.put(option, category); } } if (categoryOpen != null) { for (Map.Entry category : processedConfig.entrySet()) { if (category.getValue().name.equalsIgnoreCase(categoryOpen)) { selectedCategory = category.getKey(); break; } } if (selectedCategory == null) { for (Map.Entry category : processedConfig.entrySet()) { if ( category .getValue() .name.toLowerCase() .startsWith(categoryOpen.toLowerCase()) ) { selectedCategory = category.getKey(); break; } } } if (selectedCategory == null) { for (Map.Entry category : processedConfig.entrySet()) { if ( category .getValue() .name.toLowerCase() .contains(categoryOpen.toLowerCase()) ) { selectedCategory = category.getKey(); break; } } } } } private LinkedHashMap getCurrentConfigEditing() { return new LinkedHashMap<>(processedConfig); } private LinkedHashMap getOptionsInCategory( ConfigProcessor.ProcessedCategory cat ) { return new LinkedHashMap<>(cat.options); } public String getSelectedCategory() { return selectedCategory; } public String getSelectedCategoryName() { return processedConfig.get(selectedCategory).name; } private void setSelectedCategory(String category) { selectedCategory = category; optionsScroll.setValue(0); } public void render() { optionsScroll.tick(); categoryScroll.tick(); List tooltipToDisplay = null; long currentTime = System.currentTimeMillis(); long delta = currentTime - openedMillis; ScaledResolution scaledResolution = new ScaledResolution( Minecraft.getMinecraft() ); int width = scaledResolution.getScaledWidth(); int height = scaledResolution.getScaledHeight(); int mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth; int mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1; float opacityFactor = LerpUtils.sigmoidZeroOne(delta / 500f); RenderUtils.drawGradientRect( 0, 0, 0, width, height, (int) (0x80 * opacityFactor) << 24 | 0x101010, (int) (0x90 * opacityFactor) << 24 | 0x101010 ); int xSize = Math.min( scaledResolution.getScaledWidth() - 100 / scaledResolution.getScaleFactor(), 500 ); int ySize = Math.min( scaledResolution.getScaledHeight() - 100 / scaledResolution.getScaleFactor(), 400 ); int x = (scaledResolution.getScaledWidth() - xSize) / 2; int y = (scaledResolution.getScaledHeight() - ySize) / 2; int adjScaleFactor = Math.max(2, scaledResolution.getScaleFactor()); int openingXSize = xSize; int openingYSize = ySize; if (delta < 150) { openingXSize = (int) (delta * xSize / 150); openingYSize = 5; } else if (delta < 300) { openingYSize = 5 + (int) (delta - 150) * (ySize - 5) / 150; } RenderUtils.drawFloatingRectDark( (scaledResolution.getScaledWidth() - openingXSize) / 2, (scaledResolution.getScaledHeight() - openingYSize) / 2, openingXSize, openingYSize ); GlScissorStack.clear(); GlScissorStack.push( (scaledResolution.getScaledWidth() - openingXSize) / 2, (scaledResolution.getScaledHeight() - openingYSize) / 2, (scaledResolution.getScaledWidth() + openingXSize) / 2, (scaledResolution.getScaledHeight() + openingYSize) / 2, scaledResolution ); RenderUtils.drawFloatingRectDark(x + 5, y + 5, xSize - 10, 20, false); FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; TextRenderUtils.drawStringCenteredScaledMaxWidth( "SkyBlockHud by " + EnumChatFormatting.RED + "ThatGravyBoat" + EnumChatFormatting.RESET + ", config by " + EnumChatFormatting.DARK_PURPLE + "Moulberry", fr, x + xSize / 2f, y + 15, false, 200, 0xa0a0a0 ); RenderUtils.drawFloatingRectDark( x + 4, y + 49 - 20, 140, ySize - 54 + 20, false ); int innerPadding = 20 / adjScaleFactor; int innerLeft = x + 4 + innerPadding; int innerRight = x + 144 - innerPadding; int innerTop = y + 49 + innerPadding; int innerBottom = y + ySize - 5 - innerPadding; Gui.drawRect(innerLeft, innerTop, innerLeft + 1, innerBottom, 0xff08080E); //Left Gui.drawRect(innerLeft + 1, innerTop, innerRight, innerTop + 1, 0xff08080E); //Top Gui.drawRect( innerRight - 1, innerTop + 1, innerRight, innerBottom, 0xff28282E ); //Right Gui.drawRect( innerLeft + 1, innerBottom - 1, innerRight - 1, innerBottom, 0xff28282E ); //Bottom Gui.drawRect( innerLeft + 1, innerTop + 1, innerRight - 1, innerBottom - 1, 0x6008080E ); //Middle GlScissorStack.push( 0, innerTop + 1, scaledResolution.getScaledWidth(), innerBottom - 1, scaledResolution ); float catBarSize = 1; int catY = -categoryScroll.getValue(); LinkedHashMap currentConfigEditing = getCurrentConfigEditing(); for (Map.Entry entry : currentConfigEditing.entrySet()) { String selectedCategory = getSelectedCategory(); if ( selectedCategory == null || !currentConfigEditing.containsKey(selectedCategory) ) { setSelectedCategory(entry.getKey()); } String catName = entry.getValue().name; if (entry.getKey().equals(getSelectedCategory())) { catName = EnumChatFormatting.DARK_AQUA.toString() + EnumChatFormatting.UNDERLINE + catName; } else { catName = EnumChatFormatting.GRAY + catName; } TextRenderUtils.drawStringCenteredScaledMaxWidth( catName, fr, x + 75, y + 70 + catY, false, 100, -1 ); catY += 15; if (catY > 0) { catBarSize = LerpUtils.clampZeroOne( (float) (innerBottom - innerTop - 2) / (catY + 5 + categoryScroll.getValue()) ); } } float catBarStart = categoryScroll.getValue() / (float) (catY + categoryScroll.getValue()); float catBarEnd = catBarStart + catBarSize; if (catBarEnd > 1) { catBarEnd = 1; if ( categoryScroll.getTarget() / (float) (catY + categoryScroll.getValue()) + catBarSize < 1 ) { int target = optionsScroll.getTarget(); categoryScroll.setValue( (int) Math.ceil( (catY + 5 + categoryScroll.getValue()) - catBarSize * (catY + 5 + categoryScroll.getValue()) ) ); categoryScroll.setTarget(target); } else { categoryScroll.setValue( (int) Math.ceil( (catY + 5 + categoryScroll.getValue()) - catBarSize * (catY + 5 + categoryScroll.getValue()) ) ); } } int catDist = innerBottom - innerTop - 12; Gui.drawRect( innerLeft + 2, innerTop + 5, innerLeft + 7, innerBottom - 5, 0xff101010 ); Gui.drawRect( innerLeft + 3, innerTop + 6 + (int) (catDist * catBarStart), innerLeft + 6, innerTop + 6 + (int) (catDist * catBarEnd), 0xff303030 ); GlScissorStack.pop(scaledResolution); TextRenderUtils.drawStringCenteredScaledMaxWidth( "Categories", fr, x + 75, y + 44, false, 120, 0xa368ef ); RenderUtils.drawFloatingRectDark( x + 149, y + 29, xSize - 154, ySize - 34, false ); innerLeft = x + 149 + innerPadding; innerRight = x + xSize - 5 - innerPadding; innerBottom = y + ySize - 5 - innerPadding; GlStateManager.color(1, 1, 1, 1); int rightStuffLen = 20; if ( getSelectedCategory() != null && currentConfigEditing.containsKey(getSelectedCategory()) ) { ConfigProcessor.ProcessedCategory cat = currentConfigEditing.get( getSelectedCategory() ); TextRenderUtils.drawStringScaledMaxWidth( cat.desc, fr, innerLeft + 5, y + 40, true, innerRight - innerLeft - rightStuffLen - 10, 0xb0b0b0 ); } Gui.drawRect(innerLeft, innerTop, innerLeft + 1, innerBottom, 0xff08080E); //Left Gui.drawRect(innerLeft + 1, innerTop, innerRight, innerTop + 1, 0xff08080E); //Top Gui.drawRect( innerRight - 1, innerTop + 1, innerRight, innerBottom, 0xff303036 ); //Right Gui.drawRect( innerLeft + 1, innerBottom - 1, innerRight - 1, innerBottom, 0xff303036 ); //Bottom Gui.drawRect( innerLeft + 1, innerTop + 1, innerRight - 1, innerBottom - 1, 0x6008080E ); //Middle GlScissorStack.push( innerLeft + 1, innerTop + 1, innerRight - 1, innerBottom - 1, scaledResolution ); float barSize = 1; int optionY = -optionsScroll.getValue(); if ( getSelectedCategory() != null && currentConfigEditing.containsKey(getSelectedCategory()) ) { ConfigProcessor.ProcessedCategory cat = currentConfigEditing.get( getSelectedCategory() ); int optionWidthDefault = innerRight - innerLeft - 20; GlStateManager.enableDepth(); Set activeAccordions = new HashSet<>(); for (ConfigProcessor.ProcessedOption option : getOptionsInCategory(cat) .values()) { int optionWidth = optionWidthDefault; if (option.accordionId >= 0) { if (!activeAccordions.contains(option.accordionId)) { continue; } optionWidth = optionWidthDefault - 2 * innerPadding; } GuiOptionEditor editor = option.editor; if (editor == null) { continue; } if (editor instanceof GuiOptionEditorAccordion) { GuiOptionEditorAccordion accordion = (GuiOptionEditorAccordion) editor; if (accordion.getToggled()) { activeAccordions.add(accordion.getAccordionId()); } } int optionHeight = editor.getHeight(); if ( innerTop + 5 + optionY + optionHeight > innerTop + 1 && innerTop + 5 + optionY < innerBottom - 1 ) { editor.render( (innerLeft + innerRight - optionWidth) / 2 - 5, innerTop + 5 + optionY, optionWidth ); } optionY += optionHeight + 5; } GlStateManager.disableDepth(); if (optionY > 0) { barSize = LerpUtils.clampZeroOne( (float) (innerBottom - innerTop - 2) / (optionY + 5 + optionsScroll.getValue()) ); } } GlScissorStack.pop(scaledResolution); GL11.glDisable(GL11.GL_SCISSOR_TEST); if ( getSelectedCategory() != null && currentConfigEditing.containsKey(getSelectedCategory()) ) { int optionYOverlay = -optionsScroll.getValue(); ConfigProcessor.ProcessedCategory cat = currentConfigEditing.get( getSelectedCategory() ); int optionWidthDefault = innerRight - innerLeft - 20; GlStateManager.translate(0, 0, 10); GlStateManager.enableDepth(); Set activeAccordions = new HashSet<>(); for (ConfigProcessor.ProcessedOption option : getOptionsInCategory(cat) .values()) { int optionWidth = optionWidthDefault; if (option.accordionId >= 0) { if (!activeAccordions.contains(option.accordionId)) { continue; } optionWidth = optionWidthDefault - 2 * innerPadding; } GuiOptionEditor editor = option.editor; if (editor == null) { continue; } if (editor instanceof GuiOptionEditorAccordion) { GuiOptionEditorAccordion accordion = (GuiOptionEditorAccordion) editor; if (accordion.getToggled()) { activeAccordions.add(accordion.getAccordionId()); } } int optionHeight = editor.getHeight(); if ( innerTop + 5 + optionYOverlay + optionHeight > innerTop + 1 && innerTop + 5 + optionYOverlay < innerBottom - 1 ) { editor.renderOverlay( (innerLeft + innerRight - optionWidth) / 2 - 5, innerTop + 5 + optionYOverlay, optionWidth ); } optionYOverlay += optionHeight + 5; } GlStateManager.disableDepth(); GlStateManager.translate(0, 0, -10); } GL11.glEnable(GL11.GL_SCISSOR_TEST); float barStart = optionsScroll.getValue() / (float) (optionY + optionsScroll.getValue()); float barEnd = barStart + barSize; if (barEnd > 1) { barEnd = 1; if ( optionsScroll.getTarget() / (float) (optionY + optionsScroll.getValue()) + barSize < 1 ) { int target = optionsScroll.getTarget(); optionsScroll.setValue( (int) Math.ceil( (optionY + 5 + optionsScroll.getValue()) - barSize * (optionY + 5 + optionsScroll.getValue()) ) ); optionsScroll.setTarget(target); } else { optionsScroll.setValue( (int) Math.ceil( (optionY + 5 + optionsScroll.getValue()) - barSize * (optionY + 5 + optionsScroll.getValue()) ) ); } } int dist = innerBottom - innerTop - 12; Gui.drawRect( innerRight - 10, innerTop + 5, innerRight - 5, innerBottom - 5, 0xff101010 ); Gui.drawRect( innerRight - 9, innerTop + 6 + (int) (dist * barStart), innerRight - 6, innerTop + 6 + (int) (dist * barEnd), 0xff303030 ); for (int socialIndex = 0; socialIndex < socialsIco.length; socialIndex++) { Minecraft .getMinecraft() .getTextureManager() .bindTexture(socialsIco[socialIndex]); GlStateManager.color(1, 1, 1, 1); int socialLeft = x + xSize - 23 - 18 * socialIndex; RenderUtils.drawTexturedRect(socialLeft, y + 7, 16, 16, GL11.GL_LINEAR); if ( mouseX >= socialLeft && mouseX <= socialLeft + 16 && mouseY >= y + 6 && mouseY <= y + 23 ) { tooltipToDisplay = Lists.newArrayList( EnumChatFormatting.YELLOW + "Go to: " + EnumChatFormatting.RESET + socialsLink[socialIndex] ); } } GlScissorStack.clear(); if (tooltipToDisplay != null) { TextRenderUtils.drawHoveringText( tooltipToDisplay, mouseX, mouseY, width, height, -1, fr ); } GlStateManager.translate(0, 0, -2); } public boolean mouseInput(int mouseX, int mouseY) { ScaledResolution scaledResolution = new ScaledResolution( Minecraft.getMinecraft() ); int width = scaledResolution.getScaledWidth(); int height = scaledResolution.getScaledHeight(); int xSize = Math.min(width - 100 / scaledResolution.getScaleFactor(), 500); int ySize = Math.min(height - 100 / scaledResolution.getScaleFactor(), 400); int x = (scaledResolution.getScaledWidth() - xSize) / 2; int y = (scaledResolution.getScaledHeight() - ySize) / 2; int adjScaleFactor = Math.max(2, scaledResolution.getScaleFactor()); int innerPadding = 20 / adjScaleFactor; int innerTop = y + 49 + innerPadding; int innerBottom = y + ySize - 5 - innerPadding; int innerLeft = x + 149 + innerPadding; int innerRight = x + xSize - 5 - innerPadding; int dWheel = Mouse.getEventDWheel(); if (mouseY > innerTop && mouseY < innerBottom && dWheel != 0) { if (dWheel < 0) { dWheel = -1; } if (dWheel > 0) { dWheel = 1; } if (mouseX < innerLeft) { int newTarget = categoryScroll.getTarget() - dWheel * 30; if (newTarget < 0) { newTarget = 0; } float catBarSize = 1; int catY = -newTarget; for (Map.Entry entry : getCurrentConfigEditing() .entrySet()) { if (getSelectedCategory() == null) { setSelectedCategory(entry.getKey()); } catY += 15; if (catY > 0) { catBarSize = LerpUtils.clampZeroOne( (float) (innerBottom - innerTop - 2) / (catY + 5 + newTarget) ); } } int barMax = (int) Math.floor( (catY + 5 + newTarget) - catBarSize * (catY + 5 + newTarget) ); if (newTarget > barMax) { newTarget = barMax; } categoryScroll.resetTimer(); categoryScroll.setTarget(newTarget); } else { int newTarget = optionsScroll.getTarget() - dWheel * 30; if (newTarget < 0) { newTarget = 0; } float barSize = 1; int optionY = -newTarget; if ( getSelectedCategory() != null && getCurrentConfigEditing() != null && getCurrentConfigEditing().containsKey(getSelectedCategory()) ) { ConfigProcessor.ProcessedCategory cat = getCurrentConfigEditing() .get(getSelectedCategory()); Set activeAccordions = new HashSet<>(); for (ConfigProcessor.ProcessedOption option : getOptionsInCategory( cat ) .values()) { if (option.accordionId >= 0) { if (!activeAccordions.contains(option.accordionId)) { continue; } } GuiOptionEditor editor = option.editor; if (editor == null) { continue; } if (editor instanceof GuiOptionEditorAccordion) { GuiOptionEditorAccordion accordion = (GuiOptionEditorAccordion) editor; if (accordion.getToggled()) { activeAccordions.add(accordion.getAccordionId()); } } optionY += editor.getHeight() + 5; if (optionY > 0) { barSize = LerpUtils.clampZeroOne( (float) (innerBottom - innerTop - 2) / (optionY + 5 + newTarget) ); } } } int barMax = (int) Math.floor( (optionY + 5 + newTarget) - barSize * (optionY + 5 + newTarget) ); if (newTarget > barMax) { newTarget = barMax; } optionsScroll.setTimeToReachTarget( Math.min( 150, Math.max(10, 5 * Math.abs(newTarget - optionsScroll.getValue())) ) ); optionsScroll.resetTimer(); optionsScroll.setTarget(newTarget); } } else if (Mouse.getEventButtonState() && Mouse.getEventButton() == 0) { if (getCurrentConfigEditing() != null) { int catY = -categoryScroll.getValue(); for (Map.Entry entry : getCurrentConfigEditing() .entrySet()) { if (getSelectedCategory() == null) { setSelectedCategory(entry.getKey()); } if ( mouseX >= x + 5 && mouseX <= x + 145 && mouseY >= y + 70 + catY - 7 && mouseY <= y + 70 + catY + 7 ) { setSelectedCategory(entry.getKey()); return true; } catY += 15; } } for ( int socialIndex = 0; socialIndex < socialsLink.length; socialIndex++ ) { int socialLeft = x + xSize - 23 - 18 * socialIndex; if ( mouseX >= socialLeft && mouseX <= socialLeft + 16 && mouseY >= y + 6 && mouseY <= y + 23 ) { try { Desktop.getDesktop().browse(new URI(socialsLink[socialIndex])); } catch (Exception ignored) {} return true; } } } int optionY = -optionsScroll.getValue(); if ( getSelectedCategory() != null && getCurrentConfigEditing() != null && getCurrentConfigEditing().containsKey(getSelectedCategory()) ) { int optionWidthDefault = innerRight - innerLeft - 20; ConfigProcessor.ProcessedCategory cat = getCurrentConfigEditing() .get(getSelectedCategory()); Set activeAccordions = new HashSet<>(); for (ConfigProcessor.ProcessedOption option : getOptionsInCategory(cat) .values()) { int optionWidth = optionWidthDefault; if (option.accordionId >= 0) { if (!activeAccordions.contains(option.accordionId)) { continue; } optionWidth = optionWidthDefault - 2 * innerPadding; } GuiOptionEditor editor = option.editor; if (editor == null) { continue; } if (editor instanceof GuiOptionEditorAccordion) { GuiOptionEditorAccordion accordion = (GuiOptionEditorAccordion) editor; if (accordion.getToggled()) { activeAccordions.add(accordion.getAccordionId()); } } if ( editor.mouseInputOverlay( (innerLeft + innerRight - optionWidth) / 2 - 5, innerTop + 5 + optionY, optionWidth, mouseX, mouseY ) ) { return true; } optionY += editor.getHeight() + 5; } } if ( mouseX > innerLeft && mouseX < innerRight && mouseY > innerTop && mouseY < innerBottom ) { optionY = -optionsScroll.getValue(); if ( getSelectedCategory() != null && getCurrentConfigEditing() != null && getCurrentConfigEditing().containsKey(getSelectedCategory()) ) { int optionWidthDefault = innerRight - innerLeft - 20; ConfigProcessor.ProcessedCategory cat = getCurrentConfigEditing() .get(getSelectedCategory()); Set activeAccordions = new HashSet<>(); for (ConfigProcessor.ProcessedOption option : getOptionsInCategory(cat) .values()) { int optionWidth = optionWidthDefault; if (option.accordionId >= 0) { if (!activeAccordions.contains(option.accordionId)) { continue; } optionWidth = optionWidthDefault - 2 * innerPadding; } GuiOptionEditor editor = option.editor; if (editor == null) { continue; } if (editor instanceof GuiOptionEditorAccordion) { GuiOptionEditorAccordion accordion = (GuiOptionEditorAccordion) editor; if (accordion.getToggled()) { activeAccordions.add(accordion.getAccordionId()); } } if ( editor.mouseInput( (innerLeft + innerRight - optionWidth) / 2 - 5, innerTop + 5 + optionY, optionWidth, mouseX, mouseY ) ) { return true; } optionY += editor.getHeight() + 5; } } } return true; } public boolean keyboardInput() { ScaledResolution scaledResolution = new ScaledResolution( Minecraft.getMinecraft() ); int width = scaledResolution.getScaledWidth(); int xSize = Math.min(width - 100 / scaledResolution.getScaleFactor(), 500); int adjScaleFactor = Math.max(2, scaledResolution.getScaleFactor()); int innerPadding = 20 / adjScaleFactor; int innerWidth = xSize - 154 - innerPadding * 2; if ( getSelectedCategory() != null && getCurrentConfigEditing() != null && getCurrentConfigEditing().containsKey(getSelectedCategory()) ) { ConfigProcessor.ProcessedCategory cat = getCurrentConfigEditing() .get(getSelectedCategory()); Set activeAccordions = new HashSet<>(); for (ConfigProcessor.ProcessedOption option : getOptionsInCategory(cat) .values()) { if (option.accordionId >= 0) { if (!activeAccordions.contains(option.accordionId)) { continue; } } GuiOptionEditor editor = option.editor; if (editor == null) { continue; } if (editor instanceof GuiOptionEditorAccordion) { GuiOptionEditorAccordion accordion = (GuiOptionEditorAccordion) editor; if (accordion.getToggled()) { activeAccordions.add(accordion.getAccordionId()); } } if (editor.keyboardInput()) { return true; } } } return true; } }