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: Show time tower usages and warn when none left #1506

Merged
merged 10 commits into from
Apr 29, 2024
2 changes: 2 additions & 0 deletions src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ import at.hannibal2.skyhanni.features.event.chocolatefactory.ChocolateFactoryBar
import at.hannibal2.skyhanni.features.event.chocolatefactory.ChocolateFactoryInventory
import at.hannibal2.skyhanni.features.event.chocolatefactory.ChocolateFactoryShortcut
import at.hannibal2.skyhanni.features.event.chocolatefactory.ChocolateFactoryStats
import at.hannibal2.skyhanni.features.event.chocolatefactory.ChocolateFactoryTimeTowerManager
import at.hannibal2.skyhanni.features.event.chocolatefactory.HoppityCollectionStats
import at.hannibal2.skyhanni.features.event.chocolatefactory.HoppityEggLocator
import at.hannibal2.skyhanni.features.event.chocolatefactory.HoppityEggsManager
Expand Down Expand Up @@ -629,6 +630,7 @@ class SkyHanniMod {
loadModule(ChocolateFactoryInventory)
loadModule(ChocolateFactoryStats)
loadModule(FactoryItemTooltipFeatures)
loadModule(ChocolateFactoryTimeTowerManager)
loadModule(HoppityEggsManager)
loadModule(HoppityEggLocator)
loadModule(HoppityEggsShared)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class ChocolateFactoryConfig {
ChocolateFactoryStat.EMPTY_2,
ChocolateFactoryStat.MULTIPLIER,
ChocolateFactoryStat.BARN,
ChocolateFactoryStat.TIME_TOWER,
hannibal002 marked this conversation as resolved.
Show resolved Hide resolved
ChocolateFactoryStat.LEADERBOARD_POS
));

