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: Hitman Statistics #2991

Merged
merged 41 commits into from
Dec 13, 2024
Merged
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
29ab635
Done
DavidArthurCole Nov 26, 2024
89bf974
Detekt
DavidArthurCole Nov 26, 2024
205d602
Fix infinite loop
DavidArthurCole Nov 26, 2024
aa7c365
Impr
DavidArthurCole Nov 26, 2024
aaadd83
Detekt
DavidArthurCole Nov 26, 2024
e98bddd
Runaway prot
DavidArthurCole Nov 26, 2024
b3c11ec
....
DavidArthurCole Nov 26, 2024
16160ef
Add open slots as option
DavidArthurCole Nov 26, 2024
132f657
Possible logic fix
DavidArthurCole Nov 26, 2024
ab3fec7
Nerf full slots, stat calc is bugged
DavidArthurCole Nov 26, 2024
2c76682
Detekt
DavidArthurCole Nov 27, 2024
07d3b71
Fix bugged calcs
DavidArthurCole Nov 27, 2024
a86725d
Cap at event end
DavidArthurCole Nov 27, 2024
d96f5bc
Color when cutoff by event end
DavidArthurCole Nov 27, 2024
f760d36
Up
DavidArthurCole Nov 27, 2024
9d52095
Merge branch 'hannibal002:beta' into HoppityHitman
DavidArthurCole Nov 29, 2024
471970b
Fixes
DavidArthurCole Dec 1, 2024
d35cf2f
Revert, potential fix?
DavidArthurCole Dec 1, 2024
45e8e2b
Detekt
DavidArthurCole Dec 1, 2024
7749e87
More attempts at fixes
DavidArthurCole Dec 1, 2024
dfbf8ac
Another attempt
DavidArthurCole Dec 1, 2024
53ef120
Detekt
DavidArthurCole Dec 1, 2024
0231d09
Offset
DavidArthurCole Dec 1, 2024
c8043f5
Still broken
DavidArthurCole Dec 1, 2024
2920e85
Functional, finally
DavidArthurCole Dec 2, 2024
c83a839
Merge branch 'hannibal002:beta' into HoppityHitman
DavidArthurCole Dec 2, 2024
06f0a80
Bump
DavidArthurCole Dec 2, 2024
8377514
Bump again
DavidArthurCole Dec 2, 2024
ea8c988
Merge branch 'hannibal002:beta' into HoppityHitman
DavidArthurCole Dec 4, 2024
1c6242d
Merge branch 'beta' into HoppityHitman
DavidArthurCole Dec 5, 2024
1e9ab17
Detekt
DavidArthurCole Dec 5, 2024
e552b10
Merge branch 'refs/heads/beta' into fork/DavidArthurCole/HoppityHitman
CalMWolfs Dec 6, 2024
cfddb93
Fix logic for unclaimed eggs
DavidArthurCole Dec 6, 2024
e5c061f
Changes (gramatic and syntactic, no functionality changed)
DavidArthurCole Dec 6, 2024
0f5dc94
Merge branch 'hannibal002:beta' into HoppityHitman
DavidArthurCole Dec 9, 2024
d629d92
Merge branch 'hannibal002:beta' into HoppityHitman
DavidArthurCole Dec 10, 2024
e32999b
Requested changes
DavidArthurCole Dec 10, 2024
257174a
Fixes
DavidArthurCole Dec 11, 2024
c351912
Fix logical error
DavidArthurCole Dec 12, 2024
85e3559
Merge remote-tracking branch 'upstream/beta' into HoppityHitman
DavidArthurCole Dec 12, 2024
e6b8049
Account for available in open slots
DavidArthurCole Dec 12, 2024
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
Prev Previous commit
Next Next commit
Changes (gramatic and syntactic, no functionality changed)
  • Loading branch information
