aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/thatgravyboat
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/thatgravyboat')
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/ComponentBuilder.java55
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/ComponentHandler.java124
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/GuiTextures.java37
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/SkyblockHud.java195
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/SpecialColour.java95
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/Utils.java247
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/api/LeaderboardGetter.java65
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/api/events/ProfileSwitchedEvent.java7
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarLineUpdateEvent.java22
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarPostEvent.java22
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarPreGetEvent.java18
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/commands/Commands.java42
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/commands/SimpleCommand.java58
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/config/KeyBindings.java9
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/config/SBHConfig.java486
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/config/SBHConfigEditor.java597
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/BackgroundBlur.java231
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/ChromaColour.java95
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GlScissorStack.java87
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GuiElement.java8
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementBoolean.java121
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementColour.java368
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementTextField.java534
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GuiScreenElementWrapper.java35
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/Config.java8
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/Position.java196
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/Category.java15
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigAccordionId.java14
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorAccordion.java14
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorBoolean.java12
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorButton.java15
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorColour.java13
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDraggableList.java14
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDropdown.java16
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorSlider.java18
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorText.java13
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigOption.java17
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditor.java62
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorAccordion.java82
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorBoolean.java38
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorButton.java64
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorColour.java71
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDraggableList.java284
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDropdown.java152
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorSlider.java132
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorText.java84
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiPositionEditor.java179
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/struct/ConfigProcessor.java176
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/GuiElementSlider.java123
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/MiscUtils.java104
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/Splitters.java10
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/StringUtils.java39
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpUtils.java26
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingFloat.java68
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingInteger.java76
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/render/RenderUtils.java165
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/render/TextRenderUtils.java215
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/dungeons/Classes.java50
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/dungeons/DungeonHandler.java167
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/dungeons/DungeonPlayer.java31
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/BossbarHandler.java37
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/CurrencyHandler.java81
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/HeldItemHandler.java58
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/MapHandler.java219
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/SlayerHandler.java121
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/TimeHandler.java29
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/mapicons/DwarvenIcons.java96
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/mapicons/HubIcons.java308
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/EntityTypeHelper.java21
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/EntityTypeRegistry.java25
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/SkyBlockEntity.java26
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/location/DwarvenMineHandler.java97
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/location/EndIslandHandler.java46
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/location/FarmingIslandHandler.java29
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/location/IslandHandler.java62
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/location/LocationCategory.java46
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/location/LocationHandler.java54
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/location/Locations.java157
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/location/ParkIslandHandler.java31
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinEndermanRenderer.java25
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinEntityArrow.java25
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinGuiIngameForge.java92
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinNetHandlerPlayClient.java53
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/overlay/DungeonOverlay.java150
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/overlay/GenericOverlays.java44
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/overlay/OverlayHud.java286
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/overlay/RPGHud.java102
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/playerstats/ActionBarParsing.java131
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/seasons/Season.java50
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/seasons/SeasonDateHandler.java57
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/tracker/KillTrackerHandler.java76
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/tracker/TrackerFileLoader.java171
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/tracker/TrackerHandler.java124
93 files changed, 9250 insertions, 0 deletions
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/ComponentBuilder.java b/src/main/java/com/thatgravyboat/skyblockhud/ComponentBuilder.java
new file mode 100644
index 0000000..e5299d5
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/ComponentBuilder.java
@@ -0,0 +1,55 @@
+package com.thatgravyboat.skyblockhud;
+
+public class ComponentBuilder {
+
+ public StringBuilder builder;
+
+ public ComponentBuilder(){
+ this.builder = new StringBuilder();
+ }
+
+ public ComponentBuilder apd(String text) {
+ return apd(text, '7');
+ }
+
+ public ComponentBuilder apd(String text, char[] colors) {
+ for (char color: colors) {
+ builder.append("\u00A7").append(color);
+ }
+ builder.append(text).append("\u00A7").append('r');
+ return this;
+ }
+
+ public ComponentBuilder apd(String text, char color) {
+ builder.append("\u00A7").append(color).append(text).append("\u00A7").append('r');
+ return this;
+ }
+
+ public ComponentBuilder nl(){
+ builder.append("\n");
+ return this;
+ }
+
+ public ComponentBuilder nl(String text, char color){
+ apd(text, color);
+ builder.append("\n");
+ return this;
+ }
+
+ public ComponentBuilder nl(String text, char[] colors){
+ apd(text, colors);
+ builder.append("\n");
+ return this;
+ }
+
+ public ComponentBuilder nl(String text){
+ apd(text);
+ builder.append("\n");
+ return this;
+ }
+
+ public String build() {
+ return builder.toString();
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/ComponentHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/ComponentHandler.java
new file mode 100644
index 0000000..3c671f0
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/ComponentHandler.java
@@ -0,0 +1,124 @@
+package com.thatgravyboat.skyblockhud;
+
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Ordering;
+import com.thatgravyboat.skyblockhud.dungeons.DungeonHandler;
+import com.thatgravyboat.skyblockhud.location.*;
+import com.thatgravyboat.skyblockhud.seasons.SeasonDateHandler;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.network.NetworkPlayerInfo;
+import net.minecraft.scoreboard.ScorePlayerTeam;
+import net.minecraft.world.WorldSettings;
+import net.minecraftforge.client.GuiIngameForge;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class ComponentHandler {
+ public static final Pattern SCOREBOARD_CHARACTERS = Pattern.compile("[^]\\[a-z A-Z:0-9/'.()+\\d-ยง?]");
+ private static final Ordering<NetworkPlayerInfo> sortingList = Ordering.from(new PlayerComparator());
+ private static int ticksExisted = 0;
+
+ @SubscribeEvent
+ public void onClientTick(TickEvent.ClientTickEvent event){
+ Minecraft mc = Minecraft.getMinecraft();
+ ticksExisted++;
+ boolean eventPass = false;
+ if (mc.theWorld != null) {
+ List<NetworkPlayerInfo> players = sortingList.sortedCopy(mc.thePlayer.sendQueue.getPlayerInfoMap());
+ GuiIngameForge.renderObjective = !SkyblockHud.hasSkyblockScoreboard() || !SkyblockHud.config.misc.hideScoreboard;
+ if (players != null && SkyblockHud.hasSkyblockScoreboard()){
+
+ if (ticksExisted % 60 == 0) {
+ for (NetworkPlayerInfo player : players) {
+ if (player.getDisplayName() != null) {
+ String formattedTabListPlayer = SCOREBOARD_CHARACTERS.matcher(Utils.removeColor(player.getDisplayName().getFormattedText())).replaceAll("");
+ if (LocationHandler.getCurrentLocation().equals(Locations.CATACOMBS)) {
+ if (formattedTabListPlayer.toLowerCase().contains("secrets found:"))
+ DungeonHandler.parseTotalSecrets(formattedTabListPlayer);
+ if (formattedTabListPlayer.toLowerCase().contains("deaths:"))
+ DungeonHandler.parseDeaths(formattedTabListPlayer);
+ if (formattedTabListPlayer.toLowerCase().contains("crypts:"))
+ DungeonHandler.parseCrypts(formattedTabListPlayer);
+ }else if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.DWARVENMINES)){
+ if (formattedTabListPlayer.toLowerCase().contains("mithril powder:")){
+ DwarvenMineHandler.parseMithril(formattedTabListPlayer);
+ }
+ }else if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.MUSHROOMDESERT)){
+ if (formattedTabListPlayer.toLowerCase().contains("pelts:")){
+ try {
+ FarmingIslandHandler.pelts = Integer.parseInt(formattedTabListPlayer.toLowerCase().replace("pelts:","").trim());
+ }catch (Exception ignored){}
+ }
+ }
+ }
+ }
+ if (players.size() > 80) {
+ for (int i = 61; i <= 80; i++) {
+ if (players.get(i).getDisplayName() != null) {
+ String formattedTabListPlayer = SCOREBOARD_CHARACTERS.matcher(Utils.removeColor(players.get(i).getDisplayName().getFormattedText())).replaceAll("");
+ if (formattedTabListPlayer.toLowerCase().contains("event:")) {
+ if (i < 80) {
+ if (players.get(i + 1).getDisplayName() != null) {
+ String secondLine = SCOREBOARD_CHARACTERS.matcher(Utils.removeColor(players.get(i + 1).getDisplayName().getFormattedText())).replaceAll("");
+ SeasonDateHandler.setCurrentEvent(formattedTabListPlayer.replace("Event:", ""), secondLine);
+ eventPass = true;
+ }
+ }
+ }
+ }
+ if (i == 80 && !eventPass) {
+ SeasonDateHandler.setCurrentEvent("", "");
+ }
+ }
+ }
+ }
+ if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.PARK)) {
+ if (players.size() >= 80) {
+ for (int i = 41; i <= 60; i++) {
+ if (players.get(i).getDisplayName() != null) {
+ String formattedTabListPlayer = SCOREBOARD_CHARACTERS.matcher(Utils.removeColor(players.get(i).getDisplayName().getFormattedText())).replaceAll("");
+ if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.PARK)) {
+ if (formattedTabListPlayer.toLowerCase().contains("rain:")) {
+ ParkIslandHandler.parseRain(formattedTabListPlayer.toLowerCase());
+ }
+ }
+ }
+ }
+ }
+ }else if (ParkIslandHandler.isRaining()) {
+ ParkIslandHandler.parseRain(null);
+ }
+ }
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGHEST)
+ public void onStatusBar(ClientChatReceivedEvent event){
+ if (event.type == 2){
+ if (LocationHandler.getCurrentLocation().equals(Locations.CATACOMBS)) DungeonHandler.parseSecrets(event.message.getFormattedText());
+ }
+ }
+
+ @SideOnly(Side.CLIENT)
+ static class PlayerComparator implements Comparator<NetworkPlayerInfo>
+ {
+ private PlayerComparator()
+ {
+ }
+
+ public int compare(NetworkPlayerInfo p_compare_1_, NetworkPlayerInfo p_compare_2_)
+ {
+ ScorePlayerTeam scoreplayerteam = p_compare_1_.getPlayerTeam();
+ ScorePlayerTeam scoreplayerteam1 = p_compare_2_.getPlayerTeam();
+ return ComparisonChain.start().compareTrueFirst(p_compare_1_.getGameType() != WorldSettings.GameType.SPECTATOR, p_compare_2_.getGameType() != WorldSettings.GameType.SPECTATOR).compare(scoreplayerteam != null ? scoreplayerteam.getRegisteredName() : "", scoreplayerteam1 != null ? scoreplayerteam1.getRegisteredName() : "").compare(p_compare_1_.getGameProfile().getName(), p_compare_2_.getGameProfile().getName()).result();
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/GuiTextures.java b/src/main/java/com/thatgravyboat/skyblockhud/GuiTextures.java
new file mode 100644
index 0000000..435c2a5
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/GuiTextures.java
@@ -0,0 +1,37 @@
+package com.thatgravyboat.skyblockhud;
+
+import net.minecraft.util.ResourceLocation;
+
+public class GuiTextures {
+
+ private GuiTextures() {}
+
+ public static final ResourceLocation DISCORD = new ResourceLocation("skyblockhud:discord.png");
+ public static final ResourceLocation TWITTER = new ResourceLocation("skyblockhud:twitter.png");
+
+ public static final ResourceLocation button_tex = new ResourceLocation("skyblockhud:button.png");
+
+ public static final ResourceLocation button_white = new ResourceLocation("skyblockhud:button_white.png");
+
+ public static final ResourceLocation BAR = new ResourceLocation("skyblockhud:core/bar.png");
+ public static final ResourceLocation OFF = new ResourceLocation("skyblockhud:core/toggle_off.png");
+ public static final ResourceLocation ONE = new ResourceLocation("skyblockhud:core/toggle_1.png");
+ public static final ResourceLocation TWO = new ResourceLocation("skyblockhud:core/toggle_2.png");
+ public static final ResourceLocation THREE = new ResourceLocation("skyblockhud:core/toggle_3.png");
+ public static final ResourceLocation ON = new ResourceLocation("skyblockhud:core/toggle_on.png");
+
+ public static final ResourceLocation slider_off_cap = new ResourceLocation("skyblockhud:core/slider/slider_off_cap.png");
+ public static final ResourceLocation slider_off_notch = new ResourceLocation("skyblockhud:core/slider/slider_off_notch.png");
+ public static final ResourceLocation slider_off_segment = new ResourceLocation("skyblockhud:core/slider/slider_off_segment.png");
+ public static final ResourceLocation slider_on_cap = new ResourceLocation("skyblockhud:core/slider/slider_on_cap.png");
+ public static final ResourceLocation slider_on_notch = new ResourceLocation("skyblockhud:core/slider/slider_on_notch.png");
+ public static final ResourceLocation slider_on_segment = new ResourceLocation("skyblockhud:core/slider/slider_on_segment.png");
+ public static final ResourceLocation slider_button_new = new ResourceLocation("skyblockhud:core/slider/slider_button.png");
+
+ public static final ResourceLocation overlay = new ResourceLocation("skyblockhud","stats.png");
+ public static final ResourceLocation dungeon = new ResourceLocation("skyblockhud","dungeon.png");
+ public static final ResourceLocation playerStat = new ResourceLocation("skyblockhud","playerstats.png");
+ public static final ResourceLocation bars = new ResourceLocation("skyblockhud","bars.png");
+ public static final ResourceLocation mapOverlay = new ResourceLocation("skyblockhud","maps/map_overlay.png");
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/SkyblockHud.java b/src/main/java/com/thatgravyboat/skyblockhud/SkyblockHud.java
new file mode 100644
index 0000000..a31889e
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/SkyblockHud.java
@@ -0,0 +1,195 @@
+package com.thatgravyboat.skyblockhud;
+
+import com.google.common.collect.Sets;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.thatgravyboat.skyblockhud.api.LeaderboardGetter;
+import com.thatgravyboat.skyblockhud.api.events.ProfileSwitchedEvent;
+import com.thatgravyboat.skyblockhud.commands.Commands;
+import com.thatgravyboat.skyblockhud.config.KeyBindings;
+import com.thatgravyboat.skyblockhud.config.SBHConfig;
+import com.thatgravyboat.skyblockhud.dungeons.DungeonHandler;
+import com.thatgravyboat.skyblockhud.handlers.*;
+import com.thatgravyboat.skyblockhud.location.DwarvenMineHandler;
+import com.thatgravyboat.skyblockhud.location.FarmingIslandHandler;
+import com.thatgravyboat.skyblockhud.location.IslandHandler;
+import com.thatgravyboat.skyblockhud.location.LocationHandler;
+import com.thatgravyboat.skyblockhud.overlay.DungeonOverlay;
+import com.thatgravyboat.skyblockhud.overlay.OverlayHud;
+import com.thatgravyboat.skyblockhud.overlay.RPGHud;
+import com.thatgravyboat.skyblockhud.playerstats.ActionBarParsing;
+import com.thatgravyboat.skyblockhud.seasons.SeasonDateHandler;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.scoreboard.ScoreObjective;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.entity.player.ItemTooltipEvent;
+import net.minecraftforge.fml.client.registry.ClientRegistry;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.Mod.EventHandler;
+import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
+import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+import org.lwjgl.input.Keyboard;
+
+import java.awt.*;
+import java.awt.datatransfer.StringSelection;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.Set;
+
+@Mod(modid = SkyblockHud.MODID, version = SkyblockHud.VERSION)
+public class SkyblockHud
+{
+ public static final String MODID = "skyblockhud";
+ public static final String VERSION = "1.12";
+
+ public static SBHConfig config;
+
+ private File configFile;
+
+ private static final Set<String> SKYBLOCK_IN_ALL_LANGUAGES = Sets.newHashSet("SKYBLOCK","\u7A7A\u5C9B\u751F\u5B58");
+
+ private final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
+
+ private static File configDirectory;
+
+ @EventHandler
+ public void preInit(FMLPreInitializationEvent event){
+ MinecraftForge.EVENT_BUS.register(this);
+ MinecraftForge.EVENT_BUS.register(new LeaderboardGetter());
+ MinecraftForge.EVENT_BUS.register(new SeasonDateHandler());
+ MinecraftForge.EVENT_BUS.register(new LocationHandler());
+ MinecraftForge.EVENT_BUS.register(new IslandHandler());
+ MinecraftForge.EVENT_BUS.register(new TimeHandler());
+ MinecraftForge.EVENT_BUS.register(new CurrencyHandler());
+ MinecraftForge.EVENT_BUS.register(new SlayerHandler());
+ MinecraftForge.EVENT_BUS.register(new DungeonHandler());
+ MinecraftForge.EVENT_BUS.register(new DwarvenMineHandler());
+ MinecraftForge.EVENT_BUS.register(new FarmingIslandHandler());
+
+ /* DISABLE UNTIL NEW SYSTEM
+ MinecraftForge.EVENT_BUS.register(new TrackerHandler());
+ MinecraftForge.EVENT_BUS.register(new KillTrackerHandler());
+ */
+ MinecraftForge.EVENT_BUS.register(new HeldItemHandler());
+
+ ClientRegistry.registerKeyBinding(KeyBindings.map);
+
+ MinecraftForge.EVENT_BUS.register(new ComponentHandler());
+ MinecraftForge.EVENT_BUS.register(new ActionBarParsing());
+ Commands.init();
+
+ configFile = new File(event.getModConfigurationDirectory(), "sbh-config.json");
+
+ if(configFile.exists()) {
+ try(BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(configFile), StandardCharsets.UTF_8))) {
+ config = gson.fromJson(reader, SBHConfig.class);
+ } catch(Exception ignored) { }
+ }
+
+
+ if(config == null) {
+ config = new SBHConfig();
+ saveConfig();
+ }
+
+ configDirectory = event.getModConfigurationDirectory();
+
+ Runtime.getRuntime().addShutdownHook(new Thread(this::saveConfig));
+ //Runtime.getRuntime().addShutdownHook(new Thread(() -> TrackerFileLoader.saveTrackerStatsFile(event.getModConfigurationDirectory())));
+ }
+
+ public void saveConfig() {
+ try {
+ configFile.createNewFile();
+
+ try(BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(configFile), StandardCharsets.UTF_8))) {
+ writer.write(gson.toJson(config));
+ }
+ } catch(IOException ignored) {}
+ }
+
+ @EventHandler
+ public void postInit(FMLPostInitializationEvent event){
+ MinecraftForge.EVENT_BUS.register(new OverlayHud());
+ MinecraftForge.EVENT_BUS.register(new RPGHud());
+ MinecraftForge.EVENT_BUS.register(new DungeonOverlay());
+ MinecraftForge.EVENT_BUS.register(new BossbarHandler());
+ MinecraftForge.EVENT_BUS.register(new MapHandler());
+ }
+
+ /* DISABLE UNTIL NEW SYSTEM
+
+ @EventHandler
+ public void loadComplete(FMLLoadCompleteEvent event){
+ TrackerFileLoader.loadTrackersFile();
+
+ if (TrackerFileLoader.loadTrackerStatsFile(configDirectory)){
+ TrackerFileLoader.saveTrackerStatsFile(configDirectory);
+ }
+ }
+
+ @SubscribeEvent
+ public void onLeaveServer(FMLNetworkEvent.ClientDisconnectionFromServerEvent event){
+ TrackerFileLoader.saveTrackerStatsFile(configDirectory);
+ }
+
+ */
+
+ public static boolean hasSkyblockScoreboard() {
+ Minecraft mc = Minecraft.getMinecraft();
+
+ if (mc != null && mc.theWorld != null) {
+ Scoreboard scoreboard = mc.theWorld.getScoreboard();
+ ScoreObjective sidebarObjective = scoreboard.getObjectiveInDisplaySlot(1);
+ if (sidebarObjective != null) {
+ String objectiveName = sidebarObjective.getDisplayName().replaceAll("(?i)\\u00A7.", "");
+ for (String skyblock : SKYBLOCK_IN_ALL_LANGUAGES) {
+ if (objectiveName.startsWith(skyblock)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @SubscribeEvent
+ public void onTooltip(ItemTooltipEvent event){
+ if (event.itemStack != null && Keyboard.isKeyDown(Keyboard.KEY_BACKSLASH)) {
+ try {
+ StringSelection clipboard = new StringSelection(event.itemStack.serializeNBT().toString());
+ Toolkit.getDefaultToolkit().getSystemClipboard().setContents(clipboard, clipboard);
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGHEST)
+ public void onStatusBar(ClientChatReceivedEvent event){
+ if (Utils.removeColor(event.message.getUnformattedText()).toLowerCase().trim().startsWith("your profile was changed to:")){
+ MinecraftForge.EVENT_BUS.post(new ProfileSwitchedEvent());
+ }
+ }
+
+ public static GuiScreen screenToOpen = null;
+ private static int screenTicks = 0;
+
+ @SubscribeEvent
+ public void onClientTick(TickEvent.ClientTickEvent event){
+ if (screenToOpen != null){
+ screenTicks++;
+ if (screenTicks == 5){
+ Minecraft.getMinecraft().displayGuiScreen(screenToOpen);
+ screenTicks = 0;
+ screenToOpen = null;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/SpecialColour.java b/src/main/java/com/thatgravyboat/skyblockhud/SpecialColour.java
new file mode 100644
index 0000000..8501c9d
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/SpecialColour.java
@@ -0,0 +1,95 @@
+package com.thatgravyboat.skyblockhud;
+
+import java.awt.*;
+
+public class SpecialColour {
+
+ public static String special(int chromaSpeed, int alpha, int rgb) {
+ return special(chromaSpeed, alpha, (rgb & 0xFF0000) >> 16, (rgb & 0x00FF00) >> 8, (rgb & 0x0000FF));
+ }
+
+ private static final int RADIX = 10;
+
+ public static String special(int chromaSpeed, int alpha, int r, int g, int b) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Integer.toString(chromaSpeed, RADIX)).append(":");
+ sb.append(Integer.toString(alpha, RADIX)).append(":");
+ sb.append(Integer.toString(r, RADIX)).append(":");
+ sb.append(Integer.toString(g, RADIX)).append(":");
+ sb.append(Integer.toString(b, RADIX));
+ return sb.toString();
+ }
+
+ private static int[] decompose(String csv) {
+ String[] split = csv.split(":");
+
+ int[] arr = new int[split.length];
+
+
+ for(int i=0; i<split.length; i++) {
+ arr[i] = Integer.parseInt(split[split.length-1-i], RADIX);
+ }
+ return arr;
+ }
+
+ public static int specialToSimpleRGB(String special) {
+ int[] d = decompose(special);
+ int r = d[2];
+ int g = d[1];
+ int b = d[0];
+ int a = d[3];
+ int chr = d[4];
+
+ return (a & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF);
+ }
+
+ public static int getSpeed(String special) {
+ return decompose(special)[4];
+ }
+
+ public static float getSecondsForSpeed(int speed) {
+ return (255-speed)/254f*(MAX_CHROMA_SECS-MIN_CHROMA_SECS)+MIN_CHROMA_SECS;
+ }
+
+ private static final int MIN_CHROMA_SECS = 1;
+ private static final int MAX_CHROMA_SECS = 60;
+
+ public static long startTime = -1;
+ public static int specialToChromaRGB(String special) {
+ if(startTime < 0) startTime = System.currentTimeMillis();
+
+ int[] d = decompose(special);
+ int chr = d[4];
+ int a = d[3];
+ int r = d[2];
+ int g = d[1];
+ int b = d[0];
+
+ float[] hsv = Color.RGBtoHSB(r, g, b, null);
+
+ if(chr > 0) {
+ float seconds = getSecondsForSpeed(chr);
+ hsv[0] += (System.currentTimeMillis()-startTime)/1000f/seconds;
+ hsv[0] %= 1;
+ if(hsv[0] < 0) hsv[0] += 1;
+ }
+
+ return (a & 0xFF) << 24 | (Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) & 0x00FFFFFF);
+ }
+
+ public static int rotateHue(int argb, int degrees) {
+ int a = (argb >> 24) & 0xFF;
+ int r = (argb >> 16) & 0xFF;
+ int g = (argb >> 8) & 0xFF;
+ int b = (argb) & 0xFF;
+
+ float[] hsv = Color.RGBtoHSB(r, g, b, null);
+
+ hsv[0] += degrees/360f;
+ hsv[0] %= 1;
+
+ return (a & 0xFF) << 24 | (Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) & 0x00FFFFFF);
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/Utils.java b/src/main/java/com/thatgravyboat/skyblockhud/Utils.java
new file mode 100644
index 0000000..31f6aa8
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/Utils.java
@@ -0,0 +1,247 @@
+package com.thatgravyboat.skyblockhud;
+
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.WorldRenderer;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.fml.common.Loader;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL14;
+
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+
+public class Utils {
+
+ private static LinkedList<Integer> guiScales = new LinkedList<>();
+ private static ScaledResolution lastScale = new ScaledResolution(Minecraft.getMinecraft());
+ //Labymod compatibility
+ private static FloatBuffer projectionMatrixOld = BufferUtils.createFloatBuffer(16);
+ private static FloatBuffer modelviewMatrixOld = BufferUtils.createFloatBuffer(16);
+
+
+ public static String removeColor(String input) {
+ return input.replaceAll("(?i)\\u00A7.", "");
+ }
+
+ public static String removeWhiteSpaceAndRemoveWord(String input, String replace) {return input.toLowerCase().replace( " ", "").replace(replace, ""); }
+
+ public static boolean isPlayerHoldingRedstone(EntityPlayerSP player) {
+ if (!SkyblockHud.config.main.requireRedstone) return true;
+ ArrayList<Item> redstoneItems = new ArrayList<>(Arrays.asList(Items.redstone, Items.repeater, Items.comparator, Item.getByNameOrId("minecraft:redstone_torch")));
+ if (player.getHeldItem()!=null)
+ return redstoneItems.contains(player.getHeldItem().getItem());
+ return false;
+ }
+
+ public static boolean inRangeInclusive(int value, int min, int max) {
+ return value <= max && value >= min;
+ }
+
+ public static int whatRomanNumeral(String roman){
+ switch (roman.toLowerCase()) {
+ case "i": return 1;
+ case "ii": return 2;
+ case "iii": return 3;
+ case "iv": return 4;
+ case "v": return 5;
+ case "vi": return 6;
+ case "vii": return 7;
+ case "viii": return 8;
+ case "ix": return 9;
+ case "x": return 10;
+ default: return 0;
+ }
+ }
+
+ public static String intToRomanNumeral(int i){
+ switch (i) {
+ case 1: return "I";
+ case 2: return "II";
+ case 3: return "III";
+ case 4: return "IV";
+ case 5: return "V";
+ case 6: return "VI";
+ case 7: return "VII";
+ case 8: return "VIII";
+ case 9: return "IX";
+ case 10: return "X";
+ default: return "";
+ }
+ }
+
+ public static boolean overlayShouldRender(RenderGameOverlayEvent.ElementType type, boolean... booleans){
+ return overlayShouldRender(false, type, RenderGameOverlayEvent.ElementType.HOTBAR, booleans);
+ }
+
+ public static boolean overlayShouldRender(boolean hideOnf3, RenderGameOverlayEvent.ElementType type, RenderGameOverlayEvent.ElementType checkType, boolean... booleans){
+ Minecraft mc = Minecraft.getMinecraft();
+ boolean shouldRender;
+ if (booleans.length > 1){
+ for (boolean aBoolean : booleans) if (!aBoolean) return false;
+ shouldRender = true;
+ }else shouldRender = booleans.length != 1 || booleans[0];
+ if (hideOnf3) {
+ if (mc.gameSettings.showDebugInfo || (mc.gameSettings.keyBindPlayerList.isKeyDown() && (!mc.isIntegratedServerRunning() || mc.thePlayer.sendQueue.getPlayerInfoMap().size() > 1))) {
+ return false;
+ }
+ }
+ return shouldRender && ((type == null && Loader.isModLoaded("labymod")) || type == checkType);
+ }
+
+ public static void drawStringScaledMaxWidth(String str, FontRenderer fr, float x, float y, boolean shadow, int len, int colour) {
+ int strLen = fr.getStringWidth(str);
+ float factor = len/(float)strLen;
+ factor = Math.min(1, factor);
+
+ drawStringScaled(str, fr, x, y, shadow, colour, factor);
+ }
+
+ public static void drawStringScaled(String str, FontRenderer fr, float x, float y, boolean shadow, int colour, float factor) {
+ GlStateManager.scale(factor, factor, 1);
+ fr.drawString(str, x/factor, y/factor, colour, shadow);
+ GlStateManager.scale(1/factor, 1/factor, 1);
+ }
+
+ public static void drawStringCenteredScaled(String str, FontRenderer fr, float x, float y, boolean shadow, int len, int colour) {
+ int strLen = fr.getStringWidth(str);
+ float factor = len/(float)strLen;
+ float fontHeight = 8*factor;
+
+ drawStringScaled(str, fr, x-len/2f, y-fontHeight/2f, shadow, colour, factor);
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height, float uMin, float uMax, float vMin, float vMax, int filter) {
+ GlStateManager.enableTexture2D();
+ GlStateManager.enableBlend();
+ GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter);
+
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX);
+ worldrenderer
+ .pos(x, y+height, 0.0D)
+ .tex(uMin, vMax).endVertex();
+ worldrenderer
+ .pos(x+width, y+height, 0.0D)
+ .tex(uMax, vMax).endVertex();
+ worldrenderer
+ .pos(x+width, y, 0.0D)
+ .tex(uMax, vMin).endVertex();
+ worldrenderer
+ .pos(x, y, 0.0D)
+ .tex(uMin, vMin).endVertex();
+ tessellator.draw();
+
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
+
+ GlStateManager.disableBlend();
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height) {
+ drawTexturedRect(x, y, width, height, 0, 1, 0 , 1);
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height, int filter) {
+ drawTexturedRect(x, y, width, height, 0, 1, 0 , 1, filter);
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height, float uMin, float uMax, float vMin, float vMax) {
+ drawTexturedRect(x, y, width, height, uMin, uMax, vMin , vMax, GL11.GL_LINEAR);
+ }
+
+ public static void resetGuiScale() {
+ guiScales.clear();
+ }
+
+ public static ScaledResolution peekGuiScale() {
+ return lastScale;
+ }
+
+ public static ScaledResolution pushGuiScale(int scale) {
+ if(guiScales.size() == 0) {
+ if(Loader.isModLoaded("labymod")) {
+ GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projectionMatrixOld);
+ GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelviewMatrixOld);
+ }
+ }
+
+ if(scale < 0) {
+ if(guiScales.size() > 0) {
+ guiScales.pop();
+ }
+ } else {
+ if(scale == 0) {
+ guiScales.push(Minecraft.getMinecraft().gameSettings.guiScale);
+ } else {
+ guiScales.push(scale);
+ }
+ }
+
+ int newScale = guiScales.size() > 0 ? Math.max(0, Math.min(4, guiScales.peek())) : Minecraft.getMinecraft().gameSettings.guiScale;
+ if(newScale == 0) newScale = Minecraft.getMinecraft().gameSettings.guiScale;
+
+ int oldScale = Minecraft.getMinecraft().gameSettings.guiScale;
+ Minecraft.getMinecraft().gameSettings.guiScale = newScale;
+ ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ Minecraft.getMinecraft().gameSettings.guiScale = oldScale;
+
+ if(guiScales.size() > 0) {
+ GlStateManager.viewport(0, 0, Minecraft.getMinecraft().displayWidth, Minecraft.getMinecraft().displayHeight);
+ GlStateManager.matrixMode(GL11.GL_PROJECTION);
+ GlStateManager.loadIdentity();
+ GlStateManager.ortho(0.0D,
+ scaledresolution.getScaledWidth_double(),
+ scaledresolution.getScaledHeight_double(), 0.0D, 1000.0D, 3000.0D);
+ GlStateManager.matrixMode(GL11.GL_MODELVIEW);
+ GlStateManager.loadIdentity();
+ GlStateManager.translate(0.0F, 0.0F, -2000.0F);
+ } else {
+ if(Loader.isModLoaded("labymod") && projectionMatrixOld.limit() > 0 && modelviewMatrixOld.limit() > 0) {
+ GlStateManager.matrixMode(GL11.GL_PROJECTION);
+ GL11.glLoadMatrix(projectionMatrixOld);
+ GlStateManager.matrixMode(GL11.GL_MODELVIEW);
+ GL11.glLoadMatrix(modelviewMatrixOld);
+ } else {
+ GlStateManager.matrixMode(GL11.GL_PROJECTION);
+ GlStateManager.loadIdentity();
+ GlStateManager.ortho(0.0D,
+ scaledresolution.getScaledWidth_double(),
+ scaledresolution.getScaledHeight_double(), 0.0D, 1000.0D, 3000.0D);
+ GlStateManager.matrixMode(GL11.GL_MODELVIEW);
+ GlStateManager.loadIdentity();
+ GlStateManager.translate(0.0F, 0.0F, -2000.0F);
+ }
+ }
+
+ lastScale = scaledresolution;
+ return scaledresolution;
+ }
+
+ public static void drawStringCentered(String str, FontRenderer fr, float x, float y, boolean shadow, int colour) {
+ int strLen = fr.getStringWidth(str);
+
+ float x2 = x - strLen/2f;
+ float y2 = y - fr.FONT_HEIGHT/2f;
+
+ GL11.glTranslatef(x2, y2, 0);
+ fr.drawString(str, 0, 0, colour, shadow);
+ GL11.glTranslatef(-x2, -y2, 0);
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/api/LeaderboardGetter.java b/src/main/java/com/thatgravyboat/skyblockhud/api/LeaderboardGetter.java
new file mode 100644
index 0000000..58cede8
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/api/LeaderboardGetter.java
@@ -0,0 +1,65 @@
+package com.thatgravyboat.skyblockhud.api;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.api.events.SidebarLineUpdateEvent;
+import com.thatgravyboat.skyblockhud.api.events.SidebarPostEvent;
+import com.thatgravyboat.skyblockhud.api.events.SidebarPreGetEvent;
+import net.minecraft.client.Minecraft;
+import net.minecraft.scoreboard.Score;
+import net.minecraft.scoreboard.ScoreObjective;
+import net.minecraft.scoreboard.ScorePlayerTeam;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.thatgravyboat.skyblockhud.ComponentHandler.SCOREBOARD_CHARACTERS;
+
+public class LeaderboardGetter {
+
+ private static Map<Integer, String> cachedScores = new HashMap<>();
+ private static List<String> cachedScoresList = new ArrayList<>();
+
+ private static int ticks = 0;
+
+ //This is really bad and should use the packet instead.
+
+ @SubscribeEvent
+ public void onClientUpdate(TickEvent.ClientTickEvent event){
+ if (event.phase.equals(TickEvent.Phase.START)) return;
+ ticks++;
+ if (ticks % 5 != 0) return;
+
+ Minecraft mc = Minecraft.getMinecraft();
+ if (mc.theWorld != null) {
+ Scoreboard scoreboard = mc.theWorld.getScoreboard();
+ ScoreObjective sidebarObjective = scoreboard.getObjectiveInDisplaySlot(1);
+
+ if (sidebarObjective != null && !MinecraftForge.EVENT_BUS.post(new SidebarPreGetEvent(scoreboard, sidebarObjective))) {
+ Collection<Score> scoreList = sidebarObjective.getScoreboard().getSortedScores(sidebarObjective);
+ Map<Integer, String> scores = scoreList.stream().collect(Collectors.toMap(Score::getScorePoints, this::getLine));
+
+ if (!cachedScores.equals(scores)) {
+ scores.forEach((score, name) -> {
+ if (cachedScores.get(score) == null || !cachedScores.get(score).equals(name)) {
+ MinecraftForge.EVENT_BUS.post(new SidebarLineUpdateEvent(name, SCOREBOARD_CHARACTERS.matcher(name).replaceAll("").trim(), score, scores.size(), scoreboard, sidebarObjective));
+ }
+ });
+ cachedScores = scores;
+ cachedScoresList = scores.values().stream().map(name -> SCOREBOARD_CHARACTERS.matcher(name).replaceAll("").trim()).collect(Collectors.toList());
+ }
+ MinecraftForge.EVENT_BUS.post(new SidebarPostEvent(scoreboard, sidebarObjective, cachedScoresList));
+ }
+ }
+ }
+
+ public String getLine(Score score) {
+ ScorePlayerTeam scorePlayerTeam = score.getScoreScoreboard().getPlayersTeam(score.getPlayerName());
+ return Utils.removeColor(ScorePlayerTeam.formatPlayerName(scorePlayerTeam, score.getPlayerName()));
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/api/events/ProfileSwitchedEvent.java b/src/main/java/com/thatgravyboat/skyblockhud/api/events/ProfileSwitchedEvent.java
new file mode 100644
index 0000000..015388a
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/api/events/ProfileSwitchedEvent.java
@@ -0,0 +1,7 @@
+package com.thatgravyboat.skyblockhud.api.events;
+
+import net.minecraftforge.fml.common.eventhandler.Event;
+
+public class ProfileSwitchedEvent extends Event {
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarLineUpdateEvent.java b/src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarLineUpdateEvent.java
new file mode 100644
index 0000000..2737ee9
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarLineUpdateEvent.java
@@ -0,0 +1,22 @@
+package com.thatgravyboat.skyblockhud.api.events;
+
+import net.minecraft.scoreboard.ScoreObjective;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraftforge.fml.common.eventhandler.Event;
+
+public class SidebarLineUpdateEvent extends Event {
+
+ public String rawLine;
+ public String formattedLine;
+ public int position;
+ public Scoreboard scoreboard;
+ public ScoreObjective objective;
+
+ public SidebarLineUpdateEvent(String rawLine, String formattedLine, int score, int max, Scoreboard scoreboard, ScoreObjective objective) {
+ this.rawLine = rawLine;
+ this.formattedLine = formattedLine;
+ this.position = max - score;
+ this.scoreboard = scoreboard;
+ this.objective = objective;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarPostEvent.java b/src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarPostEvent.java
new file mode 100644
index 0000000..b81859a
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarPostEvent.java
@@ -0,0 +1,22 @@
+package com.thatgravyboat.skyblockhud.api.events;
+
+import net.minecraft.scoreboard.ScoreObjective;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraftforge.fml.common.eventhandler.Event;
+
+import java.util.List;
+
+public class SidebarPostEvent extends Event {
+
+ public Scoreboard scoreboard;
+ public ScoreObjective objective;
+ public List<String> scores;
+ public String[] arrayScores;
+
+ public SidebarPostEvent(Scoreboard scoreboard, ScoreObjective objective, List<String> scores) {
+ this.scoreboard = scoreboard;
+ this.objective = objective;
+ this.scores = scores;
+ this.arrayScores = scores.toArray(new String[]{});
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarPreGetEvent.java b/src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarPreGetEvent.java
new file mode 100644
index 0000000..0db1895
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/api/events/SidebarPreGetEvent.java
@@ -0,0 +1,18 @@
+package com.thatgravyboat.skyblockhud.api.events;
+
+import net.minecraft.scoreboard.ScoreObjective;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraftforge.fml.common.eventhandler.Cancelable;
+import net.minecraftforge.fml.common.eventhandler.Event;
+
+@Cancelable
+public class SidebarPreGetEvent extends Event {
+
+ public Scoreboard scoreboard;
+ public ScoreObjective objective;
+
+ public SidebarPreGetEvent(Scoreboard scoreboard, ScoreObjective objective) {
+ this.scoreboard = scoreboard;
+ this.objective = objective;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/commands/Commands.java b/src/main/java/com/thatgravyboat/skyblockhud/commands/Commands.java
new file mode 100644
index 0000000..3ca82e7
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/commands/Commands.java
@@ -0,0 +1,42 @@
+package com.thatgravyboat.skyblockhud.commands;
+
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import com.thatgravyboat.skyblockhud.config.SBHConfigEditor;
+import com.thatgravyboat.skyblockhud.core.GuiScreenElementWrapper;
+import com.thatgravyboat.skyblockhud.handlers.MapHandler;
+import com.thatgravyboat.skyblockhud.location.LocationHandler;
+import net.minecraft.client.Minecraft;
+import net.minecraft.command.ICommandSender;
+import net.minecraftforge.client.ClientCommandHandler;
+import org.apache.commons.lang3.StringUtils;
+
+public class Commands {
+
+ private static final SimpleCommand.ProcessCommandRunnable settingsRunnable = new SimpleCommand.ProcessCommandRunnable() {
+ public void processCommand(ICommandSender sender, String[] args) {
+ if(args.length > 0) {
+ SkyblockHud.screenToOpen = new GuiScreenElementWrapper(new SBHConfigEditor(SkyblockHud.config, StringUtils.join(args, " ")));
+ } else {
+ SkyblockHud.screenToOpen = new GuiScreenElementWrapper(new SBHConfigEditor(SkyblockHud.config));
+ }
+ }
+ };
+
+ private static final SimpleCommand settingsCommand = new SimpleCommand("sbh", settingsRunnable);
+ private static final SimpleCommand settingsCommand2 = new SimpleCommand("sbhsettings", settingsRunnable);
+ private static final SimpleCommand settingsCommand3 = new SimpleCommand("sbhud", settingsRunnable);
+
+ private static final SimpleCommand mapCommand = new SimpleCommand("sbhmap", new SimpleCommand.ProcessCommandRunnable() {
+ public void processCommand(ICommandSender sender, String[] args) {
+ if (LocationHandler.getCurrentLocation().getCategory().getMap() != null)
+ SkyblockHud.screenToOpen = new MapHandler.MapScreen();
+ }
+ });
+
+ public static void init(){
+ ClientCommandHandler.instance.registerCommand(settingsCommand);
+ ClientCommandHandler.instance.registerCommand(settingsCommand2);
+ ClientCommandHandler.instance.registerCommand(settingsCommand3);
+ ClientCommandHandler.instance.registerCommand(mapCommand);
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/commands/SimpleCommand.java b/src/main/java/com/thatgravyboat/skyblockhud/commands/SimpleCommand.java
new file mode 100644
index 0000000..7fc7920
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/commands/SimpleCommand.java
@@ -0,0 +1,58 @@
+package com.thatgravyboat.skyblockhud.commands;
+
+import net.minecraft.command.CommandBase;
+import net.minecraft.command.CommandException;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.util.BlockPos;
+
+import java.util.List;
+
+/**
+ @author Moulberry
+ **/
+public class SimpleCommand extends CommandBase {
+
+ private String commandName;
+ private ProcessCommandRunnable runnable;
+ private TabCompleteRunnable tabRunnable;
+
+ public SimpleCommand(String commandName, ProcessCommandRunnable runnable) {
+ this.commandName = commandName;
+ this.runnable = runnable;
+ }
+
+ public SimpleCommand(String commandName, ProcessCommandRunnable runnable, TabCompleteRunnable tabRunnable) {
+ this.commandName = commandName;
+ this.runnable = runnable;
+ this.tabRunnable = tabRunnable;
+ }
+
+ public abstract static class ProcessCommandRunnable {
+ public abstract void processCommand(ICommandSender sender, String[] args);
+ }
+
+ public abstract static class TabCompleteRunnable {
+ public abstract List<String> tabComplete(ICommandSender sender, String[] args, BlockPos pos);
+ }
+
+ public boolean canCommandSenderUseCommand(ICommandSender sender) {
+ return true;
+ }
+
+ public String getCommandName() {
+ return commandName;
+ }
+
+ public String getCommandUsage(ICommandSender sender) {
+ return "/" + commandName;
+ }
+
+ public void processCommand(ICommandSender sender, String[] args) throws CommandException {
+ runnable.processCommand(sender, args);
+ }
+
+ public List<String> addTabCompletionOptions(ICommandSender sender, String[] args, BlockPos pos) {
+ if(tabRunnable != null) return tabRunnable.tabComplete(sender, args, pos);
+ return null;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/config/KeyBindings.java b/src/main/java/com/thatgravyboat/skyblockhud/config/KeyBindings.java
new file mode 100644
index 0000000..805b906
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/config/KeyBindings.java
@@ -0,0 +1,9 @@
+package com.thatgravyboat.skyblockhud.config;
+
+import net.minecraft.client.settings.KeyBinding;
+
+public class KeyBindings {
+ public static KeyBinding map = new KeyBinding("Opens the big map.", 50, "SkyblockHud");
+
+}
+
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/config/SBHConfig.java b/src/main/java/com/thatgravyboat/skyblockhud/config/SBHConfig.java
new file mode 100644
index 0000000..1f6e581
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/config/SBHConfig.java
@@ -0,0 +1,486 @@
+package com.thatgravyboat.skyblockhud.config;
+
+import com.google.gson.annotations.Expose;
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import com.thatgravyboat.skyblockhud.core.GuiScreenElementWrapper;
+import com.thatgravyboat.skyblockhud.core.config.Config;
+import com.thatgravyboat.skyblockhud.core.config.Position;
+import com.thatgravyboat.skyblockhud.core.config.annotations.*;
+import com.thatgravyboat.skyblockhud.core.config.gui.GuiPositionEditor;
+import net.minecraft.client.Minecraft;
+
+public class SBHConfig extends Config {
+
+ private void editOverlay(String activeConfig, int width, int height, Position position) {
+ Minecraft.getMinecraft().displayGuiScreen(
+ new GuiPositionEditor(position, width, height,
+ () -> {},
+ () -> {},
+ () -> SkyblockHud.screenToOpen = new GuiScreenElementWrapper(new SBHConfigEditor(SkyblockHud.config, activeConfig))
+ )
+ );
+ }
+
+ @Override
+ public void executeRunnable(String runnableId) {
+ String activeConfigCategory = null;
+ if(Minecraft.getMinecraft().currentScreen instanceof GuiScreenElementWrapper) {
+ GuiScreenElementWrapper wrapper = (GuiScreenElementWrapper) Minecraft.getMinecraft().currentScreen;
+ if(wrapper.element instanceof SBHConfigEditor) {
+ activeConfigCategory = ((SBHConfigEditor)wrapper.element).getSelectedCategoryName();
+ }
+ }
+
+ switch (runnableId) {
+ case "rpg":
+ editOverlay(activeConfigCategory, 120, 47, rpg.rpgHudPosition);
+ return;
+ case "d1":
+ editOverlay(activeConfigCategory, 120, 32, dungeon.dungeonPlayer1);
+ return;
+ case "d2":
+ editOverlay(activeConfigCategory, 120, 32, dungeon.dungeonPlayer2);
+ return;
+ case "d3":
+ editOverlay(activeConfigCategory, 120, 32, dungeon.dungeonPlayer3);
+ return;
+ case "d4":
+ editOverlay(activeConfigCategory, 120, 32, dungeon.dungeonPlayer4);
+ return;
+ case "main":
+ editOverlay(activeConfigCategory, 1000, 34, main.mainHudPos);
+ return;
+ case "ultimate":
+ editOverlay(activeConfigCategory, 182, 5, dungeon.barPosition);
+ return;
+ case "map":
+ editOverlay(activeConfigCategory, 72, 72, map.miniMapPosition);
+ return;
+ case "tracker":
+ editOverlay(activeConfigCategory, 120, 70, trackers.trackerPosition);
+ return;
+ }
+ }
+
+ @Expose
+ @Category(
+ name = "Misc Options",
+ desc = "Just a bunch of random options."
+ )
+ public Misc misc = new Misc();
+
+ @Expose
+ @Category(
+ name = "Main Hud",
+ desc = "All Options for the main hud."
+ )
+ public MainHud main = new MainHud();
+
+ @Expose
+ @Category(
+ name = "RPG Hud",
+ desc = "All Options for the RPG hud."
+ )
+ public RPGHud rpg = new RPGHud();
+
+ @Expose
+ @Category(
+ name = "Dungeon Hud",
+ desc = "All Options for the Dungeon hud."
+ )
+ public DungeonHud dungeon = new DungeonHud();
+
+ @Expose
+ @Category(
+ name = "Renderer",
+ desc = "All Options for rendering."
+ )
+ public Renderer renderer = new Renderer();
+
+ @Expose
+ @Category(
+ name = "Map",
+ desc = "All Options for the Map."
+ )
+ public Map map = new Map();
+
+ @Expose
+ @Category(
+ name = "Tracker",
+ desc = "All Options for the Trackers."
+ )
+ public Trackers trackers = new Trackers();
+
+ public static class Misc {
+ @Expose
+ @ConfigOption(
+ name = "Hide Scoreboard",
+ desc = "Hides the scoreboard when in skyblock."
+ )
+ @ConfigEditorBoolean
+ public boolean hideScoreboard = false;
+ }
+
+ public static class MainHud {
+ @Expose
+ @ConfigOption(
+ name = "Main Hud Position",
+ desc = ""
+ )
+ @ConfigEditorButton(
+ runnableId = "main",
+ buttonText = "Edit"
+ )
+ public Position mainHudPos = new Position(0, 1, true, false);
+
+ @Expose
+ @ConfigOption(
+ name = "Twelve Hour Clock",
+ desc = "Allows you to change the clock to be 12 hour instead of 24 hour."
+ )
+ @ConfigEditorBoolean
+ public boolean twelveHourClock = false;
+
+ @Expose
+ @ConfigOption(
+ name = "Shift hud with boss",
+ desc = "Shifts the hud when bossbar is visible."
+ )
+ @ConfigEditorBoolean
+ public boolean bossShiftHud = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Require Redstone",
+ desc = "Allows to make it so that the redstone percentage requires you to hold a redstone item to show."
+ )
+ @ConfigEditorBoolean
+ public boolean requireRedstone = true;
+ }
+
+ public static class RPGHud {
+ @Expose
+ @ConfigOption(
+ name = "Show RPG Hud",
+ desc = "Allows you to show or hide the RPG Hud."
+ )
+ @ConfigEditorBoolean
+ public boolean showRpgHud = true;
+
+ @Expose
+ @ConfigOption(
+ name = "RPG Hud Position",
+ desc = "Allows you to change the position of the RPG Hud."
+ )
+ @ConfigEditorButton(
+ runnableId = "rpg",
+ buttonText = "Edit"
+ )
+ public Position rpgHudPosition = new Position(1, 1);
+ }
+
+ public static class DungeonHud {
+
+ @Expose
+ @ConfigOption(
+ name = "Dungeon Ultimate Bar",
+ desc = ""
+ )
+ @ConfigEditorAccordion(id = 2)
+ public boolean ultimateBar = false;
+
+ @Expose
+ @ConfigOption(
+ name = "Hide Ultimate Bar",
+ desc = "Hides the custom ultimate bar."
+ )
+ @ConfigEditorBoolean()
+ @ConfigAccordionId(id = 2)
+ public boolean hideUltimateBar = false;
+
+ @Expose
+ @ConfigOption(
+ name = "Bar Position",
+ desc = "Change the position of the bar."
+ )
+ @ConfigEditorButton(
+ runnableId = "ultimate",
+ buttonText = "Edit"
+ )
+ @ConfigAccordionId(id = 2)
+ public Position barPosition = new Position(0, 50, true, false);
+
+ @Expose
+ @ConfigOption(
+ name = "Bar Loading Color",
+ desc = "The color of the bar when its loading."
+ )
+ @ConfigEditorColour()
+ @ConfigAccordionId(id = 2)
+ public String barLoadColor = "159:0:0:0:255";
+
+ @Expose
+ @ConfigOption(
+ name = "Bar Full Color",
+ desc = "The color of the bar when its full."
+ )
+ @ConfigEditorColour()
+ @ConfigAccordionId(id = 2)
+ public String barFullColor = "255:0:0:0:255";
+
+ @Expose
+ @ConfigOption(
+ name = "Bar Style",
+ desc = "Change the style of the bar"
+ )
+ @ConfigEditorDropdown(values = {"No Notch", "6 Notch", "10 Notch", "12 Notch", "20 Notch"})
+ @ConfigAccordionId(id = 2)
+ public int barStyle = 2;
+
+ @Expose
+ @ConfigOption(
+ name = "Dungeon Players",
+ desc = ""
+ )
+ @ConfigEditorAccordion(id = 1)
+ public boolean dungeonPlayerAccordion = false;
+
+ @Expose
+ @ConfigOption(
+ name = "Hide Dungeon Players",
+ desc = "Allows you to hide the dungeon player hud"
+ )
+ @ConfigEditorBoolean()
+ @ConfigAccordionId(id = 1)
+ public boolean hideDungeonPlayers = false;
+
+ @Expose
+ @ConfigOption(
+ name = "Dungeon Player Opacity",
+ desc = "Allows you to change the opacity of the dungeon players."
+ )
+ @ConfigEditorSlider(minValue = 0, maxValue = 100, minStep = 1)
+ @ConfigAccordionId(id = 1)
+ public int dungeonPlayerOpacity = 0;
+
+ @Expose
+ @ConfigOption(
+ name = "Hide Dead Players",
+ desc = "Allows you to hide players that are dead or have left."
+ )
+ @ConfigEditorBoolean()
+ @ConfigAccordionId(id = 1)
+ public boolean hideDeadDungeonPlayers = false;
+
+ @Expose
+ @ConfigOption(
+ name = "Player Position 1",
+ desc = "Change the position of this dungeon player."
+ )
+ @ConfigEditorButton(
+ runnableId = "d1",
+ buttonText = "Edit"
+ )
+ @ConfigAccordionId(id = 1)
+ public Position dungeonPlayer1 = new Position(5, 5);
+
+
+ @Expose
+ @ConfigOption(
+ name = "Player Position 2",
+ desc = "Change the position of this dungeon player."
+ )
+ @ConfigEditorButton(
+ runnableId = "d2",
+ buttonText = "Edit"
+ )
+ @ConfigAccordionId(id = 1)
+ public Position dungeonPlayer2 = new Position(5, 42);
+
+ @Expose
+ @ConfigOption(
+ name = "Player Position 3",
+ desc = "Change the position of this dungeon player."
+ )
+ @ConfigEditorButton(
+ runnableId = "d3",
+ buttonText = "Edit"
+ )
+ @ConfigAccordionId(id = 1)
+ public Position dungeonPlayer3 = new Position(5, 79);
+
+ @Expose
+ @ConfigOption(
+ name = "Player Position 4",
+ desc = "Change the position of this dungeon player."
+ )
+ @ConfigEditorButton(
+ runnableId = "d4",
+ buttonText = "Edit"
+ )
+ @ConfigAccordionId(id = 1)
+ public Position dungeonPlayer4 = new Position(5, 116);
+
+ }
+
+ public static class Renderer {
+ @Expose
+ @ConfigOption(
+ name = "Hide Boss Bar",
+ desc = "Hides Boss Bar when certain conditions are met such as the name is just wither or it starts with objective:"
+ )
+ @ConfigEditorBoolean
+ public boolean hideBossBar = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Hide XP Bar",
+ desc = "Hides xp bar."
+ )
+ @ConfigEditorBoolean
+ public boolean hideXpBar = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Hide Food",
+ desc = "Hides food."
+ )
+ @ConfigEditorBoolean
+ public boolean hideFood = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Hide air",
+ desc = "Hides air."
+ )
+ @ConfigEditorBoolean
+ public boolean hideAir = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Hide hearts",
+ desc = "Hides hearts."
+ )
+ @ConfigEditorBoolean
+ public boolean hideHearts = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Hide armor",
+ desc = "Hides armor."
+ )
+ @ConfigEditorBoolean
+ public boolean hideArmor = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Hide Animal Hearts",
+ desc = "Hides Animal Hearts."
+ )
+ @ConfigEditorBoolean
+ public boolean hideAnimalHearts = true;
+ }
+
+ public static class Map {
+ @Expose
+ @ConfigOption(
+ name = "Show Player Location",
+ desc = "This feature is off by default as Hypixel's rules are so vague that this would fall under their disallowed modifications."
+ )
+ @ConfigEditorBoolean
+ public boolean showPlayerLocation = false;
+
+ @Expose
+ @ConfigOption(
+ name = "Show Mini-Map",
+ desc = "Shows the Mini-Map on your overlay if turned off you can still use /sbhmap to see the map in fullscreen."
+ )
+ @ConfigEditorBoolean
+ public boolean showMiniMap = false;
+
+ @Expose
+ @ConfigOption(
+ name = "Mini-Map Position",
+ desc = "Allows you to change the position of the Mini-Map."
+ )
+ @ConfigEditorButton(
+ runnableId = "map",
+ buttonText = "Edit"
+ )
+ public Position miniMapPosition = new Position(0, 100, false, false);
+
+ @Expose
+ @ConfigOption(
+ name = "Icons",
+ desc = ""
+ )
+ @ConfigEditorAccordion(id = 3)
+ public boolean icons = false;
+
+ @Expose
+ @ConfigOption(
+ name = "NPC",
+ desc = "Show NPC Icons"
+ )
+ @ConfigEditorBoolean()
+ @ConfigAccordionId(id = 3)
+ public boolean showNpcIcons = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Info",
+ desc = "Show Info Icons"
+ )
+ @ConfigEditorBoolean()
+ @ConfigAccordionId(id = 3)
+ public boolean showInfoIcons = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Misc",
+ desc = "Show Misc Icons"
+ )
+ @ConfigEditorBoolean()
+ @ConfigAccordionId(id = 3)
+ public boolean showMiscIcons = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Shops",
+ desc = "Show Shop Icons"
+ )
+ @ConfigEditorBoolean()
+ @ConfigAccordionId(id = 3)
+ public boolean showShopIcons = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Quests",
+ desc = "Show Quest Icons"
+ )
+ @ConfigEditorBoolean()
+ @ConfigAccordionId(id = 3)
+ public boolean showQuestIcons = false;
+ }
+
+ public static class Trackers {
+ @Expose
+ @ConfigOption(
+ name = "Tracker Position",
+ desc = "Allows you to change the position of the Trackers."
+ )
+ @ConfigEditorButton(
+ runnableId = "tracker",
+ buttonText = "Edit"
+ )
+ public Position trackerPosition = new Position(-1, 200);
+
+ @Expose
+ @ConfigOption(
+ name = "Hide Tracker",
+ desc = "It will still track the data just in case."
+ )
+ @ConfigEditorBoolean()
+ public boolean hideTracker = false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/config/SBHConfigEditor.java b/src/main/java/com/thatgravyboat/skyblockhud/config/SBHConfigEditor.java
new file mode 100644
index 0000000..4994a5d
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/config/SBHConfigEditor.java
@@ -0,0 +1,597 @@
+package com.thatgravyboat.skyblockhud.config;
+
+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 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;
+
+import java.awt.*;
+import java.net.URI;
+import java.util.*;
+import java.util.List;
+
+import static com.thatgravyboat.skyblockhud.GuiTextures.*;
+
+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/thatgravtboat/"
+ };
+
+ 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<String, ConfigProcessor.ProcessedCategory> processedConfig;
+ private HashMap<ConfigProcessor.ProcessedOption, ConfigProcessor.ProcessedCategory> 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<String, ConfigProcessor.ProcessedCategory> category : processedConfig.entrySet()) {
+ if(category.getValue().name.equalsIgnoreCase(categoryOpen)) {
+ selectedCategory = category.getKey();
+ break;
+ }
+ }
+ if(selectedCategory == null) {
+ for(Map.Entry<String, ConfigProcessor.ProcessedCategory> category : processedConfig.entrySet()) {
+ if(category.getValue().name.toLowerCase().startsWith(categoryOpen.toLowerCase())) {
+ selectedCategory = category.getKey();
+ break;
+ }
+ }
+ }
+ if(selectedCategory == null) {
+ for(Map.Entry<String, ConfigProcessor.ProcessedCategory> category : processedConfig.entrySet()) {
+ if(category.getValue().name.toLowerCase().contains(categoryOpen.toLowerCase())) {
+ selectedCategory = category.getKey();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private LinkedHashMap<String, ConfigProcessor.ProcessedCategory> getCurrentConfigEditing() {
+ return new LinkedHashMap<>(processedConfig);
+ }
+
+ private LinkedHashMap<String, ConfigProcessor.ProcessedOption> 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<String> 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<String, ConfigProcessor.ProcessedCategory> currentConfigEditing = getCurrentConfigEditing();
+ for(Map.Entry<String, ConfigProcessor.ProcessedCategory> 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<Integer> 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<Integer> 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<String, ConfigProcessor.ProcessedCategory> 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<Integer> 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<String, ConfigProcessor.ProcessedCategory> 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<Integer> 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<Integer> 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<Integer> 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;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/BackgroundBlur.java b/src/main/java/com/thatgravyboat/skyblockhud/core/BackgroundBlur.java
new file mode 100644
index 0000000..6782052
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/BackgroundBlur.java
@@ -0,0 +1,231 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.OpenGlHelper;
+import net.minecraft.client.shader.Framebuffer;
+import net.minecraft.client.shader.Shader;
+import net.minecraft.util.Matrix4f;
+import net.minecraftforge.client.event.EntityViewRenderEvent;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import org.lwjgl.opengl.GL11;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class BackgroundBlur {
+
+ private static HashMap<Float, Framebuffer> blurOutput = new HashMap<>();
+ private static HashMap<Float, Long> lastBlurUse = new HashMap<>();
+ private static long lastBlur = 0;
+ private static HashSet<Float> requestedBlurs = new HashSet<>();
+
+ private static int fogColour = 0;
+ private static boolean registered = false;
+ public static void registerListener() {
+ if(!registered) {
+ registered = true;
+ MinecraftForge.EVENT_BUS.register(new BackgroundBlur());
+ }
+ }
+
+ private static boolean shouldBlur = true;
+
+ public static void markDirty() {
+ if(Minecraft.getMinecraft().theWorld != null) {
+ shouldBlur = true;
+ }
+ }
+
+ public static void processBlurs() {
+ if(shouldBlur) {
+ shouldBlur = false;
+
+ long currentTime = System.currentTimeMillis();
+
+ for(float blur : requestedBlurs) {
+ lastBlur = currentTime;
+ lastBlurUse.put(blur, currentTime);
+
+ int width = Minecraft.getMinecraft().displayWidth;
+ int height = Minecraft.getMinecraft().displayHeight;
+
+ Framebuffer output = blurOutput.computeIfAbsent(blur, k -> {
+ Framebuffer fb = new Framebuffer(width, height, false);
+ fb.setFramebufferFilter(GL11.GL_NEAREST);
+ return fb;
+ });
+
+ output.framebufferWidth = output.framebufferTextureWidth = width;
+ output.framebufferHeight = output.framebufferTextureHeight = height;
+
+ blurBackground(output, blur);
+ }
+
+ Set<Float> remove = new HashSet<>();
+ for(Map.Entry<Float, Long> entry : lastBlurUse.entrySet()) {
+ if(currentTime - entry.getValue() > 30*1000) {
+ remove.add(entry.getKey());
+ }
+ }
+ remove.remove(5f);
+
+ lastBlurUse.keySet().removeAll(remove);
+ blurOutput.keySet().removeAll(remove);
+
+ requestedBlurs.clear();
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGHEST)
+ public void onScreenRender(RenderGameOverlayEvent.Pre event) {
+ if(event.type == RenderGameOverlayEvent.ElementType.ALL) {
+ processBlurs();
+ }
+ }
+
+ @SubscribeEvent
+ public void onFogColour(EntityViewRenderEvent.FogColors event) {
+ fogColour = 0xff000000;
+ fogColour |= ((int)(event.red*255) & 0xFF) << 16;
+ fogColour |= ((int)(event.green*255) & 0xFF) << 8;
+ fogColour |= (int)(event.blue*255) & 0xFF;
+ }
+
+ private static Shader blurShaderHorz = null;
+ private static Shader blurShaderVert = null;
+ private static Framebuffer blurOutputHorz = null;
+
+ /**
+ * Creates a projection matrix that projects from our coordinate space [0->width; 0->height] to OpenGL coordinate
+ * space [-1 -> 1; 1 -> -1] (Note: flipped y-axis).
+ *
+ * This is so that we can render to and from the framebuffer in a way that is familiar to us, instead of needing to
+ * apply scales and translations manually.
+ */
+ private static Matrix4f createProjectionMatrix(int width, int height) {
+ Matrix4f projMatrix = new Matrix4f();
+ projMatrix.setIdentity();
+ projMatrix.m00 = 2.0F / (float)width;
+ projMatrix.m11 = 2.0F / (float)(-height);
+ projMatrix.m22 = -0.0020001999F;
+ projMatrix.m33 = 1.0F;
+ projMatrix.m03 = -1.0F;
+ projMatrix.m13 = 1.0F;
+ projMatrix.m23 = -1.0001999F;
+ return projMatrix;
+ }
+
+ private static void blurBackground(Framebuffer output, float blurFactor) {
+ if(!OpenGlHelper.isFramebufferEnabled() || !OpenGlHelper.areShadersSupported()) return;
+
+ int width = Minecraft.getMinecraft().displayWidth;
+ int height = Minecraft.getMinecraft().displayHeight;
+
+ GlStateManager.matrixMode(GL11.GL_PROJECTION);
+ GlStateManager.loadIdentity();
+ GlStateManager.ortho(0.0D, width, height, 0.0D, 1000.0D, 3000.0D);
+ GlStateManager.matrixMode(GL11.GL_MODELVIEW);
+ GlStateManager.loadIdentity();
+ GlStateManager.translate(0.0F, 0.0F, -2000.0F);
+
+ if(blurOutputHorz == null) {
+ blurOutputHorz = new Framebuffer(width, height, false);
+ blurOutputHorz.setFramebufferFilter(GL11.GL_NEAREST);
+ }
+ if(blurOutputHorz == null || output == null) {
+ return;
+ }
+ if(blurOutputHorz.framebufferWidth != width || blurOutputHorz.framebufferHeight != height) {
+ blurOutputHorz.createBindFramebuffer(width, height);
+ blurShaderHorz.setProjectionMatrix(createProjectionMatrix(width, height));
+ Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(false);
+ }
+
+ try {
+ blurShaderHorz = new Shader(Minecraft.getMinecraft().getResourceManager(), "blur",
+ Minecraft.getMinecraft().getFramebuffer(), blurOutputHorz);
+ blurShaderHorz.getShaderManager().getShaderUniform("BlurDir").set(1, 0);
+ blurShaderHorz.setProjectionMatrix(createProjectionMatrix(width, height));
+ } catch(Exception ignored) { }
+ try {
+ blurShaderVert = new Shader(Minecraft.getMinecraft().getResourceManager(), "blur",
+ blurOutputHorz, output);
+ blurShaderVert.getShaderManager().getShaderUniform("BlurDir").set(0, 1);
+ blurShaderVert.setProjectionMatrix(createProjectionMatrix(width, height));
+ } catch(Exception ignored) { }
+ if(blurShaderHorz != null && blurShaderVert != null) {
+ if(blurShaderHorz.getShaderManager().getShaderUniform("Radius") == null) {
+ //Corrupted shader?
+ return;
+ }
+
+ blurShaderHorz.getShaderManager().getShaderUniform("Radius").set(blurFactor);
+ blurShaderVert.getShaderManager().getShaderUniform("Radius").set(blurFactor);
+
+ GL11.glPushMatrix();
+ /*GL30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, Minecraft.getMinecraft().getFramebuffer().framebufferObject);
+ GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, output.framebufferObject);
+ GL30.glBlitFramebuffer(0, 0, width, height,
+ 0, 0, output.framebufferWidth, output.framebufferHeight,
+ GL11.GL_COLOR_BUFFER_BIT, GL11.GL_NEAREST);*/
+
+ blurShaderHorz.loadShader(0);
+ blurShaderVert.loadShader(0);
+ GlStateManager.enableDepth();
+ GL11.glPopMatrix();
+
+ Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(false);
+ }
+ }
+
+ public static void renderBlurredBackground(float blurStrength, int screenWidth, int screenHeight,
+ int x, int y, int blurWidth, int blurHeight) {
+ renderBlurredBackground(blurStrength, screenWidth, screenHeight, x, y, blurWidth, blurHeight, false);
+ }
+
+ /**
+ * Renders a subsection of the blurred framebuffer on to the corresponding section of the screen.
+ * Essentially, this method will "blur" the background inside the bounds specified by [x->x+blurWidth, y->y+blurHeight]
+ */
+ public static void renderBlurredBackground(float blurStrength, int screenWidth, int screenHeight,
+ int x, int y, int blurWidth, int blurHeight, boolean forcedUpdate) {
+ if(!OpenGlHelper.isFramebufferEnabled() || !OpenGlHelper.areShadersSupported()) return;
+ if(blurStrength < 0.5) return;
+ requestedBlurs.add(blurStrength);
+
+ long currentTime = System.currentTimeMillis();
+ if(currentTime - lastBlur > 300) {
+ shouldBlur = true;
+ if(currentTime - lastBlur > 400 && forcedUpdate) return;
+ }
+
+ if(blurOutput.isEmpty()) return;
+
+ Framebuffer fb = blurOutput.get(blurStrength);
+ if(fb == null) {
+ fb = blurOutput.values().iterator().next();
+ }
+
+ float uMin = x/(float)screenWidth;
+ float uMax = (x+blurWidth)/(float)screenWidth;
+ float vMin = (screenHeight-y)/(float)screenHeight;
+ float vMax = (screenHeight-y-blurHeight)/(float)screenHeight;
+
+ GlStateManager.depthMask(false);
+ Gui.drawRect(x, y, x+blurWidth, y+blurHeight, fogColour);
+ fb.bindFramebufferTexture();
+ GlStateManager.color(1f, 1f, 1f, 1f);
+ RenderUtils.drawTexturedRect(x, y, blurWidth, blurHeight, uMin, uMax, vMin, vMax);
+ fb.unbindFramebufferTexture();
+ GlStateManager.depthMask(true);
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/ChromaColour.java b/src/main/java/com/thatgravyboat/skyblockhud/core/ChromaColour.java
new file mode 100644
index 0000000..b9f4e79
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/ChromaColour.java
@@ -0,0 +1,95 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import java.awt.*;
+
+public class ChromaColour {
+
+ public static String special(int chromaSpeed, int alpha, int rgb) {
+ return special(chromaSpeed, alpha, (rgb & 0xFF0000) >> 16, (rgb & 0x00FF00) >> 8, (rgb & 0x0000FF));
+ }
+
+ private static final int RADIX = 10;
+
+ public static String special(int chromaSpeed, int alpha, int r, int g, int b) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Integer.toString(chromaSpeed, RADIX)).append(":");
+ sb.append(Integer.toString(alpha, RADIX)).append(":");
+ sb.append(Integer.toString(r, RADIX)).append(":");
+ sb.append(Integer.toString(g, RADIX)).append(":");
+ sb.append(Integer.toString(b, RADIX));
+ return sb.toString();
+ }
+
+ private static int[] decompose(String csv) {
+ String[] split = csv.split(":");
+
+ int[] arr = new int[split.length];
+
+
+ for(int i=0; i<split.length; i++) {
+ arr[i] = Integer.parseInt(split[split.length-1-i], RADIX);
+ }
+ return arr;
+ }
+
+ public static int specialToSimpleRGB(String special) {
+ int[] d = decompose(special);
+ int r = d[2];
+ int g = d[1];
+ int b = d[0];
+ int a = d[3];
+ int chr = d[4];
+
+ return (a & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF);
+ }
+
+ public static int getSpeed(String special) {
+ return decompose(special)[4];
+ }
+
+ public static float getSecondsForSpeed(int speed) {
+ return (255-speed)/254f*(MAX_CHROMA_SECS-MIN_CHROMA_SECS)+MIN_CHROMA_SECS;
+ }
+
+ private static final int MIN_CHROMA_SECS = 1;
+ private static final int MAX_CHROMA_SECS = 60;
+
+ public static long startTime = -1;
+ public static int specialToChromaRGB(String special) {
+ if(startTime < 0) startTime = System.currentTimeMillis();
+
+ int[] d = decompose(special);
+ int chr = d[4];
+ int a = d[3];
+ int r = d[2];
+ int g = d[1];
+ int b = d[0];
+
+ float[] hsv = Color.RGBtoHSB(r, g, b, null);
+
+ if(chr > 0) {
+ float seconds = getSecondsForSpeed(chr);
+ hsv[0] += (System.currentTimeMillis()-startTime)/1000f/seconds;
+ hsv[0] %= 1;
+ if(hsv[0] < 0) hsv[0] += 1;
+ }
+
+ return (a & 0xFF) << 24 | (Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) & 0x00FFFFFF);
+ }
+
+ public static int rotateHue(int argb, int degrees) {
+ int a = (argb >> 24) & 0xFF;
+ int r = (argb >> 16) & 0xFF;
+ int g = (argb >> 8) & 0xFF;
+ int b = (argb) & 0xFF;
+
+ float[] hsv = Color.RGBtoHSB(r, g, b, null);
+
+ hsv[0] += degrees/360f;
+ hsv[0] %= 1;
+
+ return (a & 0xFF) << 24 | (Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) & 0x00FFFFFF);
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GlScissorStack.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GlScissorStack.java
new file mode 100644
index 0000000..6f21e9a
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GlScissorStack.java
@@ -0,0 +1,87 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.ScaledResolution;
+import org.lwjgl.opengl.GL11;
+
+import java.util.LinkedList;
+
+public class GlScissorStack {
+
+ private static class Bounds {
+ int left;
+ int top;
+ int right;
+ int bottom;
+
+ public Bounds(int left, int top, int right, int bottom) {
+ this.left = left;
+ this.top = top;
+ this.right = right;
+ this.bottom = bottom;
+ }
+
+ public Bounds createSubBound(int left, int top, int right, int bottom) {
+ left = Math.max(left, this.left);
+ top = Math.max(top, this.top);
+ right = Math.min(right, this.right);
+ bottom = Math.min(bottom, this.bottom);
+
+ if(top > bottom) {
+ top = bottom;
+ }
+ if(left > right) {
+ left = right;
+ }
+
+ return new Bounds(left, top, right, bottom);
+ }
+
+ public void set(ScaledResolution scaledResolution) {
+ int height = Minecraft.getMinecraft().displayHeight;
+ int scale = scaledResolution.getScaleFactor();
+ GL11.glScissor(left*scale, height-bottom*scale, (right-left)*scale, (bottom-top)*scale);
+ }
+ }
+
+ private static final LinkedList<Bounds> boundsStack = new LinkedList<>();
+
+ public static void push(int left, int top, int right, int bottom, ScaledResolution scaledResolution) {
+ if(right < left) {
+ int temp = right;
+ right = left;
+ left = temp;
+ }
+ if(bottom < top) {
+ int temp = bottom;
+ bottom = top;
+ top = temp;
+ }
+ if(boundsStack.isEmpty()) {
+ boundsStack.push(new Bounds(left, top, right, bottom));
+ } else {
+ boundsStack.push(boundsStack.peek().createSubBound(left, top, right, bottom));
+ }
+ if(!boundsStack.isEmpty()) {
+ boundsStack.peek().set(scaledResolution);
+ }
+ GL11.glEnable(GL11.GL_SCISSOR_TEST);
+ }
+
+ public static void pop(ScaledResolution scaledResolution) {
+ if(!boundsStack.isEmpty()) {
+ boundsStack.pop();
+ }
+ if(boundsStack.isEmpty()) {
+ GL11.glDisable(GL11.GL_SCISSOR_TEST);
+ } else {
+ boundsStack.peek().set(scaledResolution);
+ }
+ }
+
+ public static void clear() {
+ boundsStack.clear();
+ GL11.glDisable(GL11.GL_SCISSOR_TEST);
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElement.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElement.java
new file mode 100644
index 0000000..94d2375
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElement.java
@@ -0,0 +1,8 @@
+package com.thatgravyboat.skyblockhud.core;
+
+public abstract class GuiElement {
+
+ public abstract void render();
+ public abstract boolean mouseInput(int mouseX, int mouseY);
+ public abstract boolean keyboardInput();
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementBoolean.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementBoolean.java
new file mode 100644
index 0000000..d403bef
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementBoolean.java
@@ -0,0 +1,121 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import com.thatgravyboat.skyblockhud.core.util.lerp.LerpUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.GuiTextures;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.ResourceLocation;
+import org.lwjgl.input.Mouse;
+
+import java.util.function.Consumer;
+
+public class GuiElementBoolean extends GuiElement {
+
+ public int x;
+ public int y;
+ private boolean value;
+ private int clickRadius;
+ private Consumer<Boolean> toggleCallback;
+
+ private boolean previewValue;
+ private int animation = 0;
+ private long lastMillis = 0;
+
+ private static final int xSize = 48;
+ private static final int ySize = 14;
+
+ public GuiElementBoolean(int x, int y, boolean value, Consumer<Boolean> toggleCallback) {
+ this(x, y, value, 0, toggleCallback);
+ }
+
+ public GuiElementBoolean(int x, int y, boolean value, int clickRadius, Consumer<Boolean> toggleCallback) {
+ this.x = x;
+ this.y = y;
+ this.value = value;
+ this.previewValue = value;
+ this.clickRadius = clickRadius;
+ this.toggleCallback = toggleCallback;
+ this.lastMillis = System.currentTimeMillis();
+
+ if(value) animation = 36;
+ }
+
+ @Override
+ public void render() {
+ GlStateManager.color(1, 1, 1, 1);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(GuiTextures.BAR);
+ RenderUtils.drawTexturedRect(x, y, xSize, ySize);
+
+ ResourceLocation buttonLoc = GuiTextures.ON;
+ long currentMillis = System.currentTimeMillis();
+ long deltaMillis = currentMillis - lastMillis;
+ lastMillis = currentMillis;
+ boolean passedLimit = false;
+ if(previewValue != value) {
+ if((previewValue && animation > 12) ||
+ (!previewValue && animation < 24)) {
+ passedLimit = true;
+ }
+ }
+ if(previewValue != passedLimit) {
+ animation += deltaMillis/10;
+ } else {
+ animation -= deltaMillis/10;
+ }
+ lastMillis -= deltaMillis%10;
+
+ if(previewValue == value) {
+ animation = Math.max(0, Math.min(36, animation));
+ } else if(!passedLimit) {
+ if(previewValue) {
+ animation = Math.max(0, Math.min(12, animation));
+ } else {
+ animation = Math.max(24, Math.min(36, animation));
+ }
+ } else {
+ if(previewValue) {
+ animation = Math.max(12, animation);
+ } else {
+ animation = Math.min(24, animation);
+ }
+ }
+
+ int animation = (int)(LerpUtils.sigmoidZeroOne(this.animation/36f)*36);
+ if(animation < 3) {
+ buttonLoc = GuiTextures.OFF;
+ } else if(animation < 13) {
+ buttonLoc = GuiTextures.ONE;
+ } else if(animation < 23) {
+ buttonLoc = GuiTextures.TWO;
+ } else if(animation < 33) {
+ buttonLoc = GuiTextures.THREE;
+ }
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(buttonLoc);
+ RenderUtils.drawTexturedRect(x+animation, y, 12, 14);
+ }
+
+ @Override
+ public boolean mouseInput(int mouseX, int mouseY) {
+ if(mouseX > x-clickRadius && mouseX < x+xSize+clickRadius &&
+ mouseY > y-clickRadius && mouseY < y+ySize+clickRadius) {
+ if(Mouse.getEventButton() == 0) {
+ if(Mouse.getEventButtonState()) {
+ previewValue = !value;
+ } else if(previewValue == !value) {
+ value = !value;
+ toggleCallback.accept(value);
+ }
+ }
+ } else {
+ previewValue = value;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementColour.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementColour.java
new file mode 100644
index 0000000..8774595
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementColour.java
@@ -0,0 +1,368 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.texture.DynamicTexture;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.util.function.Consumer;
+
+public class GuiElementColour extends GuiElement {
+
+ public static final ResourceLocation colour_selector_dot = new ResourceLocation("skyblockhud:core/colour_selector_dot.png");
+ public static final ResourceLocation colour_selector_bar = new ResourceLocation("skyblockhud:core/colour_selector_bar.png");
+ public static final ResourceLocation colour_selector_bar_alpha = new ResourceLocation("skyblockhud:core/colour_selector_bar_alpha.png");
+ public static final ResourceLocation colour_selector_chroma = new ResourceLocation("skyblockhud:core/colour_selector_chroma.png");
+
+ private static final ResourceLocation colourPickerLocation = new ResourceLocation("mbcore:dynamic/colourpicker");
+ private static final ResourceLocation colourPickerBarValueLocation = new ResourceLocation("mbcore:dynamic/colourpickervalue");
+ private static final ResourceLocation colourPickerBarOpacityLocation = new ResourceLocation("mbcore:dynamic/colourpickeropacity");
+ private final GuiElementTextField hexField = new GuiElementTextField("",
+ GuiElementTextField.SCALE_TEXT | GuiElementTextField.FORCE_CAPS | GuiElementTextField.NO_SPACE);
+
+ private int x;
+ private int y;
+ private final int xSize = 119;
+ private final int ySize = 89;
+
+ private float wheelAngle = 0;
+ private float wheelRadius = 0;
+
+ private int clickedComponent = -1;
+
+ private Consumer<String> colourChangedCallback;
+ private Runnable closeCallback;
+ private String colour;
+
+ public GuiElementColour(int x, int y, String initialColour, Consumer<String> colourChangedCallback,
+ Runnable closeCallback) {
+
+ final ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+
+ this.y = Math.max(10, Math.min(scaledResolution.getScaledHeight()-ySize-10, y));
+ this.x = Math.max(10, Math.min(scaledResolution.getScaledWidth()-xSize-10, x));
+
+ this.colour = initialColour;
+ this.colourChangedCallback = colourChangedCallback;
+ this.closeCallback = closeCallback;
+
+ int colour = ChromaColour.specialToSimpleRGB(initialColour);
+ Color c = new Color(colour);
+ float[] hsv = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
+ updateAngleAndRadius(hsv);
+ }
+
+ public void updateAngleAndRadius(float[] hsv) {
+ this.wheelRadius = hsv[1];
+ this.wheelAngle = hsv[0]*360;
+ }
+
+ public void render() {
+ RenderUtils.drawFloatingRectDark(x, y, xSize, ySize);
+
+ int currentColour = ChromaColour.specialToSimpleRGB(colour);
+ Color c = new Color(currentColour, true);
+ float[] hsv = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
+
+ BufferedImage bufferedImage = new BufferedImage(288, 288, BufferedImage.TYPE_INT_ARGB);
+ float borderRadius = 0.05f;
+ if(Keyboard.isKeyDown(Keyboard.KEY_N)) borderRadius = 0;
+ for(int x=-16; x<272; x++) {
+ for(int y=-16; y<272; y++) {
+ float radius = (float) Math.sqrt(((x-128)*(x-128)+(y-128)*(y-128))/16384f);
+ float angle = (float) Math.toDegrees(Math.atan((128-x)/(y-128+1E-5))+Math.PI/2);
+ if(y < 128) angle += 180;
+ if(radius <= 1) {
+ int rgb = Color.getHSBColor(angle/360f, (float)Math.pow(radius, 1.5f), hsv[2]).getRGB();
+ bufferedImage.setRGB(x+16, y+16, rgb);
+ } else if(radius <= 1+borderRadius) {
+ float invBlackAlpha = Math.abs(radius-1-borderRadius/2)/borderRadius*2;
+ float blackAlpha = 1-invBlackAlpha;
+
+ if(radius > 1+borderRadius/2) {
+ bufferedImage.setRGB(x+16, y+16, (int)(blackAlpha*255) << 24);
+ } else {
+ Color col = Color.getHSBColor(angle/360f, 1, hsv[2]);
+ int rgb = (int)(col.getRed()*invBlackAlpha) << 16 |
+ (int)(col.getGreen()*invBlackAlpha) << 8 |
+ (int)(col.getBlue()*invBlackAlpha);
+ bufferedImage.setRGB(x+16, y+16, 0xff000000 | rgb);
+ }
+
+ }
+ }
+ }
+
+ BufferedImage bufferedImageValue = new BufferedImage(10, 64, BufferedImage.TYPE_INT_ARGB);
+ for(int x=0; x<10; x++) {
+ for(int y=0; y<64; y++) {
+ if((x == 0 || x == 9) && (y == 0 || y == 63)) continue;
+
+ int rgb = Color.getHSBColor(wheelAngle/360, wheelRadius, (64-y)/64f).getRGB();
+ bufferedImageValue.setRGB(x, y, rgb);
+ }
+ }
+
+ BufferedImage bufferedImageOpacity = new BufferedImage(10, 64, BufferedImage.TYPE_INT_ARGB);
+ for(int x=0; x<10; x++) {
+ for(int y=0; y<64; y++) {
+ if((x == 0 || x == 9) && (y == 0 || y == 63)) continue;
+
+ int rgb = (currentColour & 0x00FFFFFF) | (Math.min(255, (64-y)*4) << 24);
+ bufferedImageOpacity.setRGB(x, y, rgb);
+ }
+ }
+
+ float selradius = (float)Math.pow(wheelRadius, 1/1.5f)*32;
+ int selx = (int)(Math.cos(Math.toRadians(wheelAngle))*selradius);
+ int sely = (int)(Math.sin(Math.toRadians(wheelAngle))*selradius);
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colour_selector_bar_alpha);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+5+64+5+10+5, y+5, 10, 64, GL11.GL_NEAREST);
+
+ Minecraft.getMinecraft().getTextureManager().loadTexture(colourPickerBarValueLocation, new DynamicTexture(bufferedImageValue));
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colourPickerBarValueLocation);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+5+64+5, y+5, 10, 64, GL11.GL_NEAREST);
+
+ Minecraft.getMinecraft().getTextureManager().loadTexture(colourPickerBarOpacityLocation, new DynamicTexture(bufferedImageOpacity));
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colourPickerBarOpacityLocation);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+5+64+5+10+5, y+5, 10, 64, GL11.GL_NEAREST);
+
+ int chromaSpeed = ChromaColour.getSpeed(colour);
+ int currentColourChroma = ChromaColour.specialToChromaRGB(colour);
+ Color cChroma = new Color(currentColourChroma, true);
+ float hsvChroma[] = Color.RGBtoHSB(cChroma.getRed(), cChroma.getGreen(), cChroma.getBlue(), null);
+
+ if(chromaSpeed > 0) {
+ Gui.drawRect(x+5+64+5+10+5+10+5+1, y+5+1,
+ x+5+64+5+10+5+10+5+10-1, y+5+64-1,
+ Color.HSBtoRGB(hsvChroma[0], 0.8f, 0.8f));
+ } else {
+ Gui.drawRect(x+5+64+5+10+5+10+5+1, y+5+27+1,
+ x+5+64+5+10+5+10+5+10-1, y+5+37-1,
+ Color.HSBtoRGB((hsvChroma[0]+(System.currentTimeMillis()-ChromaColour.startTime)/1000f)%1, 0.8f, 0.8f));
+ }
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colour_selector_bar);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+5+64+5, y+5, 10, 64, GL11.GL_NEAREST);
+ RenderUtils.drawTexturedRect(x+5+64+5+10+5, y+5, 10, 64, GL11.GL_NEAREST);
+
+ if(chromaSpeed > 0) {
+ RenderUtils.drawTexturedRect(x+5+64+5+10+5+10+5, y+5, 10, 64, GL11.GL_NEAREST);
+ } else {
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colour_selector_chroma);
+ RenderUtils.drawTexturedRect(x+5+64+5+10+5+10+5, y+5+27, 10, 10, GL11.GL_NEAREST);
+ }
+
+ Gui.drawRect(x+5+64+5, y+5+64-(int)(64*hsv[2]),
+ x+5+64+5+10, y+5+64-(int)(64*hsv[2])+1, 0xFF000000);
+ Gui.drawRect(x+5+64+5+10+5, y+5+64-c.getAlpha()/4,
+ x+5+64+5+10+5+10, y+5+64-c.getAlpha()/4-1, 0xFF000000);
+ if(chromaSpeed > 0) {
+ Gui.drawRect(x+5+64+5+10+5+10+5,
+ y+5+64-(int)(chromaSpeed/255f*64),
+ x+5+64+5+10+5+10+5+10,
+ y+5+64-(int)(chromaSpeed/255f*64)+1, 0xFF000000);
+ }
+
+ Minecraft.getMinecraft().getTextureManager().loadTexture(colourPickerLocation, new DynamicTexture(bufferedImage));
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colourPickerLocation);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+1, y+1, 72, 72, GL11.GL_LINEAR);
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colour_selector_dot);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+5+32+selx-4, y+5+32+sely-4, 8, 8, GL11.GL_NEAREST);
+
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(EnumChatFormatting.GRAY.toString()+Math.round(hsv[2]*100)+"",
+ Minecraft.getMinecraft().fontRendererObj,
+ x+5+64+5+5-(Math.round(hsv[2]*100)==100?1:0), y+5+64+5+5, true, 13, -1);
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(EnumChatFormatting.GRAY.toString()+Math.round(c.getAlpha()/255f*100)+"",
+ Minecraft.getMinecraft().fontRendererObj,
+ x+5+64+5+15+5, y+5+64+5+5, true, 13, -1);
+ if(chromaSpeed > 0) {
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(EnumChatFormatting.GRAY.toString()+
+ (int)ChromaColour.getSecondsForSpeed(chromaSpeed)+"s",
+ Minecraft.getMinecraft().fontRendererObj,
+ x+5+64+5+30+6, y+5+64+5+5, true, 13, -1);
+ }
+
+ hexField.setSize(48, 10);
+ if(!hexField.getFocus()) hexField.setText(Integer.toHexString(c.getRGB() & 0xFFFFFF).toUpperCase());
+
+ StringBuilder sb = new StringBuilder(EnumChatFormatting.GRAY+"#");
+ for(int i=0; i<6-hexField.getText().length(); i++) {
+ sb.append("0");
+ }
+ sb.append(EnumChatFormatting.WHITE);
+
+ hexField.setPrependText(sb.toString());
+ hexField.render(x+5+8, y+5+64+5);
+ }
+
+ public boolean mouseInput(int mouseX, int mouseY) {
+ ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ float mouseXF = (float)(Mouse.getX() * scaledResolution.getScaledWidth_double() /
+ Minecraft.getMinecraft().displayWidth);
+ float mouseYF = (float)(scaledResolution.getScaledHeight_double() - Mouse.getY() *
+ scaledResolution.getScaledHeight_double() / Minecraft.getMinecraft().displayHeight - 1);
+
+ if((Mouse.getEventButton() == 0 || Mouse.getEventButton() == 1) && Mouse.getEventButtonState()) {
+ if(mouseX > x+5+8 && mouseX < x+5+8+48) {
+ if(mouseY > y+5+64+5 && mouseY < y+5+64+5+10) {
+ hexField.mouseClicked(mouseX, mouseY, Mouse.getEventButton());
+ clickedComponent = -1;
+ return true;
+ }
+ }
+ }
+ if(!Mouse.getEventButtonState() && Mouse.getEventButton() == 0) {
+ clickedComponent = -1;
+ }
+ if(Mouse.getEventButtonState() && Mouse.getEventButton() == 0) {
+ if(mouseX >= x && mouseX <= x+119 &&
+ mouseY >= y && mouseY <= y+89) {
+ hexField.unfocus();
+
+ int xWheel = mouseX - x - 5;
+ int yWheel = mouseY - y - 5;
+
+ if(xWheel > 0 && xWheel < 64) {
+ if(yWheel > 0 && yWheel < 64) {
+ clickedComponent = 0;
+ }
+ }
+
+ int xValue = mouseX - (x+5+64+5);
+ int y = mouseY - this.y - 5;
+
+ if(y > -5 && y <= 69) {
+ if(xValue > 0 && xValue < 10) {
+ clickedComponent = 1;
+ }
+
+ int xOpacity = mouseX - (x+5+64+5+10+5);
+
+ if(xOpacity > 0 && xOpacity < 10) {
+ clickedComponent = 2;
+ }
+ }
+
+ int chromaSpeed = ChromaColour.getSpeed(colour);
+ int xChroma = mouseX - (x+5+64+5+10+5+10+5);
+ if(xChroma > 0 && xChroma < 10) {
+ if(chromaSpeed > 0) {
+ if(y > -5 && y <= 69) {
+ clickedComponent = 3;
+ }
+ } else if(mouseY > this.y+5+27 && mouseY < this.y+5+37) {
+ int currentColour = ChromaColour.specialToSimpleRGB(colour);
+ Color c = new Color(currentColour, true);
+ colour = ChromaColour.special(200, c.getAlpha(), currentColour);
+ colourChangedCallback.accept(colour);
+ }
+ }
+ } else {
+ hexField.unfocus();
+ closeCallback.run();
+ return false;
+ }
+ }
+ if(Mouse.isButtonDown(0) && clickedComponent >= 0) {
+ int currentColour = ChromaColour.specialToSimpleRGB(colour);
+ Color c = new Color(currentColour, true);
+ float[] hsv = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
+
+ float xWheel = mouseXF - x - 5;
+ float yWheel = mouseYF - y - 5;
+
+ if(clickedComponent == 0) {
+ float angle = (float) Math.toDegrees(Math.atan((32-xWheel)/(yWheel-32+1E-5))+Math.PI/2);
+ xWheel = Math.max(0, Math.min(64, xWheel));
+ yWheel = Math.max(0, Math.min(64, yWheel));
+ float radius = (float) Math.sqrt(((xWheel-32)*(xWheel-32)+(yWheel-32)*(yWheel-32))/1024f);
+ if(yWheel < 32) angle += 180;
+
+ this.wheelAngle = angle;
+ this.wheelRadius = (float)Math.pow(Math.min(1, radius), 1.5f);
+ int rgb = Color.getHSBColor(angle/360f, wheelRadius, hsv[2]).getRGB();
+ colour = ChromaColour.special(ChromaColour.getSpeed(colour), c.getAlpha(), rgb);
+ colourChangedCallback.accept(colour);
+ return true;
+ }
+
+ float y = mouseYF - this.y - 5;
+ y = Math.max(0, Math.min(64, y));
+ System.out.println(y);
+
+ if(clickedComponent == 1) {
+ int rgb = Color.getHSBColor(wheelAngle/360, wheelRadius, 1-y/64f).getRGB();
+ colour = ChromaColour.special(ChromaColour.getSpeed(colour), c.getAlpha(), rgb);
+ colourChangedCallback.accept(colour);
+ return true;
+ }
+
+ if(clickedComponent == 2) {
+ colour = ChromaColour.special(ChromaColour.getSpeed(colour),
+ 255-Math.round(y/64f*255), currentColour);
+ colourChangedCallback.accept(colour);
+ return true;
+ }
+
+ if(clickedComponent == 3) {
+ colour = ChromaColour.special(255-Math.round(y/64f*255), c.getAlpha(), currentColour);
+ colourChangedCallback.accept(colour);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean keyboardInput() {
+ if(Keyboard.getEventKeyState() && hexField.getFocus()) {
+ if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) {
+ hexField.unfocus();
+ return true;
+ }
+ String old = hexField.getText();
+
+ hexField.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey());
+
+ if(hexField.getText().length() > 6) {
+ hexField.setText(old);
+ } else {
+ try {
+ String text = hexField.getText().toLowerCase();
+
+ int rgb = Integer.parseInt(text, 16);
+ int alpha = (ChromaColour.specialToSimpleRGB(colour) >> 24) & 0xFF;
+ colour = ChromaColour.special(ChromaColour.getSpeed(colour), alpha, rgb);
+ colourChangedCallback.accept(colour);
+
+ Color c = new Color(rgb);
+ float[] hsv = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
+ updateAngleAndRadius(hsv);
+ } catch(Exception e) {};
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementTextField.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementTextField.java
new file mode 100644
index 0000000..e2d6557
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementTextField.java
@@ -0,0 +1,534 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import com.thatgravyboat.skyblockhud.core.util.StringUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.GuiTextField;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+
+import java.awt.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class GuiElementTextField {
+
+ public static final int SCALE_TEXT = 0b100000;
+ public static final int NUM_ONLY = 0b10000;
+ public static final int NO_SPACE = 0b01000;
+ public static final int FORCE_CAPS = 0b00100;
+ public static final int COLOUR = 0b00010;
+ public static final int MULTILINE = 0b00001;
+
+ private int searchBarYSize;
+ private int searchBarXSize;
+ private static final int searchBarPadding = 2;
+
+ private int options;
+
+ private boolean focus = false;
+
+ private int x;
+ private int y;
+
+ private String prependText = "";
+
+ private final GuiTextField textField = new GuiTextField(0, Minecraft.getMinecraft().fontRendererObj,
+ 0 , 0, 0, 0);
+
+ private int customBorderColour = -1;
+
+ public GuiElementTextField(String initialText, int options) {
+ this(initialText, 100, 20, options);
+ }
+
+ public GuiElementTextField(String initialText, int sizeX, int sizeY, int options) {
+ textField.setFocused(true);
+ textField.setCanLoseFocus(false);
+ textField.setMaxStringLength(999);
+ textField.setText(initialText);
+ this.searchBarXSize = sizeX;
+ this.searchBarYSize = sizeY;
+ this.options = options;
+ }
+
+ public void setMaxStringLength(int len) {
+ textField.setMaxStringLength(len);
+ }
+
+ public void setCustomBorderColour(int colour) {
+ this.customBorderColour = colour;
+ }
+
+ public String getText() {
+ return textField.getText();
+ }
+
+ public void setPrependText(String text) {
+ this.prependText = text;
+ }
+
+ public void setText(String text) {
+ if(textField.getText() == null || !textField.getText().equals(text)) {
+ textField.setText(text);
+ }
+ }
+
+ public void setSize(int searchBarXSize, int searchBarYSize) {
+ this.searchBarXSize = searchBarXSize;
+ this.searchBarYSize = searchBarYSize;
+ }
+
+ public void setOptions(int options) {
+ this.options = options;
+ }
+
+ @Override
+ public String toString() {
+ return textField.getText();
+ }
+
+ public void setFocus(boolean focus) {
+ this.focus = focus;
+ }
+ public boolean getFocus() {
+ return focus;
+ }
+
+ public int getHeight() {
+ ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ int paddingUnscaled = searchBarPadding/scaledresolution.getScaleFactor();
+
+ int numLines = org.apache.commons.lang3.StringUtils.countMatches(textField.getText(), "\n")+1;
+ int extraSize = (searchBarYSize-8)/2+8;
+ int bottomTextBox = searchBarYSize + extraSize*(numLines-1);
+
+ return bottomTextBox + paddingUnscaled*2;
+ }
+
+ public int getWidth() {
+ ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ int paddingUnscaled = searchBarPadding/scaledresolution.getScaleFactor();
+
+ return searchBarXSize + paddingUnscaled*2;
+ }
+
+ private float getScaleFactor(String str) {
+ return Math.min(1, (searchBarXSize-2)/(float)Minecraft.getMinecraft().fontRendererObj.getStringWidth(str));
+ }
+
+ private boolean isScaling() {
+ return (options & SCALE_TEXT) != 0;
+ }
+
+ private float getStringWidth(String str) {
+ if(isScaling()) {
+ return Minecraft.getMinecraft().fontRendererObj.getStringWidth(str)*getScaleFactor(str);
+ } else {
+ return Minecraft.getMinecraft().fontRendererObj.getStringWidth(str);
+ }
+ }
+
+ public int getCursorPos(int mouseX, int mouseY) {
+ int xComp = mouseX - x;
+ int yComp = mouseY - y;
+
+ int extraSize = (searchBarYSize-8)/2+8;
+
+ String renderText = prependText + textField.getText();
+
+ int lineNum = Math.round(((yComp - (searchBarYSize-8)/2))/extraSize);
+
+ Pattern patternControlCode = Pattern.compile("(?i)\\u00A7([^\\u00B6])(?!\\u00B6)");
+ String text = renderText;
+ String textNoColour = renderText;
+ if((options & COLOUR) != 0) {
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(text);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ text = matcher.replaceFirst("\u00A7"+code+"\u00B6"+code);
+ }
+ }
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(textNoColour);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ textNoColour = matcher.replaceFirst("\u00B6"+code);
+ }
+
+ int currentLine = 0;
+ int cursorIndex = 0;
+ for(; cursorIndex<textNoColour.length(); cursorIndex++) {
+ if(currentLine == lineNum) break;
+ if(textNoColour.charAt(cursorIndex) == '\n') {
+ currentLine++;
+ }
+ }
+
+
+ String textNC = textNoColour.substring(0, cursorIndex);
+ int colorCodes = org.apache.commons.lang3.StringUtils.countMatches(textNC, "\u00B6");
+ String line = text.substring(cursorIndex+(((options & COLOUR) != 0)?colorCodes*2:0)).split("\n")[0];
+ int padding = Math.min(5, searchBarXSize-strLenNoColor(line))/2;
+ String trimmed = Minecraft.getMinecraft().fontRendererObj.trimStringToWidth(line, xComp-padding);
+ int linePos = strLenNoColor(trimmed);
+ if(linePos != strLenNoColor(line)) {
+ char after = line.charAt(linePos);
+ int trimmedWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(trimmed);
+ int charWidth = Minecraft.getMinecraft().fontRendererObj.getCharWidth(after);
+ if(trimmedWidth + charWidth/2 < xComp-padding) {
+ linePos++;
+ }
+ }
+ cursorIndex += linePos;
+
+ int pre = StringUtils.cleanColour(prependText).length();
+ if(cursorIndex < pre) {
+ cursorIndex = 0;
+ } else {
+ cursorIndex -= pre;
+ }
+
+ return cursorIndex;
+ }
+
+ public void mouseClicked(int mouseX, int mouseY, int mouseButton) {
+ if(mouseButton == 1) {
+ textField.setText("");
+ } else {
+ textField.setCursorPosition(getCursorPos(mouseX, mouseY));
+ }
+ focus = true;
+ }
+
+ public void unfocus() {
+ focus = false;
+ textField.setSelectionPos(textField.getCursorPosition());
+ }
+
+ public int strLenNoColor(String str) {
+ return str.replaceAll("(?i)\\u00A7.", "").length();
+ }
+
+ public void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
+ if(focus) {
+ textField.setSelectionPos(getCursorPos(mouseX, mouseY));
+ }
+ }
+
+ public void keyTyped(char typedChar, int keyCode) {
+ if(focus) {
+ if((options & MULTILINE) != 0) { //Carriage return
+ Pattern patternControlCode = Pattern.compile("(?i)\\u00A7([^\\u00B6\n])(?!\\u00B6)");
+
+ String text = textField.getText();
+ String textNoColour = textField.getText();
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(text);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ text = matcher.replaceFirst("\u00A7"+code+"\u00B6"+code);
+ }
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(textNoColour);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ textNoColour = matcher.replaceFirst("\u00B6"+code);
+ }
+
+ if(keyCode == 28) {
+ String before = textField.getText().substring(0, textField.getCursorPosition());
+ String after = textField.getText().substring(textField.getCursorPosition());
+ int pos = textField.getCursorPosition();
+ textField.setText(before + "\n" + after);
+ textField.setCursorPosition(pos+1);
+ return;
+ } else if(keyCode == 200) { //Up
+ String textNCBeforeCursor = textNoColour.substring(0, textField.getSelectionEnd());
+ int colorCodes = org.apache.commons.lang3.StringUtils.countMatches(textNCBeforeCursor, "\u00B6");
+ String textBeforeCursor = text.substring(0, textField.getSelectionEnd()+colorCodes*2);
+
+ int numLinesBeforeCursor = org.apache.commons.lang3.StringUtils.countMatches(textBeforeCursor, "\n");
+
+ String[] split = textBeforeCursor.split("\n");
+ int textBeforeCursorWidth;
+ String lineBefore;
+ String thisLineBeforeCursor;
+ if(split.length == numLinesBeforeCursor && split.length > 0) {
+ textBeforeCursorWidth = 0;
+ lineBefore = split[split.length-1];
+ thisLineBeforeCursor = "";
+ } else if(split.length > 1) {
+ thisLineBeforeCursor = split[split.length-1];
+ lineBefore = split[split.length-2];
+ textBeforeCursorWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(thisLineBeforeCursor);
+ } else {
+ return;
+ }
+ String trimmed = Minecraft.getMinecraft().fontRendererObj
+ .trimStringToWidth(lineBefore, textBeforeCursorWidth);
+ int linePos = strLenNoColor(trimmed);
+ if(linePos != strLenNoColor(lineBefore)) {
+ char after = lineBefore.charAt(linePos);
+ int trimmedWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(trimmed);
+ int charWidth = Minecraft.getMinecraft().fontRendererObj.getCharWidth(after);
+ if(trimmedWidth + charWidth/2 < textBeforeCursorWidth) {
+ linePos++;
+ }
+ }
+ int newPos = textField.getSelectionEnd()-strLenNoColor(thisLineBeforeCursor)
+ -strLenNoColor(lineBefore)-1+linePos;
+
+ if(GuiScreen.isShiftKeyDown()) {
+ textField.setSelectionPos(newPos);
+ } else {
+ textField.setCursorPosition(newPos);
+ }
+ } else if(keyCode == 208) { //Down
+ String textNCBeforeCursor = textNoColour.substring(0, textField.getSelectionEnd());
+ int colorCodes = org.apache.commons.lang3.StringUtils.countMatches(textNCBeforeCursor, "\u00B6");
+ String textBeforeCursor = text.substring(0, textField.getSelectionEnd()+colorCodes*2);
+
+ int numLinesBeforeCursor = org.apache.commons.lang3.StringUtils.countMatches(textBeforeCursor, "\n");
+
+ String[] split = textBeforeCursor.split("\n");
+ String thisLineBeforeCursor;
+ int textBeforeCursorWidth;
+ if(split.length == numLinesBeforeCursor) {
+ thisLineBeforeCursor = "";
+ textBeforeCursorWidth = 0;
+ } else if(split.length > 0) {
+ thisLineBeforeCursor = split[split.length-1];
+ textBeforeCursorWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(thisLineBeforeCursor);
+ } else {
+ return;
+ }
+
+ String[] split2 = textNoColour.split("\n");
+ if(split2.length > numLinesBeforeCursor+1) {
+ String lineAfter = split2[numLinesBeforeCursor+1];
+ String trimmed = Minecraft.getMinecraft().fontRendererObj
+ .trimStringToWidth(lineAfter, textBeforeCursorWidth);
+ int linePos = strLenNoColor(trimmed);
+ if(linePos != strLenNoColor(lineAfter)) {
+ char after = lineAfter.charAt(linePos);
+ int trimmedWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(trimmed);
+ int charWidth = Minecraft.getMinecraft().fontRendererObj.getCharWidth(after);
+ if(trimmedWidth + charWidth/2 < textBeforeCursorWidth) {
+ linePos++;
+ }
+ }
+ int newPos = textField.getSelectionEnd()-strLenNoColor(thisLineBeforeCursor)
+ +strLenNoColor(split2[numLinesBeforeCursor])+1+linePos;
+
+ if(GuiScreen.isShiftKeyDown()) {
+ textField.setSelectionPos(newPos);
+ } else {
+ textField.setCursorPosition(newPos);
+ }
+ }
+ }
+ }
+
+ String old = textField.getText();
+ if((options & FORCE_CAPS) != 0) typedChar = Character.toUpperCase(typedChar);
+ if((options & NO_SPACE) != 0 && typedChar == ' ') return;
+
+ textField.setFocused(true);
+ textField.textboxKeyTyped(typedChar, keyCode);
+
+ if((options & COLOUR) != 0) {
+ if(typedChar == '&') {
+ int pos = textField.getCursorPosition()-2;
+ if(pos >= 0 && pos < textField.getText().length()) {
+ if(textField.getText().charAt(pos) == '&') {
+ String before = textField.getText().substring(0, pos);
+ String after = "";
+ if(pos+2 < textField.getText().length()) {
+ after = textField.getText().substring(pos+2);
+ }
+ textField.setText(before + "\u00A7" + after);
+ textField.setCursorPosition(pos+1);
+ }
+ }
+ }
+ }
+
+ if((options & NUM_ONLY) != 0 && textField.getText().matches("[^0-9.]")) textField.setText(old);
+ }
+ }
+
+ public void render(int x, int y) {
+ this.x = x;
+ this.y = y;
+ drawTextbox(x, y, searchBarXSize, searchBarYSize, searchBarPadding, textField, focus);
+ }
+
+ private void drawTextbox(int x, int y, int searchBarXSize, int searchBarYSize, int searchBarPadding,
+ GuiTextField textField, boolean focus) {
+ ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ String renderText = prependText + textField.getText();
+
+ GlStateManager.disableLighting();
+
+ /**
+ * Search bar
+ */
+ int paddingUnscaled = searchBarPadding/scaledresolution.getScaleFactor();
+ if(paddingUnscaled < 1) paddingUnscaled = 1;
+
+ int numLines = org.apache.commons.lang3.StringUtils.countMatches(renderText, "\n")+1;
+ int extraSize = (searchBarYSize-8)/2+8;
+ int bottomTextBox = y + searchBarYSize + extraSize*(numLines-1);
+
+ int borderColour = focus ? Color.GREEN.getRGB() : Color.WHITE.getRGB();
+ if(customBorderColour != -1) {
+ borderColour = customBorderColour;
+ }
+ //bar background
+ Gui.drawRect(x - paddingUnscaled,
+ y - paddingUnscaled,
+ x + searchBarXSize + paddingUnscaled,
+ bottomTextBox + paddingUnscaled, borderColour);
+ Gui.drawRect(x,
+ y,
+ x + searchBarXSize,
+ bottomTextBox, Color.BLACK.getRGB());
+
+ //bar text
+ Pattern patternControlCode = Pattern.compile("(?i)\\u00A7([^\\u00B6\n])(?!\\u00B6)");
+
+ String text = renderText;
+ String textNoColor = renderText;
+ if((options & COLOUR) != 0) {
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(text);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ text = matcher.replaceFirst("\u00A7"+code+"\u00B6"+code);
+ }
+ }
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(textNoColor);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ textNoColor = matcher.replaceFirst("\u00B6"+code);
+ }
+
+ int xStartOffset = 5;
+ float scale = 1;
+ String[] texts = text.split("\n");
+ for(int yOffI = 0; yOffI < texts.length; yOffI++) {
+ int yOff = yOffI*extraSize;
+
+ if(isScaling() && Minecraft.getMinecraft().fontRendererObj.getStringWidth(texts[yOffI])>searchBarXSize-10) {
+ scale = (searchBarXSize-2)/(float)Minecraft.getMinecraft().fontRendererObj.getStringWidth(texts[yOffI]);
+ if(scale > 1) scale=1;
+ float newLen = Minecraft.getMinecraft().fontRendererObj.getStringWidth(texts[yOffI])*scale;
+ xStartOffset = (int)((searchBarXSize-newLen)/2f);
+
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(texts[yOffI], Minecraft.getMinecraft().fontRendererObj, x+searchBarXSize/2f,
+ y+searchBarYSize/2f+yOff, false,
+ searchBarXSize-2, Color.WHITE.getRGB());
+ } else {
+ Minecraft.getMinecraft().fontRendererObj.drawString(StringUtils.trimToWidth(texts[yOffI], searchBarXSize-10), x + 5,
+ y+(searchBarYSize-8)/2+yOff, Color.WHITE.getRGB());
+ }
+ }
+
+ if(focus && System.currentTimeMillis()%1000>500) {
+ String textNCBeforeCursor = textNoColor.substring(0, textField.getCursorPosition()+prependText.length());
+ int colorCodes = org.apache.commons.lang3.StringUtils.countMatches(textNCBeforeCursor, "\u00B6");
+ String textBeforeCursor = text.substring(0, textField.getCursorPosition()+prependText.length()+(((options & COLOUR) != 0) ? colorCodes*2 : 0));
+
+ int numLinesBeforeCursor = org.apache.commons.lang3.StringUtils.countMatches(textBeforeCursor, "\n");
+ int yOff = numLinesBeforeCursor*extraSize;
+
+ String[] split = textBeforeCursor.split("\n");
+ int textBeforeCursorWidth;
+ if(split.length <= numLinesBeforeCursor || split.length == 0) {
+ textBeforeCursorWidth = 0;
+ } else {
+ textBeforeCursorWidth = (int)(Minecraft.getMinecraft().fontRendererObj.getStringWidth(split[split.length-1])*scale);
+ }
+ Gui.drawRect(x + xStartOffset + textBeforeCursorWidth,
+ y+(searchBarYSize-8)/2-1 + yOff,
+ x + xStartOffset + textBeforeCursorWidth+1,
+ y+(searchBarYSize-8)/2+9 + yOff, Color.WHITE.getRGB());
+ }
+
+ String selectedText = textField.getSelectedText();
+ if(!selectedText.isEmpty()) {
+ System.out.println("Start");
+ int leftIndex = Math.min(textField.getCursorPosition()+prependText.length(), textField.getSelectionEnd()+prependText.length());
+ int rightIndex = Math.max(textField.getCursorPosition()+prependText.length(), textField.getSelectionEnd()+prependText.length());
+
+ float texX = 0;
+ int texY = 0;
+ boolean sectionSignPrev = false;
+ boolean ignoreNext = false;
+ boolean bold = false;
+ for(int i=0; i<textNoColor.length(); i++) {
+ if(ignoreNext) {
+ ignoreNext = false;
+ continue;
+ }
+
+ char c = textNoColor.charAt(i);
+ if(sectionSignPrev) {
+ if(c != 'k' && c != 'K'
+ && c != 'm' && c != 'M'
+ && c != 'n' && c != 'N'
+ && c != 'o' && c != 'O') {
+ bold = c == 'l' || c == 'L';
+ }
+ sectionSignPrev = false;
+ if(i < prependText.length()) continue;
+ }
+ if(c == '\u00B6') {
+ sectionSignPrev = true;
+ if(i < prependText.length()) continue;
+ }
+
+ if(c == '\n') {
+ if(i >= leftIndex && i < rightIndex) {
+ Gui.drawRect(x + xStartOffset + (int)texX,
+ y+(searchBarYSize-8)/2-1 + texY,
+ x + xStartOffset + (int)texX + 3,
+ y+(searchBarYSize-8)/2+9 + texY, Color.LIGHT_GRAY.getRGB());
+ }
+
+ texX = 0;
+ texY += extraSize;
+ continue;
+ }
+
+ //String c2 = bold ? EnumChatFormatting.BOLD.toString() : "" + c;
+
+ System.out.println("Adding len for char:"+c+":"+Integer.toHexString(c));
+ int len = Minecraft.getMinecraft().fontRendererObj.getStringWidth(String.valueOf(c));
+ if(bold) len++;
+ if(i >= leftIndex && i < rightIndex) {
+ Gui.drawRect(x + xStartOffset + (int)texX,
+ y+(searchBarYSize-8)/2-1 + texY,
+ x + xStartOffset + (int)(texX + len*scale),
+ y+(searchBarYSize-8)/2+9 + texY, Color.LIGHT_GRAY.getRGB());
+
+ TextRenderUtils.drawStringScaled(String.valueOf(c), Minecraft.getMinecraft().fontRendererObj,
+ x + xStartOffset + texX,
+ y+searchBarYSize/2f-scale*8/2f + texY, false, Color.BLACK.getRGB(), scale);
+ if(bold) {
+ TextRenderUtils.drawStringScaled(String.valueOf(c), Minecraft.getMinecraft().fontRendererObj,
+ x + xStartOffset + texX + 1,
+ y+searchBarYSize/2f-scale*8/2f + texY, false, Color.BLACK.getRGB(), scale);
+ }
+ }
+
+ texX += len*scale;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GuiScreenElementWrapper.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiScreenElementWrapper.java
new file mode 100644
index 0000000..234cc2d
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiScreenElementWrapper.java
@@ -0,0 +1,35 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import net.minecraft.client.gui.GuiScreen;
+import org.lwjgl.input.Mouse;
+
+import java.io.IOException;
+
+public class GuiScreenElementWrapper extends GuiScreen {
+
+ public final GuiElement element;
+
+ public GuiScreenElementWrapper(GuiElement element) {
+ this.element = element;
+ }
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ super.drawScreen(mouseX, mouseY, partialTicks);
+ element.render();
+ }
+
+ @Override
+ public void handleMouseInput() throws IOException {
+ super.handleMouseInput();
+ int i = Mouse.getEventX() * this.width / this.mc.displayWidth;
+ int j = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1;
+ element.mouseInput(i, j);
+ }
+
+ @Override
+ public void handleKeyboardInput() throws IOException {
+ super.handleKeyboardInput();
+ element.keyboardInput();
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/Config.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/Config.java
new file mode 100644
index 0000000..40f8c0e
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/Config.java
@@ -0,0 +1,8 @@
+package com.thatgravyboat.skyblockhud.core.config;
+
+public class Config {
+
+ public void executeRunnable(String runnableId) {
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/Position.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/Position.java
new file mode 100644
index 0000000..1c2de86
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/Position.java
@@ -0,0 +1,196 @@
+package com.thatgravyboat.skyblockhud.core.config;
+
+import com.google.gson.annotations.Expose;
+import net.minecraft.client.gui.ScaledResolution;
+
+public class Position {
+
+ @Expose
+ private int x;
+ @Expose
+ private int y;
+ @Expose
+ private boolean centerX;
+ @Expose
+ private boolean centerY;
+
+
+ private static final int EDGE_OFFSET = 0;
+
+ public Position(int x, int y) {
+ this(x, y, false, false);
+ }
+
+ public Position(int x, int y, boolean centerX, boolean centerY) {
+ this.x = x;
+ this.y = y;
+ this.centerX = centerX;
+ this.centerY = centerY;
+ }
+
+ public void set(Position other) {
+ this.x = other.x;
+ this.y = other.y;
+ this.centerX = other.centerX;
+ this.centerY = other.centerY;
+ }
+
+ public Position clone() {
+ return new Position(x, y, centerX, centerY);
+ }
+
+ public boolean isCenterX() {
+ return centerX;
+ }
+
+ public boolean isCenterY() {
+ return centerY;
+ }
+
+ public int getRawX() {
+ return x;
+ }
+
+ public int getRawY() {
+ return y;
+ }
+
+ public int getAbsX(ScaledResolution scaledResolution, int objWidth) {
+ int width = scaledResolution.getScaledWidth();
+
+ if(centerX) {
+ return width/2 + x;
+ }
+
+ int ret = x;
+ if(x < 0) {
+ ret = width + x - objWidth;
+ }
+
+ if(ret < 0) ret = 0;
+ if(ret > width - objWidth) ret = width - objWidth;
+
+ return ret;
+ }
+
+ public int getAbsY(ScaledResolution scaledResolution, int objHeight) {
+ int height = scaledResolution.getScaledHeight();
+
+ if(centerY) {
+ return height/2 + y;
+ }
+
+ int ret = y;
+ if(y < 0) {
+ ret = height + y - objHeight;
+ }
+
+ if(ret < 0) ret = 0;
+ if(ret > height - objHeight) ret = height - objHeight;
+
+ return ret;
+ }
+
+ public int moveX(int deltaX, int objWidth, ScaledResolution scaledResolution) {
+ int screenWidth = scaledResolution.getScaledWidth();
+ boolean wasPositiveX = this.x >= 0;
+ this.x += deltaX;
+
+ if(centerX) {
+ if(wasPositiveX) {
+ if(this.x > screenWidth/2-objWidth/2) {
+ deltaX += screenWidth/2-objWidth/2-this.x;
+ this.x = screenWidth/2-objWidth/2;
+ }
+ } else {
+ if(this.x < -screenWidth/2+objWidth/2) {
+ deltaX += -screenWidth/2+objWidth/2-this.x;
+ this.x = -screenWidth/2+objWidth/2;
+ }
+ }
+ return deltaX;
+ }
+
+ if(wasPositiveX) {
+ if(this.x < EDGE_OFFSET) {
+ deltaX += EDGE_OFFSET-this.x;
+ this.x = EDGE_OFFSET;
+ }
+ if(this.x > screenWidth-EDGE_OFFSET) {
+ deltaX += screenWidth-EDGE_OFFSET-this.x;
+ this.x = screenWidth-EDGE_OFFSET;
+ }
+ } else {
+ if(this.x+1 > -EDGE_OFFSET) {
+ deltaX += -EDGE_OFFSET-1-this.x;
+ this.x = -EDGE_OFFSET-1;
+ }
+ if(this.x+screenWidth < EDGE_OFFSET) {
+ deltaX += EDGE_OFFSET-screenWidth-this.x;
+ this.x = EDGE_OFFSET-screenWidth;
+ }
+ }
+
+ if(this.x >= 0 && this.x+objWidth/2 > screenWidth/2) {
+ this.x -= screenWidth - objWidth;
+ }
+ if(this.x < 0 && this.x+objWidth/2 <= -screenWidth/2) {
+ this.x += screenWidth - objWidth;
+ }
+ return deltaX;
+ }
+
+ public int moveY(int deltaY, int objHeight, ScaledResolution scaledResolution) {
+ int screenHeight = scaledResolution.getScaledHeight();
+ boolean wasPositiveY = this.y >= 0;
+ this.y += deltaY;
+
+ if(centerY) {
+ if(wasPositiveY) {
+ if(this.y > screenHeight/2-objHeight/2) {
+ deltaY += screenHeight/2-objHeight/2-this.y;
+ this.y = screenHeight/2-objHeight/2;
+ }
+ } else {
+ if(this.y < -screenHeight/2+objHeight/2) {
+ deltaY += -screenHeight/2+objHeight/2-this.y;
+ this.y = -screenHeight/2+objHeight/2;
+ }
+ }
+ return deltaY;
+ }
+
+ if(wasPositiveY) {
+ if(this.y < EDGE_OFFSET) {
+ deltaY += EDGE_OFFSET-this.y;
+ this.y = EDGE_OFFSET;
+ }
+ if(this.y > screenHeight-EDGE_OFFSET) {
+ deltaY += screenHeight-EDGE_OFFSET-this.y;
+ this.y = screenHeight-EDGE_OFFSET;
+ }
+ } else {
+ if(this.y+1 > -EDGE_OFFSET) {
+ deltaY += -EDGE_OFFSET-1-this.y;
+ this.y = -EDGE_OFFSET-1;
+ }
+ if(this.y+screenHeight < EDGE_OFFSET) {
+ deltaY += EDGE_OFFSET-screenHeight-this.y;
+ this.y = EDGE_OFFSET-screenHeight;
+ }
+ }
+
+ if(this.y >= 0 && this.y-objHeight/2 > screenHeight/2) {
+ this.y -= screenHeight - objHeight;
+ }
+ if(this.y < 0 && this.y-objHeight/2 <= -screenHeight/2) {
+ this.y += screenHeight - objHeight;
+ }
+ return deltaY;
+ }
+
+ public boolean rightAligned(ScaledResolution scaledResolution, int objWidth){
+ return this.getAbsX(scaledResolution, objWidth) > (scaledResolution.getScaledWidth() / 2);
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/Category.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/Category.java
new file mode 100644
index 0000000..e1d51ac
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/Category.java
@@ -0,0 +1,15 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Category {
+
+ String name();
+ String desc();
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigAccordionId.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigAccordionId.java
new file mode 100644
index 0000000..8e2836f
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigAccordionId.java
@@ -0,0 +1,14 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigAccordionId {
+
+ int id();
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorAccordion.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorAccordion.java
new file mode 100644
index 0000000..acb60af
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorAccordion.java
@@ -0,0 +1,14 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorAccordion {
+
+ int id();
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorBoolean.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorBoolean.java
new file mode 100644
index 0000000..b1846d3
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorBoolean.java
@@ -0,0 +1,12 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorBoolean {
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorButton.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorButton.java
new file mode 100644
index 0000000..32425d1
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorButton.java
@@ -0,0 +1,15 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorButton {
+
+ String runnableId();
+ String buttonText() default "";
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorColour.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorColour.java
new file mode 100644
index 0000000..1e5713c
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorColour.java
@@ -0,0 +1,13 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorColour {
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDraggableList.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDraggableList.java
new file mode 100644
index 0000000..963c305
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDraggableList.java
@@ -0,0 +1,14 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorDraggableList {
+
+ String[] exampleText();
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDropdown.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDropdown.java
new file mode 100644
index 0000000..5e48e25
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDropdown.java
@@ -0,0 +1,16 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorDropdown {
+
+ String[] values();
+ int initialIndex() default 0;
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorSlider.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorSlider.java
new file mode 100644
index 0000000..a0fea03
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorSlider.java
@@ -0,0 +1,18 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorSlider {
+
+ float minValue();
+ float maxValue();
+
+ float minStep();
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorText.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorText.java
new file mode 100644
index 0000000..0c00561
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorText.java
@@ -0,0 +1,13 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorText {
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigOption.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigOption.java
new file mode 100644
index 0000000..845d01f
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigOption.java
@@ -0,0 +1,17 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigOption {
+
+ String name();
+ String desc();
+ int subcategoryId() default -1;
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditor.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditor.java
new file mode 100644
index 0000000..e978420
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditor.java
@@ -0,0 +1,62 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.renderer.GlStateManager;
+
+public abstract class GuiOptionEditor {
+
+ protected final ConfigProcessor.ProcessedOption option;
+ private static final int HEIGHT = 45;
+
+ public GuiOptionEditor(ConfigProcessor.ProcessedOption option) {
+ this.option = option;
+ }
+
+ public void render(int x, int y, int width) {
+ int height = getHeight();
+
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+ RenderUtils.drawFloatingRectDark(x, y, width, height, true);
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(option.name,
+ fr, x+width/6f, y+13, true, width/3-10, 0xc0c0c0);
+
+ int maxLines = 5;
+ float scale = 1;
+ int lineCount = fr.listFormattedStringToWidth(option.desc, width*2/3-10).size();
+
+ if(lineCount <= 0) return;
+
+ float paraHeight = 9 * lineCount - 1;
+
+ while(paraHeight >= HEIGHT-10) {
+ scale -= 1/8f;
+ lineCount = fr.listFormattedStringToWidth(option.desc, (int)(width*2/3/scale-10)).size();
+ paraHeight = (int)(9*scale * lineCount - 1*scale);
+ }
+
+ GlStateManager.pushMatrix();
+ GlStateManager.translate(x+5+width/3f, y+HEIGHT/2f-paraHeight/2, 0);
+ GlStateManager.scale(scale, scale, 1);
+
+ fr.drawSplitString(option.desc, 0, 0, (int)(width*2/3/scale-10), 0xc0c0c0);
+
+ GlStateManager.popMatrix();
+ }
+
+ public int getHeight() {
+ return HEIGHT;
+ }
+
+ public abstract boolean mouseInput(int x, int y, int width, int mouseX, int mouseY);
+ public abstract boolean keyboardInput();
+
+ public boolean mouseInputOverlay(int x, int y, int width, int mouseX, int mouseY) {
+ return false;
+ }
+ public void renderOverlay(int x, int y, int width) {}
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorAccordion.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorAccordion.java
new file mode 100644
index 0000000..c2b129d
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorAccordion.java
@@ -0,0 +1,82 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.WorldRenderer;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+public class GuiOptionEditorAccordion extends GuiOptionEditor {
+
+ private int accordionId;
+ private boolean accordionToggled;
+
+ public GuiOptionEditorAccordion(ConfigProcessor.ProcessedOption option, int accordionId) {
+ super(option);
+ this.accordionToggled = (boolean) option.get();
+ this.accordionId = accordionId;
+ }
+
+ @Override
+ public int getHeight() {
+ return 20;
+ }
+
+ public int getAccordionId() {
+ return accordionId;
+ }
+
+ public boolean getToggled() {
+ return accordionToggled;
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ int height = getHeight();
+ RenderUtils.drawFloatingRectDark(x, y, width, height, true);
+
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ GlStateManager.enableBlend();
+ GlStateManager.disableTexture2D();
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
+ GlStateManager.color(1, 1, 1, 1);
+ worldrenderer.begin(GL11.GL_TRIANGLES, DefaultVertexFormats.POSITION);
+ if(accordionToggled) {
+ worldrenderer.pos((double)x+6, (double)y+6, 0.0D).endVertex();
+ worldrenderer.pos((double)x+9.75f, (double)y+13.5f, 0.0D).endVertex();
+ worldrenderer.pos((double)x+13.5f, (double)y+6, 0.0D).endVertex();
+ } else {
+ worldrenderer.pos((double)x+6, (double)y+13.5f, 0.0D).endVertex();
+ worldrenderer.pos((double)x+13.5f, (double)y+9.75f, 0.0D).endVertex();
+ worldrenderer.pos((double)x+6, (double)y+6, 0.0D).endVertex();
+ }
+ tessellator.draw();
+ GlStateManager.enableTexture2D();
+ GlStateManager.disableBlend();
+
+ TextRenderUtils.drawStringScaledMaxWidth(option.name, Minecraft.getMinecraft().fontRendererObj,
+ x+18, y+6, false, width-10, 0xc0c0c0);
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ if(Mouse.getEventButtonState() && mouseX > x && mouseX < x+ width &&
+ mouseY > y && mouseY < y+getHeight()) {
+ accordionToggled = !accordionToggled;
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorBoolean.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorBoolean.java
new file mode 100644
index 0000000..3ea70c0
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorBoolean.java
@@ -0,0 +1,38 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.GuiElementBoolean;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+
+public class GuiOptionEditorBoolean extends GuiOptionEditor {
+
+ private final GuiElementBoolean bool;
+
+ public GuiOptionEditorBoolean(ConfigProcessor.ProcessedOption option) {
+ super(option);
+
+ bool = new GuiElementBoolean(0, 0, (boolean)option.get(), 10, option::set);
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+ int height = getHeight();
+
+ bool.x = x+width/6-24;
+ bool.y = y+height-7-14;
+ bool.render();
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+ bool.x = x+width/6-24;
+ bool.y = y+height-7-14;
+ return bool.mouseInput(mouseX, mouseY);
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorButton.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorButton.java
new file mode 100644
index 0000000..752b4bf
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorButton.java
@@ -0,0 +1,64 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.ChromaColour;
+import com.thatgravyboat.skyblockhud.core.config.Config;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.input.Mouse;
+
+import static com.thatgravyboat.skyblockhud.GuiTextures.button_tex;
+
+public class GuiOptionEditorButton extends GuiOptionEditor {
+
+ private final String runnableId;
+ private String buttonText;
+ private Config config;
+
+ public GuiOptionEditorButton(ConfigProcessor.ProcessedOption option, String runnableId, String buttonText, Config config) {
+ super(option);
+ this.runnableId = runnableId;
+ this.config = config;
+
+ this.buttonText = buttonText;
+ if(this.buttonText != null && this.buttonText.isEmpty()) this.buttonText = null;
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+
+ int height = getHeight();
+
+ GlStateManager.color(1, 1, 1, 1);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(button_tex);
+ RenderUtils.drawTexturedRect(x+width/6-24, y+height-7-14, 48, 16);
+
+ if(buttonText != null) {
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(buttonText, Minecraft.getMinecraft().fontRendererObj,
+ x+width/6, y+height-7-6,
+ false, 44, 0xFF303030);
+ }
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ if(Mouse.getEventButtonState()) {
+ int height = getHeight();
+ if(mouseX > x+width/6-24 && mouseX < x+width/6+24 &&
+ mouseY > y+height-7-14 && mouseY < y+height-7+2) {
+ config.executeRunnable(runnableId);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorColour.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorColour.java
new file mode 100644
index 0000000..1f03954
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorColour.java
@@ -0,0 +1,71 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.ChromaColour;
+import com.thatgravyboat.skyblockhud.core.GuiElementColour;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.input.Mouse;
+
+import static com.thatgravyboat.skyblockhud.GuiTextures.*;
+
+public class GuiOptionEditorColour extends GuiOptionEditor {
+
+ private String chromaColour;
+ private GuiElementColour colourElement = null;
+
+ public GuiOptionEditorColour(ConfigProcessor.ProcessedOption option) {
+ super(option);
+
+ this.chromaColour = (String)option.get();
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+ int height = getHeight();
+
+ int argb = ChromaColour.specialToChromaRGB(chromaColour);
+ int r = (argb >> 16) & 0xFF;
+ int g = (argb >> 8) & 0xFF;
+ int b = argb & 0xFF;
+ GlStateManager.color(r/255f, g/255f, b/255f, 1);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(button_white);
+ RenderUtils.drawTexturedRect(x+width/6f-24, y+height-7-14, 48, 16);
+ }
+
+ @Override
+ public void renderOverlay(int x, int y, int width) {
+ if(colourElement != null) {
+ colourElement.render();
+ }
+ }
+
+ @Override
+ public boolean mouseInputOverlay(int x, int y, int width, int mouseX, int mouseY) {
+ return colourElement != null && colourElement.mouseInput(mouseX, mouseY);
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+
+ if(Mouse.getEventButtonState() && Mouse.getEventButton() == 0 &&
+ mouseX > x+width/6-24 && mouseX < x+width/6+24 &&
+ mouseY > y+height-7-14 && mouseY < y+height-7+2) {
+ colourElement = new GuiElementColour(mouseX, mouseY, (String) option.get(), (val) -> {
+ option.set(val);
+ chromaColour = val;
+ }, () -> colourElement = null);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return colourElement != null && colourElement.keyboardInput();
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDraggableList.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDraggableList.java
new file mode 100644
index 0000000..59f9e82
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDraggableList.java
@@ -0,0 +1,284 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.lerp.LerpUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.thatgravyboat.skyblockhud.GuiTextures.button_tex;
+
+public class GuiOptionEditorDraggableList extends GuiOptionEditor {
+
+ private static final ResourceLocation DELETE = new ResourceLocation("notenoughupdates:core/delete.png");
+
+ private String[] exampleText;
+ private List<Integer> activeText;
+ private int currentDragging = -1;
+ private int dragStartIndex = -1;
+
+ private long trashHoverTime = -1;
+
+ private int dragOffsetX = -1;
+ private int dragOffsetY = -1;
+
+ private boolean dropdownOpen = false;
+
+ public GuiOptionEditorDraggableList(ConfigProcessor.ProcessedOption option, String[] exampleText) {
+ super(option);
+
+ this.exampleText = exampleText;
+ this.activeText = (List<Integer>) option.get();
+ }
+
+ @Override
+ public int getHeight() {
+ int height = super.getHeight() + 13;
+
+ for(int strIndex : activeText) {
+ String str = exampleText[strIndex];
+ height += 10 * str.split("\n").length;
+ }
+
+ return height;
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+
+ int height = getHeight();
+
+ GlStateManager.color(1, 1, 1, 1);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(button_tex);
+ RenderUtils.drawTexturedRect(x+width/6f-24, y+45-7-14, 48, 16);
+
+ TextRenderUtils.drawStringCenteredScaledMaxWidth("Add", Minecraft.getMinecraft().fontRendererObj,
+ x+width/6f, y+45-7-6,
+ false, 44, 0xFF303030);
+
+ long currentTime = System.currentTimeMillis();
+ float greenBlue = LerpUtils.clampZeroOne(((trashHoverTime < 0 ? 250 : 0) + trashHoverTime - currentTime)/250f);
+ GlStateManager.color(1, greenBlue, greenBlue, 1);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(DELETE);
+ Utils.drawTexturedRect(x+width/6f+27, y+45-7-13, 11, 14, GL11.GL_NEAREST);
+
+ Gui.drawRect(x+5, y+45, x+width-5, y+height-5, 0xffdddddd);
+ Gui.drawRect(x+6, y+46, x+width-6, y+height-6, 0xff000000);
+
+ int i = 0;
+ int yOff=0;
+ for(int strIndex : activeText) {
+ String str = exampleText[strIndex];
+
+ String[] multilines = str.split("\n");
+
+ int ySize = multilines.length * 10;
+
+ if(i++ != dragStartIndex) {
+ for(int multilineIndex=0; multilineIndex<multilines.length; multilineIndex++) {
+ String line = multilines[multilineIndex];
+ Utils.drawStringScaledMaxWidth(line+EnumChatFormatting.RESET, Minecraft.getMinecraft().fontRendererObj,
+ x+20, y+50+yOff+multilineIndex*10, true, width-20, 0xffffffff);
+ }
+ Minecraft.getMinecraft().fontRendererObj.drawString("\u2261", x+10, y+50+yOff + ySize/2f - 4, 0xffffff, true);
+ }
+
+ yOff += ySize;
+ }
+ }
+
+ @Override
+ public void renderOverlay(int x, int y, int width) {
+ super.renderOverlay(x, y, width);
+
+ if(dropdownOpen) {
+ List<Integer> remaining = new ArrayList<>();
+ for(int i=0; i<exampleText.length; i++) {
+ remaining.add(i);
+ }
+ remaining.removeAll(activeText);
+
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+ int dropdownWidth = Math.min(width/2-10, 150);
+ int left = dragOffsetX;
+ int top = dragOffsetY;
+
+ int dropdownHeight = -1 + 12*remaining.size();
+
+ int main = 0xff202026;
+ int outline = 0xff404046;
+ Gui.drawRect(left, top, left+1, top+dropdownHeight, outline); //Left
+ Gui.drawRect(left+1, top, left+dropdownWidth, top+1, outline); //Top
+ Gui.drawRect(left+dropdownWidth-1, top+1, left+dropdownWidth, top+dropdownHeight, outline); //Right
+ Gui.drawRect(left+1, top+dropdownHeight-1, left+dropdownWidth-1, top+dropdownHeight, outline); //Bottom
+ Gui.drawRect(left+1, top+1, left+dropdownWidth-1, top+dropdownHeight-1, main); //Middle
+
+ int dropdownY = -1;
+ for(int strIndex : remaining) {
+ String str = exampleText[strIndex];
+ if(str.isEmpty()) {
+ str = "<NONE>";
+ }
+ TextRenderUtils.drawStringScaledMaxWidth(str.replaceAll("(\n.*)+", " ..."),
+ fr, left+3, top+3+dropdownY, false, dropdownWidth-6, 0xffa0a0a0);
+ dropdownY += 12;
+ }
+ } else if(currentDragging >= 0) {
+ int opacity = 0x80;
+ long currentTime = System.currentTimeMillis();
+ if(trashHoverTime < 0) {
+ float greenBlue = LerpUtils.clampZeroOne((currentTime + trashHoverTime)/250f);
+ opacity = (int)(opacity * greenBlue);
+ } else {
+ float greenBlue = LerpUtils.clampZeroOne((250 + trashHoverTime - currentTime)/250f);
+ opacity = (int)(opacity * greenBlue);
+ }
+
+ if(opacity < 20) return;
+
+ ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ int mouseX = Mouse.getX() * scaledResolution.getScaledWidth() / Minecraft.getMinecraft().displayWidth;
+ int mouseY = scaledResolution.getScaledHeight() - Mouse.getY() * scaledResolution.getScaledHeight() / Minecraft.getMinecraft().displayHeight - 1;
+
+ String str = exampleText[currentDragging];
+
+ String[] multilines = str.split("\n");
+
+ GlStateManager.enableBlend();
+ for(int multilineIndex=0; multilineIndex<multilines.length; multilineIndex++) {
+ String line = multilines[multilineIndex];
+ Utils.drawStringScaledMaxWidth(line+EnumChatFormatting.RESET, Minecraft.getMinecraft().fontRendererObj,
+ dragOffsetX + mouseX + 10, dragOffsetY + mouseY + multilineIndex*10, true, width-20, 0xffffff | (opacity << 24));
+ }
+
+ int ySize = multilines.length * 10;
+
+ Minecraft.getMinecraft().fontRendererObj.drawString("\u2261",
+ dragOffsetX + mouseX,
+ dragOffsetY + mouseY + ySize/2f - 4, 0xffffff, true);
+ }
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ if(!Mouse.getEventButtonState() && !dropdownOpen &&
+ dragStartIndex >= 0 && Mouse.getEventButton() == 0 &&
+ mouseX >= x+width/6+27-3 && mouseX <= x+width/6+27+11+3 &&
+ mouseY >= y+45-7-13-3 && mouseY <= y+45-7-13+14+3) {
+ activeText.remove(dragStartIndex);
+ currentDragging = -1;
+ dragStartIndex = -1;
+ return false;
+ }
+
+ if(!Mouse.isButtonDown(0) || dropdownOpen) {
+ currentDragging = -1;
+ dragStartIndex = -1;
+ if(trashHoverTime > 0) trashHoverTime = -System.currentTimeMillis();
+ } else if(currentDragging >= 0 &&
+ mouseX >= x+width/6+27-3 && mouseX <= x+width/6+27+11+3 &&
+ mouseY >= y+45-7-13-3 && mouseY <= y+45-7-13+14+3) {
+ if(trashHoverTime < 0) trashHoverTime = System.currentTimeMillis();
+ } else {
+ if(trashHoverTime > 0) trashHoverTime = -System.currentTimeMillis();
+ }
+
+ if(Mouse.getEventButtonState()) {
+ int height = getHeight();
+
+ if(dropdownOpen) {
+ List<Integer> remaining = new ArrayList<>();
+ for(int i=0; i<exampleText.length; i++) {
+ remaining.add(i);
+ }
+ remaining.removeAll(activeText);
+
+ int dropdownWidth = Math.min(width/2-10, 150);
+ int left = dragOffsetX;
+ int top = dragOffsetY;
+
+ int dropdownHeight = -1 + 12*remaining.size();
+
+ if(mouseX > left && mouseX < left+dropdownWidth &&
+ mouseY > top && mouseY < top + dropdownHeight) {
+ int dropdownY = -1;
+ for(int strIndex : remaining) {
+ if(mouseY < top+dropdownY+12) {
+ activeText.add(0, strIndex);
+ if(remaining.size() == 1) dropdownOpen = false;
+ return true;
+ }
+
+ dropdownY += 12;
+ }
+ }
+
+ dropdownOpen = false;
+ return true;
+ }
+
+ if(activeText.size() < exampleText.length &&
+ mouseX > x+width/6-24 && mouseX < x+width/6+24 &&
+ mouseY > y+45-7-14 && mouseY < y+45-7+2) {
+ dropdownOpen = !dropdownOpen;
+ dragOffsetX = mouseX;
+ dragOffsetY = mouseY;
+ return true;
+ }
+
+ if(Mouse.getEventButton() == 0 &&
+ mouseX > x+5 && mouseX < x+width-5 &&
+ mouseY > y+45 && mouseY < y+height-6) {
+ int yOff=0;
+ int i = 0;
+ for(int strIndex : activeText) {
+ int ySize = 10 * exampleText[strIndex].split("\n").length;
+ if(mouseY < y+50+yOff+ySize) {
+ dragOffsetX = x+10 - mouseX;
+ dragOffsetY = y+50+yOff - mouseY;
+
+ currentDragging = strIndex;
+ dragStartIndex = i;
+ break;
+ }
+ yOff += ySize;
+ i++;
+ }
+ }
+ } else if(Mouse.getEventButton() == -1 && currentDragging >= 0) {
+ int yOff=0;
+ int i = 0;
+ for(int strIndex : activeText) {
+ if(dragOffsetY + mouseY + 4 < y+50+yOff+10) {
+ activeText.remove(dragStartIndex);
+ activeText.add(i, currentDragging);
+
+ dragStartIndex = i;
+ break;
+ }
+ yOff += 10 * exampleText[strIndex].split("\n").length;
+ i++;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDropdown.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDropdown.java
new file mode 100644
index 0000000..5c797ad
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDropdown.java
@@ -0,0 +1,152 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+public class GuiOptionEditorDropdown extends GuiOptionEditor {
+
+ private final String[] values;
+ private final boolean useOrdinal;
+ private int selected;
+ private boolean open = false;
+
+ public GuiOptionEditorDropdown(ConfigProcessor.ProcessedOption option, String[] values, int selected, boolean useOrdinal) {
+ super(option);
+ if(selected >= values.length) selected = values.length;
+ this.values = values;
+ this.selected = selected;
+ this.useOrdinal = useOrdinal;
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+
+ if(!open) {
+ int height = getHeight();
+
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+ int dropdownWidth = Math.min(width/3-10, 80);
+ int left = x+width/6-dropdownWidth/2;
+ int top = y+height-7-14;
+
+ String selectedString = " - Select - ";
+ if(selected >= 0 && selected < values.length) {
+ selectedString = values[selected];
+ }
+
+ RenderUtils.drawFloatingRectDark(left, top, dropdownWidth, 14, false);
+ TextRenderUtils.drawStringScaled("\u25BC", fr, left+dropdownWidth-10, y+height-7-15, false, 0xffa0a0a0, 2);
+
+ TextRenderUtils.drawStringScaledMaxWidth(selectedString, fr, left+3, top+3, false,
+ dropdownWidth-16, 0xffa0a0a0);
+ }
+ }
+
+ @Override
+ public void renderOverlay(int x, int y, int width) {
+ if(open) {
+ String selectedString = " - Select - ";
+ if(selected >= 0 && selected < values.length) {
+ selectedString = values[selected];
+ }
+
+ int height = getHeight();
+
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+ int dropdownWidth = Math.min(width/3-10, 80);
+ int left = x+width/6-dropdownWidth/2;
+ int top = y+height-7-14;
+
+ int dropdownHeight = 13 + 12*values.length;
+
+ int main = 0xff202026;
+ int blue = 0xff2355ad;
+ Gui.drawRect(left, top, left+1, top+dropdownHeight, blue); //Left
+ Gui.drawRect(left+1, top, left+dropdownWidth, top+1, blue); //Top
+ Gui.drawRect(left+dropdownWidth-1, top+1, left+dropdownWidth, top+dropdownHeight, blue); //Right
+ Gui.drawRect(left+1, top+dropdownHeight-1, left+dropdownWidth-1, top+dropdownHeight, blue); //Bottom
+ Gui.drawRect(left+1, top+1, left+dropdownWidth-1, top+dropdownHeight-1, main); //Middle
+
+ Gui.drawRect(left+1, top+14-1, left+dropdownWidth-1, top+14, blue); //Bar
+
+ int dropdownY = 13;
+ for(String option : values) {
+ if(option.isEmpty()) {
+ option = "<NONE>";
+ }
+ TextRenderUtils.drawStringScaledMaxWidth(option, fr, left+3, top+3+dropdownY, false, dropdownWidth-6, 0xffa0a0a0);
+ dropdownY += 12;
+ }
+
+ TextRenderUtils.drawStringScaled("\u25B2", fr, left+dropdownWidth-10, y+height-7-15, false, 0xffa0a0a0, 2);
+
+
+ TextRenderUtils.drawStringScaledMaxWidth(selectedString, fr, left+3, top+3, false,
+ dropdownWidth-16, 0xffa0a0a0);
+ }
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+
+ int left = x+width/6-40;
+ int top = y+height-7-14;
+
+ if(Mouse.getEventButtonState() && Mouse.getEventButton() == 0) {
+ if(mouseX >= left && mouseX <= left+80 &&
+ mouseY >= top && mouseY <= top+14) {
+ open = !open;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean mouseInputOverlay(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+
+ int left = x+width/6-40;
+ int top = y+height-7-14;
+
+ if(Mouse.getEventButtonState() && Mouse.getEventButton() == 0) {
+ if(!(mouseX >= left && mouseX <= left+80 &&
+ mouseY >= top && mouseY <= top+14) && open) {
+ open = false;
+ if(mouseX >= left && mouseX <= left+80) {
+ int dropdownY = 13;
+ for(int ordinal=0; ordinal < values.length; ordinal++) {
+ if(mouseY >= top+3+dropdownY && mouseY <= top+3+dropdownY+12) {
+ selected = ordinal;
+ if(useOrdinal) {
+ option.set(selected);
+ } else {
+ option.set(values[selected]);
+ }
+ return true;
+ }
+ dropdownY += 12;
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorSlider.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorSlider.java
new file mode 100644
index 0000000..a37b952
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorSlider.java
@@ -0,0 +1,132 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.GuiElementBoolean;
+import com.thatgravyboat.skyblockhud.core.GuiElementTextField;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.GuiElementSlider;
+import net.minecraft.client.Minecraft;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+
+public class GuiOptionEditorSlider extends GuiOptionEditor {
+
+ private final GuiElementSlider slider;
+ private final GuiElementTextField textField;
+
+ public GuiOptionEditorSlider(ConfigProcessor.ProcessedOption option, float minValue, float maxValue, float minStep) {
+ super(option);
+ if(minStep < 0) minStep = 0.01f;
+
+ float floatVal = ((Number)option.get()).floatValue();
+ {
+ String strVal;
+ if(floatVal % 1 == 0) {
+ strVal = Integer.toString((int)floatVal);
+ } else {
+ strVal = Float.toString(floatVal);
+ }
+ textField = new GuiElementTextField(strVal,
+ GuiElementTextField.NO_SPACE | GuiElementTextField.NUM_ONLY | GuiElementTextField.SCALE_TEXT);
+ }
+
+ slider = new GuiElementSlider(0, 0, 80, minValue, maxValue, minStep, floatVal, (val) -> {
+ option.set(val);
+
+ String strVal;
+ if(val % 1 == 0) {
+ strVal = Integer.toString(val.intValue());
+ } else {
+ strVal = Float.toString(val);
+ strVal = strVal.replaceAll("(\\.\\d\\d\\d)(?:\\d)+", "$1");
+ strVal = strVal.replaceAll("0+$", "");
+ }
+ textField.setText(strVal);
+ });
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+ int height = getHeight();
+
+ int fullWidth = Math.min(width/3-10, 80);
+ int sliderWidth = (fullWidth-5)*3/4;
+ int textFieldWidth = (fullWidth-5)/4;
+
+ slider.x = x+width/6-fullWidth/2;
+ slider.y = y+height-7-14;
+ slider.width = sliderWidth;
+ slider.render();
+
+ if(textField.getFocus()) {
+ textField.setOptions(GuiElementTextField.NO_SPACE | GuiElementTextField.NUM_ONLY);
+ textField.setSize(Minecraft.getMinecraft().fontRendererObj.getStringWidth(textField.getText())+10,
+ 16);
+ } else {
+ textField.setSize(textFieldWidth, 16);
+ textField.setOptions(GuiElementTextField.NO_SPACE | GuiElementTextField.NUM_ONLY | GuiElementTextField.SCALE_TEXT);
+ }
+
+ textField.render(x+width/6-fullWidth/2+sliderWidth+5, y+height-7-14);
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+
+ int fullWidth = Math.min(width/3-10, 80);
+ int sliderWidth = (fullWidth-5)*3/4;
+ int textFieldWidth = (fullWidth-5)/4;
+
+ slider.x = x+width/6-fullWidth/2;
+ slider.y = y+height-7-14;
+ slider.width = sliderWidth;
+ if(slider.mouseInput(mouseX, mouseY)) {
+ textField.unfocus();
+ return true;
+ }
+
+ if(textField.getFocus()) {
+ textFieldWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(textField.getText())+10;
+ }
+
+ int textFieldX = x+width/6-fullWidth/2+sliderWidth+5;
+ int textFieldY = y+height-7-14;
+ textField.setSize(textFieldWidth, 16);
+
+ if(Mouse.getEventButtonState() && (Mouse.getEventButton() == 0 || Mouse.getEventButton() == 1)) {
+ if(mouseX > textFieldX && mouseX < textFieldX+textFieldWidth &&
+ mouseY > textFieldY && mouseY < textFieldY+16) {
+ textField.mouseClicked(mouseX, mouseY, Mouse.getEventButton());
+ return true;
+ }
+ textField.unfocus();
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ if(Keyboard.getEventKeyState() && textField.getFocus()) {
+ textField.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey());
+
+ try {
+ textField.setCustomBorderColour(0xffffffff);
+ float f = Float.parseFloat(textField.getText());
+ if(option.set(f)) {
+ slider.setValue(f);
+ } else {
+ textField.setCustomBorderColour(0xff0000ff);
+ }
+ } catch(Exception e) {
+ textField.setCustomBorderColour(0xffff0000);
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorText.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorText.java
new file mode 100644
index 0000000..e42d5b3
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorText.java
@@ -0,0 +1,84 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.GuiElementBoolean;
+import com.thatgravyboat.skyblockhud.core.GuiElementTextField;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.GuiElementSlider;
+import net.minecraft.client.Minecraft;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+
+public class GuiOptionEditorText extends GuiOptionEditor {
+
+ private final GuiElementTextField textField;
+
+ public GuiOptionEditorText(ConfigProcessor.ProcessedOption option) {
+ super(option);
+
+ textField = new GuiElementTextField((String)option.get(), 0);
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+ int height = getHeight();
+
+ int fullWidth = Math.min(width/3-10, 80);
+
+ int textFieldX = x+width/6-fullWidth/2;
+ if(textField.getFocus()) {
+ fullWidth = Math.max(fullWidth, Minecraft.getMinecraft().fontRendererObj.getStringWidth(textField.getText())+10);
+ }
+
+ textField.setSize(fullWidth, 16);
+
+ textField.render(textFieldX, y+height-7-14);
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+
+ int fullWidth = Math.min(width/3-10, 80);
+
+ int textFieldX = x+width/6-fullWidth/2;
+
+ if(textField.getFocus()) {
+ fullWidth = Math.max(fullWidth, Minecraft.getMinecraft().fontRendererObj.getStringWidth(textField.getText())+10);
+ }
+
+ int textFieldY = y+height-7-14;
+ textField.setSize(fullWidth, 16);
+
+ if(Mouse.getEventButtonState() && (Mouse.getEventButton() == 0 || Mouse.getEventButton() == 1)) {
+ if(mouseX > textFieldX && mouseX < textFieldX+fullWidth &&
+ mouseY > textFieldY && mouseY < textFieldY+16) {
+ textField.mouseClicked(mouseX, mouseY, Mouse.getEventButton());
+ return true;
+ }
+ textField.unfocus();
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ if(Keyboard.getEventKeyState() && textField.getFocus()) {
+ Keyboard.enableRepeatEvents(true);
+ textField.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey());
+
+ try {
+ textField.setCustomBorderColour(0xffffffff);
+ option.set(textField.getText());
+ } catch(Exception e) {
+ textField.setCustomBorderColour(0xffff0000);
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiPositionEditor.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiPositionEditor.java
new file mode 100644
index 0000000..e178843
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiPositionEditor.java
@@ -0,0 +1,179 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.core.config.Position;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+
+import java.io.IOException;
+
+public class GuiPositionEditor extends GuiScreen {
+
+ private final Position position;
+ private Position originalPosition;
+ private final int elementWidth;
+ private final int elementHeight;
+ private final Runnable renderCallback;
+ private final Runnable positionChangedCallback;
+ private final Runnable closedCallback;
+ private boolean clicked = false;
+ private int grabbedX = 0;
+ private int grabbedY = 0;
+
+ private int guiScaleOverride = -1;
+
+ public GuiPositionEditor(Position position, int elementWidth, int elementHeight,
+ Runnable renderCallback,
+ Runnable positionChangedCallback,
+ Runnable closedCallback) {
+ this.position = position;
+ this.originalPosition = position.clone();
+ this.elementWidth = elementWidth == -1 ? this.width : elementWidth;
+ this.elementHeight = elementHeight;
+ this.renderCallback = renderCallback;
+ this.positionChangedCallback = positionChangedCallback;
+ this.closedCallback = closedCallback;
+ }
+
+ public GuiPositionEditor withScale(int scale) {
+ this.guiScaleOverride = scale;
+ return this;
+ }
+
+ @Override
+ public void onGuiClosed() {
+ super.onGuiClosed();
+ closedCallback.run();
+ }
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ super.drawScreen(mouseX, mouseY, partialTicks);
+ ScaledResolution scaledResolution;
+ if(guiScaleOverride >= 0) {
+ scaledResolution = Utils.pushGuiScale(guiScaleOverride);
+ } else {
+ scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ }
+
+ this.width = scaledResolution.getScaledWidth();
+ this.height = scaledResolution.getScaledHeight();
+ mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth;
+ mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1;
+
+ drawDefaultBackground();
+
+ if(clicked) {
+ grabbedX += position.moveX(mouseX - grabbedX, elementWidth, scaledResolution);
+ grabbedY += position.moveY(mouseY - grabbedY, elementHeight, scaledResolution);
+ }
+
+ renderCallback.run();
+
+ int x = position.getAbsX(scaledResolution, elementWidth);
+ int y = position.getAbsY(scaledResolution, elementHeight);
+
+ if(position.isCenterX()) x -= elementWidth/2;
+ if(position.isCenterY()) y -= elementHeight/2;
+ Gui.drawRect(x, y, x+elementWidth, y+elementHeight, 0x80404040);
+
+ if(guiScaleOverride >= 0) {
+ Utils.pushGuiScale(-1);
+ }
+
+ scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ Utils.drawStringCentered("Position Editor", Minecraft.getMinecraft().fontRendererObj,
+ scaledResolution.getScaledWidth()/2f, 8, true, 0xffffff);
+ Utils.drawStringCentered("R to Reset - Arrow keys/mouse to move", Minecraft.getMinecraft().fontRendererObj,
+ scaledResolution.getScaledWidth()/2f, 18, true, 0xffffff);
+ }
+
+ @Override
+ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
+ super.mouseClicked(mouseX, mouseY, mouseButton);
+
+ if(mouseButton == 0) {
+ ScaledResolution scaledResolution;
+ if(guiScaleOverride >= 0) {
+ scaledResolution = Utils.pushGuiScale(guiScaleOverride);
+ } else {
+ scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ }
+ mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth;
+ mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1;
+
+ int x = position.getAbsX(scaledResolution, elementWidth);
+ int y = position.getAbsY(scaledResolution, elementHeight);
+ if(position.isCenterX()) x -= elementWidth/2;
+ if(position.isCenterY()) y -= elementHeight/2;
+
+ if(mouseX >= x && mouseY >= y &&
+ mouseX <= x+elementWidth && mouseY <= y+elementHeight) {
+ clicked = true;
+ grabbedX = mouseX;
+ grabbedY = mouseY;
+ }
+
+ if(guiScaleOverride >= 0) {
+ Utils.pushGuiScale(-1);
+ }
+ }
+ }
+
+ @Override
+ protected void keyTyped(char typedChar, int keyCode) throws IOException {
+ Keyboard.enableRepeatEvents(true);
+
+ if(keyCode == Keyboard.KEY_R) {
+ position.set(originalPosition);
+ } else if(!clicked) {
+ boolean shiftHeld = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT);
+ int dist = shiftHeld ? 10 : 1;
+ if(keyCode == Keyboard.KEY_DOWN) {
+ position.moveY(dist, elementHeight, new ScaledResolution(Minecraft.getMinecraft()));
+ } else if(keyCode == Keyboard.KEY_UP) {
+ position.moveY(-dist, elementHeight, new ScaledResolution(Minecraft.getMinecraft()));
+ } else if(keyCode == Keyboard.KEY_LEFT) {
+ position.moveX(-dist, elementWidth, new ScaledResolution(Minecraft.getMinecraft()));
+ } else if(keyCode == Keyboard.KEY_RIGHT) {
+ position.moveX(dist, elementWidth, new ScaledResolution(Minecraft.getMinecraft()));
+ }
+ }
+ super.keyTyped(typedChar, keyCode);
+ }
+
+ @Override
+ protected void mouseReleased(int mouseX, int mouseY, int state) {
+ super.mouseReleased(mouseX, mouseY, state);
+ clicked = false;
+ }
+
+ @Override
+ protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
+ super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick);
+
+ if(clicked) {
+ ScaledResolution scaledResolution;
+ if(guiScaleOverride >= 0) {
+ scaledResolution = Utils.pushGuiScale(guiScaleOverride);
+ } else {
+ scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ }
+ mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth;
+ mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1;
+
+ grabbedX += position.moveX(mouseX - grabbedX, elementWidth, scaledResolution);
+ grabbedY += position.moveY(mouseY - grabbedY, elementHeight, scaledResolution);
+ positionChangedCallback.run();
+
+ if(guiScaleOverride >= 0) {
+ Utils.pushGuiScale(-1);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/struct/ConfigProcessor.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/struct/ConfigProcessor.java
new file mode 100644
index 0000000..aef92d8
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/struct/ConfigProcessor.java
@@ -0,0 +1,176 @@
+package com.thatgravyboat.skyblockhud.core.config.struct;
+
+import com.google.gson.annotations.Expose;
+import com.thatgravyboat.skyblockhud.core.config.Config;
+import com.thatgravyboat.skyblockhud.core.config.annotations.*;
+import com.thatgravyboat.skyblockhud.core.config.gui.*;
+import com.thatgravyboat.skyblockhud.core.config.Config;
+
+import java.lang.reflect.Field;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+public class ConfigProcessor {
+
+ public static class ProcessedCategory {
+ public final String name;
+ public final String desc;
+ public final LinkedHashMap<String, ProcessedOption> options = new LinkedHashMap<>();
+
+ public ProcessedCategory(String name, String desc) {
+ this.name = name;
+ this.desc = desc;
+ }
+ }
+
+ public static class ProcessedOption {
+ public final String name;
+ public final String desc;
+ public final int subcategoryId;
+ public GuiOptionEditor editor;
+
+ public int accordionId = -1;
+
+ private final Field field;
+ private final Object container;
+
+ public ProcessedOption(String name, String desc, int subcategoryId, Field field, Object container) {
+ this.name = name;
+ this.desc = desc;
+ this.subcategoryId = subcategoryId;
+
+ this.field = field;
+ this.container = container;
+ }
+
+ public Object get() {
+ try {
+ return field.get(container);
+ } catch(Exception e) {
+ return null;
+ }
+ }
+
+ public boolean set(Object value) {
+ try {
+ if(field.getType() == int.class && value instanceof Number) {
+ field.set(container, ((Number)value).intValue());
+ } else {
+ field.set(container, value);
+ }
+ return true;
+ } catch(Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+ }
+
+ public static LinkedHashMap<String, ProcessedCategory> create(Config config) {
+ LinkedHashMap<String, ProcessedCategory> processedConfig = new LinkedHashMap<>();
+ for(Field categoryField : config.getClass().getDeclaredFields()) {
+ boolean exposePresent = categoryField.isAnnotationPresent(Expose.class);
+ boolean categoryPresent = categoryField.isAnnotationPresent(Category.class);
+
+ if(exposePresent && categoryPresent) {
+ Object categoryObj;
+ try {
+ categoryObj = categoryField.get(config);
+ } catch(Exception e) {
+ //System.err.printf("Failed to load config category %s. Field was not accessible.\n", categoryField.getName());
+ continue;
+ }
+
+ Category categoryAnnotation = categoryField.getAnnotation(Category.class);
+ ProcessedCategory cat = new ProcessedCategory(
+ categoryAnnotation.name(),
+ categoryAnnotation.desc()
+ );
+ processedConfig.put(categoryField.getName(), cat);
+
+ for(Field optionField : categoryObj.getClass().getDeclaredFields()) {
+ boolean optionPresent = optionField.isAnnotationPresent(ConfigOption.class);
+
+ if(optionPresent) {
+ ConfigOption optionAnnotation = optionField.getAnnotation(ConfigOption.class);
+ ProcessedOption option = new ProcessedOption(
+ optionAnnotation.name(),
+ optionAnnotation.desc(),
+ optionAnnotation.subcategoryId(),
+ optionField,
+ categoryObj
+ );
+ if(optionField.isAnnotationPresent(ConfigAccordionId.class)) {
+ ConfigAccordionId annotation = optionField.getAnnotation(ConfigAccordionId.class);
+ option.accordionId = annotation.id();
+ }
+
+ GuiOptionEditor editor = null;
+ Class<?> optionType = optionField.getType();
+ if(optionField.isAnnotationPresent(ConfigEditorButton.class)) {
+ ConfigEditorButton configEditorAnnotation = optionField.getAnnotation(ConfigEditorButton.class);
+ editor = new GuiOptionEditorButton(option, configEditorAnnotation.runnableId(), configEditorAnnotation.buttonText(), config);
+ }
+ if(optionType.isAssignableFrom(boolean.class) &&
+ optionField.isAnnotationPresent(ConfigEditorBoolean.class)) {
+ editor = new GuiOptionEditorBoolean(option);
+ }
+ if(optionType.isAssignableFrom(boolean.class) &&
+ optionField.isAnnotationPresent(ConfigEditorAccordion.class)) {
+ ConfigEditorAccordion configEditorAnnotation = optionField.getAnnotation(ConfigEditorAccordion.class);
+ editor = new GuiOptionEditorAccordion(option, configEditorAnnotation.id());
+ }
+ if(optionType.isAssignableFrom(int.class)) {
+ if(optionField.isAnnotationPresent(ConfigEditorDropdown.class)) {
+ ConfigEditorDropdown configEditorAnnotation = optionField.getAnnotation(ConfigEditorDropdown.class);
+ editor = new GuiOptionEditorDropdown(option, configEditorAnnotation.values(), (int)option.get(), true);
+ }
+ }
+ if(optionType.isAssignableFrom(List.class)) {
+ if(optionField.isAnnotationPresent(ConfigEditorDraggableList.class)) {
+ ConfigEditorDraggableList configEditorAnnotation = optionField.getAnnotation(ConfigEditorDraggableList.class);
+ editor = new GuiOptionEditorDraggableList(option, configEditorAnnotation.exampleText());
+ }
+ }
+ if(optionType.isAssignableFrom(String.class)) {
+ if(optionField.isAnnotationPresent(ConfigEditorDropdown.class)) {
+ ConfigEditorDropdown configEditorAnnotation = optionField.getAnnotation(ConfigEditorDropdown.class);
+ editor = new GuiOptionEditorDropdown(option, configEditorAnnotation.values(),
+ configEditorAnnotation.initialIndex(), false);
+ } else if(optionField.isAnnotationPresent(ConfigEditorColour.class)) {
+ editor = new GuiOptionEditorColour(option);
+ } else if(optionField.isAnnotationPresent(ConfigEditorText.class)) {
+ editor = new GuiOptionEditorText(option);
+ }
+ }
+ if(optionType.isAssignableFrom(int.class) ||
+ optionType.isAssignableFrom(float.class) ||
+ optionType.isAssignableFrom(double.class)) {
+ if(optionField.isAnnotationPresent(ConfigEditorSlider.class)) {
+ ConfigEditorSlider configEditorAnnotation = optionField.getAnnotation(ConfigEditorSlider.class);
+ editor = new GuiOptionEditorSlider(option, configEditorAnnotation.minValue(),
+ configEditorAnnotation.maxValue(), configEditorAnnotation.minStep());
+ }
+ }
+ if(optionType.isAssignableFrom(String.class)) {
+ if(optionField.isAnnotationPresent(ConfigEditorDropdown.class)) {
+ ConfigEditorDropdown configEditorAnnotation = optionField.getAnnotation(ConfigEditorDropdown.class);
+ editor = new GuiOptionEditorDropdown(option, configEditorAnnotation.values(), 0,false);
+ }
+ }
+ if(editor == null) {
+ //System.err.printf("Failed to load config option %s. Could not find suitable editor.\n", optionField.getName());
+ continue;
+ }
+ option.editor = editor;
+ cat.options.put(optionField.getName(), option);
+ }
+ }
+ } else if(exposePresent || categoryPresent) {
+ //System.err.printf("Failed to load config category %s. Both @Expose and @Category must be present.\n", categoryField.getName());
+ }
+ }
+ return processedConfig;
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/GuiElementSlider.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/GuiElementSlider.java
new file mode 100644
index 0000000..ee963c2
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/GuiElementSlider.java
@@ -0,0 +1,123 @@
+package com.thatgravyboat.skyblockhud.core.util;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.core.GuiElement;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+import java.util.function.Consumer;
+
+import static com.thatgravyboat.skyblockhud.GuiTextures.*;
+
+public class GuiElementSlider extends GuiElement {
+
+ public int x;
+ public int y;
+ public int width;
+ private static final int HEIGHT = 16;
+
+ private float minValue;
+ private float maxValue;
+ private float minStep;
+
+ private float value;
+ private Consumer<Float> setCallback;
+
+ private boolean clicked = false;
+
+ public GuiElementSlider(int x, int y, int width, float minValue, float maxValue, float minStep,
+ float value, Consumer<Float> setCallback) {
+ if(minStep < 0) minStep = 0.01f;
+
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.minValue = minValue;
+ this.maxValue = maxValue;
+ this.minStep = minStep;
+ this.value = value;
+ this.setCallback = setCallback;
+ }
+
+ public void setValue(float value) {
+ this.value = value;
+ }
+
+ @Override
+ public void render() {
+ final ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ int mouseX = Mouse.getX() * scaledResolution.getScaledWidth() / Minecraft.getMinecraft().displayWidth;
+
+ float value = this.value;
+ if(clicked) {
+ value = (mouseX-x)*(maxValue-minValue)/width+minValue;
+ value = Math.max(minValue, Math.min(maxValue, value));
+ value = Math.round(value/minStep)*minStep;
+ }
+
+ float sliderAmount = Math.max(0, Math.min(1, (value-minValue)/(maxValue-minValue)));
+ int sliderAmountI = (int)(width*sliderAmount);
+
+ GlStateManager.color(1f, 1f, 1f, 1f);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(slider_on_cap);
+ Utils.drawTexturedRect(x, y, 4, HEIGHT, GL11.GL_NEAREST);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(slider_off_cap);
+ Utils.drawTexturedRect(x+width-4, y, 4, HEIGHT, GL11.GL_NEAREST);
+
+ if(sliderAmountI > 5) {
+ Minecraft.getMinecraft().getTextureManager().bindTexture(slider_on_segment);
+ Utils.drawTexturedRect(x+4, y, sliderAmountI-4, HEIGHT, GL11.GL_NEAREST);
+ }
+
+ if(sliderAmountI < width-5) {
+ Minecraft.getMinecraft().getTextureManager().bindTexture(slider_off_segment);
+ Utils.drawTexturedRect(x+sliderAmountI, y, width-4-sliderAmountI, HEIGHT, GL11.GL_NEAREST);
+ }
+
+ for(int i=1; i<4; i++) {
+ int notchX = x+width*i/4-1;
+ Minecraft.getMinecraft().getTextureManager().bindTexture(notchX > x+sliderAmountI ? slider_off_notch : slider_on_notch);
+ Utils.drawTexturedRect(notchX, y+(HEIGHT-4)/2f, 2, 4, GL11.GL_NEAREST);
+ }
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(slider_button_new);
+ Utils.drawTexturedRect(x+sliderAmountI-4, y, 8, HEIGHT, GL11.GL_NEAREST);
+ }
+
+ @Override
+ public boolean mouseInput(int mouseX, int mouseY) {
+ if(!Mouse.isButtonDown(0)) {
+ clicked = false;
+ }
+
+ if(Mouse.getEventButton() == 0) {
+ clicked = Mouse.getEventButtonState() && mouseX > x && mouseX < x+width && mouseY > y && mouseY < y+HEIGHT;
+ if(clicked) {
+ value = (mouseX-x)*(maxValue-minValue)/width+minValue;
+ value = Math.max(minValue, Math.min(maxValue, value));
+ value = (float)(Math.round(value/minStep)*(double)minStep);
+ setCallback.accept(value);
+ return true;
+ }
+ }
+
+ if(!Mouse.getEventButtonState() && Mouse.getEventButton() == -1 && clicked) {
+ value = (mouseX-x)*(maxValue-minValue)/width+minValue;
+ value = Math.max(minValue, Math.min(maxValue, value));
+ value = Math.round(value/minStep)*minStep;
+ setCallback.accept(value);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/MiscUtils.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/MiscUtils.java
new file mode 100644
index 0000000..da89e75
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/MiscUtils.java
@@ -0,0 +1,104 @@
+package com.thatgravyboat.skyblockhud.core.util;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.ResourceLocation;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.input.Cursor;
+import org.lwjgl.input.Mouse;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.datatransfer.StringSelection;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.IntBuffer;
+import java.nio.file.Files;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class MiscUtils {
+
+ public static void copyToClipboard(String str) {
+ Toolkit.getDefaultToolkit().getSystemClipboard()
+ .setContents(new StringSelection(str), null);
+ }
+ private static void unzip(InputStream src, File dest) {
+ //buffer for read and write data to file
+ byte[] buffer = new byte[1024];
+ try {
+ ZipInputStream zis = new ZipInputStream(src);
+ ZipEntry ze = zis.getNextEntry();
+ while(ze != null){
+ if(!ze.isDirectory()) {
+ String fileName = ze.getName();
+ File newFile = new File(dest, fileName);
+ //create directories for sub directories in zip
+ new File(newFile.getParent()).mkdirs();
+ FileOutputStream fos = new FileOutputStream(newFile);
+ int len;
+ while ((len = zis.read(buffer)) > 0) {
+ fos.write(buffer, 0, len);
+ }
+ fos.close();
+ }
+ //close this ZipEntry
+ zis.closeEntry();
+ ze = zis.getNextEntry();
+ }
+ //close last ZipEntry
+ zis.closeEntry();
+ zis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void recursiveDelete(File file) {
+ if(file.isDirectory() && !Files.isSymbolicLink(file.toPath())) {
+ for(File child : file.listFiles()) {
+ recursiveDelete(child);
+ }
+ }
+ file.delete();
+ }
+
+ private static String currentCursor = null;
+
+ public static void resetCursor() {
+ if(currentCursor == null) {
+ return;
+ }
+ currentCursor = null;
+ try { Mouse.setNativeCursor(null); } catch(Exception ignored) {}
+ }
+
+ public static void setCursor(ResourceLocation loc, int hotspotX, int hotspotY) {
+ if(currentCursor != null && loc.getResourcePath().equals(currentCursor)) {
+ return;
+ }
+ currentCursor = loc.getResourcePath();
+ try {
+ BufferedImage image = ImageIO.read(Minecraft.getMinecraft()
+ .getResourceManager().getResource(loc).getInputStream());
+ int maxSize = Cursor.getMaxCursorSize();
+ IntBuffer buffer = BufferUtils.createIntBuffer(maxSize*maxSize);
+ for(int i=0; i<maxSize*maxSize; i++) {
+ int cursorX = i%maxSize;
+ int cursorY = i/maxSize;
+ if(cursorX >= image.getWidth() || cursorY >= image.getHeight()) {
+ buffer.put(0x00000000);
+ } else {
+ buffer.put(image.getRGB(cursorX, image.getHeight()-1-cursorY));
+ }
+ }
+ buffer.flip();
+ Mouse.setNativeCursor(new Cursor(maxSize, maxSize, hotspotX, hotspotY, 1,
+ buffer, null));
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/Splitters.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/Splitters.java
new file mode 100644
index 0000000..9736803
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/Splitters.java
@@ -0,0 +1,10 @@
+package com.thatgravyboat.skyblockhud.core.util;
+
+import com.google.common.base.Splitter;
+
+public class Splitters {
+
+ public static final Splitter NEWLINE_SPLITTER = Splitter.on('\n');
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/StringUtils.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/StringUtils.java
new file mode 100644
index 0000000..d8fd0e6
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/StringUtils.java
@@ -0,0 +1,39 @@
+package com.thatgravyboat.skyblockhud.core.util;
+
+import com.google.common.collect.Sets;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+
+import java.util.Set;
+
+public class StringUtils {
+
+ public static final Set<String> PROTOCOLS = Sets.newHashSet("http", "https");
+
+ public static String cleanColour(String in) {
+ return in.replaceAll("(?i)\\u00A7.", "");
+ }
+
+ public static String cleanColourNotModifiers(String in) {
+ return in.replaceAll("(?i)\\u00A7[0-9a-f]", "\u00A7r");
+ }
+
+ public static String trimToWidth(String str, int len) {
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+ String trim = fr.trimStringToWidth(str, len);
+
+ if(str.length() != trim.length() && !trim.endsWith(" ")) {
+ char next = str.charAt(trim.length());
+ if(next != ' ') {
+ String[] split = trim.split(" ");
+ String last = split[split.length-1];
+ if(last.length() < 8) {
+ trim = trim.substring(0, trim.length()-last.length());
+ }
+ }
+ }
+
+ return trim;
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpUtils.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpUtils.java
new file mode 100644
index 0000000..e742ab1
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpUtils.java
@@ -0,0 +1,26 @@
+package com.thatgravyboat.skyblockhud.core.util.lerp;
+
+public class LerpUtils {
+
+ public static float clampZeroOne(float f) {
+ return Math.max(0, Math.min(1, f));
+ }
+
+ public static float sigmoid(float val) {
+ return (float)(1/(1 + Math.exp(-val)));
+ }
+
+ private static final float sigmoidStr = 8;
+ private static final float sigmoidA = -1/(sigmoid(-0.5f * sigmoidStr) - sigmoid(0.5f * sigmoidStr));
+ private static final float sigmoidB = sigmoidA*sigmoid(-0.5f * sigmoidStr);
+ public static float sigmoidZeroOne(float f) {
+ f = clampZeroOne(f);
+ return sigmoidA*sigmoid(sigmoidStr*(f-0.5f))-sigmoidB;
+ }
+
+ public static float lerp(float a, float b, float amount) {
+ return b + (a - b) * clampZeroOne(amount);
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingFloat.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingFloat.java
new file mode 100644
index 0000000..4831956
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingFloat.java
@@ -0,0 +1,68 @@
+package com.thatgravyboat.skyblockhud.core.util.lerp;
+
+public class LerpingFloat {
+
+ private int timeSpent;
+ private long lastMillis;
+ private int timeToReachTarget;
+
+ private float targetValue;
+ private float lerpValue;
+
+ public LerpingFloat(float initialValue, int timeToReachTarget) {
+ this.targetValue = this.lerpValue = initialValue;
+ this.timeToReachTarget = timeToReachTarget;
+ }
+
+ public LerpingFloat(int initialValue) {
+ this(initialValue, 200);
+ }
+
+ public void tick() {
+ int lastTimeSpent = timeSpent;
+ this.timeSpent += System.currentTimeMillis() - lastMillis;
+
+ float lastDistPercentToTarget = lastTimeSpent/(float)timeToReachTarget;
+ float distPercentToTarget = timeSpent/(float)timeToReachTarget;
+ float fac = (1-lastDistPercentToTarget)/lastDistPercentToTarget;
+
+ float startValue = lerpValue - (targetValue - lerpValue)/fac;
+
+ float dist = targetValue - startValue;
+ if(dist == 0) return;
+
+ float oldLerpValue = lerpValue;
+ if(distPercentToTarget >= 1) {
+ lerpValue = targetValue;
+ } else {
+ lerpValue = startValue + dist*distPercentToTarget;
+ }
+
+ if(lerpValue == oldLerpValue) {
+ timeSpent = lastTimeSpent;
+ } else {
+ this.lastMillis = System.currentTimeMillis();
+ }
+ }
+
+ public void resetTimer() {
+ this.timeSpent = 0;
+ this.lastMillis = System.currentTimeMillis();
+ }
+
+ public void setTarget(float targetValue) {
+ this.targetValue = targetValue;
+ }
+
+ public void setValue(float value) {
+ this.targetValue = this.lerpValue = value;
+ }
+
+ public float getValue() {
+ return lerpValue;
+ }
+
+ public float getTarget() {
+ return targetValue;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingInteger.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingInteger.java
new file mode 100644
index 0000000..48ae5ad
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingInteger.java
@@ -0,0 +1,76 @@
+package com.thatgravyboat.skyblockhud.core.util.lerp;
+
+public class LerpingInteger {
+
+ private int timeSpent;
+ private long lastMillis;
+ private int timeToReachTarget;
+
+ private int targetValue;
+ private int lerpValue;
+
+ public LerpingInteger(int initialValue, int timeToReachTarget) {
+ this.targetValue = this.lerpValue = initialValue;
+ this.timeToReachTarget = timeToReachTarget;
+ }
+
+ public LerpingInteger(int initialValue) {
+ this(initialValue, 200);
+ }
+
+ public void tick() {
+ int lastTimeSpent = timeSpent;
+ this.timeSpent += System.currentTimeMillis() - lastMillis;
+
+ float lastDistPercentToTarget = lastTimeSpent/(float)timeToReachTarget;
+ float distPercentToTarget = timeSpent/(float)timeToReachTarget;
+ float fac = (1-lastDistPercentToTarget)/lastDistPercentToTarget;
+
+ int startValue = lerpValue - (int)((targetValue - lerpValue)/fac);
+
+ int dist = targetValue - startValue;
+ if(dist == 0) return;
+
+ int oldLerpValue = lerpValue;
+ if(distPercentToTarget >= 1) {
+ lerpValue = targetValue;
+ } else {
+ lerpValue = startValue + (int)(dist*distPercentToTarget);
+ }
+
+ if(lerpValue == oldLerpValue) {
+ timeSpent = lastTimeSpent;
+ } else {
+ this.lastMillis = System.currentTimeMillis();
+ }
+ }
+
+ public int getTimeSpent() {
+ return timeSpent;
+ }
+
+ public void resetTimer() {
+ this.timeSpent = 0;
+ this.lastMillis = System.currentTimeMillis();
+ }
+
+ public void setTimeToReachTarget(int timeToReachTarget) {
+ this.timeToReachTarget = timeToReachTarget;
+ }
+
+ public void setTarget(int targetValue) {
+ this.targetValue = targetValue;
+ }
+
+ public void setValue(int value) {
+ this.targetValue = this.lerpValue = value;
+ }
+
+ public int getValue() {
+ return lerpValue;
+ }
+
+ public int getTarget() {
+ return targetValue;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/RenderUtils.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/RenderUtils.java
new file mode 100644
index 0000000..95d1b05
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/RenderUtils.java
@@ -0,0 +1,165 @@
+package com.thatgravyboat.skyblockhud.core.util.render;
+
+import com.thatgravyboat.skyblockhud.core.BackgroundBlur;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.OpenGlHelper;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.WorldRenderer;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL14;
+
+public class RenderUtils {
+
+ public static void drawFloatingRectDark(int x, int y, int width, int height) {
+ drawFloatingRectDark(x, y, width, height, true);
+ }
+
+ public static void drawFloatingRectDark(int x, int y, int width, int height, boolean shadow) {
+ int alpha = 0xf0000000;
+
+ if(OpenGlHelper.isFramebufferEnabled()) {
+ ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ BackgroundBlur.renderBlurredBackground(15, scaledResolution.getScaledWidth(),
+ scaledResolution.getScaledHeight(), x, y, width, height, true);
+ } else {
+ alpha = 0xff000000;
+ }
+
+ int main = alpha | 0x202026;
+ int light = 0xff303036;
+ int dark = 0xff101016;
+ Gui.drawRect(x, y, x+1, y+height, light); //Left
+ Gui.drawRect(x+1, y, x+width, y+1, light); //Top
+ Gui.drawRect(x+width-1, y+1, x+width, y+height, dark); //Right
+ Gui.drawRect(x+1, y+height-1, x+width-1, y+height, dark); //Bottom
+ Gui.drawRect(x+1, y+1, x+width-1, y+height-1, main); //Middle
+ if(shadow) {
+ Gui.drawRect(x+width, y+2, x+width+2, y+height+2, 0x70000000); //Right shadow
+ Gui.drawRect(x+2, y+height, x+width, y+height+2, 0x70000000); //Bottom shadow
+ }
+ }
+
+ public static void drawFloatingRect(int x, int y, int width, int height) {
+ drawFloatingRectWithAlpha(x, y, width, height, 0xFF, true);
+ }
+
+ public static void drawFloatingRectWithAlpha(int x, int y, int width, int height, int alpha, boolean shadow) {
+ int main = (alpha << 24) | 0xc0c0c0;
+ int light = (alpha << 24) | 0xf0f0f0;
+ int dark = (alpha << 24) | 0x909090;
+ Gui.drawRect(x, y, x+1, y+height, light); //Left
+ Gui.drawRect(x+1, y, x+width, y+1, light); //Top
+ Gui.drawRect(x+width-1, y+1, x+width, y+height, dark); //Right
+ Gui.drawRect(x+1, y+height-1, x+width-1, y+height, dark); //Bottom
+ Gui.drawRect(x+1, y+1, x+width-1, y+height-1, main); //Middle
+ if(shadow) {
+ Gui.drawRect(x+width, y+2, x+width+2, y+height+2, (alpha*3/5) << 24); //Right shadow
+ Gui.drawRect(x+2, y+height, x+width, y+height+2, (alpha*3/5) << 24); //Bottom shadow
+ }
+ }
+
+ public static void drawTexturedModalRect(int x, int y, int textureX, int textureY, int width, int height){
+ double f = 0.00390625;
+ double f1 = 0.00390625;
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX);
+ worldrenderer.pos(x + 0.0, y + height, 0.0).tex((textureX + 0.0) * f, (textureY + height) * f1).endVertex();
+ worldrenderer.pos(x + width, y + height, 0.0).tex((textureX + width) * f, (textureY + height) * f1).endVertex();
+ worldrenderer.pos(x + width, y + 0.0, 0.0).tex((textureX + width) * f, (textureY + 0.0) * f1).endVertex();
+ worldrenderer.pos(x + 0.0, y + 0.0, 0.0).tex((textureX + 0.0) * f, (textureY + 0.0) * f1).endVertex();
+ tessellator.draw();
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height) {
+ drawTexturedRect(x, y, width, height, 0, 1, 0 , 1);
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height, int filter) {
+ drawTexturedRect(x, y, width, height, 0, 1, 0 , 1, filter);
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height, float uMin, float uMax, float vMin, float vMax) {
+ drawTexturedRect(x, y, width, height, uMin, uMax, vMin , vMax, GL11.GL_NEAREST);
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height, float uMin, float uMax, float vMin, float vMax, int filter) {
+ GlStateManager.enableBlend();
+ GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ drawTexturedRectNoBlend(x, y, width, height, uMin, uMax, vMin, vMax, filter);
+
+ GlStateManager.disableBlend();
+ }
+
+ public static void drawTexturedRectNoBlend(float x, float y, float width, float height, float uMin, float uMax, float vMin, float vMax, int filter) {
+ GlStateManager.enableTexture2D();
+
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter);
+
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX);
+ worldrenderer
+ .pos(x, y+height, 0.0D)
+ .tex(uMin, vMax).endVertex();
+ worldrenderer
+ .pos(x+width, y+height, 0.0D)
+ .tex(uMax, vMax).endVertex();
+ worldrenderer
+ .pos(x+width, y, 0.0D)
+ .tex(uMax, vMin).endVertex();
+ worldrenderer
+ .pos(x, y, 0.0D)
+ .tex(uMin, vMin).endVertex();
+ tessellator.draw();
+
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
+ }
+
+ public static void drawGradientRect(int zLevel, int left, int top, int right, int bottom, int startColor, int endColor) {
+ float startAlpha = (float)(startColor >> 24 & 255) / 255.0F;
+ float startRed = (float)(startColor >> 16 & 255) / 255.0F;
+ float startGreen = (float)(startColor >> 8 & 255) / 255.0F;
+ float startBlue = (float)(startColor & 255) / 255.0F;
+ float endAlpha = (float)(endColor >> 24 & 255) / 255.0F;
+ float endRed = (float)(endColor >> 16 & 255) / 255.0F;
+ float endGreen = (float)(endColor >> 8 & 255) / 255.0F;
+ float endBlue = (float)(endColor & 255) / 255.0F;
+
+ GlStateManager.disableTexture2D();
+ GlStateManager.enableBlend();
+ GlStateManager.disableAlpha();
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
+ GlStateManager.shadeModel(7425);
+
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ worldrenderer.begin(7, DefaultVertexFormats.POSITION_COLOR);
+ worldrenderer.pos(right, top, zLevel).color(startRed, startGreen, startBlue, startAlpha).endVertex();
+ worldrenderer.pos(left, top, zLevel).color(startRed, startGreen, startBlue, startAlpha).endVertex();
+ worldrenderer.pos(left, bottom, zLevel).color(endRed, endGreen, endBlue, endAlpha).endVertex();
+ worldrenderer.pos(right, bottom, zLevel).color(endRed, endGreen, endBlue, endAlpha).endVertex();
+ tessellator.draw();
+
+ GlStateManager.shadeModel(7424);
+ GlStateManager.disableBlend();
+ GlStateManager.enableAlpha();
+ GlStateManager.enableTexture2D();
+ }
+
+ public static void drawInnerBox(int left, int top, int width, int height){
+ Gui.drawRect(left, top, left+width, top+height, 0x6008080E); //Middle
+ Gui.drawRect(left, top, left+1, top+height, 0xff08080E); //Left
+ Gui.drawRect(left, top, left+width, top+1, 0xff08080E); //Top
+ Gui.drawRect(left+width-1, top, left+width, top+height, 0xff28282E); //Right
+ Gui.drawRect(left, top+height-1, left+width, top+height, 0xff28282E); //Bottom
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/TextRenderUtils.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/TextRenderUtils.java
new file mode 100644
index 0000000..fce2f82
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/TextRenderUtils.java
@@ -0,0 +1,215 @@
+package com.thatgravyboat.skyblockhud.core.util.render;
+
+import com.thatgravyboat.skyblockhud.core.util.StringUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.RenderHelper;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import org.lwjgl.opengl.GL11;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TextRenderUtils {
+
+ public static int getCharVertLen(char c) {
+ if("acegmnopqrsuvwxyz".indexOf(c) >= 0) {
+ return 5;
+ } else {
+ return 7;
+ }
+ }
+
+ public static float getVerticalHeight(String str) {
+ str = StringUtils.cleanColour(str);
+ float height = 0;
+ for(int i=0; i<str.length(); i++) {
+ char c = str.charAt(i);
+ int charHeight = getCharVertLen(c);
+ height += charHeight + 1.5f;
+ }
+ return height;
+ }
+
+ public static void drawStringVertical(String str, FontRenderer fr, float x, float y, boolean shadow, int colour) {
+ String format = FontRenderer.getFormatFromString(str);
+ str = StringUtils.cleanColour(str);
+ for(int i=0; i<str.length(); i++) {
+ char c = str.charAt(i);
+
+ int charHeight = getCharVertLen(c);
+ int charWidth = fr.getCharWidth(c);
+ fr.drawString(format+c, x+(5-charWidth)/2f, y-7+charHeight, colour, shadow);
+
+ y += charHeight + 1.5f;
+ }
+ }
+
+ public static void drawStringScaledMaxWidth(String str, FontRenderer fr, float x, float y, boolean shadow, int len, int colour) {
+ int strLen = fr.getStringWidth(str);
+ float factor = len/(float)strLen;
+ factor = Math.min(1, factor);
+
+ drawStringScaled(str, fr, x, y, shadow, colour, factor);
+ }
+
+ public static void drawStringCentered(String str, FontRenderer fr, float x, float y, boolean shadow, int colour) {
+ int strLen = fr.getStringWidth(str);
+
+ float x2 = x - strLen/2f;
+ float y2 = y - fr.FONT_HEIGHT/2f;
+
+ GL11.glTranslatef(x2, y2, 0);
+ fr.drawString(str, 0, 0, colour, shadow);
+ GL11.glTranslatef(-x2, -y2, 0);
+ }
+
+ public static void drawStringScaled(String str, FontRenderer fr, float x, float y, boolean shadow, int colour, float factor) {
+ GlStateManager.scale(factor, factor, 1);
+ fr.drawString(str, x/factor, y/factor, colour, shadow);
+ GlStateManager.scale(1/factor, 1/factor, 1);
+ }
+
+ public static void drawStringCenteredScaledMaxWidth(String str, FontRenderer fr, float x, float y, boolean shadow, int len, int colour) {
+ int strLen = fr.getStringWidth(str);
+ float factor = len/(float)strLen;
+ factor = Math.min(1, factor);
+ int newLen = Math.min(strLen, len);
+
+ float fontHeight = 8*factor;
+
+ drawStringScaled(str, fr, x-newLen/2, y-fontHeight/2, shadow, colour, factor);
+ }
+
+ public static void renderToolTip(ItemStack stack, int mouseX, int mouseY, int screenWidth, int screenHeight, FontRenderer fontStd) {
+ List<String> list = stack.getTooltip(Minecraft.getMinecraft().thePlayer,
+ Minecraft.getMinecraft().gameSettings.advancedItemTooltips);
+
+ for (int i = 0; i < list.size(); ++i) {
+ if (i == 0) {
+ list.set(i, stack.getRarity().rarityColor + list.get(i));
+ } else {
+ list.set(i, EnumChatFormatting.GRAY + list.get(i));
+ }
+ }
+
+ FontRenderer font = stack.getItem().getFontRenderer(stack);
+ drawHoveringText(list, mouseX, mouseY, screenWidth, screenHeight, -1, font == null ? fontStd : font);
+ }
+
+ public static void drawHoveringText(List<String> textLines, final int mouseX, final int mouseY, final int screenWidth, final int screenHeight, final int maxTextWidth, FontRenderer font) {
+ if (!textLines.isEmpty()) {
+ GlStateManager.disableRescaleNormal();
+ RenderHelper.disableStandardItemLighting();
+ GlStateManager.disableLighting();
+ GlStateManager.disableDepth();
+ int tooltipTextWidth = 0;
+
+ for (String textLine : textLines) {
+ int textLineWidth = font.getStringWidth(textLine);
+
+ if (textLineWidth > tooltipTextWidth) {
+ tooltipTextWidth = textLineWidth;
+ }
+ }
+
+ boolean needsWrap = false;
+
+ int titleLinesCount = 1;
+ int tooltipX = mouseX + 12;
+ if (tooltipX + tooltipTextWidth + 4 > screenWidth) {
+ tooltipX = mouseX - 16 - tooltipTextWidth;
+ if (tooltipX < 4) // if the tooltip doesn't fit on the screen
+ {
+ if (mouseX > screenWidth / 2) {
+ tooltipTextWidth = mouseX - 12 - 8;
+ } else {
+ tooltipTextWidth = screenWidth - 16 - mouseX;
+ }
+ needsWrap = true;
+ }
+ }
+
+ if (maxTextWidth > 0 && tooltipTextWidth > maxTextWidth) {
+ tooltipTextWidth = maxTextWidth;
+ needsWrap = true;
+ }
+
+ if (needsWrap) {
+ int wrappedTooltipWidth = 0;
+ List<String> wrappedTextLines = new ArrayList<String>();
+ for (int i = 0; i < textLines.size(); i++) {
+ String textLine = textLines.get(i);
+ List<String> wrappedLine = font.listFormattedStringToWidth(textLine, tooltipTextWidth);
+ if (i == 0) {
+ titleLinesCount = wrappedLine.size();
+ }
+
+ for (String line : wrappedLine) {
+ int lineWidth = font.getStringWidth(line);
+ if (lineWidth > wrappedTooltipWidth) {
+ wrappedTooltipWidth = lineWidth;
+ }
+ wrappedTextLines.add(line);
+ }
+ }
+ tooltipTextWidth = wrappedTooltipWidth;
+ textLines = wrappedTextLines;
+
+ if (mouseX > screenWidth / 2) {
+ tooltipX = mouseX - 16 - tooltipTextWidth;
+ } else {
+ tooltipX = mouseX + 12;
+ }
+ }
+
+ int tooltipY = mouseY - 12;
+ int tooltipHeight = 8;
+
+ if (textLines.size() > 1) {
+ tooltipHeight += (textLines.size() - 1) * 10;
+ if (textLines.size() > titleLinesCount) {
+ tooltipHeight += 2; // gap between title lines and next lines
+ }
+ }
+
+ if (tooltipY + tooltipHeight + 6 > screenHeight) {
+ tooltipY = screenHeight - tooltipHeight - 6;
+ }
+
+ final int zLevel = 300;
+ final int backgroundColor = 0xF0100010;
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 4, tooltipX + tooltipTextWidth + 3, tooltipY - 3, backgroundColor, backgroundColor);
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY + tooltipHeight + 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 4, backgroundColor, backgroundColor);
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor);
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 4, tooltipY - 3, tooltipX - 3, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor);
+ RenderUtils.drawGradientRect(zLevel, tooltipX + tooltipTextWidth + 3, tooltipY - 3, tooltipX + tooltipTextWidth + 4, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor);
+ final int borderColorStart = 0x505000FF;
+ final int borderColorEnd = (borderColorStart & 0xFEFEFE) >> 1 | borderColorStart & 0xFF000000;
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3 + 1, tooltipX - 3 + 1, tooltipY + tooltipHeight + 3 - 1, borderColorStart, borderColorEnd);
+ RenderUtils.drawGradientRect(zLevel, tooltipX + tooltipTextWidth + 2, tooltipY - 3 + 1, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3 - 1, borderColorStart, borderColorEnd);
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY - 3 + 1, borderColorStart, borderColorStart);
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY + tooltipHeight + 2, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, borderColorEnd, borderColorEnd);
+
+ for (int lineNumber = 0; lineNumber < textLines.size(); ++lineNumber) {
+ String line = textLines.get(lineNumber);
+ font.drawStringWithShadow(line, (float) tooltipX, (float) tooltipY, -1);
+
+ if (lineNumber + 1 == titleLinesCount) {
+ tooltipY += 2;
+ }
+
+ tooltipY += 10;
+ }
+
+ GlStateManager.enableLighting();
+ GlStateManager.enableDepth();
+ RenderHelper.enableStandardItemLighting();
+ GlStateManager.enableRescaleNormal();
+ }
+ GlStateManager.disableLighting();
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/dungeons/Classes.java b/src/main/java/com/thatgravyboat/skyblockhud/dungeons/Classes.java
new file mode 100644
index 0000000..14634d8
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/dungeons/Classes.java
@@ -0,0 +1,50 @@
+package com.thatgravyboat.skyblockhud.dungeons;
+
+public enum Classes {
+
+ H("Healer", "H", 154),
+ M("Mage", "M", 90),
+ B("Berserk", "B", 122),
+ A("Archer", "A", 58),
+ T("Tank", "T", 186);
+
+ private final String displayName;
+ private final String classId;
+ private final int textureY;
+
+ Classes(String name, String id, int textureY)
+ {
+ this.displayName = name;
+ this.classId = id;
+ this.textureY = textureY;
+ }
+
+ public String getDisplayName() {
+ return this.displayName;
+ }
+
+ public String getClassId() {
+ return this.classId;
+ }
+
+ public int getTextureY() {
+ return this.textureY;
+ }
+
+ public static Classes findClass(String input){
+ if (input.length() == 1){
+ try{
+ return Classes.valueOf(input.toUpperCase());
+ } catch (IllegalArgumentException ignored){}
+ } else if (input.length() == 3){
+ try{
+ return Classes.valueOf(input.replace("[", "").replace("]", "").toUpperCase());
+ } catch (IllegalArgumentException ignored){}
+ } else {
+ for(Classes clazz : Classes.values()){
+ if (clazz.displayName.equalsIgnoreCase(input)) return clazz;
+ }
+ }
+ return B;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/dungeons/DungeonHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/dungeons/DungeonHandler.java
new file mode 100644
index 0000000..4524c0f
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/dungeons/DungeonHandler.java
@@ -0,0 +1,167 @@
+package com.thatgravyboat.skyblockhud.dungeons;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.api.events.SidebarLineUpdateEvent;
+import com.thatgravyboat.skyblockhud.api.events.SidebarPostEvent;
+import com.thatgravyboat.skyblockhud.location.LocationHandler;
+import com.thatgravyboat.skyblockhud.location.Locations;
+import net.minecraft.client.Minecraft;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DungeonHandler {
+ private static final HashMap<String, DungeonPlayer> dungeonPlayersMap = new HashMap<>();
+ private static int dungeonTime = 0;
+ private static int dungeonCleared = 0;
+ private static boolean bloodKey = false;
+ private static int witherKeys = 0;
+ private static int maxSecrets = 0;
+ private static int secrets = 0;
+ private static int totalSecrets = 0;
+ private static int deaths = 0;
+ private static int crypts = 0;
+
+ private static final Pattern DungeonPlayerRegex = Pattern.compile("^\\[([HMBAT])] ([\\w]+) ([0-9]+|DEAD)$");
+
+ @SubscribeEvent
+ public void onSidebarLineUpdate(SidebarLineUpdateEvent event){
+ if (LocationHandler.getCurrentLocation().equals(Locations.CATACOMBS)) {
+ DungeonHandler.checkForDungeonTime(event.formattedLine);
+ DungeonHandler.checkForDungeonCleared(event.formattedLine);
+ DungeonHandler.checkForDungeonKeys(event.formattedLine, event.rawLine);
+ DungeonHandler.checkForDungeonPlayers(event.formattedLine, Minecraft.getMinecraft());
+ }
+ }
+
+ @SubscribeEvent
+ public void onSidebarPost(SidebarPostEvent event){
+ if (!LocationHandler.getCurrentLocation().equals(Locations.CATACOMBS)) {
+ DungeonHandler.clearDungeonStats();
+ }
+ }
+
+ public static void checkForDungeonPlayers(String scoreLine, Minecraft mc){
+ Matcher dungeonMatcher = DungeonPlayerRegex.matcher(scoreLine);
+ if (dungeonMatcher.matches() && DungeonHandler.dungeonTime > 0){
+ Classes playerClass = Classes.valueOf(dungeonMatcher.group(1));
+ String displayName = dungeonMatcher.group(2);
+ String health = dungeonMatcher.group(3);
+ if (!mc.thePlayer.getName().toLowerCase().startsWith(displayName.toLowerCase().trim())){
+ int healthNum = 0;
+ if (!health.equalsIgnoreCase("dead")){
+ try {
+ healthNum = Integer.parseInt(health);
+ } catch (NumberFormatException ignored){}
+ }
+ DungeonPlayer player = new DungeonPlayer(playerClass, displayName, healthNum, health.equalsIgnoreCase("dead"));
+ dungeonPlayersMap.put(displayName.toLowerCase(),player);
+ }
+ }
+ }
+
+ public static void checkForDungeonTime(String scoreLine){
+ if (scoreLine.toLowerCase().trim().contains("time elapsed:")){
+ String timeLine = scoreLine.toLowerCase().trim().replace("time elapsed:", "");
+ String[] times = timeLine.split("m ");
+ int time = 0;
+ try {
+ time += Integer.parseInt(times[0].replace(" ", "").replace("m", "")) * 60;
+ time += Integer.parseInt(times[1].replace(" ", "").replace("s", ""));
+ } catch (NumberFormatException ignored){}
+ dungeonTime = time;
+ }
+ }
+
+ public static void checkForDungeonCleared(String scoreline){
+ if (scoreline.toLowerCase().trim().contains("dungeon cleared:")){
+ String dungeonClearedText = scoreline.toLowerCase().trim().replace("dungeon cleared:", "").replace(" ", "").replace("%", "");
+ try{
+ dungeonCleared = Integer.parseInt(dungeonClearedText);
+ }catch (NumberFormatException ignored){ }
+ }
+ }
+
+ public static void checkForDungeonKeys(String scoreline, String rawString){
+ if (scoreline.toLowerCase().trim().contains("keys:")){
+ String dungeonClearedText = scoreline.toLowerCase().trim().replace("keys:", "").replace(" ", "").replace("x", "");
+ bloodKey = rawString.contains("\u2713");
+ try{
+ witherKeys = Integer.parseInt(dungeonClearedText);
+ }catch (NumberFormatException ignored){}
+ }
+ }
+
+ public static void parseSecrets(String statusBar){
+ boolean hasSecrets = false;
+ String[] parts = statusBar.split(" {4,}");
+ for (String part : parts) {
+ if (part.toLowerCase().contains("secrets") && !statusBar.toLowerCase().contains("no secrets")){
+ hasSecrets = true;
+ try {
+ String secret = Utils.removeColor(part.replace("Secrets", "")).replace(" ", "");
+ maxSecrets = Integer.parseInt(secret.split("/")[1]);
+ secrets = Integer.parseInt(secret.split("/")[0]);
+ }catch (NumberFormatException ignored){ }
+ }
+ }
+ if (!hasSecrets){
+ maxSecrets = 0;
+ secrets = 0;
+ }
+ }
+
+ public static void parseTotalSecrets(String playerName){
+ if (playerName.toLowerCase().contains("secrets found:")){
+ String totalSecret = Utils.removeColor(playerName.toLowerCase().replace("secrets found:", "")).replace(" ", "");
+ try {
+ totalSecrets = Integer.parseInt(totalSecret);
+ }catch (NumberFormatException ignored){}
+ }
+ }
+
+ public static void parseDeaths(String playerName){
+ if (playerName.toLowerCase().contains("deaths:")){
+ String death = Utils.removeColor(playerName.toLowerCase().replace("deaths:", "")).replace("(", "").replace(")", "").replace(" ", "");
+ try {
+ deaths = Integer.parseInt(death);
+ }catch (NumberFormatException ignored){}
+ }
+ }
+
+ public static void parseCrypts(String playerName){
+ if (playerName.toLowerCase().contains("crypts:")){
+ String crypt = Utils.removeColor(playerName.toLowerCase().replace("crypts:", "")).replace(" ", "");
+ try {
+ crypts = Integer.parseInt(crypt);
+ }catch (NumberFormatException ignored){}
+ }
+ }
+
+ public static void clearDungeonStats(){
+ dungeonPlayersMap.clear();
+ dungeonTime = 0;
+ dungeonCleared = 0;
+ bloodKey = false;
+ witherKeys = 0;
+ maxSecrets = 0;
+ secrets = 0;
+ totalSecrets = 0;
+ deaths = 0;
+ crypts = 0;
+ }
+
+ public static HashMap<String,DungeonPlayer> getDungeonPlayers(){ return dungeonPlayersMap; }
+
+ public static int getDungeonTime() { return dungeonTime; }
+ public static int getDungeonCleared() { return dungeonCleared; }
+ public static int getWitherKeys() { return witherKeys; }
+ public static boolean hasBloodkey() { return bloodKey; }
+ public static int getMaxSecrets() { return maxSecrets; }
+ public static int getSecrets() { return secrets; }
+ public static int getDeaths() { return deaths; }
+ public static int getTotalSecrets() { return totalSecrets; }
+ public static int getCrypts() { return crypts; }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/dungeons/DungeonPlayer.java b/src/main/java/com/thatgravyboat/skyblockhud/dungeons/DungeonPlayer.java
new file mode 100644
index 0000000..e34424d
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/dungeons/DungeonPlayer.java
@@ -0,0 +1,31 @@
+package com.thatgravyboat.skyblockhud.dungeons;
+
+public class DungeonPlayer {
+ private final Classes dungeonClass;
+ private final String name;
+ private final int health;
+ private final boolean dead;
+
+ public DungeonPlayer(Classes playersClass, String playersName, int playersHealth, boolean isDead) {
+ this.dungeonClass = playersClass;
+ this.name = playersName;
+ this.health = isDead ? 0 : playersHealth;
+ this.dead = isDead;
+ }
+
+ public Classes getDungeonClass() {
+ return this.dungeonClass;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public int getHealth() {
+ return this.dead ? 0 : this.health;
+ }
+
+ public boolean isDead() {
+ return this.dead;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/BossbarHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/BossbarHandler.java
new file mode 100644
index 0000000..38fa874
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/BossbarHandler.java
@@ -0,0 +1,37 @@
+package com.thatgravyboat.skyblockhud.handlers;
+
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.location.DwarvenMineHandler;
+import com.thatgravyboat.skyblockhud.location.LocationHandler;
+import com.thatgravyboat.skyblockhud.location.Locations;
+import net.minecraft.entity.boss.BossStatus;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+public class BossbarHandler {
+
+ public static boolean bossBarRendered = true;
+
+ @SubscribeEvent(priority = EventPriority.LOWEST)
+ public void onBossbarRender(RenderGameOverlayEvent.Pre event) {
+ if (event.type == RenderGameOverlayEvent.ElementType.BOSSHEALTH && BossStatus.bossName != null) {
+ bossBarRendered = !event.isCanceled();
+ if (!SkyblockHud.config.main.bossShiftHud){
+ bossBarRendered = false;
+ }
+ String bossName = Utils.removeColor(BossStatus.bossName);
+ if (SkyblockHud.config.renderer.hideBossBar && DwarvenMineHandler.currentEvent == DwarvenMineHandler.Event.NONE && !LocationHandler.getCurrentLocation().equals(Locations.CATACOMBS)){
+ if (bossName.equalsIgnoreCase("wither")){
+ event.setCanceled(true);
+ bossBarRendered = false;
+ }
+ if (bossName.toLowerCase().startsWith("objective:")){
+ event.setCanceled(true);
+ bossBarRendered = false;
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/CurrencyHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/CurrencyHandler.java
new file mode 100644
index 0000000..12b04d8
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/CurrencyHandler.java
@@ -0,0 +1,81 @@
+package com.thatgravyboat.skyblockhud.handlers;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.api.events.SidebarLineUpdateEvent;
+import com.thatgravyboat.skyblockhud.api.events.SidebarPostEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+public class CurrencyHandler {
+
+ private static int bits = 0;
+ private static double coins = 0;
+
+ public static void setBits(int amount){ bits = amount; }
+ public static void setCoins(double amount){ coins = amount; }
+ public static int getBits(){ return bits; }
+ public static double getCoins(){ return coins; }
+
+ @SubscribeEvent
+ public void onSidebarLineUpdate(SidebarLineUpdateEvent event){
+ if (Utils.removeColor(event.formattedLine.toLowerCase().trim()).contains("purse:") || Utils.removeColor(event.formattedLine.toLowerCase().trim()).contains("piggy:")) {
+ CurrencyHandler.checkCoins(event.formattedLine);
+ }
+ if (Utils.removeColor(event.formattedLine.toLowerCase().trim()).contains("bits:") && !event.formattedLine.toLowerCase().contains("(")) {
+ CurrencyHandler.checkBits(event.formattedLine);
+ }
+ }
+
+ @SubscribeEvent
+ public void onSidebarPost(SidebarPostEvent event){
+ if (!Arrays.toString(event.arrayScores).toLowerCase().contains("bits:")){
+ CurrencyHandler.setBits(0);
+ }
+ }
+
+ public static String getCoinsFormatted(){
+
+ DecimalFormat formatter = new DecimalFormat("#,###.0", DecimalFormatSymbols.getInstance(Locale.CANADA));
+ String output = formatter.format(coins);
+ if (output.equals(".0")) output = "0.0";
+ else if (output.equals(",0")) output = "0,0";
+ return output;
+ }
+
+ public static String getBitsFormatted(){
+ DecimalFormat formatter = new DecimalFormat("#.#", DecimalFormatSymbols.getInstance(Locale.CANADA));
+ formatter.setRoundingMode(RoundingMode.FLOOR);
+ return bits > 999 ? formatter.format((double)bits / 1000)+ "k" : String.valueOf(bits);
+ }
+
+ public static void checkCoins(String formatedScoreboardLine){
+ String purse = Utils.removeWhiteSpaceAndRemoveWord(
+ Utils.removeColor(formatedScoreboardLine.toLowerCase().trim()),
+ Utils.removeColor(formatedScoreboardLine.toLowerCase().trim()).contains("purse:") ? "purse:" : "piggy:"
+ ).replace(",", "");
+ if (!purse.contains("(") && !purse.contains("+")) {
+ try {
+ double coins = Double.parseDouble(Pattern.compile("[^0-9.]").matcher(purse).replaceAll(""));
+ CurrencyHandler.setCoins(coins);
+ } catch (IllegalArgumentException ex) {
+ System.out.println("Failed to parse purse, please report to ThatGravyBoat. Purse Text: " + purse);
+ }
+ }
+ }
+
+ public static void checkBits(String formatedScoreboardLine){
+ String bits = Utils.removeWhiteSpaceAndRemoveWord(Utils.removeColor(formatedScoreboardLine.toLowerCase().trim()), "bits:").replace(",", "");
+ try {
+ int bit = Integer.parseInt(Pattern.compile("[^0-9]").matcher(bits).replaceAll(""));
+ CurrencyHandler.setBits(bit);
+ } catch (IllegalArgumentException ex) {
+ System.out.println("Failed to parse bits, please report to ThatGravyBoat. Bits Text: " + bits);
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/HeldItemHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/HeldItemHandler.java
new file mode 100644
index 0000000..1a401f2
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/HeldItemHandler.java
@@ -0,0 +1,58 @@
+package com.thatgravyboat.skyblockhud.handlers;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.overlay.GenericOverlays;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+public class HeldItemHandler extends Gui {
+
+ public void drawFuelBar(Minecraft mc, int current, int max){
+ GenericOverlays.drawSmallBar(mc, 100,100,(double)current/(double)max,1.0d,0xff00ff,0xffff00, 0);
+ drawString(mc.fontRendererObj, "Fuel - " + Math.round(((double)current/(double)max)*100) + "%", 100, 100, 0xffffff);
+ }
+
+ public boolean isDrill(ItemStack stack){
+ if (stack == null) return false;
+ if (!stack.getTagCompound().hasKey("ExtraAttributes")) return false;
+ return stack.getTagCompound().getCompoundTag("ExtraAttributes").hasKey("drill_fuel");
+ }
+
+ public String getDrillFuel(ItemStack stack){
+ NBTTagCompound display = stack.getTagCompound().getCompoundTag("display");
+ NBTTagList lore = display.getTagList("Lore", 8);
+ for (int i = lore.tagCount() - 1; i >= 0; i--) {
+ String line = Utils.removeColor(lore.getStringTagAt(i));
+ if (line.trim().startsWith("Fuel:")){
+ return line;
+ }
+ }
+ return "";
+ }
+
+ @SubscribeEvent
+ public void drawOverlay(RenderGameOverlayEvent.Post event){
+ /*
+ if (Utils.overlayShouldRender(event.type, SkyblockHud.hasSkyblockScoreboard())){
+ Minecraft mc = Minecraft.getMinecraft();
+ ItemStack stack = mc.thePlayer.getHeldItem();
+ if (isDrill(stack)){
+ try {
+ String drill = getDrillFuel(stack).replace("Fuel:", "").trim();
+ String[] fuel = drill.split("/");
+ if (fuel.length == 2) {
+ int current = Integer.parseInt(fuel[0].replace(",", ""));
+ int max = Integer.parseInt(fuel[1].replace("k", "")) * 1000;
+ drawFuelBar(mc, current, max);
+ }
+ }catch (Exception ignored){}
+ }
+ }
+ */
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/MapHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/MapHandler.java
new file mode 100644
index 0000000..d2946ef
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/MapHandler.java
@@ -0,0 +1,219 @@
+package com.thatgravyboat.skyblockhud.handlers;
+
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.config.KeyBindings;
+import com.thatgravyboat.skyblockhud.config.SBHConfig;
+import com.thatgravyboat.skyblockhud.core.config.Position;
+import com.thatgravyboat.skyblockhud.handlers.mapicons.DwarvenIcons;
+import com.thatgravyboat.skyblockhud.handlers.mapicons.HubIcons;
+import com.thatgravyboat.skyblockhud.location.LocationHandler;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.settings.GameSettings;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+import org.lwjgl.opengl.GL11;
+
+import javax.vecmath.Vector2f;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static com.thatgravyboat.skyblockhud.GuiTextures.mapOverlay;
+
+public class MapHandler {
+
+ public enum MapIconTypes {
+ SHOPS,
+ MISC,
+ NPC,
+ INFO,
+ QUEST
+ }
+
+ public static class MapIcon {
+ public Vector2f position;
+ public ResourceLocation icon;
+ public String tooltip;
+ public String command;
+ public MapIconTypes type;
+
+ public MapIcon(Vector2f pos, ResourceLocation icon, String tooltip, MapIconTypes type){
+ this(pos,icon,tooltip,type,"");
+ }
+
+ public MapIcon(Vector2f pos, ResourceLocation icon, String tooltip, MapIconTypes type, String command){
+ this.position = pos;
+ this.icon = icon;
+ this.tooltip = tooltip;
+ this.type = type;
+ this.command = command;
+ }
+
+ public boolean canRender(){
+ SBHConfig.Map mapConfig = SkyblockHud.config.map;
+ if (mapConfig.showInfoIcons && type.equals(MapIconTypes.INFO)) return true;
+ else if (mapConfig.showMiscIcons && type.equals(MapIconTypes.MISC)) return true;
+ else if (mapConfig.showNpcIcons && type.equals(MapIconTypes.NPC)) return true;
+ else if (mapConfig.showQuestIcons && type.equals(MapIconTypes.QUEST)) return true;
+ else return mapConfig.showShopIcons && type.equals(MapIconTypes.SHOPS);
+ }
+ }
+
+ public enum Maps {
+ HUB(0.5f,494,444,294,218, 294,224, new ResourceLocation("skyblockhud", "maps/hub.png"), HubIcons.hubIcons),
+ MUSHROOM(1.0f,318,316,-84,605, -84,612, new ResourceLocation("skyblockhud", "maps/mushroom.png"), Collections.emptyList()),
+ SPIDERS(1.0f,270,238,400,362, 400,364, new ResourceLocation("skyblockhud", "maps/spidersden.png"), Collections.emptyList()),
+ NETHER(0.5f,257,371,436,732, 433,736, new ResourceLocation("skyblockhud", "maps/fort.png"), Collections.emptyList()),
+ BARN(1.5f,135,130,-82,320, -81,318, new ResourceLocation("skyblockhud", "maps/barn.png"), Collections.emptyList()),
+ DWARVEN(0.5f, 409, 461, 206, 160, 202, 166, new ResourceLocation("skyblockhud", "maps/dwarven.png"), DwarvenIcons.dwarvenIcons),
+ PARK(1.0f,211, 230, 480, 133, 478,134, new ResourceLocation("skyblockhud", "maps/park.png"), Collections.emptyList());
+
+ public float scaleFactor;
+ public int width;
+ public int height;
+ public int xMiniOffset;
+ public int yMiniOffset;
+ public int xOffset;
+ public int yOffset;
+ public ResourceLocation mapTexture;
+ public List<MapIcon> icons;
+
+
+ Maps(float scaleFactor, int width, int height, int xMiniOffset, int yMiniOffset, int xOffset, int yOffset, ResourceLocation mapTexture, List<MapIcon> icons){
+ this.scaleFactor = scaleFactor;
+ this.width = width;
+ this.height = height;
+ this.xMiniOffset = xMiniOffset;
+ this.yMiniOffset = yMiniOffset;
+ this.xOffset = xOffset;
+ this.yOffset = yOffset;
+ this.mapTexture = mapTexture;
+ this.icons = icons;
+ }
+ }
+
+ @SubscribeEvent
+ public void renderOverlay(RenderGameOverlayEvent.Post event) {
+ if (Utils.overlayShouldRender(event.type, SkyblockHud.hasSkyblockScoreboard(), SkyblockHud.config.map.showMiniMap)) {
+ Minecraft mc = Minecraft.getMinecraft();
+ if (mc.currentScreen instanceof MapScreen) return;
+ if (LocationHandler.getCurrentLocation().getCategory().getMap() == null) return;
+ if (mc.thePlayer != null){
+ MapHandler.Maps map = LocationHandler.getCurrentLocation().getCategory().getMap();
+ mc.renderEngine.bindTexture(mapOverlay);
+ GlStateManager.color(1.0f,1.0f, 1.0f,1.0f);
+ Position pos = SkyblockHud.config.map.miniMapPosition;
+ Gui.drawModalRectWithCustomSizedTexture(pos.getAbsX(event.resolution, 72),pos.getAbsY(event.resolution, 72),72,0,72,72,256,256);
+ mc.renderEngine.bindTexture(map.mapTexture);
+
+ int x = mc.thePlayer.getPosition().getX() + map.xMiniOffset;
+ int z = mc.thePlayer.getPosition().getZ() + map.yMiniOffset;
+ float u = (x / (map.width / 256f)) - 33f;
+ float v = (z / (map.height / 256f)) - 28f;
+
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
+
+ Gui.drawModalRectWithCustomSizedTexture(pos.getAbsX(event.resolution, 72) + 4,
+ pos.getAbsY(event.resolution, 72) + 2,
+ u,
+ v,
+ 64,64,
+ 256,256);
+
+ if (SkyblockHud.config.map.showPlayerLocation) {
+ mc.fontRendererObj.drawString("\u2022", pos.getAbsX(event.resolution, 72) + 36,pos.getAbsY(event.resolution, 72) + 34, 0xff0000, false);
+ }
+
+ GlStateManager.color(1.0f,1.0f, 1.0f,1.0f);
+ mc.renderEngine.bindTexture(mapOverlay);
+ Gui.drawModalRectWithCustomSizedTexture(pos.getAbsX(event.resolution, 72),pos.getAbsY(event.resolution, 72),0,0,72,72,256,256);
+ String keyCode = GameSettings.getKeyDisplayString(KeyBindings.map.getKeyCode());
+ Utils.drawStringCenteredScaled(keyCode, mc.fontRendererObj, pos.getAbsX(event.resolution, 64) + 58,pos.getAbsY(event.resolution, 72) + 66, false, 6,0xFFFFFF);
+ BlockPos playerPos = mc.thePlayer.getPosition();
+ String position = String.format("%d/%d/%d", playerPos.getX(), playerPos.getY(), playerPos.getZ());
+ Utils.drawStringCenteredScaled(position, mc.fontRendererObj, pos.getAbsX(event.resolution, 64) + 30,pos.getAbsY(event.resolution, 72) + 66, false, 36,0xFFFFFF);
+ GlStateManager.color(1.0f,1.0f, 1.0f,1.0f);
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public void clientTick(TickEvent.ClientTickEvent event){
+ if (KeyBindings.map.isPressed() && LocationHandler.getCurrentLocation().getCategory().getMap() != null && SkyblockHud.hasSkyblockScoreboard())
+ SkyblockHud.screenToOpen = new MapScreen();
+ }
+
+
+ public static class MapScreen extends GuiScreen {
+
+ public MapHandler.Maps map = LocationHandler.getCurrentLocation().getCategory().getMap();
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ GlStateManager.color(1.0f, 1.0f, 1.0f,1.0f);
+ this.drawWorldBackground(0);
+ this.mc.renderEngine.bindTexture(map.mapTexture);
+ float mapX = (width / 2f)-((map.width / 2f) * map.scaleFactor);
+ float mapY = (height / 2f)-((map.height / 2f) * map.scaleFactor);
+ Gui.drawModalRectWithCustomSizedTexture((int)mapX, (int)mapY, 0,0,(int)(map.width * map.scaleFactor),(int)(map.height * map.scaleFactor), (int)(map.width * map.scaleFactor), (int)(map.height * map.scaleFactor));
+ drawIcons((int)mapX, (int)mapY);
+ if (this.mc.thePlayer != null && SkyblockHud.config.map.showPlayerLocation){
+ int x = this.mc.thePlayer.getPosition().getX() + map.xOffset;
+ int z = this.mc.thePlayer.getPosition().getZ() + map.yOffset;
+ fontRendererObj.drawString("\u2022", (int)(x * map.scaleFactor + mapX), (int)(z * map.scaleFactor + mapY), 0xff0000);
+ }
+ GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
+ onTooltip(mouseX, mouseY, (int)mapX, (int)mapY);
+ }
+
+ public void drawIcons(int startX, int startY){
+ if (map.icons == null) return;
+ for (MapIcon icon : map.icons) {
+ if (!icon.canRender()) continue;
+ GlStateManager.enableBlend();
+ GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
+ this.mc.renderEngine.bindTexture(icon.icon);
+ float x = ((icon.position.x + map.xOffset) * map.scaleFactor) + startX - 4;
+ float y = ((icon.position.y + map.yOffset) * map.scaleFactor) + startY - 4;
+ Gui.drawModalRectWithCustomSizedTexture((int)x, (int)y, 0,0,8,8, 8, 8);
+ GlStateManager.color(1.0f,1.0f,1.0f,1.0f);
+ }
+ }
+
+ public void onTooltip(int mouseX, int mouseY, int startX, int startY){
+ if (map.icons == null) return;
+ for (MapIcon icon : map.icons) {
+ if (!icon.canRender()) continue;
+ if (Utils.inRangeInclusive(mouseX, (int)((icon.position.x + map.xOffset) * map.scaleFactor) + startX - 4, (int)((icon.position.x + map.xOffset) * map.scaleFactor) + startX + 4) &&
+ Utils.inRangeInclusive(mouseY, (int)((icon.position.y + map.yOffset) * map.scaleFactor) + startY - 4, (int)((icon.position.y + map.yOffset) * map.scaleFactor) + startY + 4)){
+ drawHoveringText(Arrays.asList(icon.tooltip.split("\n")), mouseX, mouseY);
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
+ int mapX = (int)((width / 2f)-((map.width / 2f) * map.scaleFactor));
+ int mapY = (int)((height / 2f)-((map.height / 2f) * map.scaleFactor));
+ for (MapIcon icon : map.icons) {
+ if (!icon.canRender()) continue;
+ if (Utils.inRangeInclusive(mouseX, (int)((icon.position.x + map.xOffset) * map.scaleFactor) + mapX - 4, (int)((icon.position.x + map.xOffset) * map.scaleFactor) + mapX + 4) &&
+ Utils.inRangeInclusive(mouseY, (int)((icon.position.y + map.yOffset) * map.scaleFactor) + mapY - 4, (int)((icon.position.y + map.yOffset) * map.scaleFactor) + mapY + 4)){
+ if (!icon.command.isEmpty()){
+ this.mc.thePlayer.sendChatMessage("/"+icon.command);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/SlayerHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/SlayerHandler.java
new file mode 100644
index 0000000..36833cc
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/SlayerHandler.java
@@ -0,0 +1,121 @@
+package com.thatgravyboat.skyblockhud.handlers;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.api.events.SidebarLineUpdateEvent;
+import com.thatgravyboat.skyblockhud.api.events.SidebarPostEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+public class SlayerHandler {
+
+ public enum slayerTypes {
+
+ ZOMBIE(34, "Revenant Horror"),
+ WOLF(42, "Sven Packmaster"),
+ SPIDER(50, "Tarantula Broodfather"),
+ VOIDGLOOMSERAPH(50, "Voidgloom Seraph"),
+ NONE(0,"");
+
+ private final String displayName;
+ private final int x;
+
+ slayerTypes(int x, String displayName){
+ this.displayName = displayName;
+ this.x = x;
+ }
+
+ public String getDisplayName() { return displayName; }
+
+ public int getX() { return x; }
+ }
+
+ public static slayerTypes currentSlayer = slayerTypes.NONE;
+ public static int slayerTier = 0;
+ public static boolean isDoingSlayer = false;
+ public static int progress = 0;
+ public static int maxKills = 0;
+ public static boolean bossSlain = false;
+ public static boolean isKillingBoss = false;
+ public static void clearSlayer(){
+ currentSlayer = slayerTypes.NONE;
+ isDoingSlayer = false;
+ progress = 0;
+ maxKills = 0;
+ bossSlain = false;
+ isKillingBoss = false;
+ }
+
+ @SubscribeEvent
+ public void onSidebarPost(SidebarPostEvent event){
+ String arrayString = Arrays.toString(event.arrayScores);
+ isDoingSlayer = Arrays.toString(event.arrayScores).contains("Slayer Quest");
+ if (isDoingSlayer && (currentSlayer.equals(slayerTypes.NONE) || !arrayString.replace(" ", "").contains(currentSlayer.getDisplayName().replace(" ", "")+Utils.intToRomanNumeral(slayerTier)))) {
+ for (int i = 0; i < event.scores.size(); i++) {
+ String line = event.scores.get(i);
+ if (line.contains("Slayer Quest") && event.scores.size() > 3){
+ String slayer = event.scores.get(i - 1).toLowerCase();
+ SlayerHandler.slayerTypes selectedSlayer = SlayerHandler.slayerTypes.NONE;
+ for (slayerTypes types : slayerTypes.values()){
+ if (slayer.contains(types.displayName.toLowerCase(Locale.ENGLISH))) {
+ selectedSlayer = types;
+ break;
+ }
+ }
+ SlayerHandler.currentSlayer = selectedSlayer;
+ SlayerHandler.slayerTier = Utils.whatRomanNumeral(slayer.replace(selectedSlayer.getDisplayName().toLowerCase(), "").replace(" ", ""));
+ break;
+ }
+ }
+ }
+
+ if (!isDoingSlayer) {
+ clearSlayer();
+ }
+ }
+
+ @SubscribeEvent
+ public void onSidebarLineUpdate(SidebarLineUpdateEvent event){
+ if (isDoingSlayer){
+ String line = event.formattedLine.toLowerCase();
+ if (line.contains("/") && (line.contains("kills") || (line.contains("xp")))){
+ SlayerHandler.bossSlain = false;
+ SlayerHandler.isKillingBoss = false;
+ String[] killsText = line.replace(" ", "").replace("kills", "").split("/");
+ if (line.contains("xp"))
+ killsText = line.replace(" ", "")
+ .replace("(", "")
+ .replace(")", "")
+ .replace("combatxp", "")
+ .split("/");
+ try {
+ progress = Integer.parseInt(killsText[0]);
+ } catch (Exception ignored){}
+ try {
+ maxKills = Integer.parseInt(killsText[1]);
+ } catch (Exception ignored){}
+ }else if(line.contains("slay the boss")) {
+ SlayerHandler.bossSlain = false;
+ SlayerHandler.isKillingBoss = true;
+ SlayerHandler.maxKills = 0;
+ SlayerHandler.progress = 0;
+ }else if (line.contains("boss slain")){
+ SlayerHandler.isKillingBoss = false;
+ SlayerHandler.maxKills = 0;
+ SlayerHandler.progress = 0;
+ SlayerHandler.bossSlain = true;
+ }
+ if (maxKills == 0 && progress == 0){
+ SlayerHandler.maxKills = 0;
+ SlayerHandler.progress = 0;
+ }
+ }
+ }
+
+
+
+
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/TimeHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/TimeHandler.java
new file mode 100644
index 0000000..06edf6b
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/TimeHandler.java
@@ -0,0 +1,29 @@
+package com.thatgravyboat.skyblockhud.handlers;
+
+import com.thatgravyboat.skyblockhud.api.events.SidebarLineUpdateEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import org.apache.logging.log4j.LogManager;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+public class TimeHandler {
+
+ public static long time;
+
+ @SubscribeEvent
+ public void onSidebarLineUpdate(SidebarLineUpdateEvent event){
+ if (Pattern.matches("([0-9]*):([0-9]*)(pm|am)", event.formattedLine.toLowerCase().trim())) {
+ boolean isPm = event.formattedLine.toLowerCase().trim().endsWith("pm");
+ SimpleDateFormat parseFormat = new SimpleDateFormat("hh:mm a", Locale.CANADA);
+ String currentTimeString = event.formattedLine.replace(" ", "").replace(isPm ? "pm" : "am", isPm ? " pm" : " am");
+ try {
+ time = (parseFormat.parse(currentTimeString).getTime() - parseFormat.parse("00:00 am").getTime()) / 1000L;
+ } catch (ParseException ignored) {
+ LogManager.getLogger().warn("timeformat error: " + currentTimeString);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/mapicons/DwarvenIcons.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/mapicons/DwarvenIcons.java
new file mode 100644
index 0000000..5cd58bc
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/mapicons/DwarvenIcons.java
@@ -0,0 +1,96 @@
+package com.thatgravyboat.skyblockhud.handlers.mapicons;
+
+import com.thatgravyboat.skyblockhud.ComponentBuilder;
+import com.thatgravyboat.skyblockhud.handlers.MapHandler;
+import net.minecraft.util.ResourceLocation;
+
+import javax.vecmath.Vector2f;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DwarvenIcons {
+
+ public static List<MapHandler.MapIcon> dwarvenIcons = new ArrayList<>();
+
+ static {
+ setupNpcIcons();
+ setupMiscIcons();
+ setupInfoIcons();
+ setupShopIcons();
+ setupQuestIcons();
+ }
+
+ private static void setupNpcIcons(){
+ dwarvenIcons.add(new MapHandler.MapIcon(
+ new Vector2f(129, 187),
+ new ResourceLocation("skyblockhud", "maps/icons/puzzle.png"),
+ new ComponentBuilder()
+ .nl("Puzzler", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Puzzler gives you a small puzzle each day to solve and")
+ .nl("gives you 1000 mithril powder.")
+ .build(),
+ MapHandler.MapIconTypes.NPC
+ )
+ );
+ }
+
+ private static void setupMiscIcons(){}
+
+ private static void setupInfoIcons(){
+ dwarvenIcons.add(new MapHandler.MapIcon(
+ new Vector2f(129, 187),
+ new ResourceLocation("skyblockhud", "maps/icons/crown.png"),
+ new ComponentBuilder()
+ .nl("King", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The King allows you to first start commissions and if you click")
+ .nl("each king which change every skyblock day you will get")
+ .nl("the King Talisman.")
+ .nl()
+ .apd("Click to open HOTM", new char[]{'6', 'l'})
+ .build(),
+ MapHandler.MapIconTypes.INFO,
+ "hotm"
+ )
+ );
+ }
+
+ private static void setupShopIcons(){
+ dwarvenIcons.add(new MapHandler.MapIcon(
+ new Vector2f(4, 8),
+ new ResourceLocation("skyblockhud", "maps/icons/blacksmith.png"),
+ new ComponentBuilder()
+ .nl("Forge", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Forge is where you can go craft special items")
+ .nl("and fuel your drill.")
+ .nl("NPCS", new char[]{'c','l'})
+ .nl(" Forger - Allows you to forge special items")
+ .nl(" Jotraeline Greatforge - Allows you to refuel your drill.")
+ .nl()
+ .apd("Click to warp", new char[]{'6', 'l'})
+ .build(),
+ MapHandler.MapIconTypes.SHOPS,
+ "warpforge"
+ )
+ );
+ }
+
+ private static void setupQuestIcons(){
+ dwarvenIcons.add(new MapHandler.MapIcon(
+ new Vector2f(67, 204),
+ new ResourceLocation("skyblockhud", "maps/icons/special.png"),
+ new ComponentBuilder()
+ .nl("Royal Resident", new char[]{'a','l'})
+ .nl("The Royal Resident is a quest where you right")
+ .nl("click them for a bit to obtain and if you continue")
+ .nl("to right click them for about 7 hours it will give")
+ .apd("the achievement Royal Conversation.")
+ .build(),
+ MapHandler.MapIconTypes.QUEST
+ )
+ );
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/mapicons/HubIcons.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/mapicons/HubIcons.java
new file mode 100644
index 0000000..ad1dbc3
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/mapicons/HubIcons.java
@@ -0,0 +1,308 @@
+package com.thatgravyboat.skyblockhud.handlers.mapicons;
+
+import com.thatgravyboat.skyblockhud.ComponentBuilder;
+import com.thatgravyboat.skyblockhud.handlers.MapHandler;
+import net.minecraft.util.ResourceLocation;
+
+import javax.vecmath.Vector2f;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HubIcons {
+
+ public static List<MapHandler.MapIcon> hubIcons = new ArrayList<>();
+
+ static {
+ setupNpcIcons();
+ setupMiscIcons();
+ setupInfoIcons();
+ setupShopIcons();
+ setupQuestIcons();
+ }
+
+ private static void setupNpcIcons() {
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(-2, -34),
+ new ResourceLocation("skyblockhud", "maps/icons/special.png"),
+ new ComponentBuilder()
+ .nl("Event Hut", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Event Hut is where special event npcs")
+ .nl("are during some events.")
+ .nl("NPCS", new char[]{'c','l'})
+ .nl(" Baker - During New Years")
+ .nl(" Jerry - While Winter Island is opened")
+ .nl(" Fear Mongerer - During Spooky Festival")
+ .apd(" Oringo - During Traveling Zoo")
+ .build(),
+ MapHandler.MapIconTypes.NPC
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(135, 142),
+ new ResourceLocation("skyblockhud", "maps/icons/fairy.png"),
+ new ComponentBuilder()
+ .nl("Fairy", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Fairy is where you go when you find fairy souls")
+ .apd("to trade them in to get permanent stat upgrades.")
+ .build(),
+ MapHandler.MapIconTypes.NPC
+ )
+ );
+ }
+
+ private static void setupShopIcons() {
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(-50, -22),
+ new ResourceLocation("skyblockhud", "maps/icons/building.png"),
+ new ComponentBuilder()
+ .nl("Builder's House", new char[]{'a','l'})
+ .nl("NPCS", new char[]{'c','l'})
+ .nl(" Wool Weaver")
+ .nl(" Builder")
+ .apd(" Mad Redstone Engineer")
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(-78, -46),
+ new ResourceLocation("skyblockhud", "maps/icons/bar.png"),
+ new ComponentBuilder()
+ .nl("Tavern", new char[]{'a','l'})
+ .nl("NPCS", new char[]{'c','l'})
+ .nl(" Bartender")
+ .nl(" Maddox the slayer")
+ .nl("Description", 'l')
+ .nl("The Tavern is where maddox the slayer is located you can")
+ .nl("start slayer quests with them to unlock")
+ .apd("new items the more slayer bosses you kill.")
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(36, -82),
+ new ResourceLocation("skyblockhud", "maps/icons/vet.png"),
+ new ComponentBuilder()
+ .nl("Vet", new char[]{'a','l'})
+ .nl("NPCS", new char[]{'c','l'})
+ .nl(" Bea")
+ .nl(" Zog")
+ .nl(" Kat")
+ .nl(" George")
+ .nl("Description", 'l')
+ .nl("The Vet is where you go to upgrade your pet")
+ .nl("at Kat or to buy pet upgrade items from Zog")
+ .nl("or trade in your pet at George and if you're")
+ .apd("a new player you can buy a bee pet from Bea.")
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(58, -73),
+ new ResourceLocation("skyblockhud", "maps/icons/fishing_merchant.png"),
+ new ComponentBuilder()
+ .nl("Fishing Merchant", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Fishing Merchant allows you to buy")
+ .nl("fishing related items and he has his friend")
+ .nl("joe whose in the house hes setup")
+ .apd("in front of who sells sponges.")
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(46, -53),
+ new ResourceLocation("skyblockhud", "maps/icons/witch.png"),
+ new ComponentBuilder()
+ .nl("Alchemist", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Alchemist allows you to buy")
+ .apd("potion making related items")
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(-4, -128),
+ new ResourceLocation("skyblockhud", "maps/icons/metal_merchants.png"),
+ new ComponentBuilder()
+ .nl("Blacksmith Merchants", new char[]{'a','l'})
+ .nl("Merchants", new char[]{'c','l'})
+ .nl(" Weaponsmith - Weapon Related Items")
+ .nl(" Armorsmith - Armor Related Items")
+ .apd(" Mine Merchant - Mining Related Items")
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(-30, -120),
+ new ResourceLocation("skyblockhud", "maps/icons/blacksmith.png"),
+ new ComponentBuilder()
+ .nl("Blacksmith", new char[]{'a','l'})
+ .nl("NPCS", new char[]{'c','l'})
+ .nl(" Blacksmith")
+ .nl(" Dusk")
+ .nl(" Smithmonger")
+ .nl("Description", 'l')
+ .nl("The Blacksmith lets you reforge your items")
+ .nl("while the Smithmonger allows you to buy reforge stones")
+ .apd("and Dusk allows you to combine and apply runes.")
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(124, 180),
+ new ResourceLocation("skyblockhud", "maps/icons/dark_bar.png"),
+ new ComponentBuilder()
+ .nl("Dark Bar", new char[]{'a','l'})
+ .nl("NPCS", new char[]{'c','l'})
+ .nl(" Shifty")
+ .nl(" Lucius")
+ .nl("Description", 'l')
+ .nl("The Dark Bar is where you can buy special")
+ .nl("brews from Shifty and you can buy special")
+ .nl("items from Lucius after buying a certain")
+ .apd("amount of items from the Dark Auction.")
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(92, 185),
+ new ResourceLocation("skyblockhud", "maps/icons/dark_ah.png"),
+ new ComponentBuilder()
+ .nl("Dark Auction", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Dark Auction allows you to buy")
+ .nl("super special items from Sirius the")
+ .apd("auctioneer in a special auction.")
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(-245, 52),
+ new ResourceLocation("skyblockhud", "maps/icons/scroll.png"),
+ new ComponentBuilder()
+ .nl("Lonely Philosopher", new char[]{'a','l'})
+ .nl("Shop", new char[]{'6','l'})
+ .nl(" Travel Scroll to Hub Castle")
+ .nl()
+ .nl(" Cost")
+ .nl(" 150,000 Coins", '6')
+ .nl()
+ .apd(" Requires ")
+ .apd("MVP", 'b')
+ .apd("+", 'c')
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(24, -38),
+ new ResourceLocation("skyblockhud", "maps/icons/tux.png"),
+ new ComponentBuilder()
+ .nl("Fashion Shop", new char[]{'a','l'})
+ .nl("NPCS", new char[]{'c','l'})
+ .nl(" Wool Weaver")
+ .nl(" Builder")
+ .apd(" Mad Redstone Engineer")
+ .build(),
+ MapHandler.MapIconTypes.SHOPS
+ )
+ );
+ }
+
+ private static void setupMiscIcons() {
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(-24, -53),
+ new ResourceLocation("skyblockhud", "maps/icons/bank.png"),
+ new ComponentBuilder()
+ .nl("Bank", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Bank is where you can store your money on skyblock")
+ .apd("you can also store some items in the vault.")
+ .build(),
+ MapHandler.MapIconTypes.MISC
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(-26, -80),
+ new ResourceLocation("skyblockhud", "maps/icons/ah.png"),
+ new ComponentBuilder()
+ .nl("Auction House", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Auction House is where you can auction off your")
+ .apd("precious items in skyblock to make a profit.")
+ .build(),
+ MapHandler.MapIconTypes.MISC
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(-38, -66),
+ new ResourceLocation("skyblockhud", "maps/icons/bazaar.png"),
+ new ComponentBuilder()
+ .nl("Bazaar", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Bazaar is where you can sell specific items")
+ .nl("on a sort of stock market and request and")
+ .apd("sell items at a specific price.")
+ .build(),
+ MapHandler.MapIconTypes.MISC
+ )
+ );
+ }
+
+ private static void setupInfoIcons() {
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(8, -95),
+ new ResourceLocation("skyblockhud", "maps/icons/community.png"),
+ new ComponentBuilder()
+ .nl("Community Center", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("The Community Center is where you can vote")
+ .nl("for your favorite election candidate,")
+ .nl("access the community shop, upgrade your")
+ .apd("account, and help with city projects.")
+ .build(),
+ MapHandler.MapIconTypes.INFO
+ )
+ );
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(150, 45),
+ new ResourceLocation("skyblockhud", "maps/icons/fishing.png"),
+ new ComponentBuilder()
+ .nl("Fisherman's Hut", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("This is a spot where people regularly")
+ .nl("do their fishing, this is one")
+ .apd("of many spots.")
+ .build(),
+ MapHandler.MapIconTypes.INFO
+ )
+ );
+ }
+
+ private static void setupQuestIcons() {
+ hubIcons.add(new MapHandler.MapIcon(
+ new Vector2f(-8, -10),
+ new ResourceLocation("skyblockhud", "maps/icons/painter.png"),
+ new ComponentBuilder()
+ .nl("Marco", new char[]{'a','l'})
+ .nl("Description", 'l')
+ .nl("Marco is an NPC that has no other uses")
+ .nl("besides giving you a spray can for")
+ .apd("completing a quest.")
+ .build(),
+ MapHandler.MapIconTypes.QUEST
+ )
+ );
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/EntityTypeHelper.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/EntityTypeHelper.java
new file mode 100644
index 0000000..afff109
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/EntityTypeHelper.java
@@ -0,0 +1,21 @@
+package com.thatgravyboat.skyblockhud.handlers.sbentities;
+
+import com.thatgravyboat.skyblockhud.location.LocationHandler;
+import com.thatgravyboat.skyblockhud.location.Locations;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.monster.EntityEnderman;
+import net.minecraft.init.Blocks;
+
+public class EntityTypeHelper {
+
+ public static boolean isZealot(Entity entity) {
+ if (entity instanceof EntityEnderman) {
+ EntityEnderman enderman = ((EntityEnderman) entity);
+ double maxHealthBase = enderman.getAttributeMap().getAttributeInstanceByName("generic.maxHealth").getBaseValue();
+ if (maxHealthBase == 13000d || (maxHealthBase == 2000d && enderman.getHeldBlockState().getBlock().equals(Blocks.end_portal_frame))) {
+ return LocationHandler.getCurrentLocation().equals(Locations.DRAGONSNEST);
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/EntityTypeRegistry.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/EntityTypeRegistry.java
new file mode 100644
index 0000000..d45fa1f
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/EntityTypeRegistry.java
@@ -0,0 +1,25 @@
+package com.thatgravyboat.skyblockhud.handlers.sbentities;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.monster.EntityEnderman;
+
+import java.util.List;
+import java.util.Map;
+
+public class EntityTypeRegistry {
+
+ private static final Map<Class<? extends Entity>, List<SkyBlockEntity>> entities = Maps.newHashMap();
+
+ static {
+ entities.put(EntityEnderman.class, ImmutableList.of(SkyBlockEntity.of("zealot", EntityTypeHelper::isZealot)));
+ }
+
+ public static String getEntityId(Entity entity){
+ if (!entities.containsKey(entity.getClass())) return null;
+ for (SkyBlockEntity skyBlockEntity : entities.get(entity.getClass())) if (skyBlockEntity.isEntity(entity)) return skyBlockEntity.getName();
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/SkyBlockEntity.java b/src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/SkyBlockEntity.java
new file mode 100644
index 0000000..ca10b53
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/handlers/sbentities/SkyBlockEntity.java
@@ -0,0 +1,26 @@
+package com.thatgravyboat.skyblockhud.handlers.sbentities;
+
+import net.minecraft.entity.Entity;
+
+import java.util.function.Predicate;
+
+public class SkyBlockEntity {
+
+ private final String name;
+ private final Predicate<Entity> predicate;
+
+ public static SkyBlockEntity of(String name, Predicate<Entity> predicate){
+ return new SkyBlockEntity(name, predicate);
+ }
+
+ private SkyBlockEntity(String name, Predicate<Entity> predicate){
+ this.name = name;
+ this.predicate = predicate;
+ }
+
+ public String getName(){ return name; }
+
+ public boolean isEntity(Entity entity) {
+ return predicate.test(entity);
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/location/DwarvenMineHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/location/DwarvenMineHandler.java
new file mode 100644
index 0000000..32f6c80
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/location/DwarvenMineHandler.java
@@ -0,0 +1,97 @@
+package com.thatgravyboat.skyblockhud.location;
+
+import com.thatgravyboat.skyblockhud.api.events.SidebarLineUpdateEvent;
+import com.thatgravyboat.skyblockhud.api.events.SidebarPostEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Arrays;
+import java.util.Locale;
+
+public class DwarvenMineHandler {
+
+ public enum Event {
+ NONE(0, "Unknown"),
+ TICKET(107, "Raffle"),
+ GOBLIN(99, "Goblin Raid");
+
+ public int x;
+ public String displayName;
+
+ Event(int x, String displayName){
+ this.x = x;
+ this.displayName = displayName;
+ }
+ }
+
+ public static int mithril;
+
+ public static int eventMax;
+ public static int eventProgress;
+ public static Event currentEvent;
+
+ private static final DecimalFormat formatter = new DecimalFormat("#,###", DecimalFormatSymbols.getInstance(Locale.CANADA));
+
+ public static String getMithrilFormatted(){
+ String output = formatter.format(mithril);
+ if (output.equals(".0")) output = "0.0";
+ else if (output.equals(",0")) output = "0,0";
+ return output;
+ }
+
+ public static void parseMithril(String line){
+ try{
+ mithril = Integer.parseInt(line.toLowerCase().replace("mithril powder:", "").trim());
+ }catch (Exception ignored){}
+ }
+
+ @SubscribeEvent
+ public void onSidebarLineUpdate(SidebarLineUpdateEvent event){
+ if (event.formattedLine.toLowerCase().contains("mithril")){
+ try{
+ mithril = Integer.parseInt(event.formattedLine.toLowerCase().replace("mithril:","").trim());
+ }catch (Exception ignored){}
+ }
+ if (event.formattedLine.toLowerCase().contains("event")){
+ if (event.formattedLine.toLowerCase().contains("raffle")){
+ DwarvenMineHandler.currentEvent = Event.TICKET;
+ }else if (event.formattedLine.toLowerCase().contains("goblin raid")){
+ DwarvenMineHandler.currentEvent = Event.GOBLIN;
+ }
+ }
+ if (DwarvenMineHandler.currentEvent != Event.NONE){
+ if (DwarvenMineHandler.currentEvent == Event.TICKET && event.formattedLine.toLowerCase().contains("tickets:")){
+ if (event.formattedLine.toLowerCase().contains("pool:")){
+ try{
+ eventMax = Integer.parseInt(event.formattedLine.toLowerCase().replace("pool:","").trim().split("/")[0].trim());
+ }catch (Exception ignored){}
+ }else if (event.formattedLine.toLowerCase().contains("tickets:")){
+ try{
+ eventProgress = Integer.parseInt(event.formattedLine.toLowerCase().replace("tickets:", "").split("\\(")[0].trim());
+ }catch (Exception ignored){}
+ }
+ }else if (DwarvenMineHandler.currentEvent == Event.GOBLIN){
+ if (event.formattedLine.toLowerCase().contains("remaining:")){
+ try{
+ eventMax = Integer.parseInt(event.formattedLine.toLowerCase().replace("goblins","").replace("remaining:","").trim());
+ }catch (Exception ignored){}
+ }else if (event.formattedLine.toLowerCase().contains("your kills:") && !event.formattedLine.toLowerCase().contains("(")){
+ try{
+ eventProgress = Integer.parseInt(event.formattedLine.toLowerCase().replace("your kills:","").trim());
+ }catch (Exception ignored){}
+ }
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public void onSidebarPost(SidebarPostEvent event) {
+ String arrayString = Arrays.toString(event.arrayScores);
+ if (!arrayString.toLowerCase().contains("event:")){
+ DwarvenMineHandler.currentEvent = Event.NONE;
+ DwarvenMineHandler.eventProgress = 0;
+ DwarvenMineHandler.eventMax = 0;
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/location/EndIslandHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/location/EndIslandHandler.java
new file mode 100644
index 0000000..e8be465
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/location/EndIslandHandler.java
@@ -0,0 +1,46 @@
+package com.thatgravyboat.skyblockhud.location;
+
+public class EndIslandHandler {
+ public enum dragonTypes {
+ PROTECTOR("Protector Dragon", 9000000),
+ OLD("Old Dragon", 15000000),
+ WISE("Wise Dragon", 9000000),
+ UNSTABLE("Unstable Dragon", 9000000),
+ YOUNG("Young Dragon", 7500000),
+ STRONG("Strong Dragon", 9000000),
+ SUPERIOR("Superior Dragon", 12000000),
+ NODRAGON("", 0);
+
+ private final String displayName;
+ private final int maxHealth;
+
+ dragonTypes(String displayName, int maxHealth){
+ this.displayName = displayName;
+ this.maxHealth = maxHealth;
+ }
+
+ public String getDisplayName(){
+ return this.displayName;
+ }
+
+ public int getMaxHealth() {
+ return this.maxHealth;
+ }
+
+ public static dragonTypes findDragon(String input){
+ if (input.contains(" ")){
+ try {
+ return dragonTypes.valueOf(input.toLowerCase().replace("dragon", "").replace(" ", "").toUpperCase());
+ } catch(IllegalArgumentException ignored) {
+ return NODRAGON;
+ }
+ } else {
+ try { return dragonTypes.valueOf(input); } catch(IllegalArgumentException ignored) { return NODRAGON; }
+ }
+ }
+ }
+
+ private static dragonTypes currentDragon = dragonTypes.NODRAGON;
+
+ public static void setCurrentDragon(dragonTypes dragon) { currentDragon = dragon; }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/location/FarmingIslandHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/location/FarmingIslandHandler.java
new file mode 100644
index 0000000..03a698b
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/location/FarmingIslandHandler.java
@@ -0,0 +1,29 @@
+package com.thatgravyboat.skyblockhud.location;
+
+import com.thatgravyboat.skyblockhud.api.events.SidebarPostEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.Arrays;
+
+public class FarmingIslandHandler {
+
+ public static Locations location = Locations.NONE;
+ public static int pelts;
+
+ @SubscribeEvent
+ public void onSidebarPost(SidebarPostEvent event) {
+ boolean isTracking = Arrays.toString(event.arrayScores).toLowerCase().contains("tracker mob location:");
+ if (isTracking && location == Locations.NONE) {
+ for (int i = 0; i < event.scores.size(); i++) {
+ String line = event.scores.get(i);
+ if (line.toLowerCase().contains("tracker mob location:") && i > 2){
+ location = Locations.get(event.scores.get(i - 1).toLowerCase());
+ break;
+ }
+ }
+ }
+ if (!isTracking && location != Locations.NONE){
+ location = Locations.NONE;
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/location/IslandHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/location/IslandHandler.java
new file mode 100644
index 0000000..d6ab81e
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/location/IslandHandler.java
@@ -0,0 +1,62 @@
+package com.thatgravyboat.skyblockhud.location;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.api.events.ProfileSwitchedEvent;
+import com.thatgravyboat.skyblockhud.api.events.SidebarLineUpdateEvent;
+import com.thatgravyboat.skyblockhud.api.events.SidebarPostEvent;
+import com.thatgravyboat.skyblockhud.handlers.CurrencyHandler;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.Arrays;
+
+public class IslandHandler {
+
+ public static int flightTime;
+ public static boolean hadFlightTime;
+
+ public static int redstone;
+ public static boolean hadRedstone;
+
+ @SubscribeEvent
+ public void onSidebarLineUpdate(SidebarLineUpdateEvent event){
+ hadFlightTime = checkFlightDuration(event.formattedLine);
+ hadRedstone = checkRestone(event.formattedLine);
+ }
+
+ @SubscribeEvent
+ public void onProfileSwitch(ProfileSwitchedEvent event){
+ flightTime = 0;
+ }
+
+ public static boolean checkFlightDuration(String formatedScoreboardLine){
+ if (LocationHandler.getCurrentLocation() == Locations.YOURISLAND && Utils.removeColor(formatedScoreboardLine.toLowerCase().trim()).contains("flight duration:")){
+ String timeString = formatedScoreboardLine.toLowerCase().replace("flight duration:", "").replace(" ", "");
+ String[] times = timeString.split(":");
+ if (times.length == 2){
+ int s = 0;
+ try { s += Integer.parseInt(times[0]) * 60; } catch (NumberFormatException ignored){}
+ try { s += Integer.parseInt(times[1]); } catch (NumberFormatException ignored){}
+ flightTime = s - 1;
+ } else if (times.length == 3){
+ int s = 0;
+ try { s += Integer.parseInt(times[0]) * 3600; } catch (NumberFormatException ignored){}
+ try { s += Integer.parseInt(times[1]) * 60; } catch (NumberFormatException ignored){}
+ try { s += Integer.parseInt(times[2]); } catch (NumberFormatException ignored){}
+ flightTime = s - 1;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean checkRestone(String formatedScoreboardLine){
+ if (LocationHandler.getCurrentLocation() == Locations.YOURISLAND) {
+ if (formatedScoreboardLine.toLowerCase().contains("redstone:"))
+ return true;
+ try {
+ redstone = formatedScoreboardLine.toLowerCase().contains("redstone:") ? Integer.parseInt(Utils.removeWhiteSpaceAndRemoveWord(formatedScoreboardLine, "redstone:")) : 0;
+ }catch (Exception ignored){}
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/location/LocationCategory.java b/src/main/java/com/thatgravyboat/skyblockhud/location/LocationCategory.java
new file mode 100644
index 0000000..817645d
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/location/LocationCategory.java
@@ -0,0 +1,46 @@
+package com.thatgravyboat.skyblockhud.location;
+
+import com.thatgravyboat.skyblockhud.handlers.MapHandler;
+import static com.thatgravyboat.skyblockhud.handlers.MapHandler.Maps;
+
+public enum LocationCategory {
+
+ ERROR("error", 34),
+ ISLAND("island",43),
+ HUB("hub",34, Maps.HUB),
+ BARN("barn",67, Maps.BARN),
+ MUSHROOMDESERT("mushroomdesert",75, Maps.MUSHROOM),
+ GOLDMINE("gold_mine",83),
+ DEEPCAVERNS("deepcaverns",91),
+ SPIDERSDEN("spiders_den",99, Maps.SPIDERS),
+ PARK("park",51, Maps.PARK),
+ FORTRESS("fortress",107, Maps.NETHER),
+ DUNGEONHUB("dungeonhub",115),
+ JERRY("jerry",59),
+ THEEND("the_end",123),
+ DWARVENMINES("dwarven_mines", 131, Maps.DWARVEN),
+ CRYSTALHOLLOWS("crystal_hollows", 131);
+
+
+ private final String name;
+ private final int texturePos;
+ private final MapHandler.Maps map;
+
+ LocationCategory(String name, int texturePos){
+ this(name, texturePos, null);
+ }
+
+ LocationCategory(String name, int texturePos, MapHandler.Maps map){
+ this.name = name;
+ this.texturePos = texturePos;
+ this.map = map;
+ }
+
+ public String getName(){
+ return this.name;
+ }
+ public int getTexturePos(){
+ return this.texturePos;
+ }
+ public MapHandler.Maps getMap() { return this.map; }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/location/LocationHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/location/LocationHandler.java
new file mode 100644
index 0000000..274baf8
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/location/LocationHandler.java
@@ -0,0 +1,54 @@
+package com.thatgravyboat.skyblockhud.location;
+
+import com.thatgravyboat.skyblockhud.api.events.SidebarLineUpdateEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class LocationHandler {
+
+ private static Locations currentLocation = Locations.NONE;
+ private static final List<String> UndocumentedLocations = new ArrayList<>();
+
+
+ @SubscribeEvent
+ public void onSidebarLineUpdate(SidebarLineUpdateEvent event){
+ if (event.rawLine.contains("\u23E3")) {
+ String objectiveName = event.objective.getDisplayName().replaceAll("(?i)\\u00A7.", "");
+ if (objectiveName.toLowerCase(Locale.ENGLISH).endsWith("guest")){
+ LocationHandler.setCurrentLocation(Locations.GUESTISLAND);
+ }else {
+ LocationHandler.handleLocation(event.formattedLine);
+ }
+ }
+ }
+
+ public static void setCurrentLocation(String location){
+ currentLocation = Locations.get(location);
+ }
+
+ public static void setCurrentLocation(Locations location){
+ currentLocation = location;
+ }
+
+ public static Locations getCurrentLocation(){ return currentLocation; }
+
+ public static void handleLocation(String locationLine){
+ String location = locationLine.replace(" ", "").toUpperCase(Locale.ENGLISH).trim();
+ if (location.startsWith("THECATACOMBS")){
+ currentLocation = Locations.CATACOMBS;
+ }
+ else setCurrentLocation(location.replaceAll("[^A-Za-z0-9]", ""));
+ }
+
+
+ public static void reportUndocumentedLocation(String locationId){
+ if (!UndocumentedLocations.contains(locationId)){
+ UndocumentedLocations.add(locationId);
+ System.out.println("Missing Location value for: " + locationId);
+ }
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/location/Locations.java b/src/main/java/com/thatgravyboat/skyblockhud/location/Locations.java
new file mode 100644
index 0000000..887ac11
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/location/Locations.java
@@ -0,0 +1,157 @@
+package com.thatgravyboat.skyblockhud.location;
+
+public enum Locations {
+
+ //ERROR LOCATIONS
+ DEFAULT("unknown", "Error", LocationCategory.ERROR),
+ NONE("none", "Unknown", LocationCategory.ERROR),
+ //ISLAND
+ YOURISLAND("yourisland", "Your Island", LocationCategory.ISLAND),
+ GUESTISLAND("guestisland", "Guest Island", LocationCategory.ISLAND),
+ MOULBERRYSISLAND("moulberryisland", "Cool Dude Hub", LocationCategory.ISLAND),
+ //HUB
+ VILLAGE("village", "Village", LocationCategory.HUB),
+ AUCTIONHOUSE("auctionhouse", "Auction House", LocationCategory.HUB),
+ BAZAARALLEY("bazaaralley", "Bazaar Alley", LocationCategory.HUB),
+ BANK("bank", "Bank", LocationCategory.HUB),
+ FASHIONSHOP("fashionshop", "Fashion Shop", LocationCategory.HUB),
+ COLOSSEUM("colosseum", "Colosseum", LocationCategory.HUB),
+ COLOSSEUMARENA("colosseumarena", "Colosseum Arena", LocationCategory.HUB),
+ MOUNTAIN("mountain", "Mountain", LocationCategory.HUB),
+ HIGHLEVEL("highlevel", "High Level", LocationCategory.HUB),
+ WILDERNESS("wilderness", "Wilderness", LocationCategory.HUB),
+ FISHERMANSHUT("fishermanshut", "Fisherman's Hut", LocationCategory.HUB),
+ FLOWERHOUSE("flowerhouse", "Flower House", LocationCategory.HUB),
+ CANVASROOM("canvasroom", "Canvas Room", LocationCategory.HUB),
+ TAVERN("tavern", "Tavern", LocationCategory.HUB),
+ FOREST("forest", "Forest", LocationCategory.HUB),
+ RUINS("ruins", "Ruins", LocationCategory.HUB),
+ GRAVEYARD("graveyard", "Graveyard", LocationCategory.HUB),
+ COALMINE("coalmine", "Coal Mine", LocationCategory.HUB),
+ FARM("farm", "Farm", LocationCategory.HUB),
+ LIBRARY("library", "Library", LocationCategory.HUB),
+ COMMUNITYCENTER("communitycenter", "Community Center", LocationCategory.HUB),
+ ELECTIONROOM("electionroom", "Election Room", LocationCategory.HUB),
+ BUILDERSHOUSE("buildershouse", "Builder's House", LocationCategory.HUB),
+ BLACKSMITH("blacksmith", "Blacksmith", LocationCategory.HUB),
+ FARMHOUSE("farmhouse", "Farmhouse", LocationCategory.HUB),
+ WIZARDTOWER("wizardtower", "Wizard Tower", LocationCategory.HUB),
+ //THE BARN
+ THEBARN("thebarn", "The Barn", LocationCategory.BARN),
+ WINDMILL("windmill", "Windmill", LocationCategory.BARN),
+ //MUSHROOM DESERT
+ MUSHROOMDESERT("mushroomdesert", "Mushroom Desert", LocationCategory.MUSHROOMDESERT),
+ DESERTSETTLEMENT("desertsettlement", "Desert Settlement", LocationCategory.MUSHROOMDESERT),
+ OASIS("oasis", "Oasis", LocationCategory.MUSHROOMDESERT),
+ MUSHROOMGORGE("mushroomgorge", "Mushroom Gorge", LocationCategory.MUSHROOMDESERT),
+ SHEPHERDSKEEP("shepherdskeep", "Shepherds Keep", LocationCategory.MUSHROOMDESERT),
+ JAKESHOUSE("jakeshouse", "Jake's House", LocationCategory.MUSHROOMDESERT),
+ TREASUREHUNTERCAMP("treasurehuntercamp", "Treasure Hunter Camp", LocationCategory.MUSHROOMDESERT),
+ GLOWINGMUSHROOMCAVE("glowingmushroomcave", "Glowing Mushroom Cave", LocationCategory.MUSHROOMDESERT),
+ TRAPPERSDEN("trappersden", "Trappers Den", LocationCategory.MUSHROOMDESERT),
+ OVERGROWNMUSHROOMCAVE("overgrownmushroomcave", "Overgrown Mushroom Cave", LocationCategory.MUSHROOMDESERT),
+ //GOLD MINE
+ GOLDMINE("goldmine", "Gold Mine", LocationCategory.GOLDMINE),
+ //DEEP CAVERNS
+ DEEPCAVERNS("deepcaverns", "Deep Caverns", LocationCategory.DEEPCAVERNS),
+ GUNPOWDERMINES("gunpowdermines", "Gunpowder Mines", LocationCategory.DEEPCAVERNS),
+ LAPISQUARRY("lapisquarry", "Lapis Quarry", LocationCategory.DEEPCAVERNS),
+ PIGMANSDEN("pigmansden", "Pigman's Den", LocationCategory.DEEPCAVERNS),
+ SLIMEHILL("slimehill", "Slimehill", LocationCategory.DEEPCAVERNS),
+ DIAMONDRESERVE("diamondreserve", "Diamond Reserve", LocationCategory.DEEPCAVERNS),
+ OBSIDIANSANCTUARY("obsidiansanctuary", "Obsidian Sanctuary", LocationCategory.DEEPCAVERNS),
+ //SPIDERS DEN
+ SPIDERSDEN("spidersden", "Spider's Den", LocationCategory.SPIDERSDEN),
+
+ //THE END
+ THEEND("theend", "The End", LocationCategory.THEEND),
+ DRAGONSNEST("dragonsnest", "Dragon's Nest", LocationCategory.THEEND),
+ VOIDSEPULTURE("voidsepulture", "Void Sepulture", LocationCategory.THEEND),
+ //PARK
+ HOWLINGCAVE("howlingcave", "Howling Cave", LocationCategory.PARK),
+ BIRCHPARK("birchpark", "Birch Park", LocationCategory.PARK),
+ SPRUCEWOODS("sprucewoods", "Spruce Woods", LocationCategory.PARK),
+ DARKTHICKET("darkthicket", "Dark Thicket", LocationCategory.PARK),
+ SAVANNAWOODLAND("savannawoodland", "Savanna Woodland", LocationCategory.PARK),
+ JUNGLEISLAND("jungleisland", "Jungle Island", LocationCategory.PARK),
+ //BLAZING FORTRESS
+ BLAZINGFORTRESS("blazingfortress", "Blazing Fortress", LocationCategory.FORTRESS),
+ //DUNGEONS
+ DUNGEONHUB("dungeonhub", "Dungeon Hub", LocationCategory.DUNGEONHUB),
+ CATACOMBS("catacombs", "The Catacombs", LocationCategory.DUNGEONHUB),
+ CATACOMBSENTRANCE("catacombsentrance", "Catacombs Entrance", LocationCategory.DUNGEONHUB),
+ //JERRYISLAND
+ JERRYSWORKSHOP("jerrysworkshop", "Jerry's Workshop", LocationCategory.JERRY),
+ JERRYPOND("jerrypond", "Jerry Pond", LocationCategory.JERRY),
+ //DWARVENMINES
+ THELIFT("thelift", "The Lift", LocationCategory.DWARVENMINES),
+ DWARVENVILLAGE("dwarvenvillage", "Dwarven Village", LocationCategory.DWARVENMINES),
+ DWARVENMINES("dwarvenmines", "Dwarven Mines", LocationCategory.DWARVENMINES),
+ LAVASPRINGS("lavasprings", "Lava Springs", LocationCategory.DWARVENMINES),
+ PALACEBRIDGE("palacebridge", "Palace Bridge", LocationCategory.DWARVENMINES),
+ ROYALPALACE("royalpalace", "Royal Palace", LocationCategory.DWARVENMINES),
+ GRANDLIBRARY("grandlibrary", "Grand Library", LocationCategory.DWARVENMINES),
+ ROYALQUARTERS("royalquarters", "Royal Quarters", LocationCategory.DWARVENMINES),
+ BARRACKSOFHEROES("barracksofheroes", "Barracks of Heroes", LocationCategory.DWARVENMINES),
+ HANGINGCOURT("hangingcourt", "Hanging Court", LocationCategory.DWARVENMINES),
+ GREATICEWALL("greaticewall", "Great Ice Wall", LocationCategory.DWARVENMINES),
+ GOBLINBURROWS("goblinburrows", "Goblin Burrows", LocationCategory.DWARVENMINES),
+ FARRESERVE("farreserve", "Far Reserve", LocationCategory.DWARVENMINES),
+ CCMINECARTSCO("ccminecartco", "Minecart Co.", LocationCategory.DWARVENMINES),
+ UPPERMINES("uppermines", "Upper Mines", LocationCategory.DWARVENMINES),
+ RAMPARTSQUARRY("rampartsquarry", "Ramparts Quarry", LocationCategory.DWARVENMINES),
+ GATESTOTHEMINES("gatestothemines", "Gates to The Mines", LocationCategory.DWARVENMINES),
+ FORGEBASIN("forgebasin", "Forge Basin", LocationCategory.DWARVENMINES),
+ THEFORGE("theforge", "The Forge", LocationCategory.DWARVENMINES),
+ CLIFFSIDEVEINS("cliffsideveins", "Cliffside Veins", LocationCategory.DWARVENMINES),
+ DIVANSGATEWAY("divansgateway", "Divan's Gateway", LocationCategory.DWARVENMINES),
+ THEMIST("themist", "The Mist", LocationCategory.DWARVENMINES),
+ ROYALMINES("royalmines", "Royal Mines", LocationCategory.DWARVENMINES),
+ ARISTOCRATPASSAGE("aristocratpassage", "Aristocrat Passage", LocationCategory.DWARVENMINES),
+ MINERSGUILD("minersguild", "Miner's Guild", LocationCategory.DWARVENMINES),
+ //CRYSTALHOLLOWS
+ JUNGLE("jungle", "Jungle", LocationCategory.CRYSTALHOLLOWS),
+ MAMGAFIELDS("magmafields", "Magma Fields", LocationCategory.CRYSTALHOLLOWS),
+ GOBLINHOLDOUT("goblinholdout", "Goblin Holdout", LocationCategory.CRYSTALHOLLOWS),
+ CRYSTALNUCLEUS("crystalnucleus", "Crystal Nucleus", LocationCategory.CRYSTALHOLLOWS),
+ PERCURSORREMNANTS("precursorremnants", "Precursor Remnants", LocationCategory.CRYSTALHOLLOWS),
+ MITHRILDEPOSITS("mithrildeposits", "Mithril Deposits", LocationCategory.CRYSTALHOLLOWS);
+
+
+ private final String name;
+ private final String displayName;
+ private final LocationCategory category;
+
+ Locations(String name, String displayName, LocationCategory category){
+ this.name = name;
+ this.displayName = displayName;
+ this.category = category;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getDisplayName() {
+ return this.displayName;
+ }
+
+ public LocationCategory getCategory() {
+ return this.category;
+ }
+
+ static public Locations get(String id) {
+ try {
+ return Locations.valueOf(id.replace(" ", "").toUpperCase());
+ } catch (IllegalArgumentException ex) {
+ LocationHandler.reportUndocumentedLocation(id);
+ return DEFAULT;
+ }
+ }
+
+
+ @Override
+ public String toString() {
+ return this.name;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/location/ParkIslandHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/location/ParkIslandHandler.java
new file mode 100644
index 0000000..2a41391
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/location/ParkIslandHandler.java
@@ -0,0 +1,31 @@
+package com.thatgravyboat.skyblockhud.location;
+
+import javax.annotation.Nullable;
+
+public class ParkIslandHandler {
+
+ private static boolean isRaining = false;
+ private static String rainTime = "";
+
+ public static void parseRain(@Nullable String tabLine){
+ if (tabLine == null){
+ isRaining = false;
+ rainTime = "";
+ }
+ else if (tabLine.toLowerCase().contains("rain:")){
+ if (tabLine.toLowerCase().contains("no rain")) isRaining = false;
+ else {
+ rainTime = tabLine.toLowerCase().replace("rain:", "").replace(" ", "");
+ isRaining = true;
+ }
+ }
+ }
+
+ public static String getRainTime(){
+ return rainTime;
+ }
+
+ public static boolean isRaining(){
+ return isRaining;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinEndermanRenderer.java b/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinEndermanRenderer.java
new file mode 100644
index 0000000..3cdbae5
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinEndermanRenderer.java
@@ -0,0 +1,25 @@
+package com.thatgravyboat.skyblockhud.mixins;
+
+import com.thatgravyboat.skyblockhud.SpecialColour;
+import com.thatgravyboat.skyblockhud.handlers.sbentities.EntityTypeHelper;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.entity.RenderEnderman;
+import net.minecraft.entity.monster.EntityEnderman;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.awt.*;
+
+@Mixin(RenderEnderman.class)
+public class MixinEndermanRenderer {
+
+ @Inject(method = "doRender(Lnet/minecraft/entity/monster/EntityEnderman;DDDFF)V", at = @At("HEAD"))
+ public void onRender(EntityEnderman entity, double x, double y, double z, float entityYaw, float partialTicks, CallbackInfo ci){
+ if (EntityTypeHelper.isZealot(entity)){
+ Color color = new Color(SpecialColour.specialToChromaRGB("255:255:0:48:255"));
+ GlStateManager.color(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 255f);
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinEntityArrow.java b/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinEntityArrow.java
new file mode 100644
index 0000000..93537ea
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinEntityArrow.java
@@ -0,0 +1,25 @@
+package com.thatgravyboat.skyblockhud.mixins;
+
+import com.thatgravyboat.skyblockhud.tracker.KillTrackerHandler;
+import net.minecraft.client.Minecraft;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.projectile.EntityArrow;
+import net.minecraft.util.MovingObjectPosition;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+
+@Mixin(EntityArrow.class)
+public class MixinEntityArrow {
+ //Disabled as kill tracker stuff not fully added yet.
+ @Shadow public Entity shootingEntity;
+
+ @ModifyVariable(method = "onUpdate", at = @At(value = "STORE", ordinal = 1))
+ public MovingObjectPosition onUpdate(MovingObjectPosition position){
+ if (position != null && position.entityHit != null && this.shootingEntity != null && this.shootingEntity.getUniqueID().equals(Minecraft.getMinecraft().thePlayer.getUniqueID())) {
+ KillTrackerHandler.attackedEntities.add(position.entityHit.getUniqueID());
+ }
+ return position;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinGuiIngameForge.java b/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinGuiIngameForge.java
new file mode 100644
index 0000000..ce7c228
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinGuiIngameForge.java
@@ -0,0 +1,92 @@
+package com.thatgravyboat.skyblockhud.mixins;
+
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import net.minecraftforge.client.GuiIngameForge;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.common.MinecraftForge;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import static net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType.*;
+
+@Mixin(GuiIngameForge.class)
+public class MixinGuiIngameForge {
+
+ @Shadow(remap = false)
+ private RenderGameOverlayEvent eventParent;
+
+ @Inject(method = "renderArmor", at = @At("HEAD"), cancellable = true, remap = false)
+ public void onRenderArmor(int width, int height, CallbackInfo ci){
+ if (SkyblockHud.config.renderer.hideArmor && SkyblockHud.hasSkyblockScoreboard()){
+ ci.cancel();
+ if (pre(ARMOR)) return;
+ post(ARMOR);
+ }
+ }
+
+ @Inject(method = "renderHealth", at = @At("HEAD"), cancellable = true, remap = false)
+ public void onRenderHealth(int width, int height, CallbackInfo ci){
+ if (SkyblockHud.config.renderer.hideHearts && SkyblockHud.hasSkyblockScoreboard()){
+ ci.cancel();
+ if (pre(HEALTH)) return;
+ post(HEALTH);
+ }
+ }
+
+ @Inject(method = "renderAir", at = @At("HEAD"), cancellable = true, remap = false)
+ public void onRenderAir(int width, int height, CallbackInfo ci){
+ if (SkyblockHud.config.renderer.hideAir && SkyblockHud.hasSkyblockScoreboard()){
+ ci.cancel();
+ if (pre(AIR)) return;
+ post(AIR);
+ }
+ }
+
+ @Inject(method = "renderHealthMount", at = @At("HEAD"), cancellable = true, remap = false)
+ public void onRenderHealthMount(int width, int height, CallbackInfo ci){
+ if (SkyblockHud.config.renderer.hideAnimalHearts && SkyblockHud.hasSkyblockScoreboard()){
+ ci.cancel();
+ if (pre(HEALTHMOUNT)) return;
+ post(HEALTHMOUNT);
+ }
+ }
+
+ @Inject(method = "renderExperience", at = @At("HEAD"), cancellable = true, remap = false)
+ public void onRenderExperience(int width, int height, CallbackInfo ci){
+ if (SkyblockHud.config.renderer.hideXpBar && SkyblockHud.hasSkyblockScoreboard()){
+ ci.cancel();
+ if (pre(EXPERIENCE)) return;
+ post(EXPERIENCE);
+ }
+ }
+
+ @Inject(method = "renderJumpBar", at = @At("HEAD"), cancellable = true, remap = false)
+ public void onRenderJumpBar(int width, int height, CallbackInfo ci){
+ if (SkyblockHud.config.renderer.hideXpBar && SkyblockHud.hasSkyblockScoreboard()){
+ ci.cancel();
+ if (pre(JUMPBAR)) return;
+ post(JUMPBAR);
+ }
+ }
+
+ @Inject(method = "renderFood", at = @At("HEAD"), cancellable = true, remap = false)
+ public void onRenderFood(int width, int height, CallbackInfo ci){
+ if (SkyblockHud.config.renderer.hideFood && SkyblockHud.hasSkyblockScoreboard()){
+ ci.cancel();
+ if (pre(FOOD)) return;
+ post(FOOD);
+ }
+ }
+
+ private boolean pre(RenderGameOverlayEvent.ElementType type)
+ {
+ return MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Pre(eventParent, type));
+ }
+ private void post(RenderGameOverlayEvent.ElementType type)
+ {
+ MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Post(eventParent, type));
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinNetHandlerPlayClient.java b/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinNetHandlerPlayClient.java
new file mode 100644
index 0000000..679da20
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/mixins/MixinNetHandlerPlayClient.java
@@ -0,0 +1,53 @@
+package com.thatgravyboat.skyblockhud.mixins;
+
+import net.minecraft.client.network.NetHandlerPlayClient;
+import net.minecraft.network.play.server.S3EPacketTeams;
+import net.minecraft.scoreboard.Scoreboard;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+
+@Mixin(NetHandlerPlayClient.class)
+public class MixinNetHandlerPlayClient {
+
+ /* DISABLE UNTIL NEW SYSTEM
+ @Inject(method = "handleSetSlot", at = @At("HEAD"))
+ public void onHandleSetSlot(S2FPacketSetSlot packetIn, CallbackInfo ci){
+ if (SkyblockHud.hasSkyblockScoreboard()) {
+ Minecraft mc = Minecraft.getMinecraft();
+ PacketThreadUtil.checkThreadAndEnqueue(packetIn, mc.getNetHandler(), mc);
+ if (packetIn.func_149175_c() == 0) {
+ ItemStack stack = packetIn.func_149174_e();
+
+ if (stack != null && stack.hasTagCompound()) {
+ if (stack.getTagCompound().hasKey("ExtraAttributes")) {
+ NBTTagCompound extraAttributes = stack.getTagCompound().getCompoundTag("ExtraAttributes");
+ String id = extraAttributes.getString("id");
+ ItemStack slotStack = Minecraft.getMinecraft().thePlayer.inventoryContainer.getSlot(packetIn.func_149173_d()).getStack();
+ int changeAmount = stack.stackSize - (slotStack == null ? 0 : slotStack.stackSize);
+ String eId = null;
+ int eLvl = -1;
+ if (extraAttributes.hasKey("enchantments")) {
+ NBTTagCompound enchantments = extraAttributes.getCompoundTag("enchantments");
+ if (enchantments.getKeySet().size() == 1) {
+ for (String e : enchantments.getKeySet()) { eId = e; break; }
+ if (eId != null) eLvl = enchantments.getInteger(eId);
+ }
+ }
+ TrackerHandler.onItemAdded(id, changeAmount, eId, eLvl);
+ }
+ }
+ }
+ }
+ }
+ */
+
+ @Inject(method = "handleTeams", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/network/play/server/S3EPacketTeams;getAction()I", ordinal = 0, shift = At.Shift.BEFORE), cancellable = true)
+ public void handleTeams(S3EPacketTeams packetIn, CallbackInfo ci, Scoreboard scoreboard){
+ //This stops Hypixel from being stupid and spamming our logs because they dont have different ids for things.
+ if (scoreboard.getTeam(packetIn.getName()) != null && packetIn.getAction() == 0) ci.cancel();
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/overlay/DungeonOverlay.java b/src/main/java/com/thatgravyboat/skyblockhud/overlay/DungeonOverlay.java
new file mode 100644
index 0000000..4a3b36f
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/overlay/DungeonOverlay.java
@@ -0,0 +1,150 @@
+package com.thatgravyboat.skyblockhud.overlay;
+
+import com.thatgravyboat.skyblockhud.GuiTextures;
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import com.thatgravyboat.skyblockhud.SpecialColour;
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.config.SBHConfig;
+import com.thatgravyboat.skyblockhud.core.config.Position;
+import com.thatgravyboat.skyblockhud.dungeons.Classes;
+import com.thatgravyboat.skyblockhud.dungeons.DungeonHandler;
+import com.thatgravyboat.skyblockhud.dungeons.DungeonPlayer;
+import com.thatgravyboat.skyblockhud.handlers.BossbarHandler;
+import com.thatgravyboat.skyblockhud.location.LocationHandler;
+import com.thatgravyboat.skyblockhud.location.Locations;
+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.entity.boss.BossStatus;
+import net.minecraftforge.client.GuiIngameForge;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+public class DungeonOverlay extends Gui {
+ private static final FontRenderer font = Minecraft.getMinecraft().fontRendererObj;
+ private static boolean bossBarVisible = false;
+
+ public void drawDungeonPlayer(String name, int health, boolean isDead, Classes dungeonClass, int x, int y) {
+ if (!SkyblockHud.config.dungeon.hideDeadDungeonPlayers || !isDead) {
+ GlStateManager.enableBlend();
+ Minecraft mc = Minecraft.getMinecraft();
+ mc.renderEngine.bindTexture(GuiTextures.dungeon);
+
+ String healthString = isDead ? "DEAD" : Integer.toString(health);
+ GlStateManager.color(1.0F, 1.0F, 1.0F, (float) SkyblockHud.config.dungeon.dungeonPlayerOpacity / 100);
+ drawTexturedModalRect(x, y, 0, dungeonClass.getTextureY(), 120, 32);
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ drawString(font, name, x + 50, y + 6, 0xFFFFFF);
+ drawString(font, healthString, x + 50, y + font.FONT_HEIGHT + 9, 0xFF2B2B);
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ }
+ }
+
+ public void drawDungeonClock(int width, int offset, Minecraft mc){
+ GlStateManager.enableBlend();
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ int dungeonTime = DungeonHandler.getDungeonTime();
+ int dungeonTimeMin = dungeonTime /60;
+ int dungeonTimeSec = dungeonTime - dungeonTimeMin * 60;
+ drawTexturedModalRect((width/ 2) - 17, offset + (bossBarVisible ? 17 : 0), 0, 0, 34, 34);
+ mc.renderEngine.bindTexture(GuiTextures.dungeon);
+ drawTexturedModalRect((width/ 2) - 7, offset + (bossBarVisible ? 20 : 3), 16, 50, 3, 8);
+ drawTexturedModalRect((width/ 2) - 7, offset + (bossBarVisible ? 30 : 13), 19, 50, 3, 8);
+ String dungeonTimeElapsed = (dungeonTimeMin > 9 ? String.valueOf(dungeonTimeMin) : "0" + dungeonTimeMin) + ":" + (dungeonTimeSec > 9 ? String.valueOf(dungeonTimeSec) : "0" + dungeonTimeSec);
+ drawCenteredString(font, dungeonTimeElapsed, (width/ 2), offset + (bossBarVisible ? 40 : 23), 0xFFFF55);
+ //KEYS
+ drawString(font, (DungeonHandler.hasBloodkey() ? "\u2714" : "x"), (width/ 2), offset + (bossBarVisible ? 19 : 2), (DungeonHandler.hasBloodkey() ? 0x55FF55 : 0xAA0000));
+ drawString(font, DungeonHandler.getWitherKeys()+"x", (width/ 2), offset + (bossBarVisible ? 30 : 13), 0x555555);
+ //CLEARED PERCENTAGE
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ int clearPercent = DungeonHandler.getDungeonCleared();
+ String clearPercentage = "Dungeon Cleared: \u00A7" + (clearPercent <= 20 ? "4" : clearPercent <= 50 ? "6" : clearPercent <= 80 ? "e" : "a") + clearPercent + "%";
+ drawTexturedModalRect((width / 2) + 17, offset + (bossBarVisible ? 20 : 3), 2, 34, font.getStringWidth(clearPercentage) + 3, 14);
+ drawTexturedModalRect(((width / 2) + 17) + font.getStringWidth(clearPercentage) + 3, offset + (bossBarVisible ? 20 : 3), 252, 34, 4, 14);
+ drawString(font, clearPercentage, (width / 2) + 18, offset + (bossBarVisible ? 23 : 6), 0xAAAAAA);
+
+ //DEATHS
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ int deaths = DungeonHandler.getDeaths();
+ String deathText = "Deaths: " + deaths;
+ drawTexturedModalRect((width / 2) + 17, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(deathText) + 3, 14);
+ drawTexturedModalRect(((width / 2) + 17) + font.getStringWidth(deathText) + 3, offset + (bossBarVisible ? 35 : 18), 252, 34, 4, 14);
+ drawString(font, deathText, (width / 2) + 18, offset + (bossBarVisible ? 38 : 21), 0xAAAAAA);
+
+ //SECRETS
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ int maxSecrets = DungeonHandler.getMaxSecrets();
+ int secrets = DungeonHandler.getSecrets();
+ int totalSecrets = DungeonHandler.getTotalSecrets();
+ String secretsText = "Secrets: " + secrets + "/" + maxSecrets + " (" + totalSecrets + ")";
+ drawTexturedModalRect((width/ 2) - 17 - (font.getStringWidth(secretsText)) - 4, offset + (bossBarVisible ? 20 : 3), 0, 34, 2, 14);
+ drawTexturedModalRect(((width/ 2) - 17 - (font.getStringWidth(secretsText))) - 2, offset + (bossBarVisible ? 20 : 3), 2, 34, font.getStringWidth(secretsText) + 2, 14);
+ drawString(font, secretsText, (width/ 2) - 17 - (font.getStringWidth(secretsText)) , offset + (bossBarVisible ? 23 : 6), 0xAAAAAA);
+
+ //CRYPTS
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ int crypts = DungeonHandler.getCrypts();
+ String cryptText = "Crypts: " + crypts;
+ drawTexturedModalRect((width/ 2) - 17 - (font.getStringWidth(cryptText)) - 4, offset + (bossBarVisible ? 35 : 18), 0, 34, 2, 14);
+ drawTexturedModalRect(((width/ 2) - 17 - (font.getStringWidth(cryptText))) - 2, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(cryptText) + 2, 14);
+ drawString(font, cryptText, (width/ 2) - 17 - (font.getStringWidth(cryptText)) , offset + (bossBarVisible ? 38 : 21), 0xAAAAAA);
+ }
+
+ public void drawUltimateBar(Minecraft mc, ScaledResolution resolution){
+ if (!SkyblockHud.config.dungeon.hideUltimateBar) {
+ float percentage = mc.thePlayer.experience;
+ SBHConfig.DungeonHud dungeonHud = SkyblockHud.config.dungeon;
+ Position position = dungeonHud.barPosition;
+
+ int x = position.getAbsX(resolution, 182);
+ int y = position.getAbsY(resolution, 5);
+
+ GenericOverlays.drawLargeBar(mc,
+ x - 91, y,
+ percentage, 0.999f,
+ SpecialColour.specialToChromaRGB(dungeonHud.barLoadColor),
+ SpecialColour.specialToChromaRGB(dungeonHud.barFullColor),
+ dungeonHud.barStyle);
+ }
+ }
+
+ @SubscribeEvent
+ public void renderOverlay(RenderGameOverlayEvent.Post event) {
+ Minecraft mc = Minecraft.getMinecraft();
+ if (Utils.overlayShouldRender(event.type, SkyblockHud.hasSkyblockScoreboard(), LocationHandler.getCurrentLocation().equals(Locations.CATACOMBS))) {
+ bossBarVisible = BossStatus.statusBarTime > 0 && GuiIngameForge.renderBossHealth && BossbarHandler.bossBarRendered;
+ GlStateManager.enableBlend();
+ drawUltimateBar(mc, event.resolution);
+
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ if (!SkyblockHud.config.dungeon.hideDungeonPlayers) {
+ int[] hardCodedPos = new int[]{5, 42, 79, 116};
+ Position[] positions = new Position[]{SkyblockHud.config.dungeon.dungeonPlayer1, SkyblockHud.config.dungeon.dungeonPlayer2, SkyblockHud.config.dungeon.dungeonPlayer3, SkyblockHud.config.dungeon.dungeonPlayer4};
+ for (int i = 0; i < Math.min(DungeonHandler.getDungeonPlayers().values().size(), 4); i++) {
+ DungeonPlayer player = (DungeonPlayer) DungeonHandler.getDungeonPlayers().values().toArray()[i];
+ int posX;
+ int posY;
+ try {
+ posX = positions[i].getAbsX(event.resolution, 120);
+ } catch (ArrayIndexOutOfBoundsException ignored) {
+ posX = hardCodedPos[i];
+ }
+ try {
+ posY = positions[i].getAbsY(event.resolution, 120);
+ } catch (ArrayIndexOutOfBoundsException ignored) {
+ posY = 0;
+ }
+ drawDungeonPlayer(player.getName(), player.getHealth(), player.isDead(), player.getDungeonClass(), posX, posY);
+ }
+ }
+ drawDungeonClock(event.resolution.getScaledWidth(), SkyblockHud.config.main.mainHudPos.getAbsY(event.resolution, 34), mc);
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/overlay/GenericOverlays.java b/src/main/java/com/thatgravyboat/skyblockhud/overlay/GenericOverlays.java
new file mode 100644
index 0000000..d987c86
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/overlay/GenericOverlays.java
@@ -0,0 +1,44 @@
+package com.thatgravyboat.skyblockhud.overlay;
+
+import com.thatgravyboat.skyblockhud.GuiTextures;
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.renderer.GlStateManager;
+
+import java.awt.Color;
+
+
+public class GenericOverlays extends Gui {
+
+ public static void drawLargeBar(Minecraft mc, int x, int y, float percentage, float max, int fullColor, int loadingColor, int barStyle){
+ if (SkyblockHud.hasSkyblockScoreboard()) {
+ mc.renderEngine.bindTexture(GuiTextures.bars);
+ Color color = new Color(percentage == max ? fullColor : loadingColor);
+
+ RenderUtils.drawTexturedModalRect(x, y, 0, 0, 182, 5);
+ GlStateManager.color(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
+ RenderUtils.drawTexturedModalRect(x, y, 0, 30, 182, 5);
+ RenderUtils.drawTexturedModalRect(x, y, 0, 5, (int) (182 * percentage), 5);
+ if (barStyle != 0) {
+ RenderUtils.drawTexturedModalRect(x, y, 0, 5 + (barStyle * 5), 182, 5);
+ }
+ }
+ }
+
+ public static void drawSmallBar(Minecraft mc, int x, int y, double percentage, double max, int fullColor, int loadingColor, int barStyle){
+ if (SkyblockHud.hasSkyblockScoreboard()) {
+ mc.renderEngine.bindTexture(GuiTextures.bars);
+ Color color = new Color(percentage == max ? fullColor : loadingColor);
+ GlStateManager.enableBlend();
+ RenderUtils.drawTexturedModalRect(x, y, 0, 35, 62, 5);
+ GlStateManager.color(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
+ RenderUtils.drawTexturedModalRect(x, y, 0, 65, 62, 5);
+ RenderUtils.drawTexturedModalRect(x, y, 0, 40, (int) (62 * percentage), 5);
+ if (barStyle != 0) {
+ RenderUtils.drawTexturedModalRect(x, y, 0, 45 + (barStyle * 5), 62, 5);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/overlay/OverlayHud.java b/src/main/java/com/thatgravyboat/skyblockhud/overlay/OverlayHud.java
new file mode 100644
index 0000000..8cdd44c
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/overlay/OverlayHud.java
@@ -0,0 +1,286 @@
+package com.thatgravyboat.skyblockhud.overlay;
+
+import com.thatgravyboat.skyblockhud.GuiTextures;
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.handlers.BossbarHandler;
+import com.thatgravyboat.skyblockhud.handlers.CurrencyHandler;
+import com.thatgravyboat.skyblockhud.handlers.SlayerHandler;
+import com.thatgravyboat.skyblockhud.handlers.TimeHandler;
+import com.thatgravyboat.skyblockhud.location.*;
+import com.thatgravyboat.skyblockhud.seasons.SeasonDateHandler;
+import com.thatgravyboat.skyblockhud.seasons.Season;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.entity.boss.BossStatus;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.client.GuiIngameForge;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+
+public class OverlayHud extends Gui {
+ private static final FontRenderer font = Minecraft.getMinecraft().fontRendererObj;
+
+ //STATS
+ private static boolean eventToggle;
+
+ public static boolean bossBarVisible = false;
+
+ public void drawClock(int width, int offset, Minecraft mc){
+ GlStateManager.enableBlend();
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ //CLOCK
+ int timeMin = (int) (TimeHandler.time / 60);
+ int timeHour = timeMin / 60;
+ timeMin = timeMin - (timeHour * 60);
+ String militaryTime = timeHour + ":" + (timeMin == 0 ? timeMin + "0" : timeMin);
+ int time12Hour = timeHour >= 12 ? timeHour - 12 : timeHour;
+ String normalTime = (time12Hour == 0 ? "00" : String.valueOf(time12Hour)) + ":" + (timeMin == 0 ? "00" : timeMin) + (timeHour >= 12 ? "pm" : "am");
+
+ drawTexturedModalRect((width/ 2) - 17, offset + (bossBarVisible ? 17 : 0), 0, 0, 34, 34);
+ drawTexturedModalRect((width/ 2) - 4, offset + (bossBarVisible ? 24 : 7), (timeHour > 19 || timeHour < 4) ? 43 : 43 + 8, 0, 8, 8);
+ if (SkyblockHud.config.main.twelveHourClock) drawScaledString(0.8f, width/ 2, offset + (bossBarVisible ? 38 : 21), normalTime, (timeHour > 19 || timeHour < 4) ? 0xAFB8CC : 0xFFFF55);
+ else drawCenteredString(font, militaryTime, (width/ 2), offset + (bossBarVisible ? 38 : 21), (timeHour > 19 || timeHour < 4) ? 0xAFB8CC : 0xFFFF55);
+
+ //PURSE
+ drawPurseAndBits(width, offset, mc);
+
+ //SEASON/DATE
+ drawSeasonAndDate(width, offset, mc);
+
+ //REDSTONE PERCENT
+ drawRedstone(width, offset, mc);
+
+ // LOCATION
+ drawLocation(width, offset, mc);
+
+ //EXTRA SLOT
+ if (LocationHandler.getCurrentLocation().equals(Locations.YOURISLAND)) {
+ if (IslandHandler.flightTime > 0) drawFlightDuration(width, offset, mc);
+ }
+ else if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.MUSHROOMDESERT)){
+ drawTrapperOrPelts(width, offset, mc);
+ }
+ else if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.DWARVENMINES)) {
+ if (DwarvenMineHandler.currentEvent != DwarvenMineHandler.Event.NONE){
+ drawDwarvenEvent(width, offset, mc);
+ }else {
+ drawMithril(width, offset, mc);
+ }
+ }
+ else if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.PARK) && ParkIslandHandler.isRaining()){
+ if (LocationHandler.getCurrentLocation().equals(Locations.HOWLINGCAVE)){
+ drawSlayer(width, offset, mc);
+ }else drawRainDuration(width, offset, mc);
+ }else if (SlayerHandler.isDoingSlayer){
+ drawSlayer(width, offset, mc);
+ }
+ }
+
+ public void drawSeasonAndDate(int width, int offset, Minecraft mc){
+ if (SeasonDateHandler.getCurrentSeason() != Season.ERROR) {
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ if (mc.thePlayer.ticksExisted % 100 == 0 && eventToggle) eventToggle = false;
+ if (mc.thePlayer.ticksExisted % 600 == 0) eventToggle = true;
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ String dateText = SeasonDateHandler.getFancySeasonAndDate();
+ if (eventToggle && !SeasonDateHandler.getCurrentEvent().isEmpty() && !SeasonDateHandler.getCurrentEventTime().isEmpty()) dateText = SeasonDateHandler.getCurrentEvent().trim() + " " + SeasonDateHandler.getCurrentEventTime().trim();
+ drawTexturedModalRect((width / 2) + 17, offset + (bossBarVisible ? 20 : 3), 2, 34, font.getStringWidth(dateText) + 9, 14);
+ drawTexturedModalRect(((width / 2) + 17) + font.getStringWidth(dateText) + 9, offset + (bossBarVisible ? 20 : 3), 252, 34, 4, 14);
+ drawTexturedModalRect(((width / 2) + 17) + font.getStringWidth(dateText) + 2, offset + (bossBarVisible ? 23 : 6), SeasonDateHandler.getCurrentSeason().getTextureX(), 16, 8, 8);
+ drawString(font, dateText, (width / 2) + 18, offset + (bossBarVisible ? 23 : 6), 0xffffff);
+ }
+ }
+
+ public void drawLocation(int width, int offset, Minecraft mc){
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ drawTexturedModalRect((width/ 2) - 33 - (font.getStringWidth(LocationHandler.getCurrentLocation().getDisplayName())), offset + (bossBarVisible ? 20 : 3), 0, 34, 2, 14);
+ drawTexturedModalRect(((width/ 2) - 33 - (font.getStringWidth(LocationHandler.getCurrentLocation().getDisplayName()))) + 2, offset + (bossBarVisible ? 20 : 3), 2, 34, font.getStringWidth(LocationHandler.getCurrentLocation().getDisplayName()) + 14, 14);
+ drawTexturedModalRect(((width/ 2) - 33 - (font.getStringWidth(LocationHandler.getCurrentLocation().getDisplayName()))) + 4, offset + (bossBarVisible ? 23 : 6), LocationHandler.getCurrentLocation().getCategory().getTexturePos(), 8, 8, 8);
+ drawString(font,
+ LocationHandler.getCurrentLocation().getDisplayName(),
+ (width/ 2) - 19 - (font.getStringWidth(LocationHandler.getCurrentLocation().getDisplayName())) ,
+ offset + (bossBarVisible ? 23 : 6),
+ 0xFFFFFF
+ );
+ }
+
+ public void drawRedstone(int width, int offset, Minecraft mc){
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ int redstoneColor = IslandHandler.redstone > 90 ? 0xFF0000 : IslandHandler.redstone > 75 ? 0xC45B00 : IslandHandler.redstone > 50 ? 0xFFFF55 : 0x55FF55;
+ if (IslandHandler.redstone > 0 && Utils.isPlayerHoldingRedstone(mc.thePlayer)) {
+ drawTexturedModalRect((width/ 2) - 15, offset + (bossBarVisible ? 51 : 34), 0, 48, 30, 18);
+ drawTexturedModalRect((width/ 2) - 4, offset + (bossBarVisible ? 51 : 34), 59, 0, 8, 8);
+ drawCenteredString(mc.fontRendererObj, IslandHandler.redstone+"%", (width/ 2), offset + (bossBarVisible ? 58 : 41), redstoneColor);
+ }
+ }
+
+ public void drawPurseAndBits(int width, int offset, Minecraft mc){
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ int xPos = (width/ 2) + 17;
+
+ //COINS
+ drawTexturedModalRect(xPos, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(CurrencyHandler.getCoinsFormatted()) + 11, 14);
+ drawTexturedModalRect(xPos + 1, offset + (bossBarVisible ? 37 : 20), 34, 0, 8, 8);
+ drawString(font, CurrencyHandler.getCoinsFormatted(), xPos + 10, offset + (bossBarVisible ? 38 : 21), 0xFFAA00);
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ xPos += font.getStringWidth(CurrencyHandler.getCoinsFormatted()) + 11;
+
+ //BITS
+ if (CurrencyHandler.getBits() > 0) {
+ drawTexturedModalRect(xPos, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(CurrencyHandler.getBitsFormatted()) + 11, 14);
+ drawTexturedModalRect(xPos + 1, offset + (bossBarVisible ? 37 : 20), 75, 0, 8, 8);
+ drawString(font, CurrencyHandler.getBitsFormatted(), xPos + 10, offset + (bossBarVisible ? 38 : 21), 0x55FFFF);
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ xPos += font.getStringWidth(CurrencyHandler.getBitsFormatted()) + 11;
+ }
+
+ drawTexturedModalRect(xPos, offset + (bossBarVisible ? 35 : 18), 252, 34, 4, 14);
+ }
+
+ public void drawFlightDuration(int width, int offset, Minecraft mc ){
+ if (LocationHandler.getCurrentLocation().equals(Locations.YOURISLAND)){
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ DecimalFormat flightFormat = new DecimalFormat("#.#", DecimalFormatSymbols.getInstance(Locale.CANADA));
+ String duration;
+ if (IslandHandler.flightTime < 60) duration = IslandHandler.flightTime + "s";
+ else if (IslandHandler.flightTime < 3600) duration = flightFormat.format((double)IslandHandler.flightTime / 60) + "m";
+ else if (IslandHandler.flightTime < 86400) duration = flightFormat.format((double)IslandHandler.flightTime / 3600) + "hr";
+ else if (IslandHandler.flightTime < 86460) duration = flightFormat.format((double)IslandHandler.flightTime / 86400) + "day";
+ else duration = flightFormat.format((double)IslandHandler.flightTime / 86400) + "days";
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ drawTexturedModalRect((width/ 2) - 33 - (font.getStringWidth(duration)), offset + (bossBarVisible ? 35 : 18), 0, 34, 2, 14);
+ drawTexturedModalRect(((width/ 2) - 33 - (font.getStringWidth(duration))) + 2, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(duration) + 14, 14);
+ drawTexturedModalRect(((width/ 2) - 33 - (font.getStringWidth(duration))) + 4, offset + (bossBarVisible ? 38 : 21), 67, 0, 8, 8);
+ drawString(font, duration, (width/ 2) - 19 - (font.getStringWidth(duration)) , offset + (bossBarVisible ? 38 : 21), 0xFFFFFF);
+ }
+ }
+
+ public void drawRainDuration(int width, int offset, Minecraft mc ){
+ if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.PARK)){
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ String duration = "Rain: " + ParkIslandHandler.getRainTime();
+ drawTexturedModalRect((width/ 2) - 33 - (font.getStringWidth(duration)), offset + (bossBarVisible ? 35 : 18), 0, 34, 2, 14);
+ drawTexturedModalRect(((width/ 2) - 33 - (font.getStringWidth(duration))) + 2, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(duration) + 14, 14);
+ drawTexturedModalRect(((width/ 2) - 33 - (font.getStringWidth(duration))) + 4, offset + (bossBarVisible ? 38 : 21), 83, 0, 8, 8);
+ drawString(font, duration, (width/ 2) - 19 - (font.getStringWidth(duration)) , offset + (bossBarVisible ? 38 : 21), 0xFFFFFF);
+ }
+ }
+
+ public void drawSlayer(int width, int offset, Minecraft mc){
+ if (SlayerHandler.isDoingSlayer){
+ int kills = SlayerHandler.progress;
+ int maxKills = SlayerHandler.maxKills;
+ int tier = SlayerHandler.slayerTier;
+ SlayerHandler.slayerTypes slayerType = SlayerHandler.currentSlayer;
+ if (slayerType != SlayerHandler.slayerTypes.NONE) {
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(EnumChatFormatting.GREEN);
+ stringBuilder.append(Utils.intToRomanNumeral(tier));
+ stringBuilder.append(" ");
+ if (SlayerHandler.isKillingBoss){
+ stringBuilder.append(EnumChatFormatting.RED);
+ stringBuilder.append("Slay Boss!");
+ } else if (SlayerHandler.bossSlain){
+ stringBuilder.append(EnumChatFormatting.RED);
+ stringBuilder.append("Boss Slain!");
+ } else if (kills == 0 && maxKills == 0){
+ stringBuilder.append(EnumChatFormatting.RED);
+ stringBuilder.append("Not Slaying!");
+ } else {
+ stringBuilder.append(EnumChatFormatting.YELLOW);
+ stringBuilder.append(kills);
+ stringBuilder.append(EnumChatFormatting.GRAY);
+ stringBuilder.append("/");
+ stringBuilder.append(EnumChatFormatting.RED);
+ stringBuilder.append(maxKills);
+ }
+ String text = stringBuilder.toString();
+ drawTexturedModalRect((width / 2) - 33 - (font.getStringWidth(text)), offset + (bossBarVisible ? 35 : 18), 0, 34, 2, 14);
+ drawTexturedModalRect(((width / 2) - 33 - (font.getStringWidth(text))) + 2, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(text) + 14, 14);
+ drawTexturedModalRect(((width / 2) - 33 - (font.getStringWidth(text))) + 4, offset + (bossBarVisible ? 38 : 21), slayerType.getX(), 24, 8, 8);
+ drawString(font, text, (width / 2) - 19 - (font.getStringWidth(text)), offset + (bossBarVisible ? 38 : 21), 0xFFFFFF);
+
+ }
+ }
+ }
+
+ public void drawMithril(int width, int offset, Minecraft mc){
+ if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.DWARVENMINES)){
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ String mithril = DwarvenMineHandler.getMithrilFormatted();
+ drawTexturedModalRect((width/ 2) - 33 - (font.getStringWidth(mithril)), offset + (bossBarVisible ? 35 : 18), 0, 34, 2, 14);
+ drawTexturedModalRect(((width/ 2) - 33 - (font.getStringWidth(mithril))) + 2, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(mithril) + 14, 14);
+ drawTexturedModalRect(((width/ 2) - 33 - (font.getStringWidth(mithril))) + 4, offset + (bossBarVisible ? 38 : 21), 91, 0, 8, 8);
+ drawString(font, mithril, (width/ 2) - 19 - (font.getStringWidth(mithril)) , offset + (bossBarVisible ? 38 : 21), 0x00C896);
+ }
+ }
+
+ public void drawTrapperOrPelts(int width, int offset, Minecraft mc){
+ if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.MUSHROOMDESERT)){
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ String duration = FarmingIslandHandler.location != Locations.NONE ? FarmingIslandHandler.location.getDisplayName() : ""+FarmingIslandHandler.pelts;
+ drawTexturedModalRect((width/ 2) - 33 - (font.getStringWidth(duration)), offset + (bossBarVisible ? 35 : 18), 0, 34, 2, 14);
+ drawTexturedModalRect(((width/ 2) - 33 - (font.getStringWidth(duration))) + 2, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(duration) + 14, 14);
+ drawTexturedModalRect(((width/ 2) - 33 - (font.getStringWidth(duration))) + 4, offset + (bossBarVisible ? 38 : 21), FarmingIslandHandler.location != Locations.NONE ? 123 : 115, 0, 8, 8);
+ drawString(font, duration, (width/ 2) - 19 - (font.getStringWidth(duration)) , offset + (bossBarVisible ? 38 : 21), 0xFFFFFF);
+ }
+ }
+
+ public void drawDwarvenEvent(int width, int offset, Minecraft mc){
+ if (LocationHandler.getCurrentLocation().getCategory().equals(LocationCategory.DWARVENMINES)){
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ mc.renderEngine.bindTexture(GuiTextures.overlay);
+ if (DwarvenMineHandler.eventMax > 0) {
+ String duration = DwarvenMineHandler.eventProgress + "/" + DwarvenMineHandler.eventMax;
+ drawTexturedModalRect((width / 2) - 33 - (font.getStringWidth(duration)), offset + (bossBarVisible ? 35 : 18), 0, 34, 2, 14);
+ drawTexturedModalRect(((width / 2) - 33 - (font.getStringWidth(duration))) + 2, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(duration) + 14, 14);
+ drawTexturedModalRect(((width / 2) - 33 - (font.getStringWidth(duration))) + 4, offset + (bossBarVisible ? 38 : 21), DwarvenMineHandler.currentEvent.x, 0, 8, 8);
+ drawString(font, duration, (width / 2) - 19 - (font.getStringWidth(duration)), offset + (bossBarVisible ? 38 : 21), 0xFFFFFF);
+ }else {
+ String text = DwarvenMineHandler.currentEvent.displayName;
+ drawTexturedModalRect((width / 2) - 33 - (font.getStringWidth(text)), offset + (bossBarVisible ? 35 : 18), 0, 34, 2, 14);
+ drawTexturedModalRect(((width / 2) - 33 - (font.getStringWidth(text))) + 2, offset + (bossBarVisible ? 35 : 18), 2, 34, font.getStringWidth(text) + 14, 14);
+ drawTexturedModalRect(((width / 2) - 33 - (font.getStringWidth(text))) + 4, offset + (bossBarVisible ? 38 : 21), DwarvenMineHandler.currentEvent.x, 0, 8, 8);
+ drawString(font, text, (width / 2) - 19 - (font.getStringWidth(text)), offset + (bossBarVisible ? 38 : 21), 0xFFFFFF);
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public void renderOverlay(RenderGameOverlayEvent.Post event) {
+ if (Utils.overlayShouldRender(event.type, SkyblockHud.hasSkyblockScoreboard())) {
+ bossBarVisible = BossStatus.statusBarTime > 0 && GuiIngameForge.renderBossHealth && BossbarHandler.bossBarRendered;
+ Minecraft mc = Minecraft.getMinecraft();
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ if (LocationHandler.getCurrentLocation() != Locations.CATACOMBS){
+ drawClock(event.resolution.getScaledWidth(), SkyblockHud.config.main.mainHudPos.getAbsY(event.resolution, 34), mc);
+ }
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ }
+ }
+
+
+ public void drawScaledString(float factor, int x, int y, String text, int color){
+ GlStateManager.scale(factor, factor, 1);
+ drawCenteredString(font, text, (int)(x/factor), (int)(y/factor), color);
+ GlStateManager.scale(1/factor, 1/factor, 1);
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/overlay/RPGHud.java b/src/main/java/com/thatgravyboat/skyblockhud/overlay/RPGHud.java
new file mode 100644
index 0000000..06cf94a
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/overlay/RPGHud.java
@@ -0,0 +1,102 @@
+package com.thatgravyboat.skyblockhud.overlay;
+
+import com.mojang.realmsclient.gui.ChatFormatting;
+import com.thatgravyboat.skyblockhud.GuiTextures;
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.core.config.Position;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+
+public class RPGHud extends Gui {
+ private static int mana, maxMana, overflow = 0;
+ private static int health, maxHealth = 0;
+ private static int defense = 0;
+
+ public static void updateMana(int current, int max){
+ mana = current;
+ maxMana = max;
+ }
+
+ public static void updateOverflow(int current){
+ overflow = current;
+ }
+
+ public static void updateHealth(int current, int max){
+ health = current;
+ maxHealth = max;
+ }
+ public static void updateDefense(int input){ defense = input; }
+
+ public static void manaPredictionUpdate(boolean isIncrease, int decrease){
+ mana = isIncrease ? Math.min(mana + (maxMana / 50), maxMana) : mana - decrease;
+ }
+
+ private static final DecimalFormat decimalFormat = new DecimalFormat("#.##");
+
+ static {
+ decimalFormat.setGroupingUsed(true);
+ decimalFormat.setGroupingSize(3);
+ }
+
+ @SubscribeEvent
+ public void renderOverlay(RenderGameOverlayEvent.Post event) {
+ if (Utils.overlayShouldRender(event.type, SkyblockHud.hasSkyblockScoreboard(), SkyblockHud.config.renderer.hideXpBar))
+ MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Post(new RenderGameOverlayEvent(event.partialTicks, event.resolution), RenderGameOverlayEvent.ElementType.EXPERIENCE));
+ if (Utils.overlayShouldRender(event.type, SkyblockHud.hasSkyblockScoreboard(), SkyblockHud.config.rpg.showRpgHud)) {
+ Minecraft mc = Minecraft.getMinecraft();
+ GlStateManager.enableBlend();
+ GlStateManager.color(1.0f,1.0f,1.0f,1.0f);
+ FontRenderer font = mc.fontRendererObj;
+ if (mc.thePlayer.getHealth() < mc.thePlayer.getMaxHealth()){
+ health = Math.max((int) (maxHealth * (mc.thePlayer.getHealth() / mc.thePlayer.getMaxHealth())), health);
+ }
+
+ mc.renderEngine.bindTexture(GuiTextures.playerStat);
+ Position position = SkyblockHud.config.rpg.rpgHudPosition;
+
+ int x = position.getAbsX(event.resolution, 120);
+ int y = position.getAbsY(event.resolution, 47);
+
+ boolean rightAligned = position.rightAligned(event.resolution, 120);
+
+ drawTexturedModalRect(x,y,rightAligned ? 131 : 5,6,120,47);
+ float manaWidth = Math.min(57 * ((float)mana/(float)maxMana), 57);
+ drawTexturedModalRect(rightAligned ? x + 16 : 47 + x,17 + y,rightAligned ? 199 : 0,64,(int)manaWidth,4);
+ float healthWidth = Math.min(70 * ((float)health/(float)maxHealth), 70);
+ drawTexturedModalRect(rightAligned ? x + 3 : 47+ x,22+ y,rightAligned ? 186 : 0,68,(int)healthWidth,5);
+
+ if (health > maxHealth) {
+ float absorptionWidth = Math.min(70 * ((float) (health - maxHealth) / (float) maxHealth), 70);
+ drawTexturedModalRect(rightAligned ? x + 3 : 47 + x, 22 + y, rightAligned ? 186 : 0, 77, (int) absorptionWidth, 5);
+ }
+
+ float xpWidth = 67 * mc.thePlayer.experience;
+ drawTexturedModalRect(rightAligned ? x + 7 : 45+ x,28+ y,rightAligned ? 189 : 0,73,(int)xpWidth,4);
+ //Air in water
+ NumberFormat myFormat = NumberFormat.getInstance();
+ myFormat.setGroupingUsed(true);
+ if (mc.thePlayer.getAir() < 300){
+ float airWidth = 60 * ((float)mc.thePlayer.getAir() / 300);
+ drawTexturedModalRect(rightAligned ? x + 17 : 39 + x,33+ y,rightAligned ? 192 : 0,82,64,6);
+ drawTexturedModalRect(rightAligned ? x + 19 : 41+ x,33+ y,rightAligned ? 196 : 0,88,(int)airWidth,4);
+ }
+ GlStateManager.scale(0.75f, 0.75f, 1);
+ drawCenteredString(mc.fontRendererObj, ""+mc.thePlayer.experienceLevel, (rightAligned ? 130 : 0) + (int)(15+ x/0.75f), (int)(45+ y/0.75f), 8453920);
+ GlStateManager.scale(1/0.75f, 1/0.75f, 1);
+ GlStateManager.scale(0.75f, 0.75f, 1);
+ font.drawString( ChatFormatting.RED + " \u2764 " + health +"/"+maxHealth,(rightAligned ? -40 : 0) + (int)(64+ x/0.75f), (int)(8+ y/0.75f), 0xffffff, false);
+ GlStateManager.scale(1/0.75f, 1/0.75f, 1);
+ GlStateManager.color(255,255,255);
+ GlStateManager.disableBlend();
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/playerstats/ActionBarParsing.java b/src/main/java/com/thatgravyboat/skyblockhud/playerstats/ActionBarParsing.java
new file mode 100644
index 0000000..b287ad6
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/playerstats/ActionBarParsing.java
@@ -0,0 +1,131 @@
+package com.thatgravyboat.skyblockhud.playerstats;
+
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.overlay.RPGHud;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.IChatComponent;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ActionBarParsing {
+
+ private static String lastActionBar = "";
+ private static String lastLowActionBar = "";
+ private static IChatComponent lastLowEditedActionBar = null;
+
+ private static final Pattern HealthRegex = Pattern.compile("([0-9]+)/([0-9]+)\u2764");
+ private static final Pattern HealingRegex = Pattern.compile("\\+([0-9]+)[\u2586\u2585\u2584\u2583\u2582\u2581]");
+ private static final Pattern DefenseRegex = Pattern.compile("([0-9]+)\u2748 Defense");
+ private static final Pattern ManaRegex = Pattern.compile("([0-9]+)/([0-9]+)\u270E Mana");
+ private static final Pattern ManaOverflowRegex = Pattern.compile("([0-9]+)/([0-9]+)\u270E ([0-9]+)\u02AC");
+ private static final Pattern ManaDecreaseRegex = Pattern.compile("-([0-9]+) Mana \\(");
+ private static final Pattern XpGainRegex = Pattern.compile("\\+(\\d*\\.?\\d*) (Farming|Mining|Combat|Foraging|Fishing|Enchanting|Alchemy|Carpentry|Runecrafting) \\((\\d*\\.?\\d*)%\\)");
+
+ private static final Pattern HealthReplaceRegex = Pattern.compile("\u00A7c([0-9]+)/([0-9]+)\u2764");
+ private static final Pattern HealingReplaceRegex = Pattern.compile("\\+\u00A7c([0-9]+)[\u2586\u2585\u2584\u2583\u2582\u2581]");
+ private static final Pattern HealthAbsorptionReplaceRegex = Pattern.compile("\u00A76([0-9]+)/([0-9]+)\u2764");
+ private static final Pattern DefenseReplaceRegex = Pattern.compile("\u00A7a([0-9]+)\u00A7a\u2748 Defense");
+ private static final Pattern ManaReplaceRegex = Pattern.compile("\u00A7b([0-9]+)/([0-9]+)\u270E Mana");
+ private static final Pattern ManaOverflowReplaceRegex = Pattern.compile("\u00A7b([0-9]+)/([0-9]+)\u270E \u00A73([0-9]+)\u02AC");
+
+ private static int ticksSinceLastPrediction = 0;
+ private static boolean predict = false;
+
+ @SubscribeEvent
+ public void tick(TickEvent.ClientTickEvent event){
+ if (predict) {
+ ticksSinceLastPrediction++;
+ if (ticksSinceLastPrediction == 20 && SkyblockHud.config.rpg.showRpgHud) {
+ ticksSinceLastPrediction = 0;
+ RPGHud.manaPredictionUpdate(true, 0);
+ }
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGHEST)
+ public void onStatusBarHigh(ClientChatReceivedEvent event){
+ if (event.type == 2 && SkyblockHud.hasSkyblockScoreboard() && SkyblockHud.config.rpg.showRpgHud){
+ parseActionBar(event.message.getUnformattedText());
+ }
+ }
+
+
+ @SubscribeEvent(priority = EventPriority.LOW)
+ public void onStatusBarLow(ClientChatReceivedEvent event){
+ if (event.type == 2 && SkyblockHud.hasSkyblockScoreboard() && SkyblockHud.config.rpg.showRpgHud){
+ String message = event.message.getUnformattedText();
+ if (lastLowEditedActionBar == null || !lastLowActionBar.equals(message)){
+ lastLowActionBar = message;
+ message = HealthReplaceRegex.matcher(message).replaceAll("");
+ message = HealthAbsorptionReplaceRegex.matcher(message).replaceAll("");
+ message = DefenseReplaceRegex.matcher(message).replaceAll("");
+ message = ManaReplaceRegex.matcher(message).replaceAll("");
+ message = ManaOverflowReplaceRegex.matcher(message).replaceAll("");
+
+ lastLowEditedActionBar = new ChatComponentText(message.trim());
+ }
+ event.message = lastLowEditedActionBar;
+ }
+ }
+
+ public static void parseActionBar(String input){
+ if (!lastActionBar.equals(input)) {
+ lastActionBar = input;
+ String bar = Utils.removeColor(input);
+
+ Matcher HealthMatcher = HealthRegex.matcher(bar);
+ Matcher DefenseMatcher = DefenseRegex.matcher(bar);
+ Matcher ManaMatcher = ManaRegex.matcher(bar);
+ Matcher ManaUseMatcher = ManaDecreaseRegex.matcher(bar);
+ Matcher ManaOverflowMatcher = ManaOverflowRegex.matcher(bar);
+ Matcher XpGainMatcher = XpGainRegex.matcher(bar);
+
+ boolean healthFound = HealthMatcher.find();
+ boolean defenseFound = DefenseMatcher.find();
+ boolean manaFound = ManaMatcher.find();
+ boolean manaUseFound = ManaUseMatcher.find();
+ boolean manaOverflowFound = ManaOverflowMatcher.find();
+ boolean xpFound = XpGainMatcher.find();
+
+
+ if (healthFound) {
+ try {
+ RPGHud.updateHealth(Integer.parseInt(HealthMatcher.group(1)), Integer.parseInt(HealthMatcher.group(2)));
+ }catch (Exception ignored){}
+ }
+ if (defenseFound) {
+ try {
+ RPGHud.updateDefense(Integer.parseInt(DefenseMatcher.group(1)));
+ }catch (Exception ignored){}
+ }else if (!xpFound && !manaUseFound){
+ RPGHud.updateDefense(0);
+ }
+ if (manaFound) {
+ try {
+ RPGHud.updateMana(Integer.parseInt(ManaMatcher.group(1)), Integer.parseInt(ManaMatcher.group(2)));
+ }catch (Exception ignored){}
+ }
+ if (!manaFound && manaOverflowFound){
+ try {
+ RPGHud.updateMana(Integer.parseInt(ManaOverflowMatcher.group(1)), Integer.parseInt(ManaOverflowMatcher.group(2)));
+ RPGHud.updateOverflow(Integer.parseInt(ManaOverflowMatcher.group(3)));
+ }catch (Exception ignored){}
+ }
+ if (!manaFound){
+ if (manaUseFound) {
+ try {
+ RPGHud.manaPredictionUpdate(false, Integer.parseInt(ManaUseMatcher.group(1)));
+ } catch (Exception ignored) {}
+ }
+ RPGHud.manaPredictionUpdate(true, 0);
+ }
+ predict = !manaFound;
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/seasons/Season.java b/src/main/java/com/thatgravyboat/skyblockhud/seasons/Season.java
new file mode 100644
index 0000000..e55c0fa
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/seasons/Season.java
@@ -0,0 +1,50 @@
+package com.thatgravyboat.skyblockhud.seasons;
+
+public enum Season {
+ EARLYSPRING("earlyspring", "Early Spring", 34, 0),
+ SPRING("spring", "Spring", 34, 31),
+ LATESPRING("latespring", "Late Spring", 34, 62),
+ EARLYSUMMER("earlysummer", "Early Summer", 42, 93),
+ SUMMER("summer", "Summer", 42, 124),
+ LATESUMMER("latesummer", "Late Summer", 42, 155),
+ EARLYAUTUMN("earlyautumn", "Early Autumn", 50, 186),
+ AUTUMN("autumn", "Autumn", 50, 217),
+ LATEAUTUMN("lateautumn", "Late Autumn", 50, 248),
+ EARLYWINTER("earlywinter", "Early Winter", 58, 279),
+ WINTER("winter", "Winter", 58, 310),
+ LATEWINTER("latewinter", "Late Winter", 58, 341),
+ ERROR("error", "Error", 0, -1);
+
+
+
+
+ private final String name;
+ private final String displayName;
+ private final int textureX;
+ private final int yearStartDay;
+
+ Season(String name, String displayName, int textureX, int yearStartDay){
+ this.name = name;
+ this.displayName = displayName;
+ this.textureX = textureX;
+ this.yearStartDay = yearStartDay;
+ }
+
+ public String getName(){
+ return this.name;
+ }
+ public String getDisplayName(){
+ return this.displayName;
+ }
+ public int getTextureX() { return this.textureX; }
+ public int getYearStartDay() { return yearStartDay; }
+
+ public static Season get(String id) {
+ try {
+ return Season.valueOf(id);
+ } catch (IllegalArgumentException ex) {
+ return ERROR;
+ }
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/seasons/SeasonDateHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/seasons/SeasonDateHandler.java
new file mode 100644
index 0000000..1e22afc
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/seasons/SeasonDateHandler.java
@@ -0,0 +1,57 @@
+package com.thatgravyboat.skyblockhud.seasons;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.api.events.SidebarLineUpdateEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.regex.Pattern;
+
+public class SeasonDateHandler {
+
+ private static Season currentSeason = Season.ERROR;
+ private static int currentDate = 1;
+ private static String currentEvent = "";
+ private static String eventTime = "";
+
+
+ @SubscribeEvent
+ public void onSidebarLineUpdate(SidebarLineUpdateEvent event){
+ if (Season.get(SeasonDateHandler.removeDate(event.formattedLine.toLowerCase()).toUpperCase()) != Season.ERROR) {
+ SeasonDateHandler.setCurrentDateAndSeason(SeasonDateHandler.removeSeason(Utils.removeColor(event.formattedLine.toLowerCase().trim())), SeasonDateHandler.removeDate(Utils.removeColor(event.formattedLine.toLowerCase().trim())).toUpperCase());
+ }
+ }
+
+ public static void setCurrentDateAndSeason(int date, String season){
+ currentDate = date;
+ currentSeason = Season.get(season);
+ }
+ public static void setCurrentEvent(String event, String time){
+ currentEvent = event;
+ eventTime = time;
+ }
+
+
+ public static Season getCurrentSeason(){ return currentSeason; }
+ public static int getCurrentDate(){ return currentDate; }
+ private static String getDataSuffix(int date) {
+ if (date > 10 && date < 14) return "th";
+ switch (date % 10){
+ case 1: return "st";
+ case 2: return "nd";
+ case 3: return "rd";
+ default: return "th";
+ }
+ }
+
+ public static String getFancySeasonAndDate(){ return currentSeason.getDisplayName() + " " + currentDate + getDataSuffix(currentDate); }
+ public static String getCurrentEvent() { return currentEvent; }
+ public static String getCurrentEventTime() { return eventTime; }
+
+ public static String removeDate(String seasonDate){
+ return Pattern.compile("[^a-zA-Z]").matcher(seasonDate.toLowerCase()).replaceAll("").replaceAll("st|nd|rd|th", "").trim();
+ }
+
+ public static int removeSeason(String seasonDate){
+ return Integer.parseInt(Pattern.compile("[^0-9]").matcher(seasonDate.toLowerCase()).replaceAll("").trim());
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/tracker/KillTrackerHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/tracker/KillTrackerHandler.java
new file mode 100644
index 0000000..6734283
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/tracker/KillTrackerHandler.java
@@ -0,0 +1,76 @@
+package com.thatgravyboat.skyblockhud.tracker;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.DamageSource;
+import net.minecraftforge.event.entity.EntityJoinWorldEvent;
+import net.minecraftforge.event.entity.living.LivingDeathEvent;
+import net.minecraftforge.event.entity.player.AttackEntityEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+public class KillTrackerHandler {
+
+ public static final Set<UUID> attackedEntities = new HashSet<>();
+
+ @SubscribeEvent
+ public void onAttack(AttackEntityEvent event){
+ if (event.target != null) {
+ attackedEntities.add(event.target.getUniqueID());
+ }
+ }
+
+ @SubscribeEvent
+ public void onDeath(LivingDeathEvent event){
+
+ if (false) {
+ //Used for testing
+ System.out.println("----------------------------------------------------------------------------------------------------------------");
+ System.out.println("Name : " + event.entity.getName());
+ System.out.println("UUID : " + event.entity.getUniqueID());
+ NBTTagCompound tag = new NBTTagCompound();
+ event.entity.writeToNBT(tag);
+ System.out.println("Tag : " + tag);
+ System.out.println("Damage : " + getDamageSourceString(event.source));
+ System.out.println("----------------------------------------------------------------------------------------------------------------");
+ }
+
+ attackedEntities.remove(event.entity.getUniqueID());
+ }
+
+ public static String getDamageSourceString(DamageSource source){
+ return "{ " +
+ source.getDamageType() +
+ ", " +
+ source.isDamageAbsolute() +
+ ", " +
+ source.isDifficultyScaled() +
+ ", " +
+ source.isFireDamage() +
+ ", " +
+ source.isProjectile() +
+ ", " +
+ source.isUnblockable() +
+ ", " +
+ source.isExplosion() +
+ ", " +
+ source.isMagicDamage() +
+ ", " +
+ source.isCreativePlayer() +
+ ", " +
+ source.getSourceOfDamage() +
+ " }";
+ }
+
+ @SubscribeEvent
+ public void onWorldChange(EntityJoinWorldEvent event){
+ if (event.entity != null) {
+ if (event.entity.getUniqueID().equals(Minecraft.getMinecraft().thePlayer.getUniqueID())) {
+ attackedEntities.clear();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/tracker/TrackerFileLoader.java b/src/main/java/com/thatgravyboat/skyblockhud/tracker/TrackerFileLoader.java
new file mode 100644
index 0000000..d7f7512
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/tracker/TrackerFileLoader.java
@@ -0,0 +1,171 @@
+package com.thatgravyboat.skyblockhud.tracker;
+
+import com.google.gson.*;
+import com.thatgravyboat.skyblockhud.location.Locations;
+import net.minecraft.client.Minecraft;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.util.ResourceLocation;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class TrackerFileLoader {
+
+ private static final Gson gson = new GsonBuilder().create();
+
+ public static ItemStack getDisplayItem(JsonObject jsonObject){
+ int meta = jsonObject.get("meta").getAsInt();
+ String displayItemId = jsonObject.get("item").getAsString();
+ Item item = Item.itemRegistry.getObject(new ResourceLocation(displayItemId));
+ ItemStack stack = new ItemStack(item, 0, meta);
+ if (jsonObject.has("skullData") && displayItemId.equals("minecraft:skull") && meta == 3){
+ stack.setTagInfo("SkullOwner",getSkullTag(jsonObject.getAsJsonObject("skullData")));
+ }
+ if (jsonObject.has("enchanted") && jsonObject.get("enchanted").getAsBoolean()) stack.setTagInfo("ench", new NBTTagList());
+ return stack;
+ }
+
+ public static NBTBase getSkullTag(JsonObject skullObject){
+ NBTTagCompound skullOwner = new NBTTagCompound();
+ NBTTagCompound properties = new NBTTagCompound();
+ NBTTagList textures = new NBTTagList();
+ NBTTagCompound value = new NBTTagCompound();
+
+ skullOwner.setString("Id", skullObject.get("id").getAsString());
+
+ value.setString("Value", skullObject.get("texture").getAsString());
+ textures.appendTag(value);
+
+ properties.setTag("textures",textures);
+
+ skullOwner.setTag("Properties", properties);
+ return skullOwner;
+ }
+
+ private static void loadTrackers(JsonObject object){
+ for (JsonElement element : object.get("trackers").getAsJsonArray()) {
+ JsonObject tracker = element.getAsJsonObject();
+ StringBuilder builder = new StringBuilder();
+ tracker.get("location").getAsJsonArray().forEach(loc -> builder.append(loc.getAsString()));
+ String location = builder.toString();
+
+
+ Map<String, ItemStack> stacks = new HashMap<>();
+ for (JsonElement drop :tracker.get("drops").getAsJsonArray()) {
+ JsonObject dropObject = drop.getAsJsonObject();
+
+ //Display Item Creation
+ ItemStack stack = getDisplayItem(dropObject.getAsJsonObject("displayItem"));
+ String itemId = dropObject.get("id").getAsString();
+
+ stacks.put(itemId, stack);
+ }
+
+ String event = tracker.has("event") ? tracker.get("event").getAsString() : null;
+
+ Map<String, Map<String, ItemStack>> events = new HashMap<>();
+ events.put(event, stacks);
+
+ if (TrackerHandler.trackers.containsKey(location)){
+ TrackerHandler.trackers.get(location).dropTrackers.put(event, stacks);
+ }else {
+ TrackerHandler.trackers.putIfAbsent(location, new TrackerHandler.TrackerData(events));
+ }
+
+ tracker.get("location").getAsJsonArray().forEach(loc -> TrackerHandler.trackerIds.put(Locations.get(loc.getAsString()), location));
+ }
+ }
+
+ private static JsonElement getTrackerFile(){
+ List<JsonObject> trackerStats = new ArrayList<>();
+ TrackerHandler.trackers.forEach((locations, trackerData) ->
+ trackerData.dropTrackers.forEach((event, drops) -> {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("location", locations);
+
+ if (event == null) jsonObject.add("event", new JsonNull());
+ else jsonObject.addProperty("event", event);
+
+ JsonObject dropsData = new JsonObject();
+ drops.forEach((s, stack) -> dropsData.addProperty(s, stack.stackSize));
+ jsonObject.add("drops", dropsData);
+ trackerStats.add(jsonObject);
+ }
+ ));
+ JsonArray stats = new JsonArray();
+ trackerStats.forEach(stats::add);
+ return stats;
+ }
+
+ public static void loadTrackersFile(){
+ try {
+ ResourceLocation trackers = new ResourceLocation("skyblockhud:data/trackers.json");
+ InputStream is = Minecraft.getMinecraft().getResourceManager().getResource(trackers).getInputStream();
+
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
+ loadTrackers(gson.fromJson(reader, JsonObject.class));
+ }
+ }catch (Exception ignored){}
+ }
+
+ public static boolean loadTrackerStatsFile(File configDirectory){
+ File configFile = new File(configDirectory, "sbh-trackers-stats.json");
+
+ try {
+ if (configFile.createNewFile()){
+ return true;
+ }
+
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(configFile), StandardCharsets.UTF_8))) {
+ JsonObject json = gson.fromJson(reader, JsonObject.class);
+ if (json.has("trackerStats")){
+ json.getAsJsonArray("trackerStats").forEach(element -> {
+ if (element.isJsonObject()){
+ JsonObject object = element.getAsJsonObject();
+ String location = object.get("location").getAsString();
+ Map<String, Map<String, ItemStack>> trackers = TrackerHandler.trackers.get(location).dropTrackers;
+
+ JsonElement event = object.get("event");
+ String eventString = event == null || event.isJsonNull() ? null : event.getAsString();
+ Map<String, ItemStack> drops = trackers.get(eventString);
+
+ if (drops != null) {
+ for (Map.Entry <String ,JsonElement> drop :object.getAsJsonObject("drops").entrySet()) {
+ if (drops.containsKey(drop.getKey())) {
+ drops.get(drop.getKey()).stackSize = drop.getValue().getAsInt();
+ }
+ }
+ drops = TrackerHandler.sortTrackers(drops);
+ trackers.put(eventString, drops);
+ }
+ }
+ });
+ }
+ }
+ } catch(Exception ignored) {}
+ return false;
+ }
+
+ public static void saveTrackerStatsFile(File configDirectory){
+ File configFile = new File(configDirectory, "sbh-trackers-stats.json");
+
+ try {
+ configFile.createNewFile();
+
+ try(BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(configFile), StandardCharsets.UTF_8))) {
+ JsonObject json = new JsonObject();
+ json.add("trackerStats", getTrackerFile());
+ writer.write(gson.toJson(json));
+ }
+ } catch(IOException ignored) {}
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/tracker/TrackerHandler.java b/src/main/java/com/thatgravyboat/skyblockhud/tracker/TrackerHandler.java
new file mode 100644
index 0000000..9d8261b
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/tracker/TrackerHandler.java
@@ -0,0 +1,124 @@
+package com.thatgravyboat.skyblockhud.tracker;
+
+import com.thatgravyboat.skyblockhud.SkyblockHud;
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.core.config.Position;
+import com.thatgravyboat.skyblockhud.location.LocationHandler;
+import com.thatgravyboat.skyblockhud.location.Locations;
+import com.thatgravyboat.skyblockhud.seasons.SeasonDateHandler;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.RenderHelper;
+import net.minecraft.client.renderer.entity.RenderItem;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.*;
+
+public class TrackerHandler {
+
+ public static class TrackerData {
+ public Map<String, Map<String,ItemStack>> dropTrackers;
+
+ public TrackerData(Map<String, Map<String,ItemStack>> trackers) {
+ this.dropTrackers = trackers;
+ }
+
+ public String getDropId(String event){
+ if (event == null || event.isEmpty() || !eventGoing() || !dropTrackers.containsKey(event.toLowerCase().trim())) return null;
+ return event.toLowerCase().trim();
+ }
+
+ private boolean eventGoing(){
+ return SeasonDateHandler.getCurrentEventTime().trim().toLowerCase().contains("ends in");
+ }
+ }
+
+ public static Map<String, TrackerData> trackers = new HashMap<>();
+ public static Map<Locations,String> trackerIds = new HashMap<>();
+
+ public static Map<String, ItemStack> sortTrackers(Map<String, ItemStack> map) {
+ List<Map.Entry<String, ItemStack>> list = new ArrayList<>(map.entrySet());
+ list.sort((entry1, entry2) -> Integer.compare(entry2.getValue().stackSize, entry1.getValue().stackSize));
+
+ Map<String, ItemStack> result = new LinkedHashMap<>();
+ for (Map.Entry<String, ItemStack> entry : list) {
+ result.put(entry.getKey(), entry.getValue());
+ }
+
+ return result;
+ }
+
+ public static void onItemAdded(String id, int amount, String enchant, int level){
+ if (SkyblockHud.hasSkyblockScoreboard() && trackerIds.containsKey(LocationHandler.getCurrentLocation())){
+ String trackerId = trackerIds.get(LocationHandler.getCurrentLocation());
+ TrackerData tracked = trackers.get(trackerId);
+ String dropTrackerId = tracked.getDropId(SeasonDateHandler.getCurrentEvent());
+ Map<String,ItemStack> tracker = tracked.dropTrackers.get(dropTrackerId);
+ String dropId = id;
+ if (enchant != null){
+ dropId = enchant.toUpperCase() + ";" + level;
+ }
+
+ if (tracker != null && tracker.containsKey(dropId)){
+ ItemStack stack = tracker.get(dropId);
+ stack.stackSize += amount;
+ tracked.dropTrackers.put(dropTrackerId, sortTrackers(tracker));
+ }
+ }
+ }
+
+ public static void drawItemStack(ItemStack stack, int x, int y) {
+ if(stack == null)return;
+ RenderItem itemRender = Minecraft.getMinecraft().getRenderItem();
+ RenderHelper.enableGUIStandardItemLighting();
+ itemRender.zLevel = -145;
+ itemRender.renderItemAndEffectIntoGUI(stack, x, y);
+ itemRender.zLevel = 0;
+ RenderHelper.disableStandardItemLighting();
+ }
+
+ @SubscribeEvent
+ public void renderOverlay(RenderGameOverlayEvent.Post event) {
+ if (Utils.overlayShouldRender(event.type, SkyblockHud.hasSkyblockScoreboard(), trackerIds.containsKey(LocationHandler.getCurrentLocation()), !SkyblockHud.config.trackers.hideTracker)) {
+ String trackerId = trackerIds.get(LocationHandler.getCurrentLocation());
+ Minecraft mc = Minecraft.getMinecraft();
+ TrackerData tracked = trackers.get(trackerId);
+
+ Map<String, ItemStack> tracker = tracked.dropTrackers.get(tracked.getDropId(SeasonDateHandler.getCurrentEvent()));
+ if (tracker != null) {
+ Position pos = SkyblockHud.config.trackers.trackerPosition;
+ int startPos = pos.getAbsX(event.resolution, (tracker.size() >= 6 ? 120 : tracker.size() * 20));
+ int y = pos.getAbsY(event.resolution, (int) (10 + Math.ceil(tracker.size() / 5d) * 20));
+
+ Gui.drawRect(startPos, y, startPos + 120, y + 10, -1072689136);
+ mc.fontRendererObj.drawString("Tracker", startPos + 4, y + 1, 0xffffff, false);
+ y += 10;
+ Gui.drawRect(startPos, y, startPos + (tracker.size() >= 6 ? 120 : tracker.size() * 20), (int) (y + (Math.ceil(tracker.size() / 5d) * 20)), 1610612736);
+ int x = startPos;
+ for (ItemStack stack : tracker.values()) {
+ String s = String.valueOf(stack.stackSize);
+ GlStateManager.disableLighting();
+ GlStateManager.enableDepth();
+ drawItemStack(stack, x, y);
+ GlStateManager.disableDepth();
+ GlStateManager.disableBlend();
+ mc.fontRendererObj.drawStringWithShadow(s, (float) (x + 19 - 2 - mc.fontRendererObj.getStringWidth(s)), (float) (y + 9), stack.stackSize < 1 ? 16733525 : 16777215);
+ GlStateManager.enableBlend();
+ GlStateManager.enableDepth();
+
+ if ((x - startPos) / 20 == 5) {
+ x = startPos;
+ y += 20;
+ } else {
+ x += 20;
+ }
+ }
+ }
+ }
+ }
+
+
+}