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

Improvement + Fix: Barn Fishing Timer #1960

Merged
merged 14 commits into from
Jul 26, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,55 @@
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
import io.github.notenoughupdates.moulconfig.observer.Property;
import org.lwjgl.input.Keyboard;

public class BarnTimerConfig {
@Expose
@ConfigOption(
name = "Barn Fishing Timer",
desc = "Show the time and amount of sea creatures while fishing on the barn via hub."
desc = "Show the time and amount of own sea creatures nearby while barn fishing."
)
@ConfigEditorBoolean
@FeatureToggle
public boolean enabled = true;
public Property<Boolean> enabled = Property.of(true);

@Expose
@ConfigOption(
name = "Worm Fishing",
desc = "Show the Barn Fishing Timer in the Crystal Hollows."
)
@ConfigEditorBoolean
public boolean crystalHollows = true;
public Property<Boolean> crystalHollows = Property.of(true);

@Expose
@ConfigOption(
name = "Lava Fishing",
desc = "Show the Barn Fishing Timer in the Crimson Isle."
)
@ConfigEditorBoolean
public boolean crimsonIsle = true;
public Property<Boolean> crimsonIsle = Property.of(true);

@Expose
@ConfigOption(
name = "Winter Fishing",
desc = "Show the Barn Fishing Timer on the Jerry's Workshop island."
)
@ConfigEditorBoolean
public boolean winterIsland = true;
public Property<Boolean> winterIsland = Property.of(true);

@Expose
@ConfigOption(
name = "Stranded Fishing",
desc = "Show the Barn Fishing Timer on all the different islands that Stranded players can visit."
)
@ConfigEditorBoolean
public boolean forStranded = true;
public Property<Boolean> forStranded = Property.of(true);

@Expose
@ConfigOption(
name = "Worm Cap Alert",
desc = "Alerts you with title and sound if you hit the Worm Sea Creature limit of 60."
desc = "Alerts you with title and sound if you hit the Worm Sea Creature limit of 20."
)
@ConfigEditorBoolean
public boolean wormLimitAlert = true;
Expand All @@ -74,6 +75,16 @@ public class BarnTimerConfig {
)
public int alertTime = 330;

@Expose
@ConfigOption(name = "Fishing Cap Alert", desc = "Gives a warning when you reach a certain amount of mobs.")
@ConfigEditorBoolean
public boolean fishingCapAlert = true;

@Expose
@ConfigOption(name = "Fishing Cap Amount", desc = "Amount of mobs at which to trigger the Fishing Cap Alert.")
@ConfigEditorSlider(minValue = 10, maxValue = 60, minStep = 1)
public int fishingCapAmount = 30;

@Expose
@ConfigLink(owner = BarnTimerConfig.class, field = "enabled")
public Position pos = new Position(10, 10, false, true);
Expand Down
222 changes: 164 additions & 58 deletions src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,126 +3,232 @@ package at.hannibal2.skyhanni.features.fishing
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
import at.hannibal2.skyhanni.data.IslandType
import at.hannibal2.skyhanni.data.mob.Mob
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.LorenzKeyPressEvent
import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
import at.hannibal2.skyhanni.events.MobEvent
import at.hannibal2.skyhanni.events.SeaCreatureFishEvent
import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.EntityUtils
import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld
import at.hannibal2.skyhanni.utils.LocationUtils
import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyClicked
import at.hannibal2.skyhanni.utils.LocationUtils.distanceTo
import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
import at.hannibal2.skyhanni.utils.LorenzVec
import at.hannibal2.skyhanni.utils.RecalculatingValue
import at.hannibal2.skyhanni.utils.RenderUtils.renderString
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.SoundUtils
import at.hannibal2.skyhanni.utils.StringUtils
import at.hannibal2.skyhanni.utils.TimeLimitedSet
import at.hannibal2.skyhanni.utils.TimeUnit
import at.hannibal2.skyhanni.utils.TimeUtils.format
import at.hannibal2.skyhanni.utils.getLorenzVec
import net.minecraft.client.Minecraft
import net.minecraft.entity.item.EntityArmorStand
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

@SkyHanniModule
object FishingTimer {

private val config get() = SkyHanniMod.feature.fishing.barnTimer
private val barnLocation = LorenzVec(108, 89, -252)
private val mobDespawnTime = mutableMapOf<Mob, SimpleTimeMark>()

private var lastSeaCreatureFished = SimpleTimeMark.farPast()
private var display: String? = null
private var lastNameFished: String? = null

private var babyMagmaSlugsToFind = 0
private var lastMagmaSlugLocation: LorenzVec? = null
private var lastMagmaSlugTime = SimpleTimeMark.farPast()
private var recentBabyMagmaSlugs = TimeLimitedSet<Mob>(2.seconds)

private var mobsToFind = 0

private val recentMobs = TimeLimitedSet<Mob>(2.seconds)
private val currentCap by RecalculatingValue(1.seconds) {
when (LorenzUtils.skyBlockIsland) {
IslandType.CRYSTAL_HOLLOWS -> 20
IslandType.CRIMSON_ISLE -> 5
else -> config.fishingCapAmount
}
}

private var rightLocation = false
private var currentCount = 0
private var startTime = SimpleTimeMark.farPast()
private var inHollows = false

@SubscribeEvent
fun onTick(event: LorenzTickEvent) {
if (!LorenzUtils.inSkyBlock) return
if (!config.enabled) return

if (event.repeatSeconds(3)) {
rightLocation = isRightLocation()
fun onSecondPassed(event: SecondPassedEvent) {
if (!isEnabled()) return
updateLocation()
if (startTime.passedSince().inWholeSeconds - config.alertTime in 0..3) {
playSound()
}
if (config.wormLimitAlert && IslandType.CRYSTAL_HOLLOWS.isInIsland()) {
if (currentCount >= 20) {
playSound()
LorenzUtils.sendTitle("§cWORM CAP FULL!!!", 2.seconds)
}
} else if (config.fishingCapAlert && currentCount >= currentCap) {
playSound()
}
}

if (!rightLocation) return
private fun playSound() = SoundUtils.repeatSound(250, 4, SoundUtils.plingSound)

if (event.isMod(5)) checkMobs()
if (event.isMod(7)) tryPlaySound()
if (config.manualResetTimer.isKeyHeld() && Minecraft.getMinecraft().currentScreen == null) {
startTime = SimpleTimeMark.now()
@SubscribeEvent
fun onMobSpawn(event: MobEvent.Spawn.SkyblockMob) {
if (!isEnabled()) return
val mob = event.mob
if (babyMagmaSlugsToFind != 0 && mob.name == "Baby Magma Slug") {
recentBabyMagmaSlugs += mob
handleBabySlugs()
return
}
if (mob.name !in SeaCreatureManager.allFishingMobs) return
recentMobs += mob
handle()
}

private fun tryPlaySound() {
if (currentCount == 0) return

val passedSince = startTime.passedSince()
val barnTimerAlertTime = (config.alertTime * 1_000).milliseconds
if (passedSince in barnTimerAlertTime..(barnTimerAlertTime + 3.seconds)) {
SoundUtils.playBeepSound()
@SubscribeEvent
fun onMobDeSpawn(event: MobEvent.DeSpawn.SkyblockMob) {
if (!isEnabled()) return
val mob = event.mob
if (mob in mobDespawnTime) {
mobDespawnTime -= mob
if (mob.name == "Magma Slug") {
lastMagmaSlugLocation = mob.baseEntity.getLorenzVec()
babyMagmaSlugsToFind += 3
lastMagmaSlugTime = SimpleTimeMark.now()
handleBabySlugs()
}
}
recentMobs -= mob
updateInfo()
}

private fun checkMobs() {
val newCount = countMobs()
@SubscribeEvent
fun onSeaCreatureFish(event: SeaCreatureFishEvent) {
if (!isEnabled()) return
if (!rightLocation) return
lastSeaCreatureFished = SimpleTimeMark.now()
lastNameFished = event.seaCreature.name
mobsToFind = if (event.doubleHook) 2 else 1
handle()
}

if (currentCount == 0 && newCount > 0) {
startTime = SimpleTimeMark.now()
private fun handle() {
if (lastSeaCreatureFished.passedSince() > 2.seconds) return
val name = lastNameFished ?: return
val mobs = recentMobs.filter { it.name == name && it !in mobDespawnTime }
.sortedBy { it.baseEntity.distanceToPlayer() }
.take(mobsToFind).ifEmpty { return }
mobsToFind -= mobs.size
mobs.forEach { mobDespawnTime[it] = SimpleTimeMark.now() }
if (mobsToFind == 0) {
recentMobs.clear()
lastNameFished = null
}
updateInfo()
}

currentCount = newCount
if (newCount == 0) {
startTime = SimpleTimeMark.farPast()
private fun handleBabySlugs() {
hannibal002 marked this conversation as resolved.
Show resolved Hide resolved
if (lastMagmaSlugTime.passedSince() > 2.seconds) return
if (babyMagmaSlugsToFind == 0) return
val location = lastMagmaSlugLocation ?: return
val slugs = recentBabyMagmaSlugs.filter { it !in mobDespawnTime }
.sortedBy { it.baseEntity.distanceTo(location) }
.take(babyMagmaSlugsToFind).ifEmpty { return }
babyMagmaSlugsToFind -= slugs.size
slugs.forEach { mobDespawnTime[it] = SimpleTimeMark.now() }
if (babyMagmaSlugsToFind == 0) {
recentBabyMagmaSlugs.clear()
lastMagmaSlugLocation = null
}
updateInfo()
}

if (inHollows && newCount >= 60 && config.wormLimitAlert) {
SoundUtils.playBeepSound()
LorenzUtils.sendTitle("§cWORM CAP FULL!!!", 2.seconds)
@SubscribeEvent
fun onKeyPress(event: LorenzKeyPressEvent) {
if (!isEnabled()) return
if (Minecraft.getMinecraft().currentScreen != null) return
if (config.manualResetTimer.isKeyClicked()) {
mobDespawnTime.replaceAll { _, _ ->
SimpleTimeMark.now()
}
}
}

private fun countMobs() =
EntityUtils.getEntities<EntityArmorStand>().map { entity -> FishingAPI.seaCreatureCount(entity) }.sum()

private fun isRightLocation(): Boolean {
inHollows = false

if (config.forStranded && LorenzUtils.isStrandedProfile) return true

if (config.crystalHollows && IslandType.CRYSTAL_HOLLOWS.isInIsland()) {
inHollows = true
return true
private fun updateInfo() {
currentCount = mobDespawnTime.entries.sumOf {
1 + it.key.extraEntities.size
}
startTime = mobDespawnTime.maxByOrNull { it.value.passedSince() }?.value ?: SimpleTimeMark.farPast()
display = createDisplay()
}

if (config.crimsonIsle && IslandType.CRIMSON_ISLE.isInIsland()) return true

if (config.winterIsland && IslandType.WINTER.isInIsland()) return true

if (!IslandType.THE_FARMING_ISLANDS.isInIsland()) {
return LocationUtils.playerLocation().distance(barnLocation) < 50
private fun updateLocation() {
rightLocation = when (LorenzUtils.skyBlockIsland) {
IslandType.CRYSTAL_HOLLOWS -> config.crystalHollows.get()
IslandType.CRIMSON_ISLE -> config.crimsonIsle.get()
IslandType.WINTER -> config.winterIsland.get()
IslandType.HUB -> barnLocation.distanceToPlayer() < 50
IslandType.PRIVATE_ISLAND -> config.forStranded.get() && LorenzUtils.isStrandedProfile
else -> false
}
}

return false
@SubscribeEvent
fun onTick(event: LorenzTickEvent) {
if (!isEnabled()) return
if (!rightLocation) return
if (currentCount == 0) return
if (!FishingAPI.isFishing()) return

display = createDisplay()
}

@SubscribeEvent
fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
if (!LorenzUtils.inSkyBlock) return
if (!config.enabled) return
if (!isEnabled()) return
if (!rightLocation) return
if (currentCount == 0) return
if (!FishingAPI.isFishing()) return

val text = display ?: return
config.pos.renderString(text, posLabel = "BarnTimer")
}

private fun createDisplay(): String {
val passedSince = startTime.passedSince()
val barnTimerAlertTime = (config.alertTime * 1_000).milliseconds
val color = if (passedSince > barnTimerAlertTime) "§c" else "§e"
val timeColor = if (passedSince > config.alertTime.seconds) "§c" else "§e"
val timeFormat = passedSince.format(TimeUnit.MINUTE)
val countColor = if (config.fishingCapAlert && currentCount >= currentCap) "§c" else "§e"
val name = StringUtils.pluralize(currentCount, "sea creature")
val text = "$color$timeFormat §8(§e$currentCount §b$name§8)"
return "$timeColor$timeFormat §8($countColor$currentCount §b$name§8)"
}

config.pos.renderString(text, posLabel = "BarnTimer")
@SubscribeEvent
fun onWorldChange(event: LorenzWorldChangeEvent) {
mobDespawnTime.clear()
recentMobs.clear()
babyMagmaSlugsToFind = 0
display = null
lastMagmaSlugLocation = null
lastMagmaSlugTime = SimpleTimeMark.farPast()
recentBabyMagmaSlugs.clear()
mobsToFind = 0
currentCount = 0
startTime = SimpleTimeMark.farPast()
}

private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled.get()

@SubscribeEvent
fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
event.move(3, "fishing.barnTimer", "fishing.barnTimer.enabled")
Expand Down
Loading