DavidArthurCole committed Dec 6, 2024
commit e5c061f7016bb41aa031b9a0093b5146f1a4b8a8
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,22 @@ import kotlin.time.Duration.Companion.minutes
@SkyHanniModule
object HitmanAPI {

private const val MINUTES_PER_DAY = 20 // Real minutes per SkyBlock day
private const val SB_HR_PER_DAY = 24 // SkyBlock hours per day
private val sortedEntries get() = HoppityEggType.sortedResettingEntries
private val orderOrdinalMap: Map<HoppityEggType, HoppityEggType> by lazy {
sortedEntries.mapIndexed { index, hoppityEggType ->
hoppityEggType to sortedEntries[(index + 1) % sortedEntries.size]
}.toMap()
}

/**
* Determine if the given meal will 'still' be claimed before the given duration
*/
private fun HoppityEggType.willBeClaimableAfter(duration: Duration): Boolean = this.timeUntil() < duration
private fun HoppityEggType.passesNotClaimed(tilSpawnDuration: Duration, initialAvailable: MutableList<HoppityEggType>) =
(initialAvailable.contains(this) || this.willBeClaimableAfter(tilSpawnDuration))

/**
* Get the time until the given number of slots are available.
*/
Expand All @@ -32,7 +41,7 @@ object HitmanAPI {
if (currentSlots >= numSlots) return Duration.ZERO
val slotCooldown = this.slotCooldown ?: return Duration.ZERO
val minutesUntilSlot = slotCooldown.timeUntil().inPartialMinutes
val minutesUntilSlots = minutesUntilSlot + ((numSlots - currentSlots - 1) * 20)
val minutesUntilSlots = minutesUntilSlot + ((numSlots - currentSlots - 1) * MINUTES_PER_DAY)
return minutesUntilSlots.minutes
}

Expand All @@ -47,52 +56,70 @@ object HitmanAPI {
for (i in 1..(this.purchasedSlots ?: 0)) {
// If the next slot would put us at the max slot count, return the number of slots
if (currentSlots + i == ((this.purchasedSlots ?: 0))) return i
val minutesUntilSlots = minutesUntilSlot + ((i - 1) * 20)
val minutesUntilSlots = minutesUntilSlot + ((i - 1) * MINUTES_PER_DAY)
if (minutesUntilSlots >= duration.inPartialMinutes) return i
}
return 0 // Should never reach here
}

/**
* Return the time until the given number of rabbits can be hunted.
* Determine the first meal that would be hunted by Hitman if given an infinite amount of time.
*/
private fun HitmanStatsStorage.getTimeToHuntCount(huntCount: Int): Duration {
var nextHuntMeal = sortedEntries.filter { !it.isClaimed() }.minByOrNull { it.timeUntil() }
private fun getFirstHuntedMeal(): HoppityEggType =
sortedEntries.filter { !it.isClaimed() }.minByOrNull { it.timeUntil() }
?: sortedEntries.minByOrNull { it.timeUntil() }
?: ErrorManager.skyHanniError("Could not find next meal to hunt")

/**
* Determine the next meal that would be hunted by Hitman if given an infinite amount of time.
*/
private fun getNextHuntedMeal(
previousMeal: HoppityEggType,
duration: Duration,
initialAvailable: MutableList<HoppityEggType>
) = sortedEntries.filter {
it.passesNotClaimed(duration, initialAvailable)
}.firstOrNull {
// Try to find the next meal on the same day
(it.resetsAt > previousMeal.resetsAt && it.altDay == previousMeal.altDay) ||
// Try to find the next meal on the next day
it.altDay != previousMeal.altDay
} ?: orderOrdinalMap[previousMeal] ?: ErrorManager.skyHanniError(
"Could not find next meal to hunt after $previousMeal"
)

/**
* Return the time until the given number of rabbits can be hunted.
*/
private fun HitmanStatsStorage.getTimeToHuntCount(targetHuntCount: Int): Duration {
// Store the initial meal to hunt
var nextHuntMeal = getFirstHuntedMeal()
// Store a list of all the meals that will be available to hunt at their next spawn
val initialAvailable: MutableList<HoppityEggType> = sortedEntries.filter {
val initialAvailable = sortedEntries.filter {
!it.isClaimed() && it != nextHuntMeal
}.toMutableList()

// Will store the total time until the given number of meals can be hunted
var tilSpawnDuration: Duration =
if (nextHuntMeal.isClaimed()) nextHuntMeal.timeUntil() + 40.minutes // 40 min for -next- cycle after spawn
if (nextHuntMeal.isClaimed()) nextHuntMeal.timeUntil() + (MINUTES_PER_DAY * 2).minutes // -next- cycle after spawn
else nextHuntMeal.timeUntil() // Otherwise, just the time until the next spawn

// Determine if the given meal will 'still' be claimed before the given duration
fun HoppityEggType.willBeClaimableAfter(duration: Duration): Boolean = this.timeUntil() < duration
fun HoppityEggType.passesNotClaimed() =
(initialAvailable.contains(this) || this.willBeClaimableAfter(tilSpawnDuration))

// Determine how many hunts we need to perform
val huntRange = (1 + (this.availableEggs ?: 0))..targetHuntCount
// Loop through the meals until the given number of meals can be hunted
for (i in (1 + (this.availableEggs ?: 0))..<huntCount) {
val candidate = sortedEntries.firstOrNull {
// Try to find the next meal on the same day
it.resetsAt > nextHuntMeal.resetsAt && it.altDay == nextHuntMeal.altDay && it.passesNotClaimed()
} ?: sortedEntries.firstOrNull {
// Try to find the next meal on the next day
it.altDay != nextHuntMeal.altDay && it.passesNotClaimed()
} ?: orderOrdinalMap[nextHuntMeal] ?: ErrorManager.skyHanniError(
"Could not find next meal to hunt after $nextHuntMeal"
)
for (i in huntRange) {
// Determine the next meal to hunt
val candidate = getNextHuntedMeal(nextHuntMeal, tilSpawnDuration, initialAvailable)

// If the meal was initially available, we don't need to wait for it to spawn
if (initialAvailable.contains(candidate)) initialAvailable.remove(candidate)
// Otherwise we need do, so we add the time until the next spawn
else tilSpawnDuration += candidate.timeFromAnother(nextHuntMeal)

// Cycle through
nextHuntMeal = candidate
}

return tilSpawnDuration
}

Expand All @@ -101,8 +128,8 @@ object HitmanAPI {
*/
private fun HoppityEggType.timeFromAnother(another: HoppityEggType): Duration {
val diffInSbHours = when {
this == another -> 48
this.altDay != another.altDay -> 24 - another.resetsAt + this.resetsAt
this == another -> (SB_HR_PER_DAY * 2)
this.altDay != another.altDay -> SB_HR_PER_DAY - another.resetsAt + this.resetsAt
this.resetsAt > another.resetsAt -> this.resetsAt - another.resetsAt
else -> another.resetsAt - this.resetsAt
}
Expand All @@ -120,7 +147,7 @@ object HitmanAPI {
if (allSlotsCooldown.isInPast()) return this.purchasedSlots ?: 0

val minutesUntilAll = allSlotsCooldown.timeUntil().inPartialMinutes
val slotsOnCooldown = ceil(minutesUntilAll / 20).toInt()
val slotsOnCooldown = ceil(minutesUntilAll / MINUTES_PER_DAY).toInt()
return (this.purchasedSlots ?: 0) - slotsOnCooldown
}

Expand All @@ -132,7 +159,7 @@ object HitmanAPI {
val eventEndMark = HoppityAPI.getEventEndMark() ?: return Pair(Duration.ZERO, false)

var slotsToFill = slotsOpenNow
for (i in (0..99)) { // Runaway protection
for (i in (0..10)) { // Runaway protection
// Calculate time needed to fill this many slots
val timeToSlots = this.getTimeToHuntCount(slotsToFill)

Expand Down Expand Up @@ -185,8 +212,8 @@ object HitmanAPI {
}.minByOrNull { it.resetsAt } ?: ErrorManager.skyHanniError("Could not find next meal after all slots")

// Return the adjusted time until the next meal
val dayDiff = if (nextMealAfterAllSlots.altDay != isAllSlotDayAlt) 1 else 0
val hourDiff = nextMealAfterAllSlots.resetsAt - sbTimeAllSlots.hour + dayDiff * 24
return Pair(timeToSlots + (hourDiff * SkyBlockTime.SKYBLOCK_HOUR_MILLIS).milliseconds, false)
val sbDayDiff = if (nextMealAfterAllSlots.altDay != isAllSlotDayAlt) 1 else 0
val sbHourDiff = nextMealAfterAllSlots.resetsAt - sbTimeAllSlots.hour + sbDayDiff * SB_HR_PER_DAY
return Pair(timeToSlots + (sbHourDiff * SkyBlockTime.SKYBLOCK_HOUR_MILLIS).milliseconds, false)
}
}
Loading