/*
* Copyright (C) 2022-2024 NotEnoughUpdates contributors
*
* This file is part of NotEnoughUpdates.
*
* NotEnoughUpdates is free software: you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* NotEnoughUpdates is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with NotEnoughUpdates. If not, see .
*/
package io.github.moulberry.notenoughupdates.miscgui;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe;
import io.github.moulberry.notenoughupdates.core.BackgroundBlur;
import io.github.moulberry.notenoughupdates.events.RepositoryReloadEvent;
import io.github.moulberry.notenoughupdates.util.ItemUtils;
import io.github.moulberry.notenoughupdates.util.JsonUtils;
import io.github.moulberry.notenoughupdates.util.SkyBlockTime;
import io.github.moulberry.notenoughupdates.util.Utils;
import kotlin.Pair;
import lombok.var;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.gui.inventory.GuiChest;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.inventory.ContainerChest;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Tuple;
import net.minecraftforge.client.ClientCommandHandler;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import java.io.File;
import java.time.Duration;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static io.github.moulberry.notenoughupdates.util.GuiTextures.help;
@NEUAutoSubscribe
public class CalendarOverlay {
private static final ResourceLocation BACKGROUND = new ResourceLocation("notenoughupdates:calendar/background.png");
private static final ResourceLocation DISPLAYBAR = new ResourceLocation("notenoughupdates:calendar/displaybar.png");
private static final ResourceLocation TOAST = new ResourceLocation("notenoughupdates:calendar/toast.png");
private static JsonObject farmingEventTypes = null;
private static boolean enabled = false;
private boolean isTimerRendered = false;
long thunderStormEpoch = 1692826500000L;
long oringoEpoch = 1583153700000L;
long oringoInterval = 223200000L;
long rainInterval = 3600000L;
long thunderFrequency = 3;
long rainDuration = 1200 * 1000L;
List> externalEvents = new ArrayList<>();
public static void setEnabled(boolean enabled) {
CalendarOverlay.enabled = enabled;
}
public static boolean isEnabled() {
return enabled;
}
private int guiLeft = -1;
private int guiTop = -1;
private int xSize = 168;
private int ySize = 170;
private static final Pattern CALENDAR_PATTERN = Pattern.compile(
"((?:Early |Late )?(?:Spring|Summer|Fall|Winter)), Year ([0-9]+)");
private int jingleIndex = -1;
private final TreeMap> eventMap = new TreeMap<>();
private List jfFavouriteSelect = null;
private int jfFavouriteSelectIndex = 0;
private int jfFavouriteSelectX = 0;
private int jfFavouriteSelectY = 0;
List> specialEvents = new ArrayList<>();
private boolean drawTimerForeground = false;
private static long spookyStart = 0;
private static final long SECOND = 1000;
private static final long MINUTE = SECOND * 60;
private static final long HOUR = MINUTE * 60;
private static final long DAY = HOUR * 24;
private static final long DA_OFFSET = 1000 * 60 * 55; // Dark Auction
private static final long JF_OFFSET = 1000 * 60 * 15; // Jacob's Farming Contest
private static final ItemStack DA_STACK; // Dark Auction
private static final ItemStack JF_STACK; // Jacob's Farming Contest
private static final ItemStack STAR_CULT_STACK = Utils.createItemStack(
Items.nether_star,
"Cult of the Fallen Star",
"NEU Calendar Item"
); // Star Cult Stack
static {
NBTTagCompound tag = new NBTTagCompound();
tag.setString("event_id", "dark_auction");
//tag.setTag("ench", new NBTTagList());
DA_STACK = new ItemStack(Items.netherbrick);
DA_STACK.setTagCompound(tag);
tag.setString("event_id", "jacob_farming");
JF_STACK = new ItemStack(Items.wheat);
JF_STACK.setTagCompound(tag);
}
public long getTimeOffset(String time) {
long offset = 0;
StringBuilder numS = new StringBuilder();
for (int timeIndex = 0; timeIndex < time.length(); timeIndex++) {
char c = time.charAt(timeIndex);
if (c >= '0' && c <= '9') {
numS.append(c);
} else {
try {
int num = Integer.parseInt(numS.toString());
switch (c) {
case 'd':
offset += num * DAY;
continue;
case 'h':
offset += num * HOUR;
continue;
case 'm':
offset += num * MINUTE;
continue;
case 's':
offset += num * SECOND;
continue;
}
} catch (Exception ignored) {
}
numS = new StringBuilder();
}
}
return offset;
}
private static final long SKYBLOCK_START = 1559829300000L; //Day 0, Year 0
public void handleJinglePlayer() {
if (jingleIndex == 0) {
if (NotEnoughUpdates.INSTANCE.config.calendar.eventNotificationSounds) {
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(
new ResourceLocation("notenoughupdates:calendar_notif_jingle")
));
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(
new ResourceLocation("notenoughupdates:calendar_notif_in")
));
}
jingleIndex = -15 * 20;
} else if (jingleIndex >= 1) {
if (NotEnoughUpdates.INSTANCE.config.calendar.eventNotificationSounds) {
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(
new ResourceLocation("notenoughupdates:calendar_notif_in")
));
}
jingleIndex = -15 * 20;
} else if (jingleIndex < -1) {
jingleIndex++;
}
if (jingleIndex == -20 * 6 - 10) {
if (NotEnoughUpdates.INSTANCE.config.calendar.eventNotificationSounds) {
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(
new ResourceLocation("notenoughupdates:calendar_notif_out")
));
}
}
}
public Set getEventsAt(long timestamp) {
return eventMap.computeIfAbsent(timestamp, k -> new TreeSet<>());
}
JsonObject getFarmingEventTypes() {
if (farmingEventTypes == null) {
farmingEventTypes = NotEnoughUpdates.INSTANCE.manager.getJsonFromFile(new File(
NotEnoughUpdates.INSTANCE.manager.configLocation,
"farmingEventTypes.json"
));
if (farmingEventTypes == null) {
farmingEventTypes = new JsonObject();
}
}
return farmingEventTypes;
}
public void fillRepeatingEvents(int nextHours) {
long currentTime = System.currentTimeMillis();
long floorHour = (currentTime / HOUR) * HOUR;
for (int i = 0; i < nextHours; i++) {
long daEvent = floorHour + i * HOUR + DA_OFFSET;
long jfEvent = floorHour + i * HOUR + JF_OFFSET;
if (daEvent > currentTime) {
getEventsAt(daEvent).add(new SBEvent("dark_auction",
EnumChatFormatting.DARK_PURPLE + "Dark Auction", false, DA_STACK, null, MINUTE * 5
));
}
if (jfEvent > currentTime) {
SBEvent jf = new SBEvent("jacob_farming",
EnumChatFormatting.YELLOW + "Jacob's Farming Contest", false, JF_STACK, null, MINUTE * 20
);
if (farmingEventTypes != null && farmingEventTypes.has("" + jfEvent) &&
farmingEventTypes.get("" + jfEvent).isJsonArray()) {
JsonArray arr = farmingEventTypes.get("" + jfEvent).getAsJsonArray();
jf.desc = new ArrayList<>();
for (JsonElement e : arr) {
jf.desc.add(EnumChatFormatting.YELLOW + "\u25CB " + e.getAsString());
jf.id += ":" + e.getAsString();
}
}
getEventsAt(jfEvent).add(jf);
}
}
}
public void populateDefaultEvents() {
if (eventMap.size() <= 20) {
fillRepeatingEvents(25 - eventMap.size());
fillSpecialMayors(4);
fillWeather();
fillRepoMandatedEvents();
fillStarCult();
}
}
public void fillRepoMandatedEvents() {
for (Pair externalEvent : externalEvents) {
addEvent(
SkyBlockTime.Companion.fromInstant(Instant.ofEpochMilli(externalEvent.component1())),
externalEvent.component2()
);
}
}
public void fillStarCult() {
SkyBlockTime now = SkyBlockTime.now();
long STAR_CULT_DURATION = 60 * 1000L * 6;
List allTimes = new ArrayList<>();
allTimes.add(new SkyBlockTime(now.getYear() - 1, 12, 28, 0, 0, 0));
for (int i = 1; i <= 12; i++) {
for (int d = 7; d < 30; d += 7) {
allTimes.add(new SkyBlockTime(now.getYear(), i, d, 0, 0, 0));
}
}
for (SkyBlockTime allTime : allTimes) {
addEvent(
allTime,
new SBEvent(
"starcult",
"§3Cult of the Fallen Star",
false,
STAR_CULT_STACK,
Arrays.asList(
"§3The Cult of the Fallen Star meets then.",
"§3Attending may give a reward.",
"§3You can find them near the Star in the Dwarven Mines."
),
STAR_CULT_DURATION,
true
)
);
}
}
private void fillWeather() {
long currentTime = System.currentTimeMillis();
long timeSinceLastThunderStart = (currentTime - thunderStormEpoch) % (rainInterval * thunderFrequency);
long lastThunderStart = currentTime - timeSinceLastThunderStart;
for (int i = 0; i < 11; i++) {
long eventTimer = lastThunderStart + rainInterval * i;
if (i % thunderFrequency == 0) {
addEvent(SkyBlockTime.Companion.fromInstant(Instant.ofEpochMilli(eventTimer)), new SBEvent(
"spiders_den_thunder",
"§9Spider's Den Thunder",
true,
new ItemStack(Blocks.slime_block),
Arrays.asList("§aIt will rain in the Spider's Den", "§aand Toxic Rain Slimes will spawn"),
rainDuration,
true
));
} else {
addEvent(
SkyBlockTime.Companion.fromInstant(Instant.ofEpochMilli(eventTimer)),
new SBEvent(
"spiders_den_rain",
"§9Spider's Den Rain",
false,
new ItemStack(Items.slime_ball),
Arrays.asList("§aIt will rain in the Spider's Den", "§aand Rain Slimes will spawn"),
rainDuration,
true
)
);
}
}
}
// Blue Whale -> Tiger -> Lion -> Monkey -> Elephant -> Giraffe
String[] oringoPets = new String[]{
"§6Elephant",
"§6Giraffe",
"§6Blue Whale",
"§6Tiger",
"§6Lion",
"§6Monkey",
};
public String getZooPet(long startTime) {
long time = startTime - oringoEpoch;
return "§7Pet available: " + oringoPets[(int) ((time / oringoInterval) % 6)];
}
@SubscribeEvent
public void tick(RepositoryReloadEvent event) {
JsonObject calendarJson = NotEnoughUpdates.INSTANCE.manager.getJsonFromFile(new File(
event.getRepositoryRoot(),
"constants/calendar.json"
));
if (calendarJson == null) return;
if (calendarJson.has("thunderEpochStart")) {
thunderStormEpoch = calendarJson.get("thunderEpochStart").getAsLong();
}
if (calendarJson.has("rainInterval")) {
rainInterval = calendarJson.get("rainInterval").getAsLong();
}
if (calendarJson.has("thunderFrequency")) {
thunderFrequency = calendarJson.get("thunderFrequency").getAsLong();
}
if (calendarJson.has("rainDuration")) {
rainDuration = calendarJson.get("rainDuration").getAsLong();
}
if (calendarJson.has("external")) {
List> externalEvents = new ArrayList<>();
for (JsonElement external : calendarJson.getAsJsonArray("external")) {
if (!(external instanceof JsonObject)) continue;
SBEvent sbEvent = new SBEvent(
Utils.getElementAsString(Utils.getElement(external, "id"), "external"),
Utils.getElementAsString(Utils.getElement(external, "display"), "§aExternal Event"),
Utils.getElementAsBool(Utils.getElement(external, "special"), false),
NotEnoughUpdates.INSTANCE.manager.createItem(Utils.getElementAsString(Utils.getElement(
external,
"itemStack"
), "painting")),
JsonUtils.getJsonArrayOrEmpty((JsonObject) external, "description", it -> Utils.getElementAsString(it, "")),
Utils.getElementAsInt(Utils.getElement(external, "duration"), -1),
true
);
long start = Utils.getElementAsLong(Utils.getElement(external, "start"), 0);
externalEvents.add(new Pair<>(start, sbEvent));
}
this.externalEvents = externalEvents;
}
eventMap.clear();
}
@SubscribeEvent
public void tick(TickEvent.ClientTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
handleJinglePlayer();
getFarmingEventTypes();
if (!(Minecraft.getMinecraft().currentScreen instanceof GuiChest)) {
jfFavouriteSelect = null;
populateDefaultEvents();
return;
}
GuiChest eventGui = (GuiChest) Minecraft.getMinecraft().currentScreen;
ContainerChest cc = (ContainerChest) eventGui.inventorySlots;
String containerName = cc.getLowerChestInventory().getDisplayName().getUnformattedText();
Matcher matcher = CALENDAR_PATTERN.matcher(Utils.cleanColour(containerName));
if (farmingEventTypes != null && matcher.matches()) scrapeMonthlyCalendar(matcher, cc);
if (!enabled) {
jfFavouriteSelect = null;
populateDefaultEvents();
return;
}
if (!containerName.trim().equals("Calendar and Events")) {
setEnabled(false);
return;
}
eventMap.clear();
populateDefaultEvents();
scrapeOverviewPage(cc);
}
public void addEvent(SkyBlockTime time, SBEvent event) {
if (time.toInstant().isBefore(Instant.now()) &&
time.toInstant().plus(event.lastsFor, ChronoUnit.MILLIS).isBefore(Instant.now())) return;
getEventsAt(time.toMillis()).add(event);
}
private void fillSpecialMayors(int cycles) {
int currentYear = SkyBlockTime.now().getYear();
int baseYear = currentYear - currentYear % 24;
int scorpiusOffset = 192 % 24;
int derpyOffset = 200 % 24;
int jerryOffset = 208 % 24;
for (int i = 0; i < cycles; i++) {
int thisBaseYear = baseYear + i * 24 + 1;
addEvent(
new SkyBlockTime(thisBaseYear + scorpiusOffset, 3, 27, 0, 0, 0),
new SBEvent("special_mayor:scorpius",
"§dScorpius' Candidacy",
true,
Utils.createSkull(
"Scorpius",
"ba2cd37d-a0e4-4dc5-b15c-d79ee1051aae",
"ewogICJ0aW1lc3RhbXAiIDogMTU5Nzc4MTc1NzIxOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGYyNmZhMGM0NzUzNmU3OGUzMzcyNTdkODk4YWY4YjFlYmM4N2MwODk0NTAzMzc1MjM0MDM1ZmYyYzdlZjhmMCIKICAgIH0KICB9Cn0"
),
Arrays.asList("§eScorpius becomes a special Mayor candidate"), -1, true
)
);
addEvent(
new SkyBlockTime(thisBaseYear + derpyOffset, 3, 27, 0, 0, 0),
new SBEvent(
"special_mayor:derpy",
"§dDerpy's Candidacy",
true,
Utils.createSkull(
"Derpy",
"ab36a707-96d3-3db1-ab36-a70796d3adb1",
"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjQ1MGQxMjY5Mjg4NmM0N2IwNzhhMzhmNGQ0OTJhY2ZlNjEyMTZlMmQwMjM3YWI4MjQzMzQwOWIzMDQ2YjQ2NCJ9fX0"
),
Arrays.asList("§eDerpy becomes a special Mayor candidate"), -1, true
)
);
addEvent(
new SkyBlockTime(thisBaseYear + jerryOffset, 3, 27, 0, 0, 0),
new SBEvent(
"special_mayor:jerry",
"§dJerry's Candidacy",
true,
Utils.createSkull(
"Jerry",
"0a9e8efb-9191-4c81-80f5-e27ca5433156",
"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODIyZDhlNzUxYzhmMmZkNGM4OTQyYzQ0YmRiMmY1Y2E0ZDhhZThlNTc1ZWQzZWIzNGMxOGE4NmU5M2IifX19"
),
Arrays.asList("§eJerry becomes a special Mayor candidate"), -1, true
)
);
}
}
private void scrapeOverviewPage(ContainerChest cc) {
long currentTime = System.currentTimeMillis();
String lastsForText = EnumChatFormatting.GRAY + "Event lasts for " + EnumChatFormatting.YELLOW;
String startsInText = EnumChatFormatting.GRAY + "Starts in: " + EnumChatFormatting.YELLOW;
for (int i = 0; i < 21; i++) {
int itemIndex = 10 + i + (i / 7) * 2;
ItemStack item = cc.getLowerChestInventory().getStackInSlot(itemIndex);
if (item == null) continue;
List lore = ItemUtils.getLore(item);
if (lore.isEmpty()) continue;
String first = lore.get(0);
if (first.startsWith(startsInText)) {
boolean zoo = false;
if (item.hasDisplayName()) {
zoo = item.getDisplayName().equals("§aTraveling Zoo");
}
String time = Utils.cleanColour(first.substring(startsInText.length()));
long eventTime = currentTime + getTimeOffset(time);
long lastsFor = -1;
List desc = new ArrayList<>();
boolean foundBreak = false;
for (String line : lore) {
if (foundBreak) {
desc.add(line);
} else {
if (line.startsWith(lastsForText)) {
String lastsForS = Utils.cleanColour(line.substring(lastsForText.length()));
lastsFor = getTimeOffset(lastsForS);
}
if (Utils.cleanColour(line).trim().isEmpty()) {
foundBreak = true;
}
}
}
if (zoo) {
desc.add("");
desc.add(getZooPet(eventTime));
}
getEventsAt(eventTime).add(new SBEvent(
getIdForDisplayName(item.getDisplayName()), item.getDisplayName(),
true, item, desc, lastsFor
));
}
}
}
private void scrapeMonthlyCalendar(Matcher matcher, ContainerChest cc) {
try {
int year = Integer.parseInt(matcher.group(2));
String month = matcher.group(1);
boolean changed = false;
for (int i = 0; i < 31; i++) {
ItemStack item = cc.getLowerChestInventory().getStackInSlot(1 + (i % 7) + (i / 7) * 9);
if (item == null) continue;
SkyBlockTime sbt = SkyBlockTime.Companion.fromDayMonthYear(i + 1, month, year);
if (sbt == null) continue;
JsonArray array = new JsonArray();
for (String line : ItemUtils.getLore(item)) {
if (line.startsWith("§e○") || line.startsWith("§6☘")) {
array.add(new JsonPrimitive(Utils.cleanColour(line.substring(4))));
}
}
if (array.size() == 3) {
String prop = String.valueOf(sbt.toMillis());
changed |= (!farmingEventTypes.has(prop) || !farmingEventTypes.get(prop).isJsonArray() ||
farmingEventTypes.get(prop).getAsJsonArray().equals(array));
farmingEventTypes.add(prop, array);
}
}
if (changed) {
File f = new File(
NotEnoughUpdates.INSTANCE.manager.configLocation,
"farmingEventTypes.json"
);
NotEnoughUpdates.INSTANCE.manager.writeJson(farmingEventTypes, f);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@SubscribeEvent
public void onGuiDraw(GuiScreenEvent.DrawScreenEvent.Pre event) {
if (!(Minecraft.getMinecraft().currentScreen instanceof GuiChest)) {
return;
}
if (!enabled) {
return;
}
GuiChest eventGui = (GuiChest) Minecraft.getMinecraft().currentScreen;
ContainerChest cc = (ContainerChest) eventGui.inventorySlots;
String containerName = cc.getLowerChestInventory().getDisplayName().getUnformattedText();
if (!containerName.trim().equals("Calendar and Events")) {
setEnabled(false);
return;
}
event.setCanceled(true);
List tooltipToDisplay = null;
int mouseX = event.mouseX;
int mouseY = event.mouseY;
long currentTime = System.currentTimeMillis();
xSize = 168;
ySize = 170;
ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
int width = scaledResolution.getScaledWidth();
int height = scaledResolution.getScaledHeight();
guiLeft = (width - xSize) / 2;
guiTop = (height - ySize) / 2;
Utils.drawGradientRect(0, 0, width, height, -1072689136, -804253680);
renderBlurredBackground(10, width, height, guiLeft + 3, guiTop + 3, 162, 14);
renderBlurredBackground(10, width, height, guiLeft + 3, guiTop + 26, 14, 141);
renderBlurredBackground(10, width, height, guiLeft + 151, guiTop + 26, 14, 141);
renderBlurredBackground(10, width, height, guiLeft + 26, guiTop + 26, 116, 141);
Minecraft.getMinecraft().getTextureManager().bindTexture(BACKGROUND);
Utils.drawTexturedRect(guiLeft, guiTop, xSize, ySize, GL11.GL_NEAREST);
GlStateManager.translate(0, 0, 10);
FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
fr.drawString("Daily", guiLeft + 29, guiTop + 30, 0xffffaa00);
int specialLen = fr.getStringWidth("Special");
fr.drawString("Special", guiLeft + 139 - specialLen, guiTop + 30, 0xffffaa00);
ItemStack mayorStack = cc.getLowerChestInventory().getStackInSlot(37);
if (mayorStack != null) {
String mayor = mayorStack.getDisplayName();
float verticalHeight = Utils.getVerticalHeight(mayor);
Utils.drawStringVertical(mayor, guiLeft + 8, guiTop + 96 - verticalHeight / 2,
false, -1
);
}
String calendar = EnumChatFormatting.GREEN + "Calendar";
float calendarHeight = Utils.getVerticalHeight(calendar);
Utils.drawStringVertical(calendar, guiLeft + xSize - 12, guiTop + 60 - calendarHeight / 2,
false, -1
);
String rewards = EnumChatFormatting.GOLD + "Rewards";
float rewardsHeight = Utils.getVerticalHeight(rewards);
Utils.drawStringVertical(rewards, guiLeft + xSize - 12, guiTop + 132 - rewardsHeight / 2,
false, -1
);
if (mouseY >= guiTop + 26 && mouseY <= guiTop + 26 + 141) {
if (mouseX >= guiLeft + 3 && mouseX <= guiLeft + 3 + 14) {
if (mayorStack != null)
tooltipToDisplay = mayorStack.getTooltip(Minecraft.getMinecraft().thePlayer, false);
} else if (mouseX >= guiLeft + 151 && mouseX <= guiLeft + 151 + 14) {
if (mouseY <= guiTop + 26 + 70) {
ItemStack calendarStack = cc.getLowerChestInventory().getStackInSlot(41);
if (calendarStack != null)
tooltipToDisplay = calendarStack.getTooltip(Minecraft.getMinecraft().thePlayer, false);
} else {
ItemStack rewardsStack = cc.getLowerChestInventory().getStackInSlot(36);
if (rewardsStack != null)
tooltipToDisplay = rewardsStack.getTooltip(Minecraft.getMinecraft().thePlayer, false);
}
}
}
long timeUntilNext = 0;
SBEvent nextEvent = null;
long timeUntilFirst = 0;
SBEvent firstEvent = null;
List eventFavourites = NotEnoughUpdates.INSTANCE.config.hidden.eventFavourites;
//Daily Events
int index = 0;
for (Map.Entry> sbEvents : eventMap.entrySet()) {
for (SBEvent sbEvent : sbEvents.getValue()) {
long timeUntilMillis = sbEvents.getKey() - currentTime;
int x = guiLeft + 29 + 17 * (index % 3);
int y = guiTop + 44 + 17 * (index / 3);
if (sbEvent.id.equals("spooky_festival")) {
if (sbEvents.getKey() > currentTime - HOUR && (sbEvents.getKey() < spookyStart || spookyStart == 0)) {
spookyStart = sbEvents.getKey();
}
}
if (index >= 21) {
if (nextEvent != null) break;
if (eventFavourites.isEmpty()) {
nextEvent = sbEvent;
timeUntilNext = timeUntilMillis;
} else if (eventFavourites.contains(sbEvent.id)) {
nextEvent = sbEvent;
timeUntilNext = timeUntilMillis;
}
continue;
}
if (firstEvent == null) {
firstEvent = sbEvent;
timeUntilFirst = timeUntilMillis;
}
String[] split = sbEvent.id.split(":");
boolean containsId = false;
for (int i = 1; i < split.length; i++) {
if (eventFavourites.contains(split[0] + ":" + split[i])) {
containsId = true;
break;
}
}
if (eventFavourites.isEmpty()) {
if (nextEvent == null) {
nextEvent = sbEvent;
timeUntilNext = timeUntilMillis;
}
} else if (eventFavourites.contains(split[0]) || containsId) {
if (nextEvent == null) {
nextEvent = sbEvent;
timeUntilNext = timeUntilMillis;
}
GlStateManager.depthMask(false);
GlStateManager.translate(0, 0, -2);
Gui.drawRect(x, y, x + 16, y + 16, 0xcfffbf49);
GlStateManager.translate(0, 0, 2);
GlStateManager.depthMask(true);
}
Utils.drawItemStackWithText(sbEvent.stack, x, y, "" + (index + 1));
if (mouseX >= x && mouseX <= x + 16) {
if (mouseY >= y && mouseY <= y + 16) {
tooltipToDisplay = Utils.createList(
sbEvent.display,
EnumChatFormatting.GRAY + "Starts in: " + EnumChatFormatting.YELLOW + prettyTime(timeUntilMillis, false)
);
if (!(prettyTime(timeUntilMillis, false).equals("Now!"))) {
addCountdownCalculatorToTooltip(timeUntilMillis, tooltipToDisplay);
}
if (sbEvent.lastsFor >= 0) {
tooltipToDisplay.add(EnumChatFormatting.GRAY + "Lasts for: " + EnumChatFormatting.YELLOW +
prettyTime(sbEvent.lastsFor, true));
if (!(prettyTime(timeUntilMillis, false).equals("Now!"))) {
addCountdownCalculatorToTooltip(sbEvent.lastsFor + timeUntilMillis, tooltipToDisplay);
}
if (timeUntilMillis < 0) {
tooltipToDisplay.add(EnumChatFormatting.GRAY + "Time left: " + EnumChatFormatting.YELLOW +
prettyTime(sbEvent.lastsFor + timeUntilMillis, true));
if ((prettyTime(timeUntilMillis, false).equals("Now!"))) {
addCountdownCalculatorToTooltip(sbEvent.lastsFor + timeUntilMillis, tooltipToDisplay);
}
}
}
if (sbEvent.desc != null) {
tooltipToDisplay.add("");
tooltipToDisplay.addAll(sbEvent.desc);
}
}
}
index++;
}
}
//Special Events
specialEvents = eventMap.entrySet().stream()
.flatMap(pair -> pair
.getValue()
.stream()
.map(it -> new Tuple<>(pair.getKey(), it)))
.filter(it -> it.getSecond().isSpecial)
.limit(21)
.collect(Collectors.toList());
for (int i = 0; i < 21 && i < specialEvents.size(); i++) {
Tuple pair = specialEvents.get(i);
SBEvent sbEvent = pair.getSecond();
ItemStack stack = sbEvent.getStack();
if (stack == null) continue;
int x = guiLeft + 89 + 17 * (i % 3);
int y = guiTop + 44 + 17 * (i / 3);
if (eventFavourites.contains(sbEvent.id)) {
GlStateManager.depthMask(false);
GlStateManager.translate(0, 0, -2);
Gui.drawRect(x, y, x + 16, y + 16, 0xcfffbf49);
GlStateManager.translate(0, 0, 2);
GlStateManager.depthMask(true);
}
Utils.drawItemStackWithText(stack, x, y, "" + (i + 1));
if (mouseX >= x && mouseX <= x + 16) {
if (mouseY >= y && mouseY <= y + 16) {
tooltipToDisplay = new ArrayList<>();
tooltipToDisplay.add(sbEvent.display);
tooltipToDisplay.addAll(sbEvent.desc);
tooltipToDisplay.add(Utils.prettyTime(Duration.between(
Instant.now(),
Instant.ofEpochMilli(pair.getFirst())
)));
addCountdownCalculatorToTooltip(Duration.between(
Instant.now(),
Instant.ofEpochMilli(pair.getFirst())
).toMillis(), tooltipToDisplay);
}
}
}
if (nextEvent == null) {
nextEvent = firstEvent;
timeUntilNext = timeUntilFirst;
}
if (nextEvent != null) {
String nextS = EnumChatFormatting.YELLOW + "Next: ";
int nextSLen = fr.getStringWidth(nextS);
fr.drawString(nextS, guiLeft + 8, guiTop + 6, -1, false);
String until = " " + EnumChatFormatting.YELLOW + prettyTime(timeUntilNext, false);
int untilLen = fr.getStringWidth(until);
fr.drawString(until, guiLeft + xSize - 8 - untilLen, guiTop + 6, -1, false);
int eventTitleLen = xSize - 16 - untilLen - nextSLen;
int displayWidth = fr.getStringWidth(nextEvent.display);
int spaceLen = fr.getCharWidth(' ');
if (displayWidth > eventTitleLen) {
GL11.glEnable(GL11.GL_SCISSOR_TEST);
GL11.glScissor(
(guiLeft + 8 + nextSLen) * scaledResolution.getScaleFactor(),
0,
eventTitleLen * scaledResolution.getScaleFactor(),
Minecraft.getMinecraft().displayHeight
);
fr.drawString(nextEvent.display + " " + nextEvent.display,
guiLeft + 8 + nextSLen - (float) (currentTime / 50.0 % (displayWidth + spaceLen)), guiTop + 6, -1, false
);
GL11.glDisable(GL11.GL_SCISSOR_TEST);
} else {
fr.drawString(nextEvent.display, guiLeft + 8 + nextSLen, guiTop + 6, -1, false);
}
if (mouseX > guiLeft && mouseX < guiLeft + 168) {
if (mouseY > guiTop && mouseY < guiTop + 20) {
tooltipToDisplay = Utils.createList(
nextEvent.display,
EnumChatFormatting.GRAY + "Starts in: " + EnumChatFormatting.YELLOW + prettyTime(timeUntilNext, false)
);
if (!(prettyTime(timeUntilNext, false).equals("Now!"))) {
addCountdownCalculatorToTooltip(timeUntilNext, tooltipToDisplay);
}
if (nextEvent.lastsFor >= 0) {
tooltipToDisplay.add(EnumChatFormatting.GRAY + "Lasts for: " + EnumChatFormatting.YELLOW +
prettyTime(nextEvent.lastsFor, true));
if (!(prettyTime(timeUntilNext, false).equals("Now!"))) {
addCountdownCalculatorToTooltip(nextEvent.lastsFor + timeUntilNext, tooltipToDisplay);
}
if (timeUntilNext < 0) {
tooltipToDisplay.add(EnumChatFormatting.GRAY + "Time left: " + EnumChatFormatting.YELLOW +
prettyTime(nextEvent.lastsFor + timeUntilNext, true));
if ((prettyTime(timeUntilNext, false).equals("Now!"))) {
addCountdownCalculatorToTooltip(nextEvent.lastsFor + timeUntilNext, tooltipToDisplay);
}
}
}
if (nextEvent.desc != null) {
tooltipToDisplay.add("");
tooltipToDisplay.addAll(nextEvent.desc);
}
}
}
}
GlStateManager.color(1, 1, 1, 1);
Minecraft.getMinecraft().getTextureManager().bindTexture(help);
Utils.drawTexturedRect(guiLeft + xSize - 18, guiTop + ySize + 2, 16, 16, GL11.GL_LINEAR);
if (mouseX >= guiLeft + xSize - 18 && mouseX < guiLeft + xSize - 2) {
if (mouseY >= guiTop + ySize + 2 && mouseY <= guiTop + ySize + 18) {
tooltipToDisplay = new ArrayList<>();
tooltipToDisplay.add(EnumChatFormatting.AQUA + "NEU Calendar Help");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "This calendar displays various SkyBlock events");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "'Daily' events are events that happen frequently");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "'Special' events are events that happen infrequently");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "The eventbar at the top will also show in your inventory");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "Press 'F' on an event to mark it as a favourite");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "Favourited events will show over normal events");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "Favourited events will also give a notification when it");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "is about to start and when it does start");
tooltipToDisplay.add(EnumChatFormatting.YELLOW + "");
tooltipToDisplay.add(EnumChatFormatting.DARK_GRAY + "In order to show crop types for Jacob's Farming");
tooltipToDisplay.add(EnumChatFormatting.DARK_GRAY + "contest, visit the full SkyBlock calendar and go all");
tooltipToDisplay.add(EnumChatFormatting.DARK_GRAY + "the way to the end of the SkyBlock year");
Utils.drawHoveringText(tooltipToDisplay, mouseX, mouseY, width, height, -1);
tooltipToDisplay = null;
}
}
if (jfFavouriteSelect != null) {
int arrowLen = fr.getStringWidth("> ");
int selectSizeX = 0;
int selectStringIndex = 0;
for (String s : jfFavouriteSelect) {
int sWidth = fr.getStringWidth(s);
if (selectStringIndex + 1 == jfFavouriteSelectIndex) sWidth += arrowLen;
if (sWidth > selectSizeX) {
selectSizeX = sWidth;
}
selectStringIndex++;
}
selectSizeX += +10;
GlStateManager.translate(0, 0, 19);
Gui.drawRect(jfFavouriteSelectX + 2, jfFavouriteSelectY + 2, jfFavouriteSelectX + selectSizeX + 2,
jfFavouriteSelectY + 18 + jfFavouriteSelect.size() * 10 + 2, 0xa0000000
);
GlStateManager.depthFunc(GL11.GL_LESS);
GlStateManager.translate(0, 0, 1);
Gui.drawRect(jfFavouriteSelectX + 1, jfFavouriteSelectY + 1, jfFavouriteSelectX + selectSizeX - 1,
jfFavouriteSelectY + 18 + jfFavouriteSelect.size() * 10 - 1, 0xffc0c0c0
);
Gui.drawRect(jfFavouriteSelectX, jfFavouriteSelectY, jfFavouriteSelectX + selectSizeX - 1,
jfFavouriteSelectY + 18 + jfFavouriteSelect.size() * 10 - 1, 0xfff0f0f0
);
Gui.drawRect(jfFavouriteSelectX, jfFavouriteSelectY, jfFavouriteSelectX + selectSizeX,
jfFavouriteSelectY + 18 + jfFavouriteSelect.size() * 10, 0xff909090
);
GlStateManager.depthFunc(GL11.GL_LEQUAL);
String all = (NotEnoughUpdates.INSTANCE.config.hidden.eventFavourites.contains("jacob_farming") ?
EnumChatFormatting.DARK_GREEN : EnumChatFormatting.DARK_GRAY) + "All";
if (jfFavouriteSelectIndex == 0) {
fr.drawString(
EnumChatFormatting.BLACK + "> " + all,
jfFavouriteSelectX + 5,
jfFavouriteSelectY + 5,
0xff000000
);
} else {
fr.drawString(all, jfFavouriteSelectX + 5, jfFavouriteSelectY + 5, 0xff000000);
}
fr.drawString(EnumChatFormatting.BLACK + "> ", jfFavouriteSelectX + 6,
jfFavouriteSelectY + 10 * jfFavouriteSelectIndex + 5, 0xff000000
);
selectStringIndex = 0;
for (String s : jfFavouriteSelect) {
EnumChatFormatting colour = NotEnoughUpdates.INSTANCE.config.hidden.eventFavourites.contains(
"jacob_farming:" + s)
? EnumChatFormatting.DARK_GREEN : EnumChatFormatting.DARK_GRAY;
s = (selectStringIndex + 1 == jfFavouriteSelectIndex ? EnumChatFormatting.BLACK + "> " : "") + colour + s;
fr.drawString(s, jfFavouriteSelectX + 5, jfFavouriteSelectY + 10 * selectStringIndex + 15, 0xff000000);
selectStringIndex++;
}
GlStateManager.translate(0, 0, -20);
} else if (tooltipToDisplay != null) {
Utils.drawHoveringText(tooltipToDisplay, mouseX, mouseY, width, height, -1);
}
GlStateManager.translate(0, 0, -10);
}
private static String getIdForDisplayName(String displayName) {
return Utils.cleanColour(displayName)
.toLowerCase(Locale.ROOT)
.replaceAll("[0-9]+th", "")
.replaceAll("[0-9]+nd", "")
.replaceAll("[0-9]+rd", "")
.replaceAll("[0-9]+st", "")
.replaceAll("[^a-z ]", "")
.trim()
.replace(" ", "_");
}
@SubscribeEvent
public void onGuiScreenMouse(GuiScreenEvent.MouseInputEvent.Pre event) {
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;
if (!enabled) {
if (Mouse.getEventButtonState() && NotEnoughUpdates.INSTANCE.config.calendar.showEventTimerInInventory &&
Minecraft.getMinecraft().currentScreen instanceof GuiContainer) {
xSize = 168;
ySize = 20;
guiLeft = (width - xSize) / 2;
guiTop = 5;
if (mouseX >= guiLeft && mouseX <= guiLeft + xSize && isTimerRendered) {
if (mouseY >= guiTop && mouseY <= guiTop + ySize) {
ClientCommandHandler.instance.executeCommand(Minecraft.getMinecraft().thePlayer, "/neucalendar");
}
}
}
return;
}
if (!(Minecraft.getMinecraft().currentScreen instanceof GuiChest)) {
return;
}
GuiChest eventGui = (GuiChest) Minecraft.getMinecraft().currentScreen;
ContainerChest cc = (ContainerChest) eventGui.inventorySlots;
String containerName = cc.getLowerChestInventory().getDisplayName().getUnformattedText();
if (!containerName.trim().equals("Calendar and Events")) {
setEnabled(false);
return;
}
event.setCanceled(true);
xSize = 168;
ySize = 170;
guiLeft = (width - xSize) / 2;
guiTop = (height - ySize) / 2;
if (Mouse.getEventButtonState()) {
if (jfFavouriteSelect != null) {
FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
int arrowLen = fr.getStringWidth("> ");
int selectSizeX = 0;
int selectStringIndex = 0;
for (String s : jfFavouriteSelect) {
int sWidth = fr.getStringWidth(s);
if (selectStringIndex + 1 == jfFavouriteSelectIndex) sWidth += arrowLen;
if (sWidth > selectSizeX) {
selectSizeX = sWidth;
}
selectStringIndex++;
}
selectSizeX += +10;
if (mouseX > jfFavouriteSelectX && mouseX < jfFavouriteSelectX + selectSizeX &&
mouseY > jfFavouriteSelectY && mouseY < jfFavouriteSelectY + 18 + jfFavouriteSelect.size() * 10) {
jfFavouriteSelectIndex = Math.max(0, (mouseY - jfFavouriteSelectY - 5) / 10);
List eventFavourites = NotEnoughUpdates.INSTANCE.config.hidden.eventFavourites;
String id = null;
if (jfFavouriteSelectIndex == 0) {
id = "jacob_farming";
} else if (jfFavouriteSelectIndex - 1 < jfFavouriteSelect.size()) {
id = "jacob_farming:" + jfFavouriteSelect.get(jfFavouriteSelectIndex - 1);
}
if (id != null) {
if (eventFavourites.contains(id)) {
eventFavourites.remove(id);
} else {
eventFavourites.add(id);
}
}
} else {
jfFavouriteSelect = null;
}
}
if (mouseY >= guiTop + 26 && mouseY <= guiTop + 26 + 141) {
if (mouseX >= guiLeft + 151 && mouseX <= guiLeft + 151 + 14) {
if (mouseY <= guiTop + 26 + 70) {
Minecraft.getMinecraft().playerController.windowClick(cc.windowId,
41, 2, 3, Minecraft.getMinecraft().thePlayer
);
} else {
Minecraft.getMinecraft().playerController.windowClick(cc.windowId,
36, 2, 3, Minecraft.getMinecraft().thePlayer
);
}
}
}
}
}
@SubscribeEvent
public void onGuiScreenKeyboard(GuiScreenEvent.KeyboardInputEvent.Pre event) {
if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) {
if (jfFavouriteSelect != null) {
jfFavouriteSelect = null;
event.setCanceled(true);
}
} else {
if (!enabled) {
return;
}
if (!(Minecraft.getMinecraft().currentScreen instanceof GuiChest)) {
return;
}
GuiChest eventGui = (GuiChest) Minecraft.getMinecraft().currentScreen;
ContainerChest cc = (ContainerChest) eventGui.inventorySlots;
String containerName = cc.getLowerChestInventory().getDisplayName().getUnformattedText();
if (!containerName.trim().equals("Calendar and Events")) {
setEnabled(false);
return;
}
event.setCanceled(true);
xSize = 168;
ySize = 170;
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;
guiLeft = (width - xSize) / 2;
guiTop = (height - ySize) / 2;
int keyPressed = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey();
if (Keyboard.getEventKeyState()) {
if (jfFavouriteSelect != null) {
if (keyPressed == Keyboard.KEY_DOWN) {
jfFavouriteSelectIndex++;
jfFavouriteSelectIndex %= jfFavouriteSelect.size() + 1;
} else if (keyPressed == Keyboard.KEY_UP) {
jfFavouriteSelectIndex--;
if (jfFavouriteSelectIndex < 0) jfFavouriteSelectIndex = jfFavouriteSelect.size();
} else if (keyPressed == Keyboard.KEY_RIGHT || keyPressed == Keyboard.KEY_RETURN) {
List eventFavourites = NotEnoughUpdates.INSTANCE.config.hidden.eventFavourites;
String id = null;
if (jfFavouriteSelectIndex == 0) {
id = "jacob_farming";
} else if (jfFavouriteSelectIndex - 1 < jfFavouriteSelect.size()) {
id = "jacob_farming:" + jfFavouriteSelect.get(jfFavouriteSelectIndex - 1);
}
if (id != null) {
if (eventFavourites.contains(id)) {
eventFavourites.remove(id);
} else {
eventFavourites.add(id);
}
}
} else if (keyPressed == Keyboard.KEY_LEFT ||
keyPressed == NotEnoughUpdates.INSTANCE.manager.keybindFavourite.getKeyCode()) {
jfFavouriteSelect = null;
}
} else if (keyPressed == NotEnoughUpdates.INSTANCE.manager.keybindFavourite.getKeyCode()) {
String id = null;
//Daily Events
int index = 0;
out:
for (Map.Entry> sbEvents : eventMap.entrySet()) {
for (SBEvent sbEvent : sbEvents.getValue()) {
int x = guiLeft + 29 + 17 * (index % 3);
int y = guiTop + 44 + 17 * (index / 3);
if (mouseX >= x && mouseX <= x + 16) {
if (mouseY >= y && mouseY <= y + 16) {
id = sbEvent.id;
}
}
if (++index >= 21) break out;
}
}
//Special Events
List> specialEventsLocal = specialEvents;
for (int i = 0; specialEventsLocal != null && i < 21 && i < specialEventsLocal.size(); i++) {
int x = guiLeft + 89 + 17 * (i % 3);
int y = guiTop + 44 + 17 * (i / 3);
if (mouseX >= x && mouseX <= x + 16) {
if (mouseY >= y && mouseY <= y + 16) {
id = specialEventsLocal.get(i).getSecond().id;
}
}
}
if (id != null) {
String[] split = id.split(":");
if (split.length > 1 && split[0].equals("jacob_farming")) {
jfFavouriteSelect = new ArrayList<>();
for (int i = 1; i < split.length; i++) {
jfFavouriteSelect.add(split[i]);
}
jfFavouriteSelectIndex = 0;
jfFavouriteSelectX = mouseX;
jfFavouriteSelectY = mouseY;
} else {
List eventFavourites = NotEnoughUpdates.INSTANCE.config.hidden.eventFavourites;
if (eventFavourites.contains(id)) {
eventFavourites.remove(id);
} else {
eventFavourites.add(id);
}
}
}
} else {
Minecraft.getMinecraft().dispatchKeypresses();
}
}
}
}
public Optional> getNextFavouriteEvent(boolean orGetFirst) {
populateDefaultEvents();
long currentTime = System.currentTimeMillis();
List eventFavourites = NotEnoughUpdates.INSTANCE.config.hidden.eventFavourites;
SBEvent nextAnyEvent = null;
long timeUntilNextAny = 0L;
//Daily Events
for (Map.Entry> sbEvents : eventMap.entrySet()) {
for (SBEvent sbEvent : sbEvents.getValue()) {
long timeUntilMillis = sbEvents.getKey() - currentTime;
if (timeUntilMillis < -10 * SECOND) {
continue;
}
if (nextAnyEvent == null) {
timeUntilNextAny = timeUntilMillis;
nextAnyEvent = sbEvent;
}
String[] split = sbEvent.id.split(":");
boolean containsId = false;
for (int i = 1; i < split.length; i++) {
if (eventFavourites.contains(split[0] + ":" + split[i])) {
containsId = true;
break;
}
}
if (eventFavourites.isEmpty() || eventFavourites.contains(split[0]) || containsId) {
return Optional.of(new Pair<>(sbEvent, timeUntilMillis));
}
}
}
if (orGetFirst && nextAnyEvent != null)
return Optional.of(new Pair<>(nextAnyEvent, timeUntilNextAny));
return Optional.empty();
}
@SubscribeEvent(priority = EventPriority.LOW)
public void onGuiDraw(RenderGameOverlayEvent.Post event) {
if (NotEnoughUpdates.INSTANCE.config.calendar.eventNotifications &&
event.type == RenderGameOverlayEvent.ElementType.ALL) {
GlStateManager.pushMatrix();
GlStateManager.translate(0, 0, 10);
if (!(Minecraft.getMinecraft().currentScreen instanceof GuiContainer) &&
NotEnoughUpdates.INSTANCE.isOnSkyblock()) {
var nextFavouriteEvent = getNextFavouriteEvent(false);
nextFavouriteEvent.ifPresent((nextEvent) -> {
renderToast(nextEvent.component1(), nextEvent.component2());
});
}
GlStateManager.translate(0, 0, -10);
GlStateManager.popMatrix();
}
}
public boolean renderToast(SBEvent event, long timeUntil) {
if (!NotEnoughUpdates.INSTANCE.config.calendar.eventNotifications) {
return false;
}
long currentTime = System.currentTimeMillis();
if (currentTime - spookyStart > 0 && currentTime - spookyStart < HOUR &&
NotEnoughUpdates.INSTANCE.config.calendar.spookyNightNotification) {
long delta = (currentTime - SKYBLOCK_START) % (20 * MINUTE) - 19 * 50 * SECOND - 10 * SECOND;
if (delta < 500 && delta > -8500) {
event = new SBEvent("spooky_festival_7pm", "Spooky Festival 7pm", true, new ItemStack(Items.bone), null);
timeUntil = delta;
}
}
if (event.id.equals("dark_auction")) {
timeUntil -= 30 * 1000;
}
FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
int width = scaledResolution.getScaledWidth();
int height = scaledResolution.getScaledHeight();
int ySize = 32;
int xSize = 160;
int guiLeft = (width - xSize) / 2;
int guiTop = 5;
boolean preNotification = false;
long preNotificationTime = SECOND * NotEnoughUpdates.INSTANCE.config.calendar.startingSoonTime;
if (preNotificationTime > 500 && timeUntil > 500) {
timeUntil = timeUntil - preNotificationTime;
preNotification = true;
}
if (timeUntil < 500 && timeUntil > -8500) {
if (jingleIndex == -1) {
if (preNotification) {
jingleIndex = 1;
} else {
jingleIndex = 0;
}
}
float offset;
float factor = 0;
if (timeUntil > 0) {
factor = (timeUntil / 500f);
} else if (timeUntil < -8000) {
factor = -((timeUntil + 8000) / 500f);
}
factor = (float) (1.06f / (1 + Math.exp(-7 * (factor - 0.5f))) - 0.03f);
offset = -(ySize + 5) * factor;
float y = guiTop + offset;
GlStateManager.color(1, 1, 1, 1);
Minecraft.getMinecraft().getTextureManager().bindTexture(TOAST);
Utils.drawTexturedRect(guiLeft, y, xSize, ySize, GL11.GL_NEAREST);
GlStateManager.translate(0, y, 0);
Utils.drawItemStack(event.stack, guiLeft + 6, 8);
GlStateManager.translate(0, -y, 0);
if (preNotification) {
String starting = EnumChatFormatting.YELLOW + "Event Starting in " + prettyTime(preNotificationTime, true) +
"!";
int startingWidth = fr.getStringWidth(starting);
fr.drawString(starting, Math.max(guiLeft + 23, width / 2f - startingWidth / 2f), y + 7, -1, false);
} else {
Utils.drawStringCentered(EnumChatFormatting.YELLOW + "Event Starting Now!", width / 2, y + 11, false, -1);
}
int displayWidth = fr.getStringWidth(event.display);
fr.drawString(event.display, Math.max(guiLeft + 23, width / 2f - displayWidth / 2f), y + 17, -1, false);
return true;
}
return false;
}
@SubscribeEvent
public void onGuiScreenDrawTimer(GuiScreenEvent.BackgroundDrawnEvent event) {
if (!drawTimerForeground) {
drawTimer();
}
GlStateManager.color(1, 1, 1, 1);
GlStateManager.enableBlend();
}
@SubscribeEvent
public void onGuiScreenDrawTimer(GuiScreenEvent.DrawScreenEvent.Post event) {
isTimerRendered = false;
if (drawTimerForeground) {
drawTimer();
}
}
public void drawTimer() {
GlStateManager.pushMatrix();
GlStateManager.translate(0, 0, 10);
if (Minecraft.getMinecraft().currentScreen instanceof GuiContainer && NotEnoughUpdates.INSTANCE.isOnSkyblock()) {
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;
long currentTime = System.currentTimeMillis();
xSize = 168;
ySize = 20;
long timeUntilNext = 0;
SBEvent nextEvent = null;
long timeUntilFirst = 0;
SBEvent firstEvent = null;
List nextFavourites = new ArrayList<>();
List nextFavouritesTime = new ArrayList<>();
long timeUntilMayor = 0;
SBEvent nextMayorEvent = null;
List eventFavourites = NotEnoughUpdates.INSTANCE.config.hidden.eventFavourites;
guiLeft = (width - xSize) / 2;
guiTop = 5;
populateDefaultEvents();
//Daily Events
out:
for (Map.Entry> sbEvents : eventMap.entrySet()) {
for (SBEvent sbEvent : sbEvents.getValue()) {
long timeUntilMillis = sbEvents.getKey() - currentTime;
if (timeUntilMillis < -10 * SECOND) {
continue;
}
if (sbEvent.id.equals("spooky_festival")) {
if (sbEvents.getKey() > currentTime - HOUR && (sbEvents.getKey() < spookyStart || spookyStart == 0)) {
spookyStart = sbEvents.getKey();
}
}
if (nextMayorEvent == null && !sbEvent.id.split(":")[0].equals("jacob_farming") &&
!sbEvent.id.equals("dark_auction") && !sbEvent.isArtificial) {
nextMayorEvent = sbEvent;
timeUntilMayor = timeUntilMillis;
}
if (firstEvent == null) {
firstEvent = sbEvent;
timeUntilFirst = timeUntilMillis;
}
String[] split = sbEvent.id.split(":");
boolean containsId = false;
for (int i = 1; i < split.length; i++) {
if (eventFavourites.contains(split[0] + ":" + split[i])) {
containsId = true;
break;
}
}
if (eventFavourites.isEmpty() || eventFavourites.contains(split[0]) || containsId) {
if (nextEvent == null) {
nextEvent = sbEvent;
timeUntilNext = timeUntilMillis;
}
if (nextFavourites.size() < 3) {
nextFavourites.add(sbEvent);
nextFavouritesTime.add(timeUntilMillis);
}
}
if (nextFavourites.size() >= 3 && nextMayorEvent != null) {
break out;
}
}
}
if (nextEvent != null) {
GlStateManager.translate(0, 0, 50);
boolean toastRendered = renderToast(nextEvent, timeUntilNext);
GlStateManager.translate(0, 0, -50);
if (!toastRendered && !enabled && NotEnoughUpdates.INSTANCE.config.calendar.showEventTimerInInventory) {
isTimerRendered = true;
List tooltipToDisplay = null;
FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
GlStateManager.disableFog();
GlStateManager.disableLighting();
GlStateManager.disableColorMaterial();
renderBlurredBackground(10, width, height, guiLeft + 3, guiTop + 3, xSize - 6, ySize - 6);
Minecraft.getMinecraft().getTextureManager().bindTexture(DISPLAYBAR);
Utils.drawTexturedRect(guiLeft, guiTop, xSize, 20, GL11.GL_NEAREST);
String nextS = EnumChatFormatting.YELLOW + "Next: ";
int nextSLen = fr.getStringWidth(nextS);
fr.drawString(nextS, guiLeft + 8, guiTop + 6, -1, false);
String until = " " + EnumChatFormatting.YELLOW + prettyTime(timeUntilNext, false);
int untilLen = fr.getStringWidth(until);
fr.drawString(until, guiLeft + xSize - 8 - untilLen, guiTop + 6, -1, false);
int eventTitleLen = xSize - 16 - untilLen - nextSLen;
int displayWidth = fr.getStringWidth(nextEvent.display);
int spaceLen = fr.getCharWidth(' ');
if (displayWidth > eventTitleLen) {
GL11.glEnable(GL11.GL_SCISSOR_TEST);
GL11.glScissor(
(guiLeft + 8 + nextSLen) * scaledResolution.getScaleFactor(),
0,
eventTitleLen * scaledResolution.getScaleFactor(),
Minecraft.getMinecraft().displayHeight
);
fr.drawString(nextEvent.display + " " + nextEvent.display,
guiLeft + 8 + nextSLen - (float) (currentTime / 50.0 % (displayWidth + spaceLen)), guiTop + 6, -1, false
);
GL11.glDisable(GL11.GL_SCISSOR_TEST);
} else {
if (guiLeft + xSize - 8 - untilLen > (width + displayWidth) / 2) {
Utils.drawStringCentered(nextEvent.display, width / 2f, guiTop + 10, false, -1);
} else {
fr.drawString(nextEvent.display, guiLeft + 8 + nextSLen, guiTop + 6, -1, false);
}
}
if (mouseX > guiLeft && mouseX < guiLeft + 168) {
if (mouseY > guiTop && mouseY < guiTop + 20) {
tooltipToDisplay = new ArrayList<>();
for (int i = 0; i < nextFavourites.size(); i++) {
SBEvent sbEvent = nextFavourites.get(i);
long timeUntil = nextFavouritesTime.get(i);
tooltipToDisplay.add(sbEvent.display);
tooltipToDisplay.add(
EnumChatFormatting.GRAY + "Starts in: " + EnumChatFormatting.YELLOW + prettyTime(timeUntil, false));
if (!(prettyTime(timeUntil, false).equals("Now!"))) {
addCountdownCalculatorToTooltip(timeUntil, tooltipToDisplay);
}
if (sbEvent.lastsFor >= 0) {
tooltipToDisplay.add(EnumChatFormatting.GRAY + "Lasts for: " + EnumChatFormatting.YELLOW +
prettyTime(sbEvent.lastsFor, true));
if (!(prettyTime(timeUntilNext, false).equals("Now!"))) {
addCountdownCalculatorToTooltip(sbEvent.lastsFor + timeUntil, tooltipToDisplay);
}
if (timeUntil < 0) {
tooltipToDisplay.add(EnumChatFormatting.GRAY + "Time left: " + EnumChatFormatting.YELLOW +
prettyTime(sbEvent.lastsFor + timeUntil, true));
if ((prettyTime(timeUntil, false).equals("Now!"))) {
addCountdownCalculatorToTooltip(sbEvent.lastsFor + timeUntil, tooltipToDisplay);
}
}
}
if (sbEvent.id.split(":")[0].equals("jacob_farming") && sbEvent.desc != null) {
tooltipToDisplay.addAll(sbEvent.desc);
}
if (nextMayorEvent != null || i < nextFavourites.size() - 1) {
tooltipToDisplay.add("");
}
}
if (nextMayorEvent != null) {
tooltipToDisplay.add(EnumChatFormatting.YELLOW.toString() + EnumChatFormatting.BOLD + "Next Mayor:");
tooltipToDisplay.add(nextMayorEvent.display);
tooltipToDisplay.add(EnumChatFormatting.GRAY + "Starts in: " + EnumChatFormatting.YELLOW +
prettyTime(timeUntilMayor, false));
addCountdownCalculatorToTooltip(timeUntilMayor, tooltipToDisplay);
if (nextMayorEvent.lastsFor >= 0) {
tooltipToDisplay.add(EnumChatFormatting.GRAY + "Lasts for: " + EnumChatFormatting.YELLOW +
prettyTime(nextMayorEvent.lastsFor, true));
addCountdownCalculatorToTooltip(nextMayorEvent.lastsFor + timeUntilMayor, tooltipToDisplay);
}
}
}
}
drawTimerForeground = false;
if (tooltipToDisplay != null) {
drawTimerForeground = true;
GlStateManager.translate(0, 0, 100);
Utils.drawHoveringText(tooltipToDisplay, mouseX, Math.max(17, mouseY), width, height, -1);
GlStateManager.translate(0, 0, -100);
}
}
} else if (!enabled && NotEnoughUpdates.INSTANCE.config.calendar.showEventTimerInInventory) {
FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
GlStateManager.disableFog();
GlStateManager.disableLighting();
GlStateManager.disableColorMaterial();
renderBlurredBackground(10, width, height, guiLeft + 3, guiTop + 3, xSize - 6, ySize - 6);
Minecraft.getMinecraft().getTextureManager().bindTexture(DISPLAYBAR);
Utils.drawTexturedRect(guiLeft, guiTop, xSize, 20, GL11.GL_NEAREST);
String nextS = EnumChatFormatting.RED + "Open calendar to see events";
fr.drawString(nextS, guiLeft + 8, guiTop + 6, -1, false);
}
}
GlStateManager.translate(0, 0, -10);
GlStateManager.popMatrix();
}
private void renderBlurredBackground(
float blurStrength,
int screenWidth,
int screenHeight,
int x,
int y,
int blurWidth,
int blurHeight
) {
BackgroundBlur.renderBlurredBackground(blurStrength, screenWidth, screenHeight, x, y, blurWidth, blurHeight);
Gui.drawRect(x, y, x + blurWidth, y + blurHeight, 0xc8101010);
GlStateManager.color(1, 1, 1, 1);
}
private static class SBEvent implements Comparable {
String id;
String display;
ItemStack stack;
List desc;
long lastsFor;
boolean isSpecial;
boolean isArtificial;
public SBEvent(String id, String display, boolean isSpecial, ItemStack stack, List desc) {
this(id, display, isSpecial, stack, desc, -1);
}
public SBEvent(String id, String display, boolean isSpecial, ItemStack stack, List desc, long lastsFor) {
this(id, display, isSpecial, stack, desc, lastsFor, false);
}
public SBEvent(
String id,
String display,
boolean isSpecial,
ItemStack stack,
List desc,
long lastsFor,
boolean isArtificial
) {
this.id = id;
this.isSpecial = isSpecial;
this.isArtificial = isArtificial;
this.display = display;
this.stack = stack;
this.desc = desc;
this.lastsFor = lastsFor;
}
public ItemStack getStack() {
if (stack != null) {
NBTTagCompound tag = ItemUtils.getOrCreateTag(stack);
tag.setString("event_id", id);
}
return stack;
}
@Override
public int compareTo(@NotNull CalendarOverlay.SBEvent o) {
int i;
if ((i = id.compareTo(o.id)) != 0) return i;
if ((i = display.compareTo(o.display)) != 0) return i;
if ((i = Long.compare(lastsFor, o.lastsFor)) != 0) return i;
if ((i = Boolean.compare(isSpecial, o.isSpecial)) != 0) return i;
if ((i = Boolean.compare(isArtificial, o.isArtificial)) != 0) return i;
return 0;
}
}
private String prettyTime(long millis, boolean trimmed) {
long seconds = millis / 1000 % 60;
long minutes = (millis / 1000 / 60) % 60;
long hours = (millis / 1000 / 60 / 60) % 24;
long days = (millis / 1000 / 60 / 60 / 24);
String endsIn = "";
if (millis < 0) {
endsIn += "Now!";
} else if (minutes == 0 && hours == 0 && days == 0) {
endsIn += seconds + "s";
} else if (hours == 0 && days == 0) {
if (trimmed && seconds == 0) {
endsIn += minutes + "m";
} else {
endsIn += minutes + "m" + seconds + "s";
}
} else if (days == 0) {
if (hours <= 6) {
if (trimmed && seconds == 0) {
if (minutes == 0) {
endsIn += hours + "h";
} else {
endsIn += hours + "h" + minutes + "m";
}
} else {
endsIn += hours + "h" + minutes + "m" + seconds + "s";
}
} else {
endsIn += hours + "h";
}
} else {
endsIn += days + "d" + hours + "h";
}
return endsIn;
}
private List addCountdownCalculatorToTooltip(long millis, List tooltipToModify) {
if (NotEnoughUpdates.INSTANCE.config.misc.showWhenCountdownEnds == 1 ||
NotEnoughUpdates.INSTANCE.config.misc.showWhenCountdownEnds == 2) {
String formatString = "EEEE, MMM d h:mm a";
if (NotEnoughUpdates.INSTANCE.config.misc.showWhenCountdownEnds == 2) {
formatString = "EEEE, MMM d HH:mm";
}
DateTimeFormatter useFormatter = DateTimeFormatter.ofPattern(formatString);
if (NotEnoughUpdates.INSTANCE.config.misc.useEnglishCountdown)
useFormatter = useFormatter.withLocale(Locale.ENGLISH);
tooltipToModify.add("§b" + useFormatter.format(ZonedDateTime.now().plusSeconds(((millis / 1000)))));
}
return tooltipToModify;
}
}