From 5e77ccdf9c55ba3791427b67dc9456d5ae874380 Mon Sep 17 00:00:00 2001 From: Empa <42304516+ItsEmpa@users.noreply.github.com> Date: Mon, 9 Sep 2024 12:00:51 +0200 Subject: Feature: Miniboss timer (#2042) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Co-authored-by: ItsEmpa --- .../features/nether/CrimsonMinibossRespawnTimer.kt | 273 +++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/nether/CrimsonMinibossRespawnTimer.kt (limited to 'src/main/java/at/hannibal2/skyhanni/features') diff --git a/src/main/java/at/hannibal2/skyhanni/features/nether/CrimsonMinibossRespawnTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/nether/CrimsonMinibossRespawnTimer.kt new file mode 100644 index 000000000..3c5325697 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/nether/CrimsonMinibossRespawnTimer.kt @@ -0,0 +1,273 @@ +package at.hannibal2.skyhanni.features.nether + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.data.mob.MobData +import at.hannibal2.skyhanni.events.DebugDataCollectEvent +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent +import at.hannibal2.skyhanni.events.SecondPassedEvent +import at.hannibal2.skyhanni.features.nether.CrimsonMinibossRespawnTimer.MiniBoss.Companion.isSpawned +import at.hannibal2.skyhanni.features.nether.CrimsonMinibossRespawnTimer.MiniBoss.Companion.isSpawningSoon +import at.hannibal2.skyhanni.features.nether.CrimsonMinibossRespawnTimer.MiniBoss.Companion.isTimerKnown +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.EntityUtils +import at.hannibal2.skyhanni.utils.LocationUtils.isInside +import at.hannibal2.skyhanni.utils.LocationUtils.isPlayerInside +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher +import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderable +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.TimeUtils.format +import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import at.hannibal2.skyhanni.utils.toLorenzVec +import net.minecraft.tileentity.TileEntityBeacon +import net.minecraft.util.AxisAlignedBB +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +@SkyHanniModule +object CrimsonMinibossRespawnTimer { + + private val config get() = SkyHanniMod.feature.crimsonIsle + + private val patternGroup = RepoPattern.group("crimson.miniboss") + + /** + * REGEX-TEST: §c§lBEWARE - Bladesoul Is Spawning. + */ + private val spawnPattern by patternGroup.pattern( + "spawn", + "§c§lBEWARE - (?.+) Is Spawning\\.", + ) + + /** + * REGEX-TEST: §f §r§6§lBLADESOUL DOWN! + */ + private val downPattern by patternGroup.pattern( + "down", + "§f\\s*§r§6§l(?.+) DOWN!", + ) + + private var currentAreaBoss: MiniBoss? = null + + private var display: Renderable? = null + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!isEnabled()) return + val message = event.message + downPattern.matchMatcher(message) { + val miniBoss = MiniBoss.fromName(group("name")) ?: return + miniBoss.nextSpawnTime = SimpleTimeMark.now() + 2.minutes + miniBoss.spawned = false + miniBoss.possibleSpawnTime = null + miniBoss.foundBeacon = null + update() + return + } + spawnPattern.matchMatcher(message) { + val miniBoss = MiniBoss.fromName(group("name")) ?: return + miniBoss.spawned = true + miniBoss.possibleSpawnTime = null + miniBoss.foundBeacon = null + update() + return + } + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) { + if (!isEnabled()) return + val renderable = display ?: drawDisplay() + config.minibossTimerPosition.renderRenderable(renderable, posLabel = "Miniboss Timer") + } + + @SubscribeEvent + fun onSecondPassed(event: SecondPassedEvent) { + if (!isEnabled()) return + updateArea() + update() + } + + private fun updateArea() { + MiniBoss.entries.forEach { + if (it.lastSeenArea.passedSince() > 2.minutes) { + it.nextSpawnTime = null + it.possibleSpawnTime = null + it.foundBeacon = null + it.spawned = null + } + } + currentAreaBoss = MiniBoss.entries.firstOrNull { + it.area.isPlayerInside() + } + val now = SimpleTimeMark.now() + currentAreaBoss?.lastSeenArea = now + val boss = currentAreaBoss ?: return + if (boss.isTimerKnown()) return + + val isBossInArea = MobData.skyblockMobs.filter { + it.name == boss.displayName + }.any { boss.area.isInside(it.baseEntity.position.toLorenzVec()) } + if (isBossInArea) { + boss.spawned = true + boss.foundBeacon = null + boss.possibleSpawnTime = null + return + } + boss.spawned = false + + val isThereBeacon = EntityUtils.getAllTileEntities().filter { it is TileEntityBeacon }.any { + boss.area.isInside(it.pos.toLorenzVec()) + } + if (boss.foundBeacon == true && !isThereBeacon) { + boss.foundBeacon = false + boss.possibleSpawnTime = null + boss.nextSpawnTime = now + 1.minutes + return + } + if (boss.possibleSpawnTime != null) return + if (isThereBeacon && boss.foundBeacon == null) { + boss.foundBeacon = true + boss.possibleSpawnTime = now + 1.minutes to now + 2.minutes + return + } + if (!isThereBeacon && boss.foundBeacon == null) { + boss.foundBeacon = false + boss.possibleSpawnTime = now to now + 1.minutes + } + } + + private fun update() { + display = drawDisplay() + } + + private fun drawDisplay(): Renderable { + val lines = MiniBoss.entries.map { + val timer = it.nextSpawnTime + val possibleTimer = it.possibleSpawnTime + Renderable.string( + buildString { + append("§b${it.displayName}: ") + if (it.isSpawned()) append("§aSPAWNED!") + else when { + it.isSpawningSoon() -> append("§6Soon!") + it.isTimerKnown() -> append("§e${timer?.timeUntil()?.format()}") + possibleTimer != null -> { + val (start, end) = possibleTimer + if (start.timeUntil().isNegative()) append("§e~Now - ") + else append("§e~${start.timeUntil().format()} - ") + if (end.timeUntil().isNegative()) append("§eNow") + else append("§e${end.timeUntil().format()}") + } + else -> append("§cUnknown") + } + }, + ) + } + return Renderable.verticalContainer(lines) + } + + @SubscribeEvent + fun onWorldChange(event: LorenzWorldChangeEvent) { + MiniBoss.entries.forEach { + it.nextSpawnTime = null + it.possibleSpawnTime = null + it.foundBeacon = null + it.spawned = null + it.lastSeenArea = SimpleTimeMark.farPast() + } + currentAreaBoss = null + } + + @SubscribeEvent + fun onDebug(event: DebugDataCollectEvent) { + event.title("Crimson Isle Miniboss") + event.addIrrelevant { + if (!isEnabled()) { + add("Feature is Disabled") + return@addIrrelevant + } + add("Current Area Boss: ${currentAreaBoss?.displayName}") + MiniBoss.entries.forEach { + add("") + add(it.displayName) + add(" Timer ${it.nextSpawnTime?.timeUntil()?.format()}") + add( + " Possible Timer ${it.possibleSpawnTime?.first?.timeUntil()?.format()} - " + "${ + it.possibleSpawnTime?.second?.timeUntil()?.format() + }", + ) + add(" Found Beacon ${it.foundBeacon}") + add(" Spawned ${it.spawned}") + add(" Last Seen Area ${it.lastSeenArea.passedSince().format()}") + } + } + } + + enum class MiniBoss( + val displayName: String, + val area: AxisAlignedBB, + var nextSpawnTime: SimpleTimeMark? = null, + var possibleSpawnTime: Pair? = null, + var foundBeacon: Boolean? = null, + var spawned: Boolean? = null, + var lastSeenArea: SimpleTimeMark = SimpleTimeMark.farPast(), + ) { + BLADESOUL( + "Bladesoul", + LorenzVec(-330, 80, -486).axisAlignedTo(LorenzVec(-257, 107, -545)), + ), + MAGE_OUTLAW( + "Mage Outlaw", + LorenzVec(-200, 98, -843).axisAlignedTo(LorenzVec(-162, 116, -878)), + ), + BARBARIAN_DUKE_X( + "Barbarian Duke X", + LorenzVec(-550, 101, -890).axisAlignedTo(LorenzVec(-522, 131, -918)), + ), + ASHFANG( + "Ashfang", + LorenzVec(-462, 155, -1035).axisAlignedTo(LorenzVec(-507, 131, -955)), + ), + MAGMA_BOSS( + "Magma Boss", + LorenzVec(-318, 59, -751).axisAlignedTo(LorenzVec(-442, 90, -851)), + ), + ; + + override fun toString() = displayName + + companion object { + fun fromName(spawnName: String) = entries.firstOrNull { + it.displayName.removeColor().lowercase() == spawnName.lowercase() + } + + fun MiniBoss.isTimerKnown(): Boolean { + val timer = nextSpawnTime ?: return false + return timer.passedSince() < 2.minutes + 5.seconds + } + + fun MiniBoss.isSpawningSoon(): Boolean { + if (spawned == true) return false + val timer = nextSpawnTime ?: return false + return timer.passedSince() in 0.seconds..10.seconds + } + + fun MiniBoss.isSpawned(): Boolean { + if (spawned == true) return true + val timer = nextSpawnTime ?: return false + return (timer.passedSince() - 2.minutes) in 0.seconds..20.seconds + } + } + } + + private fun isEnabled() = IslandType.CRIMSON_ISLE.isInIsland() && config.minibossRespawnTimer + +} -- cgit