package moe.nea.firmament.util import io.github.moulberry.repo.constants.Islands import io.github.moulberry.repo.constants.Islands.Warp import kotlinx.serialization.Serializable import kotlinx.serialization.serializer import kotlin.math.sqrt import kotlin.time.Duration.Companion.seconds import net.minecraft.text.Text import net.minecraft.util.math.Position import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.commands.thenExecute import moe.nea.firmament.events.CommandEvent import moe.nea.firmament.events.ProcessChatEvent import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.util.data.Config import moe.nea.firmament.util.data.ProfileSpecificDataHolder object WarpUtil { val warps: Sequence get() = RepoManager.neuRepo.constants.islands.warps .asSequence() .filter { it.warp !in ignoredWarps } val ignoredWarps = setOf("carnival", "") @Serializable data class Data( val excludedWarps: MutableSet = mutableSetOf(), ) @Config object DConfig : ProfileSpecificDataHolder(serializer(), "warp-util", ::Data) private var lastAttemptedWarp = "" private var lastWarpAttempt = TimeMark.farPast() fun findNearestWarp(island: SkyBlockIsland, pos: Position): Islands.Warp? { return warps.asSequence().filter { it.mode == island.locrawMode }.minByOrNull { if (DConfig.data?.excludedWarps?.contains(it.warp) == true) { return@minByOrNull Double.MAX_VALUE } else { return@minByOrNull squaredDist(pos, it) } } } private fun squaredDist(pos: Position, warp: Warp): Double { val dx = pos.x - warp.x val dy = pos.y - warp.y val dz = pos.z - warp.z return dx * dx + dy * dy + dz * dz } fun teleportToNearestWarp(island: SkyBlockIsland, pos: Position) { val nearestWarp = findNearestWarp(island, pos) if (nearestWarp == null) { MC.sendChat(Text.translatable("firmament.warp-util.no-warp-found", island.userFriendlyName)) return } if (island == SBData.skyblockLocation && sqrt(squaredDist(pos, nearestWarp)) > 1.1 * sqrt(squaredDist((MC.player ?: return).pos, nearestWarp)) ) { MC.sendChat(Text.translatable("firmament.warp-util.already-close", nearestWarp.warp)) return } MC.sendChat(Text.translatable("firmament.warp-util.attempting-to-warp", nearestWarp.warp)) lastWarpAttempt = TimeMark.now() lastAttemptedWarp = nearestWarp.warp MC.sendCommand("warp ${nearestWarp.warp}") } @Subscribe fun clearUnlockedWarpsCommand(event: CommandEvent.SubCommand) { event.subcommand("clearwarps") { thenExecute { DConfig.data?.excludedWarps?.clear() DConfig.markDirty() source.sendFeedback(Text.translatable("firmament.warp-util.clear-excluded")) } } } init { ProcessChatEvent.subscribe("WarpUtil:processChat") { if (it.unformattedString == "You haven't unlocked this fast travel destination!" && lastWarpAttempt.passedTime() < 2.seconds ) { DConfig.data?.excludedWarps?.add(lastAttemptedWarp) DConfig.markDirty() MC.sendChat(Text.stringifiedTranslatable("firmament.warp-util.mark-excluded", lastAttemptedWarp)) lastWarpAttempt = TimeMark.farPast() } if (it.unformattedString.startsWith("You may now fast travel to")) { DConfig.data?.excludedWarps?.clear() DConfig.markDirty() } } } }