aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni
diff options
context:
space:
mode:
authorEmpa <42304516+ItsEmpa@users.noreply.github.com>2024-09-09 12:00:51 +0200
committerGitHub <noreply@github.com>2024-09-09 12:00:51 +0200
commit5e77ccdf9c55ba3791427b67dc9456d5ae874380 (patch)
tree19d4ddbdf4e94b755c3c151c9bea2e5433c85706 /src/main/java/at/hannibal2/skyhanni
parent31134aabbf803ac8f3caf0bff98b12eef8821bc9 (diff)
downloadskyhanni-5e77ccdf9c55ba3791427b67dc9456d5ae874380.tar.gz
skyhanni-5e77ccdf9c55ba3791427b67dc9456d5ae874380.tar.bz2
skyhanni-5e77ccdf9c55ba3791427b67dc9456d5ae874380.zip
Feature: Miniboss timer (#2042)
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Co-authored-by: ItsEmpa <itsempa@users.noreply.github.com>
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/crimsonisle/CrimsonIsleConfig.java10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/nether/CrimsonMinibossRespawnTimer.kt273
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt18
3 files changed, 294 insertions, 7 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/crimsonisle/CrimsonIsleConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/crimsonisle/CrimsonIsleConfig.java
index f2398e207..b0b554224 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/crimsonisle/CrimsonIsleConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/crimsonisle/CrimsonIsleConfig.java
@@ -27,6 +27,16 @@ public class CrimsonIsleConfig {
public MatriarchHelperConfig matriarchHelper = new MatriarchHelperConfig();
@Expose
+ @ConfigOption(name = "Miniboss Respawn Timer", desc = "Shows a timer for when minibosses will respawn.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean minibossRespawnTimer = false;
+
+ @Expose
+ @ConfigLink(owner = CrimsonIsleConfig.class, field = "minibossRespawnTimer")
+ public Position minibossTimerPosition = new Position(20, 50);
+
+ @Expose
@ConfigOption(name = "Pablo NPC Helper", desc = "Show a clickable message that grabs the flower needed from your sacks.")
@ConfigEditorBoolean
@FeatureToggle
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 - (?<name>.+) Is Spawning\\.",
+ )
+
+ /**
+ * REGEX-TEST: §f §r§6§lBLADESOUL DOWN!
+ */
+ private val downPattern by patternGroup.pattern(
+ "down",
+ "§f\\s*§r§6§l(?<name>.+) 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<SimpleTimeMark, SimpleTimeMark>? = 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
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt
index 5e85bb64f..5f52508f2 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt
@@ -27,12 +27,15 @@ import net.minecraft.entity.monster.EntityEnderman
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.potion.Potion
+import net.minecraft.scoreboard.ScorePlayerTeam
+import net.minecraft.tileentity.TileEntity
import net.minecraft.util.AxisAlignedBB
import net.minecraftforge.client.event.RenderLivingEvent
//#if FORGE
import net.minecraftforge.fml.common.eventhandler.Event
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
//#endif
@SkyHanniModule
@@ -131,8 +134,7 @@ object EntityUtils {
return gameProfile.properties.entries()
.filter { it.key == "textures" }
.map { it.value }
- .firstOrNull { it.name == "textures" }
- ?.value
+ .firstOrNull { it.name == "textures" }?.value
}
inline fun <reified T : Entity> getEntitiesNextToPlayer(radius: Double): Sequence<T> =
@@ -182,6 +184,10 @@ object EntityUtils {
) it else it.toMutableList() // TODO: while i am here, i want to point out that copying the entity list does not constitute proper synchronization, but *does* make crashes because of it rarer.
}?.asSequence()?.filterNotNull() ?: emptySequence()
+ fun getAllTileEntities(): Sequence<TileEntity> = Minecraft.getMinecraft()?.theWorld?.loadedTileEntityList?.let {
+ if (Minecraft.getMinecraft().isCallingFromMinecraftThread) it else it.toMutableList()
+ }?.asSequence()?.filterNotNull() ?: emptySequence()
+
fun Entity.canBeSeen(radius: Double = 150.0) = getLorenzVec().add(y = 0.5).canBeSeen(radius)
fun getEntityByID(entityId: Int) = Minecraft.getMinecraft()?.thePlayer?.entityWorld?.getEntityByID(entityId)
@@ -217,11 +223,10 @@ object EntityUtils {
SkyHanniRenderEntityEvent.Post(event.entity, event.renderer, event.x, event.y, event.z).postAndCatch()
}
-//#if MC < 11400
+ //#if MC < 11400
@SubscribeEvent
fun onEntityRenderSpecialsPre(
- event:
- RenderLivingEvent.Specials.Pre<*>,
+ event: RenderLivingEvent.Specials.Pre<*>,
) {
val shEvent = SkyHanniRenderEntityEvent.Specials.Pre(event.entity, event.renderer, event.x, event.y, event.z)
if (shEvent.postAndCatch()) {
@@ -231,8 +236,7 @@ object EntityUtils {
@SubscribeEvent
fun onEntityRenderSpecialsPost(
- event:
- RenderLivingEvent.Specials.Post<*>,
+ event: RenderLivingEvent.Specials.Post<*>,
) {
SkyHanniRenderEntityEvent.Specials.Post(event.entity, event.renderer, event.x, event.y, event.z).postAndCatch()
}