Expand Down Expand Up @@ -90,6 +91,13 @@ public class ChocolateFactoryConfig {
@FeatureToggle
public boolean hoppityCollectionStats = true;

@Expose
@ConfigOption(name = "Time Tower Warning", desc = "Notification when you have a new time tower usage available and " +
"continuously warn when your time tower is full.")
@ConfigEditorBoolean
@FeatureToggle
public boolean timeTowerWarning = false;

@Expose
@ConfigOption(name = "Hoppity Menu Shortcut", desc = "Add a Chocolate Factory button in the SkyBlock Menu that runs /chocolatefactory on click.")
@ConfigEditorBoolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ public static class ChocolateFactoryStorage {

@Expose
public int maxRabbits = -1;

@Expose
public long nextTimeTower = 0;

@Expose
public int currentTimeTowerUses = -1;

@Expose
public int maxTimeTowerUses = 3;
}

@Expose
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ data class HoppityEggLocationsJson(
@Expose val prestigeIndex: Int,
@Expose val milestoneIndex: Int,
@Expose val leaderboardIndex: Int,
@Expose val timeTowerIndex: Int,
@Expose val maxRabbits: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble
import at.hannibal2.skyhanni.utils.NumberUtil.formatInt
import at.hannibal2.skyhanni.utils.NumberUtil.formatLong
import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimal
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.SkyblockSeason
import at.hannibal2.skyhanni.utils.SoundUtils
import at.hannibal2.skyhanni.utils.StringUtils.matchFirst
import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
import at.hannibal2.skyhanni.utils.StringUtils.matches
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import at.hannibal2.skyhanni.utils.TimeUtils
import at.hannibal2.skyhanni.utils.UtilsPatterns
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import net.minecraft.item.ItemStack
Expand Down Expand Up @@ -78,6 +80,18 @@ object ChocolateFactoryAPI {
"leaderboard.percentile",
"§7§8You are in the top §.(?<percent>[\\d.]+)%§8 of players!"
)
private val timeTowerAmountPattern by patternGroup.pattern(
"timetower.amount",
"§7Charges: §.(?<uses>\\d+)§7/§a(?<max>\\d+)"
)
private val timeTowerStatusPattern by patternGroup.pattern(
"timetower.status",
"§7Status: §.§l(?<status>INACTIVE|ACTIVE).*"
)
private val timeTowerRechargePattern by patternGroup.pattern(
"timetower.recharge",
"§7Next Charge: §a(?<duration>\\w+)"
)

var rabbitSlots = mapOf<Int, Int>()
var otherUpgradeSlots = setOf<Int>()
Expand All @@ -88,6 +102,7 @@ object ChocolateFactoryAPI {
private var prestigeIndex = 28
var milestoneIndex = 53
private var leaderboardIndex = 51
private var timeTowerIndex = 39
var maxRabbits = 395

var inChocolateFactory = false
Expand All @@ -100,6 +115,7 @@ object ChocolateFactoryAPI {
var chocolateMultiplier = 1.0
var leaderboardPosition: Int? = null
var leaderboardPercentile: Double? = null
var timeTowerActive = false

val upgradeableSlots: MutableSet<Int> = mutableSetOf()
var bestUpgrade: Int? = null
Expand All @@ -125,14 +141,14 @@ object ChocolateFactoryAPI {
}

private fun updateInventoryItems(inventory: Map<Int, ItemStack>) {
val profileStorage = profileStorage ?: return

val infoItem = InventoryUtils.getItemAtSlotIndex(infoIndex) ?: return
val prestigeItem = InventoryUtils.getItemAtSlotIndex(prestigeIndex) ?: return
val productionInfoItem = InventoryUtils.getItemAtSlotIndex(productionInfoIndex) ?: return
val leaderboardItem = InventoryUtils.getItemAtSlotIndex(leaderboardIndex) ?: return
val barnItem = InventoryUtils.getItemAtSlotIndex(barnIndex) ?: return
val timeTowerItem = InventoryUtils.getItemAtSlotIndex(timeTowerIndex) ?: return

processInfoItems(infoItem, prestigeItem, productionInfoItem, leaderboardItem)
processInfoItems(infoItem, prestigeItem, productionInfoItem, leaderboardItem, barnItem, timeTowerItem)

bestUpgrade = null
upgradeableSlots.clear()
Expand All @@ -149,15 +165,6 @@ object ChocolateFactoryAPI {
val lore = item.getLore()
val upgradeCost = lore.getUpgradeCost() ?: continue

if (slotIndex == barnIndex) {
lore.matchFirst(barnAmountPattern) {
profileStorage.currentRabbits = group("rabbits").formatInt()
profileStorage.maxRabbits = group("max").formatInt()

ChocolateFactoryBarnManager.trySendBarnFullMessage()
}
}

val canAfford = upgradeCost <= chocolateCurrent
if (canAfford) upgradeableSlots.add(slotIndex)

Expand All @@ -182,11 +189,15 @@ object ChocolateFactoryAPI {
prestigeItem: ItemStack,
productionItem: ItemStack,
leaderboardItem: ItemStack,
barnItem: ItemStack,
timeTowerItem: ItemStack,
) {
leaderboardPosition = null
leaderboardPercentile = null
val profileStorage = profileStorage ?: return

chocolateMultiplier = 1.0
timeTowerActive = false
leaderboardPosition = null
leaderboardPercentile = null

chocolateAmountPattern.matchMatcher(chocolateItem.name.removeColor()) {
chocolateCurrent = group("amount").formatLong()
Expand Down Expand Up @@ -216,6 +227,29 @@ object ChocolateFactoryAPI {
leaderboardPercentile = group("percent").formatDouble()
}
}
barnItem.getLore().matchFirst(barnAmountPattern) {
profileStorage.currentRabbits = group("rabbits").formatInt()
profileStorage.maxRabbits = group("max").formatInt()
ChocolateFactoryBarnManager.trySendBarnFullMessage()
}
for (line in timeTowerItem.getLore()) {
timeTowerAmountPattern.matchMatcher(line) {
profileStorage.currentTimeTowerUses = group("uses").formatInt()
profileStorage.maxTimeTowerUses = group("max").formatInt()
ChocolateFactoryTimeTowerManager.checkTimeTowerWarning(true)
}
timeTowerStatusPattern.matchMatcher(line) {
timeTowerActive = group("status") == "ACTIVE"
}
timeTowerRechargePattern.matchMatcher(line) {
// todo in future fix this issue with TimeUtils.getDuration
val formattedGroup = group("duration").replace("h", "h ").replace("m", "m ")

val timeUntilTower = TimeUtils.getDuration(formattedGroup)
val nextTimeTower = SimpleTimeMark.now() + timeUntilTower
profileStorage.nextTimeTower = nextTimeTower.toMillis()
}
}

if (!config.statsDisplay) return
ChocolateFactoryStats.updateDisplay()
Expand Down Expand Up @@ -250,6 +284,7 @@ object ChocolateFactoryAPI {
prestigeIndex = data.prestigeIndex
milestoneIndex = data.milestoneIndex
leaderboardIndex = data.leaderboardIndex
timeTowerIndex = data.timeTowerIndex
maxRabbits = data.maxRabbits

val disabledFeatures = event.getConstant<DisabledFeaturesJson>("DisabledFeatures")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ object ChocolateFactoryBarnManager {

val profileStorage = profileStorage ?: return

if (profileStorage.maxRabbits >= ChocolateFactoryAPI.maxRabbits) return

val remainingSpace = profileStorage.maxRabbits - profileStorage.currentRabbits
barnFull =
remainingSpace <= config.barnCapacityThreshold && profileStorage.maxRabbits < ChocolateFactoryAPI.maxRabbits
barnFull = remainingSpace <= config.barnCapacityThreshold
if (!barnFull) return

if (lastBarnFullWarning.passedSince() < 30.seconds) return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ object ChocolateFactoryStats {
val perDay = perHour * 24
val position = ChocolateFactoryAPI.leaderboardPosition?.addSeparators() ?: "???"
val percentile = ChocolateFactoryAPI.leaderboardPercentile?.let { "§7Top §a$it%" } ?: ""
val timeTowerInfo = if (ChocolateFactoryAPI.timeTowerActive) {
"§d§lActive"
} else {
"§6${ChocolateFactoryTimeTowerManager.timeTowerCharges()}"
}

displayList = formatList(buildList {
add("§6§lChocolate Factory Stats")
Expand All @@ -47,19 +52,21 @@ object ChocolateFactoryStats {
add("")
add("")
add("")

add("§eTime Tower: §6$timeTowerInfo")
})
}

private fun formatList(list: List<String>): List<String> {
return config.statsDisplayList
.filter { ChocolateFactoryAPI.currentPrestige != 1 || it != ChocolateFactoryStat.THIS_PRESTIGE }
.filter { it.shouldDisplay() }
.map { list[it.ordinal] }
}

enum class ChocolateFactoryStat(private val display: String) {
enum class ChocolateFactoryStat(private val display: String, val shouldDisplay: () -> Boolean = { true }) {
HEADER("§6§lChocolate Factory Stats"),
CURRENT("§eCurrent Chocolate: §65,272,230"),
THIS_PRESTIGE("§eThis Prestige: §6483,023,853"),
THIS_PRESTIGE("§eThis Prestige: §6483,023,853", { ChocolateFactoryAPI.currentPrestige != 1 }),
ALL_TIME("§eAll-time: §6641,119,115"),
PER_SECOND("§ePer Second: §63,780.72"),
PER_MINUTE("§ePer Minute: §6226,843.2"),
Expand All @@ -71,6 +78,7 @@ object ChocolateFactoryStats {
EMPTY(""),
EMPTY_2(""),
EMPTY_3(""),
TIME_TOWER("§eTime Tower: §62/3 Charges", { ChocolateFactoryTimeTowerManager.currentCharges() != -1 }),
;

override fun toString(): String {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package at.hannibal2.skyhanni.features.event.chocolatefactory

import at.hannibal2.skyhanni.events.ProfileJoinEvent
import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.features.fame.ReminderUtils
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.SoundUtils
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

object ChocolateFactoryTimeTowerManager {

private val config get() = ChocolateFactoryAPI.config
private val profileStorage get() = ChocolateFactoryAPI.profileStorage

private var lastTimeTowerWarning = SimpleTimeMark.farPast()

@SubscribeEvent
fun onSecondPassed(event: SecondPassedEvent) {
if (!LorenzUtils.inSkyBlock) return
if (ChocolateFactoryAPI.inChocolateFactory) return
val profileStorage = profileStorage ?: return

val nextCharge = SimpleTimeMark(profileStorage.nextTimeTower)

if (nextCharge.isInPast() && !nextCharge.isFarPast() && currentCharges() < maxCharges()) {
profileStorage.currentTimeTowerUses++

// todo in future once have Einstein rabbit account for that
val nextTimeTower = SimpleTimeMark(profileStorage.nextTimeTower) + 8.hours
profileStorage.nextTimeTower = nextTimeTower.toMillis()

if (!config.timeTowerWarning) return
ChatUtils.clickableChat(
"Your Time Tower has another charge available §7(${timeTowerCharges()})§e, " +
"Click here to use one",
onClick = {
HypixelCommands.chocolateFactory()
}
)
SoundUtils.playBeepSound()
lastTimeTowerWarning = SimpleTimeMark.now()
return
}
checkTimeTowerWarning(false)
}

fun checkTimeTowerWarning(inInventory: Boolean) {
if (!ChocolateFactoryAPI.isEnabled()) return
if (!config.timeTowerWarning) return
if (!timeTowerFull()) return
if (ReminderUtils.isBusy()) return

val warningSeparation = if (inInventory) 30.seconds else 5.minutes
if (lastTimeTowerWarning.passedSince() < warningSeparation) return

ChatUtils.clickableChat(
"§cYour Time Tower is full §7(${timeTowerCharges()})§c, " +
"Use one to avoid wasting time tower usages!",
onClick = {
HypixelCommands.chocolateFactory()
}
)
SoundUtils.playBeepSound()
lastTimeTowerWarning = SimpleTimeMark.now()
}

fun timeTowerCharges(): String {
return "${currentCharges()}/${maxCharges()} Charges"
}

fun currentCharges(): Int {
return profileStorage?.currentTimeTowerUses ?: -1
}

private fun maxCharges(): Int {
return profileStorage?.maxTimeTowerUses ?: 3
}

private fun timeTowerFull() = currentCharges() >= maxCharges()

@SubscribeEvent
fun onProfileChange(event: ProfileJoinEvent) {
lastTimeTowerWarning = SimpleTimeMark.farPast()
}
}
Loading