Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Hoppity Live Display Date/Time #2973

Open
wants to merge 8 commits into
base: beta
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorInfoText;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
import org.lwjgl.input.Keyboard;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class HoppityEventSummaryLiveDisplayConfig {
Expand All @@ -29,6 +31,56 @@ public class HoppityEventSummaryLiveDisplayConfig {
@ConfigEditorInfoText
public boolean mirrorConfigNote = false;

public enum HoppityDateTimeDisplayType {
CURRENT("Current Event"),
PAST_EVENTS("Past Events"),
NEXT_EVENT("Next Event"),
;

private final String str;

HoppityDateTimeDisplayType(String str) {
this.str = str;
}

@Override
public String toString() {
return str;
}
}

@Expose
@ConfigOption(
name = "Date/Time Display",
desc = "Display the date and time of the event in the header, for the current event, past events, or the next event."
)
@ConfigEditorDraggableList
public List<HoppityDateTimeDisplayType> dateTimeDisplay = new ArrayList<>(Collections.singletonList(
HoppityDateTimeDisplayType.CURRENT
));

public enum HoppityDateTimeFormat {
RELATIVE("Relative"),
ABSOLUTE("Absolute"),
;

private final String str;

HoppityDateTimeFormat(String str) {
this.str = str;
}

@Override
public String toString() {
return str;
}
}

@Expose
@ConfigOption(name = "Date Time Format", desc = "The format of the date and time.")
@ConfigEditorDropdown
public HoppityDateTimeFormat dateTimeFormat = HoppityDateTimeFormat.RELATIVE;

@Expose
@ConfigOption(name = "Card Toggle Keybind", desc = "Toggle the GUI element with this keybind.")
@ConfigEditorKeybind(defaultKey = Keyboard.KEY_NONE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,12 @@ object HoppityAPI {
listOf(Items.skull, Item.getItemFromBlock(Blocks.stained_glass_pane))
}

fun isHoppityEvent() = (SkyblockSeason.currentSeason == SkyblockSeason.SPRING || SkyHanniMod.feature.dev.debug.alwaysHoppitys)
fun getEventEndMark(): SimpleTimeMark? = if (isHoppityEvent()) {
SkyBlockTime.fromSbYearAndMonth(SkyBlockTime.now().year, 3).asTimeMark()
} else null
fun isHoppityEvent() = (SkyblockSeason.SPRING.isSeason() || SkyHanniMod.feature.dev.debug.alwaysHoppitys)
fun getEventEndMark(): SimpleTimeMark? = if (isHoppityEvent()) SkyBlockTime.now().year.getEventEndMark() else null
fun Int.getEventEndMark(): SimpleTimeMark =
SkyBlockTime.fromSeason(this, SkyblockSeason.SUMMER, SkyblockSeason.SkyblockSeasonModifier.EARLY).asTimeMark()
fun Int.getEventStartMark(): SimpleTimeMark =
SkyBlockTime.fromSeason(this, SkyblockSeason.SPRING, SkyblockSeason.SkyblockSeasonModifier.EARLY).asTimeMark()
fun rarityByRabbit(rabbit: String): LorenzRarity? = hoppityRarities.firstOrNull {
it.chatColorCode == rabbit.substring(0, 2)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
import at.hannibal2.skyhanni.config.commands.CommandCategory
import at.hannibal2.skyhanni.config.commands.CommandRegistrationEvent
import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEventSummaryConfig.HoppityStat
import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEventSummaryLiveDisplayConfig.HoppityDateTimeDisplayType.CURRENT
import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEventSummaryLiveDisplayConfig.HoppityDateTimeDisplayType.NEXT_EVENT
import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEventSummaryLiveDisplayConfig.HoppityDateTimeDisplayType.PAST_EVENTS
import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEventSummaryLiveDisplayConfig.HoppityDateTimeFormat.RELATIVE
import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEventSummaryLiveDisplayConfig.HoppityLiveDisplayInventoryType
import at.hannibal2.skyhanni.config.storage.ProfileSpecificStorage.HoppityEventStats
import at.hannibal2.skyhanni.config.storage.ProfileSpecificStorage.HoppityEventStats.LeaderboardPosition
Expand All @@ -25,13 +29,16 @@ import at.hannibal2.skyhanni.events.ProfileJoinEvent
import at.hannibal2.skyhanni.events.RepositoryReloadEvent
import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.events.hoppity.RabbitFoundEvent
import at.hannibal2.skyhanni.features.event.hoppity.HoppityAPI.getEventEndMark
import at.hannibal2.skyhanni.features.event.hoppity.HoppityAPI.getEventStartMark
import at.hannibal2.skyhanni.features.event.hoppity.HoppityRabbitTheFishChecker.mealEggInventoryPattern
import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI
import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateShopPrice.menuNamePattern
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
import at.hannibal2.skyhanni.utils.CollectionUtils.addString
import at.hannibal2.skyhanni.utils.CollectionUtils.sumAllValues
import at.hannibal2.skyhanni.utils.ConditionalUtils.afterChange
import at.hannibal2.skyhanni.utils.InventoryUtils
Expand Down Expand Up @@ -98,6 +105,8 @@ object HoppityEventSummary {
private var currentEventEndMark: SimpleTimeMark = SimpleTimeMark.farPast()
private var lastSnapshotServer: String? = null
private var statYear: Int = getCurrentSBYear()
private var currentTimerActive = false
private var timerSecondCounter = 0
private var onHoppityIsland = false

private fun inMatchingInventory(): Boolean {
Expand Down Expand Up @@ -145,6 +154,8 @@ object HoppityEventSummary {

private fun MutableList<StatString>.addStr(string: String, headed: Boolean = true) = this.add(StatString(string, headed))
private fun MutableList<StatString>.addEmptyLine() = this.add(StatString("", false))
fun MutableList<Renderable>.addCenteredString(string: String) =
this.add(Renderable.string(string, horizontalAlign = RenderUtils.HorizontalAlignment.CENTER))

@SubscribeEvent
fun onRepoReload(event: RepositoryReloadEvent) {
Expand Down Expand Up @@ -301,7 +312,7 @@ object HoppityEventSummary {
val showLastXHours = updateCfConfig.showForLastXHours.takeIf { it > 0 } ?: return

// Initialize the current event end mark if it hasn't been set yet
if (currentEventEndMark.isFarPast()) currentEventEndMark = HoppityAPI.getEventEndMark() ?: return
if (currentEventEndMark.isFarPast()) currentEventEndMark = getCurrentSBYear().getEventEndMark()
if (showLastXHours < 30 && currentEventEndMark.timeUntil() >= showLastXHours.hours) return

// If it's been less than {config} minutes since the last warning message, don't send another
Expand All @@ -321,11 +332,69 @@ object HoppityEventSummary {
}
}

@SubscribeEvent
fun onTick(event: SecondPassedEvent) {
if (!currentTimerActive) return
// Refresh every 5 seconds
if (timerSecondCounter++ % 5 != 0) return
lastKnownStatHash = 0
timerSecondCounter = 0
}

private fun SimpleTimeMark.formatForHoppity(): Pair<String, Boolean> =
if (liveDisplayConfig.dateTimeFormat == RELATIVE) Pair(passedSince().absoluteValue.format(maxUnits = 2), false)
else {
val timeNow = SimpleTimeMark.now().toLocalDateTime()
val timeThen = toLocalDateTime()

val yearDiff = timeThen.year - timeNow.year
val monthDiff = timeThen.monthValue - timeNow.monthValue
val dayDiff = timeThen.dayOfMonth - timeNow.dayOfMonth

val dateFormat = when {
yearDiff == 0 && monthDiff == 0 && dayDiff == 0 -> "HH:mm:ss"
(yearDiff == 0 && monthDiff == 0) || (yearDiff == 0) -> "MM-dd HH:mm"
else -> "yyyy-MM-dd HH:mm"
}
Pair(formattedDate(dateFormat), true)
}

private fun buildDisplayRenderables(stats: HoppityEventStats?, statYear: Int): List<Renderable> = buildList {
// Add title renderable with centered alignment
currentTimerActive = true
add(
Renderable.string(
"§dHoppity's Hunt #${getHoppityEventNumber(statYear)} Stats",
Renderable.verticalContainer(
buildList {
addString(
"§dHoppity's Hunt #${getHoppityEventNumber(statYear)} Stats",
horizontalAlign = RenderUtils.HorizontalAlignment.CENTER,
)
val eventEnd = statYear.getEventEndMark()
val yearNow = getCurrentSBYear()
val isHoppity = HoppityAPI.isHoppityEvent()

val isCurrentEvent = isHoppity && statYear == yearNow
val isPastEvent = statYear < yearNow || (statYear == yearNow && !isHoppity)
val isNextEvent = statYear > yearNow

if (isCurrentEvent && liveDisplayConfig.dateTimeDisplay.contains(CURRENT)) {
eventEnd.formatForHoppity().let { (str, isAbsolute) ->
val grammarWord = if (isAbsolute) "Ends" else "Ends in"
addCenteredString("§7$grammarWord §f$str")
}
} else if (isPastEvent && liveDisplayConfig.dateTimeDisplay.contains(PAST_EVENTS)) {
eventEnd.formatForHoppity().let { (str, isAbsolute) ->
val grammarWord = if (isAbsolute) "" else " ago"
addCenteredString("§7Ended §f$str$grammarWord")
}
} else if (isNextEvent && liveDisplayConfig.dateTimeDisplay.contains(NEXT_EVENT)) {
statYear.getEventStartMark().formatForHoppity().let { (str, isAbsolute) ->
val grammarWord = if (isAbsolute) "Starts" else "Starts in"
addCenteredString("§7$grammarWord §f$str")
}
} else currentTimerActive = false

},
horizontalAlign = RenderUtils.HorizontalAlignment.CENTER,
),
)
Expand Down Expand Up @@ -360,10 +429,15 @@ object HoppityEventSummary {
val storage = storage ?: return null
val statsStorage = storage.hoppityEventStats

val nextYear = getCurrentSBYear() + 1
val isAlreadyNextEvent = currentStatYear == nextYear
val predecessorYear = statsStorage.keys.filter { it < currentStatYear }.maxOrNull()
val successorYear = statsStorage.keys.filter { it > currentStatYear }.minOrNull()
val successorYear = statsStorage.keys.filter { it in (currentStatYear + 1)..<nextYear }.minOrNull()
if (predecessorYear == null && successorYear == null) return null

val isNextEventEnabled = liveDisplayConfig.dateTimeDisplay.contains(NEXT_EVENT)


return listOfNotNull(
predecessorYear?.let {
Renderable.optionalLink(
Expand All @@ -376,7 +450,12 @@ object HoppityEventSummary {
"§d[ §7Hunt #${getHoppityEventNumber(it)} §r§f§l-> §r§d]",
onClick = { statYear = it },
)
},
} ?: if (isNextEventEnabled && !isAlreadyNextEvent) {
Renderable.optionalLink(
"§d[ §7Next Hunt §r§f§l-> §r§d]",
onClick = { statYear = getCurrentSBYear() + 1 },
)
} else null,
)
}

Expand All @@ -403,18 +482,16 @@ object HoppityEventSummary {
private fun checkEnded() {
if (!config.eventSummary.enabled) return
val currentYear = getCurrentSBYear()
val currentSeason = SkyblockSeason.currentSeason
val isSpring = currentSeason == SkyblockSeason.SPRING

for ((year, stats) in getUnsummarizedYearStats()) {
val isPastYear = year < currentYear
val isPastSpring = (year == currentYear && !isSpring)
if (!isPastYear && !isPastSpring) continue

sendStatsMessage(stats, year)
storage?.let {
it.hoppityEventStats[year]?.summarized = true
} ?: ErrorManager.skyHanniError("Could not save summarization state in Hoppity Event Summarization.")
val isSpring = SkyblockSeason.SPRING.isSeason()

getUnsummarizedYearStats().filter {
it.key < currentYear || (it.key == currentYear && !isSpring)
}.forEach { (year, stats) ->
storage?.hoppityEventStats?.get(year)?.let {
// Only send the message if we're going to be able to set the stats as summarized
sendStatsMessage(stats, year)
it.summarized = true
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/at/hannibal2/skyhanni/utils/SimpleTimeMark.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ value class SimpleTimeMark(private val millis: Long) : Comparable<SimpleTimeMark
return localDateTime.format(formatter)
}

fun toLocalDateTime(): LocalDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault())

fun toMillis() = millis

fun toSkyBlockTime() = SkyBlockTime.fromInstant(Instant.ofEpochMilli(millis))
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/at/hannibal2/skyhanni/utils/SkyBlockTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ data class SkyBlockTime(
fun fromSbYear(year: Int): SkyBlockTime =
fromInstant(Instant.ofEpochMilli(SKYBLOCK_EPOCH_START_MILLIS + (SKYBLOCK_YEAR_MILLIS * year)))

fun fromSbYearAndMonth(year: Int, month: Int): SkyBlockTime =
fromInstant(
fun fromSeason(year: Int, season: SkyblockSeason, modifier: SkyblockSeason.SkyblockSeasonModifier? = null): SkyBlockTime {
return fromInstant(
Instant.ofEpochMilli(
SKYBLOCK_EPOCH_START_MILLIS +
(SKYBLOCK_YEAR_MILLIS * year) +
(SKYBLOCK_MONTH_MILLIS * (month - 1))
(SKYBLOCK_MONTH_MILLIS * (season.getMonth(modifier)))
)
)
}

fun now(): SkyBlockTime = fromInstant(Instant.now())

Expand Down
27 changes: 26 additions & 1 deletion src/main/java/at/hannibal2/skyhanni/utils/SkyblockSeason.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,36 @@ enum class SkyblockSeason(
WINTER("§9Winter", "§a5%+§cC", "§7Visitors give §a5% §7more §cCopper."),
;

enum class SkyblockSeasonModifier(
val str: String,
) {
EARLY("Early"),
NONE(""),
LATE("Late"),
;

override fun toString(): String = str
}

fun isSeason(): Boolean = currentSeason == this
fun getPerk(abbreviate: Boolean): String = if (abbreviate) abbreviatedPerk else perk
fun getSeason(abbreviate: Boolean): String = if (abbreviate) season.take(4) else season
fun getDisplayMonth(modifier: SkyblockSeasonModifier? = null): Int = getMonth(modifier) + 1
fun getMonth(modifier: SkyblockSeasonModifier? = null): Int =
when (this) {
SPRING -> 1
SUMMER -> 4
AUTUMN -> 7
WINTER -> 10
}.minus(
when (modifier) {
SkyblockSeasonModifier.EARLY -> 1
SkyblockSeasonModifier.LATE -> -1
else -> 0
}
)

companion object {

val currentSeason by RecalculatingValue(1.seconds) {
getSeasonByName(SkyBlockTime.now().monthName)
}
Expand Down
Loading