diff options
author | Linnea Gräf <nea@nea.moe> | 2024-08-28 19:04:24 +0200 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2024-08-28 19:04:24 +0200 |
commit | d2f240ff0ca0d27f417f837e706c781a98c31311 (patch) | |
tree | 0db7aff6cc14deaf36eed83889d59fd6b3a6f599 /src/main/kotlin/moe/nea/firmament/features | |
parent | a6906308163aa3b2d18fa1dc1aa71ac9bbcc83ab (diff) | |
download | firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.tar.gz firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.tar.bz2 firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.zip |
Refactor source layout
Introduce compat source sets and move all kotlin sources to the main directory
[no changelog]
Diffstat (limited to 'src/main/kotlin/moe/nea/firmament/features')
69 files changed, 0 insertions, 6224 deletions
diff --git a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt deleted file mode 100644 index 19b91de..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt +++ /dev/null @@ -1,120 +0,0 @@ - - -package moe.nea.firmament.features - -import kotlinx.serialization.Serializable -import kotlinx.serialization.serializer -import moe.nea.firmament.Firmament -import moe.nea.firmament.annotations.generated.AllSubscriptions -import moe.nea.firmament.events.FeaturesInitializedEvent -import moe.nea.firmament.events.FirmamentEvent -import moe.nea.firmament.events.subscription.Subscription -import moe.nea.firmament.features.chat.AutoCompletions -import moe.nea.firmament.features.chat.ChatLinks -import moe.nea.firmament.features.chat.QuickCommands -import moe.nea.firmament.features.debug.DebugView -import moe.nea.firmament.features.debug.DeveloperFeatures -import moe.nea.firmament.features.debug.MinorTrolling -import moe.nea.firmament.features.debug.PowerUserTools -import moe.nea.firmament.features.diana.DianaWaypoints -import moe.nea.firmament.features.events.anniversity.AnniversaryFeatures -import moe.nea.firmament.features.events.carnival.CarnivalFeatures -import moe.nea.firmament.features.fixes.CompatibliltyFeatures -import moe.nea.firmament.features.fixes.Fixes -import moe.nea.firmament.features.inventory.CraftingOverlay -import moe.nea.firmament.features.inventory.ItemRarityCosmetics -import moe.nea.firmament.features.inventory.PriceData -import moe.nea.firmament.features.inventory.SaveCursorPosition -import moe.nea.firmament.features.inventory.SlotLocking -import moe.nea.firmament.features.inventory.buttons.InventoryButtons -import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlay -import moe.nea.firmament.features.mining.PickaxeAbility -import moe.nea.firmament.features.mining.PristineProfitTracker -import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures -import moe.nea.firmament.features.world.FairySouls -import moe.nea.firmament.features.world.Waypoints -import moe.nea.firmament.util.data.DataHolder - -object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "features", ::Config) { - @Serializable - data class Config( - val enabledFeatures: MutableMap<String, Boolean> = mutableMapOf() - ) - - private val features = mutableMapOf<String, FirmamentFeature>() - - val allFeatures: Collection<FirmamentFeature> get() = features.values - - private var hasAutoloaded = false - - init { - autoload() - } - - fun autoload() { - synchronized(this) { - if (hasAutoloaded) return - loadFeature(MinorTrolling) - loadFeature(FairySouls) - loadFeature(AutoCompletions) - // TODO: loadFeature(FishingWarning) - loadFeature(SlotLocking) - loadFeature(StorageOverlay) - loadFeature(PristineProfitTracker) - loadFeature(CraftingOverlay) - loadFeature(PowerUserTools) - loadFeature(Waypoints) - loadFeature(ChatLinks) - loadFeature(InventoryButtons) - loadFeature(CompatibliltyFeatures) - loadFeature(AnniversaryFeatures) - loadFeature(QuickCommands) - loadFeature(SaveCursorPosition) - loadFeature(CustomSkyBlockTextures) - loadFeature(PriceData) - loadFeature(Fixes) - loadFeature(DianaWaypoints) - loadFeature(ItemRarityCosmetics) - loadFeature(PickaxeAbility) - loadFeature(CarnivalFeatures) - if (Firmament.DEBUG) { - loadFeature(DeveloperFeatures) - loadFeature(DebugView) - } - allFeatures.forEach { it.config } - FeaturesInitializedEvent.publish(FeaturesInitializedEvent(allFeatures.toList())) - hasAutoloaded = true - } - } - - fun subscribeEvents() { - AllSubscriptions.provideSubscriptions { - subscribeSingleEvent(it) - } - } - - private fun <T : FirmamentEvent> subscribeSingleEvent(it: Subscription<T>) { - it.eventBus.subscribe(false, it.invoke) - } - - fun loadFeature(feature: FirmamentFeature) { - synchronized(features) { - if (feature.identifier in features) { - Firmament.logger.error("Double registering feature ${feature.identifier}. Ignoring second instance $feature") - return - } - features[feature.identifier] = feature - feature.onLoad() - } - } - - fun isEnabled(identifier: String): Boolean? = - data.enabledFeatures[identifier] - - - fun setEnabled(identifier: String, value: Boolean) { - data.enabledFeatures[identifier] = value - markDirty() - } - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt b/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt deleted file mode 100644 index 2cfc4fd..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt +++ /dev/null @@ -1,23 +0,0 @@ - - -package moe.nea.firmament.features - -import moe.nea.firmament.events.subscription.SubscriptionOwner -import moe.nea.firmament.gui.config.ManagedConfig - -// TODO: remove this entire feature system and revamp config -interface FirmamentFeature : SubscriptionOwner { - val identifier: String - val defaultEnabled: Boolean - get() = true - var isEnabled: Boolean - get() = FeatureManager.isEnabled(identifier) ?: defaultEnabled - set(value) { - FeatureManager.setEnabled(identifier, value) - } - override val delegateFeature: FirmamentFeature - get() = this - val config: ManagedConfig? get() = null - fun onLoad() {} - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/chat/AutoCompletions.kt b/src/main/kotlin/moe/nea/firmament/features/chat/AutoCompletions.kt deleted file mode 100644 index 9144898..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/chat/AutoCompletions.kt +++ /dev/null @@ -1,57 +0,0 @@ - - -package moe.nea.firmament.features.chat - -import com.mojang.brigadier.arguments.StringArgumentType.string -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.commands.get -import moe.nea.firmament.commands.suggestsList -import moe.nea.firmament.commands.thenArgument -import moe.nea.firmament.commands.thenExecute -import moe.nea.firmament.events.CommandEvent -import moe.nea.firmament.events.MaskCommands -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.repo.RepoManager -import moe.nea.firmament.util.MC - -object AutoCompletions : FirmamentFeature { - - object TConfig : ManagedConfig(identifier) { - val provideWarpTabCompletion by toggle("warp-complete") { true } - val replaceWarpIsByWarpIsland by toggle("warp-is") { true } - } - - override val config: ManagedConfig? - get() = TConfig - override val identifier: String - get() = "auto-completions" - - @Subscribe - fun onMaskCommands(event: MaskCommands) { - if (TConfig.provideWarpTabCompletion) { - event.mask("warp") - } - } - - @Subscribe - fun onCommandEvent(event: CommandEvent) { - if (!TConfig.provideWarpTabCompletion) return - event.deleteCommand("warp") - event.register("warp") { - thenArgument("to", string()) { toArg -> - suggestsList { - RepoManager.neuRepo.constants?.islands?.warps?.flatMap { listOf(it.warp) + it.aliases } ?: listOf() - } - thenExecute { - val warpName = get(toArg) - if (warpName == "is" && TConfig.replaceWarpIsByWarpIsland) { - MC.sendServerCommand("warp island") - } else { - MC.sendServerCommand("warp $warpName") - } - } - } - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/chat/ChatLinks.kt b/src/main/kotlin/moe/nea/firmament/features/chat/ChatLinks.kt deleted file mode 100644 index f2cb78a..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/chat/ChatLinks.kt +++ /dev/null @@ -1,161 +0,0 @@ - - -package moe.nea.firmament.features.chat - -import io.ktor.client.request.get -import io.ktor.client.statement.bodyAsChannel -import io.ktor.utils.io.jvm.javaio.toInputStream -import java.net.URL -import java.util.Collections -import moe.nea.jarvis.api.Point -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.async -import kotlin.math.min -import net.minecraft.client.gui.screen.ChatScreen -import net.minecraft.client.texture.NativeImage -import net.minecraft.client.texture.NativeImageBackedTexture -import net.minecraft.text.ClickEvent -import net.minecraft.text.HoverEvent -import net.minecraft.text.Style -import net.minecraft.text.Text -import net.minecraft.util.Formatting -import net.minecraft.util.Identifier -import moe.nea.firmament.Firmament -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.ModifyChatEvent -import moe.nea.firmament.events.ScreenRenderPostEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.transformEachRecursively -import moe.nea.firmament.util.unformattedString - -object ChatLinks : FirmamentFeature { - override val identifier: String - get() = "chat-links" - - object TConfig : ManagedConfig(identifier) { - val enableLinks by toggle("links-enabled") { true } - val imageEnabled by toggle("image-enabled") { true } - val allowAllHosts by toggle("allow-all-hosts") { false } - val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" } - val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() } - val position by position("position", 16 * 20, 9 * 20) { Point(0.0, 0.0) } - } - - private fun isHostAllowed(host: String) = - TConfig.allowAllHosts || TConfig.actualAllowedHosts.any { it.equals(host, ignoreCase = true) } - - private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/")) - - override val config get() = TConfig - val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex() - - data class Image( - val texture: Identifier, - val width: Int, - val height: Int, - ) - - val imageCache: MutableMap<String, Deferred<Image?>> = - Collections.synchronizedMap(mutableMapOf<String, Deferred<Image?>>()) - - private fun tryCacheUrl(url: String) { - if (!isUrlAllowed(url)) { - return - } - if (url in imageCache) { - return - } - imageCache[url] = Firmament.coroutineScope.async { - try { - val response = Firmament.httpClient.get(URL(url)) - if (response.status.value == 200) { - val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob) - val image = NativeImage.read(inputStream) - val texture = MC.textureManager.registerDynamicTexture( - "dynamic_image_preview", - NativeImageBackedTexture(image) - ) - Image(texture, image.width, image.height) - } else - null - } catch (exc: Exception) { - exc.printStackTrace() - null - } - } - } - - val imageExtensions = listOf("jpg", "png", "gif", "jpeg") - fun isImageUrl(url: String): Boolean { - return (url.substringAfterLast('.').lowercase() in imageExtensions) - } - - @Subscribe - @OptIn(ExperimentalCoroutinesApi::class) - fun onRender(it: ScreenRenderPostEvent) { - if (!TConfig.imageEnabled) return - if (it.screen !is ChatScreen) return - val hoveredComponent = - MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return - val hoverEvent = hoveredComponent.hoverEvent ?: return - val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return - val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return - if (!isImageUrl(url)) return - val imageFuture = imageCache[url] ?: return - if (!imageFuture.isCompleted) return - val image = imageFuture.getCompleted() ?: return - it.drawContext.matrices.push() - val pos = TConfig.position - pos.applyTransformations(it.drawContext.matrices) - val scale = min(1F, min((9 * 20F) / image.height, (16 * 20F) / image.width)) - it.drawContext.matrices.scale(scale, scale, 1F) - it.drawContext.drawTexture( - image.texture, - 0, - 0, - 1F, - 1F, - image.width, - image.height, - image.width, - image.height, - ) - it.drawContext.matrices.pop() - } - - @Subscribe - fun onModifyChat(it: ModifyChatEvent) { - if (!TConfig.enableLinks) return - it.replaceWith = it.replaceWith.transformEachRecursively { child -> - val text = child.string - if ("://" !in text) return@transformEachRecursively child - val s = Text.empty().setStyle(child.style) - var index = 0 - while (index < text.length) { - val nextMatch = urlRegex.find(text, index) - if (nextMatch == null) { - s.append(Text.literal(text.substring(index, text.length))) - break - } - val range = nextMatch.groups[0]!!.range - val url = nextMatch.groupValues[0] - s.append(Text.literal(text.substring(index, range.first))) - s.append( - Text.literal(url).setStyle( - Style.EMPTY.withUnderline(true).withColor( - Formatting.AQUA - ).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url))) - .withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url)) - ) - ) - if (isImageUrl(url)) - tryCacheUrl(url) - index = range.last + 1 - } - s - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/chat/QuickCommands.kt b/src/main/kotlin/moe/nea/firmament/features/chat/QuickCommands.kt deleted file mode 100644 index 5944b92..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/chat/QuickCommands.kt +++ /dev/null @@ -1,100 +0,0 @@ - - -package moe.nea.firmament.features.chat - -import com.mojang.brigadier.context.CommandContext -import net.minecraft.text.Text -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.commands.DefaultSource -import moe.nea.firmament.commands.RestArgumentType -import moe.nea.firmament.commands.get -import moe.nea.firmament.commands.thenArgument -import moe.nea.firmament.commands.thenExecute -import moe.nea.firmament.events.CommandEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.SBData - -object QuickCommands : FirmamentFeature { - override val identifier: String - get() = "quick-commands" - - fun removePartialPrefix(text: String, prefix: String): String? { - var lf: String? = null - for (i in 1..prefix.length) { - if (text.startsWith(prefix.substring(0, i))) { - lf = text.substring(i) - } - } - return lf - } - - val kuudraLevelNames = listOf("NORMAL", "HOT", "BURNING", "FIERY", "INFERNAL") - val dungeonLevelNames = listOf("ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN") - - @Subscribe - fun onCommands(it: CommandEvent) { - it.register("join") { - thenArgument("what", RestArgumentType) { what -> - thenExecute { - val what = this[what] - if (!SBData.isOnSkyblock) { - MC.sendCommand("join $what") - return@thenExecute - } - val joinName = getNameForFloor(what.replace(" ", "").lowercase()) - if (joinName == null) { - source.sendFeedback(Text.stringifiedTranslatable("firmament.quick-commands.join.unknown", what)) - } else { - source.sendFeedback(Text.stringifiedTranslatable("firmament.quick-commands.join.success", - joinName)) - MC.sendCommand("joininstance $joinName") - } - } - } - thenExecute { - source.sendFeedback(Text.translatable("firmament.quick-commands.join.explain")) - } - } - } - - fun CommandContext<DefaultSource>.getNameForFloor(w: String): String? { - val kuudraLevel = removePartialPrefix(w, "kuudratier") ?: removePartialPrefix(w, "tier") - if (kuudraLevel != null) { - val l = kuudraLevel.toIntOrNull()?.let { it - 1 } ?: kuudraLevelNames.indexOfFirst { - it.startsWith( - kuudraLevel, - true - ) - } - if (l !in kuudraLevelNames.indices) { - source.sendFeedback(Text.stringifiedTranslatable("firmament.quick-commands.join.unknown-kuudra", - kuudraLevel)) - return null - } - return "KUUDRA_${kuudraLevelNames[l]}" - } - val masterLevel = removePartialPrefix(w, "master") - val normalLevel = - removePartialPrefix(w, "floor") ?: removePartialPrefix(w, "catacombs") ?: removePartialPrefix(w, "dungeons") - val dungeonLevel = masterLevel ?: normalLevel - if (dungeonLevel != null) { - val l = dungeonLevel.toIntOrNull()?.let { it - 1 } ?: dungeonLevelNames.indexOfFirst { - it.startsWith( - dungeonLevel, - true - ) - } - if (masterLevel == null && (l == -1 || null != removePartialPrefix(w, "entrance"))) { - return "CATACOMBS_ENTRANCE" - } - if (l !in dungeonLevelNames.indices) { - source.sendFeedback(Text.stringifiedTranslatable("firmament.quick-commands.join.unknown-catacombs", - kuudraLevel)) - return null - } - return "${if (masterLevel != null) "MASTER_" else ""}CATACOMBS_FLOOR_${dungeonLevelNames[l]}" - } - return null - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/debug/DebugLogger.kt b/src/main/kotlin/moe/nea/firmament/features/debug/DebugLogger.kt deleted file mode 100644 index ab06030..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/debug/DebugLogger.kt +++ /dev/null @@ -1,13 +0,0 @@ - -package moe.nea.firmament.features.debug - -import net.minecraft.text.Text -import moe.nea.firmament.util.MC - -class DebugLogger(val tag: String) { - fun isEnabled() = DeveloperFeatures.isEnabled // TODO: allow filtering by tag - fun log(text: () -> String) { - if (!isEnabled()) return - MC.sendChat(Text.literal(text())) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/debug/DebugView.kt b/src/main/kotlin/moe/nea/firmament/features/debug/DebugView.kt deleted file mode 100644 index 7e1b8ec..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/debug/DebugView.kt +++ /dev/null @@ -1,38 +0,0 @@ - - -package moe.nea.firmament.features.debug - -import moe.nea.firmament.Firmament -import moe.nea.firmament.events.TickEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.util.TimeMark - -object DebugView : FirmamentFeature { - private data class StoredVariable<T>( - val obj: T, - val timer: TimeMark, - ) - - private val storedVariables: MutableMap<String, StoredVariable<*>> = sortedMapOf() - override val identifier: String - get() = "debug-view" - override val defaultEnabled: Boolean - get() = Firmament.DEBUG - - fun <T : Any?> showVariable(label: String, obj: T) { - synchronized(this) { - storedVariables[label] = StoredVariable(obj, TimeMark.now()) - } - } - - fun recalculateDebugWidget() { - } - - override fun onLoad() { - TickEvent.subscribe { - synchronized(this) { - recalculateDebugWidget() - } - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/debug/DeveloperFeatures.kt b/src/main/kotlin/moe/nea/firmament/features/debug/DeveloperFeatures.kt deleted file mode 100644 index 20c0cfd..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/debug/DeveloperFeatures.kt +++ /dev/null @@ -1,55 +0,0 @@ - - -package moe.nea.firmament.features.debug - -import java.nio.file.Path -import java.util.concurrent.CompletableFuture -import kotlin.io.path.absolute -import kotlin.io.path.exists -import net.minecraft.client.MinecraftClient -import net.minecraft.text.Text -import moe.nea.firmament.Firmament -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.TimeMark -import moe.nea.firmament.util.errorBoundary -import moe.nea.firmament.util.iterate - -object DeveloperFeatures : FirmamentFeature { - override val identifier: String - get() = "developer" - override val config: TConfig - get() = TConfig - override val defaultEnabled: Boolean - get() = Firmament.DEBUG - - val gradleDir = - Path.of(".").absolute() - .iterate { it.parent } - .find { it.resolve("settings.gradle.kts").exists() } - - object TConfig : ManagedConfig("developer") { - val autoRebuildResources by toggle("auto-rebuild") { false } - } - - @JvmStatic - fun hookOnBeforeResourceReload(client: MinecraftClient): CompletableFuture<Void> { - val reloadFuture = if (TConfig.autoRebuildResources && isEnabled && gradleDir != null) { - val builder = ProcessBuilder("./gradlew", ":processResources") - builder.directory(gradleDir.toFile()) - builder.inheritIO() - val process = builder.start() - MC.player?.sendMessage(Text.translatable("firmament.dev.resourcerebuild.start")) - val startTime = TimeMark.now() - process.toHandle().onExit().thenApply { - MC.player?.sendMessage(Text.stringifiedTranslatable("firmament.dev.resourcerebuild.done", startTime.passedTime())) - Unit - } - } else { - CompletableFuture.completedFuture(Unit) - } - return reloadFuture.thenCompose { client.reloadResources() } - } -} - diff --git a/src/main/kotlin/moe/nea/firmament/features/debug/MinorTrolling.kt b/src/main/kotlin/moe/nea/firmament/features/debug/MinorTrolling.kt deleted file mode 100644 index 32035a6..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/debug/MinorTrolling.kt +++ /dev/null @@ -1,27 +0,0 @@ - - -package moe.nea.firmament.features.debug - -import net.minecraft.text.Text -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.ModifyChatEvent -import moe.nea.firmament.features.FirmamentFeature - - -// In memorian Dulkir -object MinorTrolling : FirmamentFeature { - override val identifier: String - get() = "minor-trolling" - - val trollers = listOf("nea89o", "lrg89") - val t = "From(?: \\[[^\\]]+])? ([^:]+): (.*)".toRegex() - - @Subscribe - fun onTroll(it: ModifyChatEvent) { - val m = t.matchEntire(it.unformattedString) ?: return - val (_, name, text) = m.groupValues - if (name !in trollers) return - if (!text.startsWith("c:")) return - it.replaceWith = Text.literal(text.substring(2).replace("&", "§")) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/debug/PowerUserTools.kt b/src/main/kotlin/moe/nea/firmament/features/debug/PowerUserTools.kt deleted file mode 100644 index 7893eff..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/debug/PowerUserTools.kt +++ /dev/null @@ -1,193 +0,0 @@ - - -package moe.nea.firmament.features.debug - -import net.minecraft.block.SkullBlock -import net.minecraft.block.entity.SkullBlockEntity -import net.minecraft.component.DataComponentTypes -import net.minecraft.entity.Entity -import net.minecraft.entity.LivingEntity -import net.minecraft.item.ItemStack -import net.minecraft.item.Items -import net.minecraft.text.Text -import net.minecraft.util.hit.BlockHitResult -import net.minecraft.util.hit.EntityHitResult -import net.minecraft.util.hit.HitResult -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.CommandEvent -import moe.nea.firmament.events.CustomItemModelEvent -import moe.nea.firmament.events.HandledScreenKeyPressedEvent -import moe.nea.firmament.events.ItemTooltipEvent -import moe.nea.firmament.events.ScreenChangeEvent -import moe.nea.firmament.events.TickEvent -import moe.nea.firmament.events.WorldKeyboardEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.mixins.accessor.AccessorHandledScreen -import moe.nea.firmament.util.ClipboardUtils -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.focusedItemStack -import moe.nea.firmament.util.skyBlockId - -object PowerUserTools : FirmamentFeature { - override val identifier: String - get() = "power-user" - - object TConfig : ManagedConfig(identifier) { - val showItemIds by toggle("show-item-id") { false } - val copyItemId by keyBindingWithDefaultUnbound("copy-item-id") - val copyTexturePackId by keyBindingWithDefaultUnbound("copy-texture-pack-id") - val copyNbtData by keyBindingWithDefaultUnbound("copy-nbt-data") - val copySkullTexture by keyBindingWithDefaultUnbound("copy-skull-texture") - val copyEntityData by keyBindingWithDefaultUnbound("entity-data") - } - - override val config - get() = TConfig - - var lastCopiedStack: Pair<ItemStack, Text>? = null - set(value) { - field = value - if (value != null) - lastCopiedStackViewTime = true - } - var lastCopiedStackViewTime = false - - override fun onLoad() { - TickEvent.subscribe { - if (!lastCopiedStackViewTime) - lastCopiedStack = null - lastCopiedStackViewTime = false - } - ScreenChangeEvent.subscribe { - lastCopiedStack = null - } - } - - fun debugFormat(itemStack: ItemStack): Text { - return Text.literal(itemStack.skyBlockId?.toString() ?: itemStack.toString()) - } - - @Subscribe - fun onEntityInfo(event: WorldKeyboardEvent) { - if (!event.matches(TConfig.copyEntityData)) return - val target = (MC.instance.crosshairTarget as? EntityHitResult)?.entity - if (target == null) { - MC.sendChat(Text.translatable("firmament.poweruser.entity.fail")) - return - } - showEntity(target) - } - - fun showEntity(target: Entity) { - MC.sendChat(Text.translatable("firmament.poweruser.entity.type", target.type)) - MC.sendChat(Text.translatable("firmament.poweruser.entity.name", target.name)) - MC.sendChat(Text.stringifiedTranslatable("firmament.poweruser.entity.position", target.pos)) - if (target is LivingEntity) { - MC.sendChat(Text.translatable("firmament.poweruser.entity.armor")) - for (armorItem in target.armorItems) { - MC.sendChat(Text.translatable("firmament.poweruser.entity.armor.item", debugFormat(armorItem))) - } - } - MC.sendChat(Text.stringifiedTranslatable("firmament.poweruser.entity.passengers", target.passengerList.size)) - target.passengerList.forEach { - showEntity(it) - } - } - - - @Subscribe - fun copyInventoryInfo(it: HandledScreenKeyPressedEvent) { - if (it.screen !is AccessorHandledScreen) return - val item = it.screen.focusedItemStack ?: return - if (it.matches(TConfig.copyItemId)) { - val sbId = item.skyBlockId - if (sbId == null) { - lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skyblockid.fail")) - return - } - ClipboardUtils.setTextContent(sbId.neuItem) - lastCopiedStack = - Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skyblockid", sbId.neuItem)) - } else if (it.matches(TConfig.copyTexturePackId)) { - val model = CustomItemModelEvent.getModelIdentifier(item) - if (model == null) { - lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.modelid.fail")) - return - } - ClipboardUtils.setTextContent(model.toString()) - lastCopiedStack = - Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.modelid", model.toString())) - } else if (it.matches(TConfig.copyNbtData)) { - // TODO: copy full nbt - val nbt = item.get(DataComponentTypes.CUSTOM_DATA)?.nbt?.toString() ?: "<empty>" - ClipboardUtils.setTextContent(nbt) - lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.nbt")) - } else if (it.matches(TConfig.copySkullTexture)) { - if (item.item != Items.PLAYER_HEAD) { - lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-skull")) - return - } - val profile = item.get(DataComponentTypes.PROFILE) - if (profile == null) { - lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-profile")) - return - } - val skullTexture = CustomSkyBlockTextures.getSkullTexture(profile) - if (skullTexture == null) { - lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-texture")) - return - } - ClipboardUtils.setTextContent(skullTexture.toString()) - lastCopiedStack = - Pair( - item, - Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString()) - ) - println("Copied skull id: $skullTexture") - } - } - - @Subscribe - fun onCopyWorldInfo(it: WorldKeyboardEvent) { - if (it.matches(TConfig.copySkullTexture)) { - val p = MC.camera ?: return - val blockHit = p.raycast(20.0, 0.0f, false) ?: return - if (blockHit.type != HitResult.Type.BLOCK || blockHit !is BlockHitResult) { - MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail")) - return - } - val blockAt = p.world.getBlockState(blockHit.blockPos)?.block - val entity = p.world.getBlockEntity(blockHit.blockPos) - if (blockAt !is SkullBlock || entity !is SkullBlockEntity || entity.owner == null) { - MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail")) - return - } - val id = CustomSkyBlockTextures.getSkullTexture(entity.owner!!) - if (id == null) { - MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail")) - } else { - ClipboardUtils.setTextContent(id.toString()) - MC.sendChat(Text.stringifiedTranslatable("firmament.tooltip.copied.skull", id.toString())) - } - } - } - - @Subscribe - fun addItemId(it: ItemTooltipEvent) { - if (TConfig.showItemIds) { - val id = it.stack.skyBlockId ?: return - it.lines.add(Text.stringifiedTranslatable("firmament.tooltip.skyblockid", id.neuItem)) - } - val (item, text) = lastCopiedStack ?: return - if (!ItemStack.areEqual(item, it.stack)) { - lastCopiedStack = null - return - } - lastCopiedStackViewTime = true - it.lines.add(text) - } - - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt b/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt deleted file mode 100644 index 39ca6d3..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt +++ /dev/null @@ -1,131 +0,0 @@ - -package moe.nea.firmament.features.diana - -import kotlin.time.Duration.Companion.seconds -import net.minecraft.particle.ParticleTypes -import net.minecraft.sound.SoundEvents -import net.minecraft.util.math.Vec3d -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.ParticleSpawnEvent -import moe.nea.firmament.events.SoundReceiveEvent -import moe.nea.firmament.events.WorldKeyboardEvent -import moe.nea.firmament.events.WorldReadyEvent -import moe.nea.firmament.events.WorldRenderLastEvent -import moe.nea.firmament.events.subscription.SubscriptionOwner -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.SBData -import moe.nea.firmament.util.SkyBlockIsland -import moe.nea.firmament.util.SkyblockId -import moe.nea.firmament.util.TimeMark -import moe.nea.firmament.util.WarpUtil -import moe.nea.firmament.util.render.RenderInWorldContext -import moe.nea.firmament.util.skyBlockId - -object AncestralSpadeSolver : SubscriptionOwner { - var lastDing = TimeMark.farPast() - private set - private val pitches = mutableListOf<Float>() - val particlePositions = mutableListOf<Vec3d>() - var nextGuess: Vec3d? = null - private set - - val ancestralSpadeId = SkyblockId("ANCESTRAL_SPADE") - private var lastTeleportAttempt = TimeMark.farPast() - - fun isEnabled() = - DianaWaypoints.TConfig.ancestralSpadeSolver - && SBData.skyblockLocation == SkyBlockIsland.HUB - && MC.player?.inventory?.containsAny { it.skyBlockId == ancestralSpadeId } == true // TODO: add a reactive property here - - @Subscribe - fun onKeyBind(event: WorldKeyboardEvent) { - if (!isEnabled()) return - if (!event.matches(DianaWaypoints.TConfig.ancestralSpadeTeleport)) return - - if (lastTeleportAttempt.passedTime() < 3.seconds) return - WarpUtil.teleportToNearestWarp(SkyBlockIsland.HUB, nextGuess ?: return) - lastTeleportAttempt = TimeMark.now() - } - - @Subscribe - fun onParticleSpawn(event: ParticleSpawnEvent) { - if (!isEnabled()) return - if (event.particleEffect != ParticleTypes.DRIPPING_LAVA) return - if (event.offset.x != 0.0F || event.offset.y != 0F || event.offset.z != 0F) - return - particlePositions.add(event.position) - if (particlePositions.size > 20) { - particlePositions.removeFirst() - } - } - - @Subscribe - fun onPlaySound(event: SoundReceiveEvent) { - if (!isEnabled()) return - if (!SoundEvents.BLOCK_NOTE_BLOCK_HARP.matchesId(event.sound.value().id)) return - - if (lastDing.passedTime() > 1.seconds) { - particlePositions.clear() - pitches.clear() - } - lastDing = TimeMark.now() - - pitches.add(event.pitch) - if (pitches.size > 20) { - pitches.removeFirst() - } - - if (particlePositions.size < 3) { - return - } - - val averagePitchDelta = - if (pitches.isEmpty()) return - else pitches - .zipWithNext { a, b -> b - a } - .average() - - val soundDistanceEstimate = (Math.E / averagePitchDelta) - particlePositions.first().distanceTo(event.position) - - if (soundDistanceEstimate > 1000) { - return - } - - val lastParticleDirection = particlePositions - .takeLast(3) - .let { (a, _, b) -> b.subtract(a) } - .normalize() - - nextGuess = event.position.add(lastParticleDirection.multiply(soundDistanceEstimate)) - } - - @Subscribe - fun onWorldRender(event: WorldRenderLastEvent) { - if (!isEnabled()) return - RenderInWorldContext.renderInWorld(event) { - nextGuess?.let { - color(1f, 1f, 0f, 0.5f) - tinyBlock(it, 1f) - color(1f, 1f, 0f, 1f) - tracer(it, lineWidth = 3f) - } - if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) { - color(0f, 1f, 0f, 0.7f) - line(particlePositions) - } - } - } - - @Subscribe - fun onSwapWorld(event: WorldReadyEvent) { - nextGuess = null - particlePositions.clear() - pitches.clear() - lastDing = TimeMark.farPast() - } - - override val delegateFeature: FirmamentFeature - get() = DianaWaypoints - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/diana/DianaWaypoints.kt b/src/main/kotlin/moe/nea/firmament/features/diana/DianaWaypoints.kt deleted file mode 100644 index 0a34eaa..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/diana/DianaWaypoints.kt +++ /dev/null @@ -1,35 +0,0 @@ - -package moe.nea.firmament.features.diana - -import moe.nea.firmament.events.AttackBlockEvent -import moe.nea.firmament.events.ParticleSpawnEvent -import moe.nea.firmament.events.ProcessChatEvent -import moe.nea.firmament.events.SoundReceiveEvent -import moe.nea.firmament.events.UseBlockEvent -import moe.nea.firmament.events.WorldKeyboardEvent -import moe.nea.firmament.events.WorldReadyEvent -import moe.nea.firmament.events.WorldRenderLastEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig - -object DianaWaypoints : FirmamentFeature { - override val identifier get() = "diana" - override val config get() = TConfig - - object TConfig : ManagedConfig(identifier) { - val ancestralSpadeSolver by toggle("ancestral-spade") { true } - val ancestralSpadeTeleport by keyBindingWithDefaultUnbound("ancestral-teleport") - val nearbyWaypoints by toggle("nearby-waypoints") { true } - } - - override fun onLoad() { - UseBlockEvent.subscribe { - NearbyBurrowsSolver.onBlockClick(it.hitResult.blockPos) - } - AttackBlockEvent.subscribe { - NearbyBurrowsSolver.onBlockClick(it.blockPos) - } - } -} - - diff --git a/src/main/kotlin/moe/nea/firmament/features/diana/NearbyBurrowsSolver.kt b/src/main/kotlin/moe/nea/firmament/features/diana/NearbyBurrowsSolver.kt deleted file mode 100644 index 7158bb9..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/diana/NearbyBurrowsSolver.kt +++ /dev/null @@ -1,144 +0,0 @@ - -package moe.nea.firmament.features.diana - -import kotlin.time.Duration.Companion.seconds -import net.minecraft.particle.ParticleTypes -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.MathHelper -import net.minecraft.util.math.Position -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.ParticleSpawnEvent -import moe.nea.firmament.events.ProcessChatEvent -import moe.nea.firmament.events.WorldReadyEvent -import moe.nea.firmament.events.WorldRenderLastEvent -import moe.nea.firmament.events.subscription.SubscriptionOwner -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.util.TimeMark -import moe.nea.firmament.util.mutableMapWithMaxSize -import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorld - -object NearbyBurrowsSolver : SubscriptionOwner { - - - private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20) - private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500) - private var lastBlockClick: BlockPos? = null - - enum class BurrowType { - START, MOB, TREASURE - } - - val burrows = mutableMapOf<BlockPos, BurrowType>() - - @Subscribe - fun onChatEvent(event: ProcessChatEvent) { - val lastClickedBurrow = lastBlockClick ?: return - if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") || - event.unformattedString.startsWith(" ☠ You were killed by") || - event.unformattedString.startsWith("You finished the Griffin burrow chain!") - ) { - markAsDug(lastClickedBurrow) - burrows.remove(lastClickedBurrow) - } - } - - - fun wasRecentlyDug(blockPos: BlockPos): Boolean { - val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast() - return lastDigTime.passedTime() < 10.seconds - } - - fun markAsDug(blockPos: BlockPos) { - recentlyDugBurrows[blockPos] = TimeMark.now() - } - - fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean { - val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast() - return lastEnchantTime.passedTime() < 4.seconds - } - - fun markAsEnchanted(blockPos: BlockPos) { - recentEnchantParticles[blockPos] = TimeMark.now() - } - - @Subscribe - fun onParticles(event: ParticleSpawnEvent) { - if (!DianaWaypoints.TConfig.nearbyWaypoints) return - - val position: BlockPos = event.position.toBlockPos().down() - - if (wasRecentlyDug(position)) return - - val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f) - - if (event.particleEffect.type == ParticleTypes.ENCHANT) { - if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) { - markAsEnchanted(position) - } - return - } - - if (!wasRecentlyEnchanted(position)) return - - if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT - && event.count == 4 - && event.speed == 0.01F - && event.offset.y == 0.1f - && isEven50Spread - ) { - burrows[position] = BurrowType.START - } - if (event.particleEffect.type == ParticleTypes.CRIT - && event.count == 3 - && event.speed == 0.01F - && event.offset.y == 0.1F - && isEven50Spread - ) { - burrows[position] = BurrowType.MOB - } - if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA - && event.count == 2 - && event.speed == 0.01F - && event.offset.y == 0.1F - && event.offset.x == 0.35F && event.offset.z == 0.35f - ) { - burrows[position] = BurrowType.TREASURE - } - } - - @Subscribe - fun onRender(event: WorldRenderLastEvent) { - if (!DianaWaypoints.TConfig.nearbyWaypoints) return - renderInWorld(event) { - for ((location, burrow) in burrows) { - when (burrow) { - BurrowType.START -> color(.2f, .8f, .2f, 0.4f) - BurrowType.MOB -> color(0.3f, 0.4f, 0.9f, 0.4f) - BurrowType.TREASURE -> color(1f, 0.7f, 0.2f, 0.4f) - } - block(location) - } - } - } - - @Subscribe - fun onSwapWorld(worldReadyEvent: WorldReadyEvent) { - burrows.clear() - recentEnchantParticles.clear() - recentlyDugBurrows.clear() - lastBlockClick = null - } - - fun onBlockClick(blockPos: BlockPos) { - if (!DianaWaypoints.TConfig.nearbyWaypoints) return - burrows.remove(blockPos) - lastBlockClick = blockPos - } - - override val delegateFeature: FirmamentFeature - get() = DianaWaypoints -} - -fun Position.toBlockPos(): BlockPos { - return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z)) -} diff --git a/src/main/kotlin/moe/nea/firmament/features/events/anniversity/AnniversaryFeatures.kt b/src/main/kotlin/moe/nea/firmament/features/events/anniversity/AnniversaryFeatures.kt deleted file mode 100644 index 8926a95..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/events/anniversity/AnniversaryFeatures.kt +++ /dev/null @@ -1,224 +0,0 @@ - -package moe.nea.firmament.features.events.anniversity - -import io.github.notenoughupdates.moulconfig.observer.ObservableList -import io.github.notenoughupdates.moulconfig.xml.Bind -import moe.nea.jarvis.api.Point -import kotlin.time.Duration.Companion.seconds -import net.minecraft.entity.passive.PigEntity -import net.minecraft.util.math.BlockPos -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.EntityInteractionEvent -import moe.nea.firmament.events.ProcessChatEvent -import moe.nea.firmament.events.TickEvent -import moe.nea.firmament.events.WorldReadyEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.gui.hud.MoulConfigHud -import moe.nea.firmament.rei.SBItemEntryDefinition -import moe.nea.firmament.repo.ItemNameLookup -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.SHORT_NUMBER_FORMAT -import moe.nea.firmament.util.SkyblockId -import moe.nea.firmament.util.TimeMark -import moe.nea.firmament.util.parseShortNumber -import moe.nea.firmament.util.useMatch - -object AnniversaryFeatures : FirmamentFeature { - override val identifier: String - get() = "anniversary" - - object TConfig : ManagedConfig(identifier) { - val enableShinyPigTracker by toggle("shiny-pigs") {true} - val trackPigCooldown by position("pig-hud", 200, 300) { Point(0.1, 0.2) } - } - - override val config: ManagedConfig? - get() = TConfig - - data class ClickedPig( - val clickedAt: TimeMark, - val startLocation: BlockPos, - val pigEntity: PigEntity - ) { - @Bind("timeLeft") - fun getTimeLeft(): Double = 1 - clickedAt.passedTime() / pigDuration - } - - val clickedPigs = ObservableList<ClickedPig>(mutableListOf()) - var lastClickedPig: PigEntity? = null - - val pigDuration = 90.seconds - - @Subscribe - fun onTick(event: TickEvent) { - clickedPigs.removeIf { it.clickedAt.passedTime() > pigDuration } - } - - val pattern = "SHINY! You extracted (?<reward>.*) from the piglet's orb!".toPattern() - - @Subscribe - fun onChat(event: ProcessChatEvent) { - if(!TConfig.enableShinyPigTracker)return - if (event.unformattedString == "Oink! Bring the pig back to the Shiny Orb!") { - val pig = lastClickedPig ?: return - // TODO: store proper location based on the orb location, maybe - val startLocation = pig.blockPos ?: return - clickedPigs.add(ClickedPig(TimeMark.now(), startLocation, pig)) - lastClickedPig = null - } - if (event.unformattedString == "SHINY! The orb is charged! Click on it for loot!") { - val player = MC.player ?: return - val lowest = - clickedPigs.minByOrNull { it.startLocation.getSquaredDistance(player.pos) } ?: return - clickedPigs.remove(lowest) - } - pattern.useMatch(event.unformattedString) { - val reward = group("reward") - val parsedReward = parseReward(reward) - addReward(parsedReward) - PigCooldown.rewards.atOnce { - PigCooldown.rewards.clear() - rewards.mapTo(PigCooldown.rewards) { PigCooldown.DisplayReward(it) } - } - } - } - - fun addReward(reward: Reward) { - val it = rewards.listIterator() - while (it.hasNext()) { - val merged = reward.mergeWith(it.next()) ?: continue - it.set(merged) - return - } - rewards.add(reward) - } - - val rewards = mutableListOf<Reward>() - - fun <T> ObservableList<T>.atOnce(block: () -> Unit) { - val oldObserver = observer - observer = null - block() - observer = oldObserver - update() - } - - sealed interface Reward { - fun mergeWith(other: Reward): Reward? - data class EXP(val amount: Double, val skill: String) : Reward { - override fun mergeWith(other: Reward): Reward? { - if (other is EXP && other.skill == skill) - return EXP(amount + other.amount, skill) - return null - } - } - - data class Coins(val amount: Double) : Reward { - override fun mergeWith(other: Reward): Reward? { - if (other is Coins) - return Coins(other.amount + amount) - return null - } - } - - data class Items(val amount: Int, val item: SkyblockId) : Reward { - override fun mergeWith(other: Reward): Reward? { - if (other is Items && other.item == item) - return Items(amount + other.amount, item) - return null - } - } - - data class Unknown(val text: String) : Reward { - override fun mergeWith(other: Reward): Reward? { - return null - } - } - } - - val expReward = "\\+(?<exp>$SHORT_NUMBER_FORMAT) (?<kind>[^ ]+) XP".toPattern() - val coinReward = "\\+(?<amount>$SHORT_NUMBER_FORMAT) coins".toPattern() - val itemReward = "(?:(?<amount>[0-9]+)x )?(?<name>.*)".toPattern() - fun parseReward(string: String): Reward { - expReward.useMatch<Unit>(string) { - val exp = parseShortNumber(group("exp")) - val kind = group("kind") - return Reward.EXP(exp, kind) - } - coinReward.useMatch<Unit>(string) { - val coins = parseShortNumber(group("amount")) - return Reward.Coins(coins) - } - itemReward.useMatch(string) { - val amount = group("amount")?.toIntOrNull() ?: 1 - val name = group("name") - val item = ItemNameLookup.guessItemByName(name, false) ?: return@useMatch - return Reward.Items(amount, item) - } - return Reward.Unknown(string) - } - - @Subscribe - fun onWorldClear(event: WorldReadyEvent) { - lastClickedPig = null - clickedPigs.clear() - } - - @Subscribe - fun onEntityClick(event: EntityInteractionEvent) { - if (event.entity is PigEntity) { - lastClickedPig = event.entity - } - } - - @Subscribe - fun init(event: WorldReadyEvent) { - PigCooldown.forceInit() - } - - object PigCooldown : MoulConfigHud("anniversary_pig", TConfig.trackPigCooldown) { - override fun shouldRender(): Boolean { - return clickedPigs.isNotEmpty() && TConfig.enableShinyPigTracker - } - - @Bind("pigs") - fun getPigs() = clickedPigs - - class DisplayReward(val backedBy: Reward) { - @Bind - fun count(): String { - return when (backedBy) { - is Reward.Coins -> backedBy.amount - is Reward.EXP -> backedBy.amount - is Reward.Items -> backedBy.amount - is Reward.Unknown -> 0 - }.toString() - } - - val itemStack = if (backedBy is Reward.Items) { - SBItemEntryDefinition.getEntry(backedBy.item, backedBy.amount) - } else { - SBItemEntryDefinition.getEntry(SkyblockId.NULL) - } - - @Bind - fun name(): String { - return when (backedBy) { - is Reward.Coins -> "Coins" - is Reward.EXP -> backedBy.skill - is Reward.Items -> itemStack.value.asItemStack().name.string - is Reward.Unknown -> backedBy.text - } - } - - @Bind - fun isKnown() = backedBy !is Reward.Unknown - } - - @get:Bind("rewards") - val rewards = ObservableList<DisplayReward>(mutableListOf()) - - } - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/events/carnival/CarnivalFeatures.kt b/src/main/kotlin/moe/nea/firmament/features/events/carnival/CarnivalFeatures.kt deleted file mode 100644 index 1e6d97a..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/events/carnival/CarnivalFeatures.kt +++ /dev/null @@ -1,17 +0,0 @@ - -package moe.nea.firmament.features.events.carnival - -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig - -object CarnivalFeatures : FirmamentFeature { - object TConfig : ManagedConfig(identifier) { - val enableBombSolver by toggle("bombs-solver") { true } - val displayTutorials by toggle("tutorials") { true } - } - - override val config: ManagedConfig? - get() = TConfig - override val identifier: String - get() = "carnival" -} diff --git a/src/main/kotlin/moe/nea/firmament/features/events/carnival/MinesweeperHelper.kt b/src/main/kotlin/moe/nea/firmament/features/events/carnival/MinesweeperHelper.kt deleted file mode 100644 index 06caf86..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/events/carnival/MinesweeperHelper.kt +++ /dev/null @@ -1,276 +0,0 @@ - -package moe.nea.firmament.features.events.carnival - -import io.github.notenoughupdates.moulconfig.observer.ObservableList -import io.github.notenoughupdates.moulconfig.platform.ModernItemStack -import io.github.notenoughupdates.moulconfig.xml.Bind -import java.util.UUID -import net.minecraft.block.Blocks -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.item.Items -import net.minecraft.text.ClickEvent -import net.minecraft.text.Text -import net.minecraft.util.math.BlockPos -import net.minecraft.world.WorldAccess -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.commands.thenExecute -import moe.nea.firmament.events.AttackBlockEvent -import moe.nea.firmament.events.CommandEvent -import moe.nea.firmament.events.EntityUpdateEvent -import moe.nea.firmament.events.ProcessChatEvent -import moe.nea.firmament.events.WorldReadyEvent -import moe.nea.firmament.events.WorldRenderLastEvent -import moe.nea.firmament.features.debug.DebugLogger -import moe.nea.firmament.util.LegacyFormattingCode -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.MoulConfigUtils -import moe.nea.firmament.util.ScreenUtil -import moe.nea.firmament.util.SkyblockId -import moe.nea.firmament.util.item.createSkullItem -import moe.nea.firmament.util.render.RenderInWorldContext -import moe.nea.firmament.util.setSkyBlockFirmamentUiId -import moe.nea.firmament.util.skyBlockId -import moe.nea.firmament.util.useMatch - -object MinesweeperHelper { - val sandBoxLow = BlockPos(-112, 72, -11) - val sandBoxHigh = BlockPos(-106, 72, -5) - val boardSize = Pair(sandBoxHigh.x - sandBoxLow.x, sandBoxHigh.z - sandBoxLow.z) - - val gameStartMessage = "[NPC] Carnival Pirateman: Good luck, matey!" - val gameEndMessage = "Fruit Digging" - val bombPattern = "MINES! There (are|is) (?<bombCount>[0-8]) bombs? hidden nearby\\.".toPattern() - val startGameQuestion = "[NPC] Carnival Pirateman: Would ye like to do some Fruit Digging?" - - - enum class Piece( - @get:Bind("fruitName") - val fruitName: String, - val points: Int, - val specialAbility: String, - val totalPerBoard: Int, - val textureHash: String, - val fruitColor: LegacyFormattingCode, - ) { - COCONUT("Coconut", - 200, - "Prevents a bomb from exploding next turn", - 3, - "10ceb1455b471d016a9f06d25f6e468df9fcf223e2c1e4795b16e84fcca264ee", - LegacyFormattingCode.DARK_PURPLE), - APPLE("Apple", - 100, - "Gains 100 points for each apple dug up", - 8, - "17ea278d6225c447c5943d652798d0bbbd1418434ce8c54c54fdac79994ddd6c", - LegacyFormattingCode.GREEN), - WATERMELON("Watermelon", - 100, - "Blows up an adjacent fruit for half the points", - 4, - "efe4ef83baf105e8dee6cf03dfe7407f1911b3b9952c891ae34139560f2931d6", - LegacyFormattingCode.DARK_BLUE), - DURIAN("Durian", - 800, - "Halves the points earned in the next turn", - 2, - "ac268d36c2c6047ffeec00124096376b56dbb4d756a55329363a1b27fcd659cd", - LegacyFormattingCode.DARK_PURPLE), - MANGO("Mango", - 300, - "Just an ordinary fruit", - 10, - "f363a62126a35537f8189343a22660de75e810c6ac004a7d3da65f1c040a839", - LegacyFormattingCode.GREEN), - DRAGON_FRUIT("Dragonfruit", - 1200, - "Halves the points earned in the next turn", - 1, - "3cc761bcb0579763d9b8ab6b7b96fa77eb6d9605a804d838fec39e7b25f95591", - LegacyFormattingCode.LIGHT_PURPLE), - POMEGRANATE("Pomegranate", - 200, - "Grants an extra 50% more points in the next turn", - 4, - "40824d18079042d5769f264f44394b95b9b99ce689688cc10c9eec3f882ccc08", - LegacyFormattingCode.DARK_BLUE), - CHERRY("Cherry", - 200, - "The second cherry grants 300 bonus points", - 2, - "c92b099a62cd2fbf8ada09dec145c75d7fda4dc57b968bea3a8fa11e37aa48b2", - LegacyFormattingCode.DARK_PURPLE), - BOMB("Bomb", - -1, - "Destroys nearby fruit", - 15, - "a76a2811d1e176a07b6d0a657b910f134896ce30850f6e80c7c83732d85381ea", - LegacyFormattingCode.DARK_RED), - RUM("Rum", - -1, - "Stops your dowsing ability for one turn", - 5, - "407b275d28b927b1bf7f6dd9f45fbdad2af8571c54c8f027d1bff6956fbf3c16", - LegacyFormattingCode.YELLOW), - ; - - val textureUrl = "http://textures.minecraft.net/texture/$textureHash" - val itemStack = createSkullItem(UUID.randomUUID(), textureUrl) - .setSkyBlockFirmamentUiId("MINESWEEPER_$name") - - @Bind - fun getIcon() = ModernItemStack.of(itemStack) - - @Bind - fun pieceLabel() = fruitColor.formattingCode + fruitName - - @Bind - fun boardLabel() = "§a$totalPerBoard§7/§rboard" - - @Bind("description") - fun getDescription() = buildString { - append(specialAbility) - if (points >= 0) { - append(" Default points: $points.") - } - } - } - - object TutorialScreen { - @get:Bind("pieces") - val pieces = ObservableList(Piece.entries.toList().reversed()) - - @get:Bind("modes") - val modes = ObservableList(DowsingMode.entries.toList()) - } - - enum class DowsingMode( - val itemType: Item, - @get:Bind("feature") - val feature: String, - @get:Bind("description") - val description: String, - ) { - MINES(Items.IRON_SHOVEL, "Bomb detection", "Tells you how many bombs are near the block"), - ANCHOR(Items.DIAMOND_SHOVEL, "Lowest fruit", "Shows you which block nearby contains the lowest scoring fruit"), - TREASURE(Items.GOLDEN_SHOVEL, "Highest fruit", "Tells you which kind of fruit is the highest scoring nearby"), - ; - - @Bind("itemType") - fun getItemStack() = ModernItemStack.of(ItemStack(itemType)) - - companion object { - val id = SkyblockId("CARNIVAL_SHOVEL") - fun fromItem(itemStack: ItemStack): DowsingMode? { - if (itemStack.skyBlockId != id) return null - return DowsingMode.entries.find { it.itemType == itemStack.item } - } - } - } - - data class BoardPosition( - val x: Int, - val y: Int - ) { - fun toBlockPos() = BlockPos(sandBoxLow.x + x, sandBoxLow.y, sandBoxLow.z + y) - - fun getBlock(world: WorldAccess) = world.getBlockState(toBlockPos()).block - fun isUnopened(world: WorldAccess) = getBlock(world) == Blocks.SAND - fun isOpened(world: WorldAccess) = getBlock(world) == Blocks.SANDSTONE - fun isScorched(world: WorldAccess) = getBlock(world) == Blocks.SANDSTONE_STAIRS - - companion object { - fun fromBlockPos(blockPos: BlockPos): BoardPosition? { - if (blockPos.y != sandBoxLow.y) return null - val x = blockPos.x - sandBoxLow.x - val y = blockPos.z - sandBoxLow.z - if (x < 0 || x >= boardSize.first) return null - if (y < 0 || y >= boardSize.second) return null - return BoardPosition(x, y) - } - } - } - - data class GameState( - val nearbyBombs: MutableMap<BoardPosition, Int> = mutableMapOf(), - val knownBombPositions: MutableSet<BoardPosition> = mutableSetOf(), - var lastClickedPosition: BoardPosition? = null, - var lastDowsingMode: DowsingMode? = null, - ) - - var gameState: GameState? = null - val log = DebugLogger("minesweeper") - - @Subscribe - fun onCommand(event: CommandEvent.SubCommand) { - event.subcommand("minesweepertutorial") { - thenExecute { - ScreenUtil.setScreenLater(MoulConfigUtils.loadScreen("carnival/minesweeper_tutorial", - TutorialScreen, - null)) - } - } - } - - @Subscribe - fun onWorldChange(event: WorldReadyEvent) { - gameState = null - } - - @Subscribe - fun onChat(event: ProcessChatEvent) { - if (CarnivalFeatures.TConfig.displayTutorials && event.unformattedString == startGameQuestion) { - MC.sendChat(Text.translatable("firmament.carnival.tutorial.minesweeper").styled { - it.withClickEvent(ClickEvent(ClickEvent.Action.RUN_COMMAND, "/firm minesweepertutorial")) - }) - } - if (!CarnivalFeatures.TConfig.enableBombSolver) { - gameState = null // TODO: replace this which a watchable property - return - } - if (event.unformattedString == gameStartMessage) { - gameState = GameState() - log.log { "Game started" } - } - if (event.unformattedString.trim() == gameEndMessage) { - gameState = null // TODO: add a loot tracker maybe? probably not, i dont think people care - log.log { "Finished game" } - } - val gs = gameState ?: return - bombPattern.useMatch(event.unformattedString) { - val bombCount = group("bombCount").toInt() - log.log { "Marking ${gs.lastClickedPosition} as having $bombCount nearby" } - val pos = gs.lastClickedPosition ?: return - gs.nearbyBombs[pos] = bombCount - } - } - - @Subscribe - fun onMobChange(event: EntityUpdateEvent) { - val gs = gameState ?: return - if (event !is EntityUpdateEvent.TrackedDataUpdate) return - // TODO: listen to state - } - - @Subscribe - fun onBlockClick(event: AttackBlockEvent) { - val gs = gameState ?: return - val boardPosition = BoardPosition.fromBlockPos(event.blockPos) - log.log { "Breaking block at ${event.blockPos} ($boardPosition)" } - gs.lastClickedPosition = boardPosition - gs.lastDowsingMode = DowsingMode.fromItem(event.player.inventory.mainHandStack) - } - - @Subscribe - fun onRender(event: WorldRenderLastEvent) { - val gs = gameState ?: return - RenderInWorldContext.renderInWorld(event) { - for ((pos, bombCount) in gs.nearbyBombs) { - this.text(pos.toBlockPos().up().toCenterPos(), Text.literal("§a$bombCount \uD83D\uDCA3")) - } - } - } - - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/fixes/CompatibliltyFeatures.kt b/src/main/kotlin/moe/nea/firmament/features/fixes/CompatibliltyFeatures.kt deleted file mode 100644 index 7c43cf6..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/fixes/CompatibliltyFeatures.kt +++ /dev/null @@ -1,51 +0,0 @@ - - -package moe.nea.firmament.features.fixes - -import net.fabricmc.loader.api.FabricLoader -import net.superkat.explosiveenhancement.api.ExplosiveApi -import net.minecraft.particle.ParticleTypes -import net.minecraft.util.math.Vec3d -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.ParticleSpawnEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.MC - -object CompatibliltyFeatures : FirmamentFeature { - override val identifier: String - get() = "compatibility" - - object TConfig : ManagedConfig(identifier) { - val enhancedExplosions by toggle("explosion-enabled") { false } - val explosionSize by integer("explosion-power", 10, 50) { 1 } - } - - override val config: ManagedConfig? - get() = TConfig - - interface ExplosiveApiWrapper { - fun spawnParticle(vec3d: Vec3d, power: Float) - } - - class ExplosiveApiWrapperImpl : ExplosiveApiWrapper { - override fun spawnParticle(vec3d: Vec3d, power: Float) { - ExplosiveApi.spawnParticles(MC.world, vec3d.x, vec3d.y, vec3d.z, TConfig.explosionSize / 10F) - } - } - - val explosiveApiWrapper = if (FabricLoader.getInstance().isModLoaded("explosiveenhancement")) { - ExplosiveApiWrapperImpl() - } else null - - @Subscribe - fun onExplosion(it: ParticleSpawnEvent) { - if (TConfig.enhancedExplosions && - it.particleEffect.type == ParticleTypes.EXPLOSION_EMITTER && - explosiveApiWrapper != null - ) { - it.cancel() - explosiveApiWrapper.spawnParticle(it.position, 2F) - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/fixes/Fixes.kt b/src/main/kotlin/moe/nea/firmament/features/fixes/Fixes.kt deleted file mode 100644 index d7b7a1c..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/fixes/Fixes.kt +++ /dev/null @@ -1,71 +0,0 @@ - - -package moe.nea.firmament.features.fixes - -import moe.nea.jarvis.api.Point -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable -import net.minecraft.client.MinecraftClient -import net.minecraft.client.option.KeyBinding -import net.minecraft.entity.player.PlayerEntity -import net.minecraft.text.Text -import net.minecraft.util.Arm -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.HudRenderEvent -import moe.nea.firmament.events.WorldKeyboardEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.errorBoundary - -object Fixes : FirmamentFeature { - override val identifier: String - get() = "fixes" - - object TConfig : ManagedConfig(identifier) { - val fixUnsignedPlayerSkins by toggle("player-skins") { true } - var autoSprint by toggle("auto-sprint") { false } - val autoSprintKeyBinding by keyBindingWithDefaultUnbound("auto-sprint-keybinding") - val autoSprintHud by position("auto-sprint-hud", 80, 10) { Point(0.0, 1.0) } - val peekChat by keyBindingWithDefaultUnbound("peek-chat") - } - - override val config: ManagedConfig - get() = TConfig - - fun handleIsPressed( - keyBinding: KeyBinding, - cir: CallbackInfoReturnable<Boolean> - ) { - if (keyBinding === MinecraftClient.getInstance().options.sprintKey && TConfig.autoSprint && MC.player?.isSprinting != true) - cir.returnValue = true - } - - @Subscribe - fun onRenderHud(it: HudRenderEvent) { - if (!TConfig.autoSprintKeyBinding.isBound) return - it.context.matrices.push() - TConfig.autoSprintHud.applyTransformations(it.context.matrices) - it.context.drawText( - MC.font, Text.translatable( - if (TConfig.autoSprint) - "firmament.fixes.auto-sprint.on" - else if (MC.player?.isSprinting == true) - "firmament.fixes.auto-sprint.sprinting" - else - "firmament.fixes.auto-sprint.not-sprinting" - ), 0, 0, -1, false - ) - it.context.matrices.pop() - } - - @Subscribe - fun onWorldKeyboard(it: WorldKeyboardEvent) { - if (it.matches(TConfig.autoSprintKeyBinding)) { - TConfig.autoSprint = !TConfig.autoSprint - } - } - - fun shouldPeekChat(): Boolean { - return TConfig.peekChat.isPressed(atLeast = true) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/CraftingOverlay.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/CraftingOverlay.kt deleted file mode 100644 index 031ef78..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/CraftingOverlay.kt +++ /dev/null @@ -1,66 +0,0 @@ - - -package moe.nea.firmament.features.inventory - -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen -import net.minecraft.item.ItemStack -import net.minecraft.util.Formatting -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.SlotRenderEvents -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry -import moe.nea.firmament.rei.SBItemEntryDefinition -import moe.nea.firmament.rei.recipes.SBCraftingRecipe -import moe.nea.firmament.util.MC - -object CraftingOverlay : FirmamentFeature { - - private var screen: GenericContainerScreen? = null - private var recipe: SBCraftingRecipe? = null - private val craftingOverlayIndices = listOf( - 10, 11, 12, - 19, 20, 21, - 28, 29, 30, - ) - - - fun setOverlay(screen: GenericContainerScreen, recipe: SBCraftingRecipe) { - this.screen = screen - this.recipe = recipe - } - - override val identifier: String - get() = "crafting-overlay" - - @Subscribe - fun onSlotRender(event: SlotRenderEvents.After) { - val slot = event.slot - val recipe = this.recipe ?: return - if (slot.inventory != screen?.screenHandler?.inventory) return - val recipeIndex = craftingOverlayIndices.indexOf(slot.index) - if (recipeIndex < 0) return - val expectedItem = recipe.neuRecipe.inputs[recipeIndex] - val actualStack = slot.stack ?: ItemStack.EMPTY!! - val actualEntry = SBItemEntryDefinition.getEntry(actualStack).value - if ((actualEntry.skyblockId.neuItem != expectedItem.itemId || actualEntry.getStackSize() < expectedItem.amount) && expectedItem.amount.toInt() != 0) { - event.context.fill( - event.slot.x, - event.slot.y, - event.slot.x + 16, - event.slot.y + 16, - 0x80FF0000.toInt() - ) - } - if (!slot.hasStack()) { - val itemStack = SBItemEntryDefinition.getEntry(expectedItem).asItemEntry().value - event.context.drawItem(itemStack, event.slot.x, event.slot.y) - event.context.drawItemInSlot( - MC.font, - itemStack, - event.slot.x, - event.slot.y, - "${Formatting.RED}${expectedItem.amount.toInt()}" - ) - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/ItemRarityCosmetics.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/ItemRarityCosmetics.kt deleted file mode 100644 index 566a813..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/ItemRarityCosmetics.kt +++ /dev/null @@ -1,85 +0,0 @@ - - -package moe.nea.firmament.features.inventory - -import java.awt.Color -import net.minecraft.client.gui.DrawContext -import net.minecraft.item.ItemStack -import net.minecraft.util.Formatting -import net.minecraft.util.Identifier -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.HotbarItemRenderEvent -import moe.nea.firmament.events.SlotRenderEvents -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.item.loreAccordingToNbt -import moe.nea.firmament.util.lastNotNullOfOrNull -import moe.nea.firmament.util.memoize -import moe.nea.firmament.util.memoizeIdentity -import moe.nea.firmament.util.unformattedString - -object ItemRarityCosmetics : FirmamentFeature { - override val identifier: String - get() = "item-rarity-cosmetics" - - object TConfig : ManagedConfig(identifier) { - val showItemRarityBackground by toggle("background") { false } - val showItemRarityInHotbar by toggle("background-hotbar") { false } - } - - override val config: ManagedConfig - get() = TConfig - - private val rarityToColor = mapOf( - "UNCOMMON" to Formatting.GREEN, - "COMMON" to Formatting.WHITE, - "RARE" to Formatting.DARK_BLUE, - "EPIC" to Formatting.DARK_PURPLE, - "LEGENDARY" to Formatting.GOLD, - "LEGENJERRY" to Formatting.GOLD, - "MYTHIC" to Formatting.LIGHT_PURPLE, - "DIVINE" to Formatting.BLUE, - "SPECIAL" to Formatting.DARK_RED, - "SUPREME" to Formatting.DARK_RED, - ).mapValues { - val c = Color(it.value.colorValue!!) - Triple(c.red / 255F, c.green / 255F, c.blue / 255F) - } - - private fun getSkyblockRarity0(itemStack: ItemStack): Triple<Float, Float, Float>? { - return itemStack.loreAccordingToNbt.lastNotNullOfOrNull { - val entry = it.unformattedString - rarityToColor.entries.find { (k, v) -> k in entry }?.value - } - } - - val getSkyblockRarity = ::getSkyblockRarity0.memoizeIdentity(100) - - - fun drawItemStackRarity(drawContext: DrawContext, x: Int, y: Int, item: ItemStack) { - val (r, g, b) = getSkyblockRarity(item) ?: return - drawContext.drawSprite( - x, y, - 0, - 16, 16, - MC.guiAtlasManager.getSprite(Identifier.of("firmament:item_rarity_background")), - r, g, b, 1F - ) - } - - - @Subscribe - fun onRenderSlot(it: SlotRenderEvents.Before) { - if (!TConfig.showItemRarityBackground) return - val stack = it.slot.stack ?: return - drawItemStackRarity(it.context, it.slot.x, it.slot.y, stack) - } - - @Subscribe - fun onRenderHotbarItem(it: HotbarItemRenderEvent) { - if (!TConfig.showItemRarityInHotbar) return - val stack = it.item - drawItemStackRarity(it.context, it.x, it.y, stack) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/PriceData.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/PriceData.kt deleted file mode 100644 index c61f8e8..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/PriceData.kt +++ /dev/null @@ -1,51 +0,0 @@ - - -package moe.nea.firmament.features.inventory - -import net.minecraft.text.Text -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.ItemTooltipEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.repo.HypixelStaticData -import moe.nea.firmament.util.FirmFormatters -import moe.nea.firmament.util.skyBlockId - -object PriceData : FirmamentFeature { - override val identifier: String - get() = "price-data" - - object TConfig : ManagedConfig(identifier) { - val tooltipEnabled by toggle("enable-always") { true } - val enableKeybinding by keyBindingWithDefaultUnbound("enable-keybind") - } - - override val config get() = TConfig - - @Subscribe - fun onItemTooltip(it: ItemTooltipEvent) { - if (!TConfig.tooltipEnabled && !TConfig.enableKeybinding.isPressed()) { - return - } - val sbId = it.stack.skyBlockId - val bazaarData = HypixelStaticData.bazaarData[sbId] - val lowestBin = HypixelStaticData.lowestBin[sbId] - if (bazaarData != null) { - it.lines.add(Text.literal("")) - it.lines.add( - Text.stringifiedTranslatable("firmament.tooltip.bazaar.sell-order", - FirmFormatters.formatCommas(bazaarData.quickStatus.sellPrice, 1)) - ) - it.lines.add( - Text.stringifiedTranslatable("firmament.tooltip.bazaar.buy-order", - FirmFormatters.formatCommas(bazaarData.quickStatus.buyPrice, 1)) - ) - } else if (lowestBin != null) { - it.lines.add(Text.literal("")) - it.lines.add( - Text.stringifiedTranslatable("firmament.tooltip.ah.lowestbin", - FirmFormatters.formatCommas(lowestBin, 1)) - ) - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/SaveCursorPosition.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/SaveCursorPosition.kt deleted file mode 100644 index 1c55753..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/SaveCursorPosition.kt +++ /dev/null @@ -1,66 +0,0 @@ - - -package moe.nea.firmament.features.inventory - -import kotlin.math.absoluteValue -import kotlin.time.Duration.Companion.milliseconds -import net.minecraft.client.util.InputUtil -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.TimeMark -import moe.nea.firmament.util.assertNotNullOr - -object SaveCursorPosition : FirmamentFeature { - override val identifier: String - get() = "save-cursor-position" - - object TConfig : ManagedConfig(identifier) { - val enable by toggle("enable") { true } - val tolerance by duration("tolerance", 10.milliseconds, 5000.milliseconds) { 500.milliseconds } - } - - override val config: TConfig - get() = TConfig - - var savedPositionedP1: Pair<Double, Double>? = null - var savedPosition: SavedPosition? = null - - data class SavedPosition( - val middle: Pair<Double, Double>, - val cursor: Pair<Double, Double>, - val savedAt: TimeMark = TimeMark.now() - ) - - @JvmStatic - fun saveCursorOriginal(positionedX: Double, positionedY: Double) { - savedPositionedP1 = Pair(positionedX, positionedY) - } - - @JvmStatic - fun loadCursor(middleX: Double, middleY: Double): Pair<Double, Double>? { - if (!TConfig.enable) return null - val lastPosition = savedPosition?.takeIf { it.savedAt.passedTime() < TConfig.tolerance } - savedPosition = null - if (lastPosition != null && - (lastPosition.middle.first - middleX).absoluteValue < 1 && - (lastPosition.middle.second - middleY).absoluteValue < 1 - ) { - InputUtil.setCursorParameters( - MC.window.handle, - InputUtil.GLFW_CURSOR_NORMAL, - lastPosition.cursor.first, - lastPosition.cursor.second - ) - return lastPosition.cursor - } - return null - } - - @JvmStatic - fun saveCursorMiddle(middleX: Double, middleY: Double) { - if (!TConfig.enable) return - val cursorPos = assertNotNullOr(savedPositionedP1) { return } - savedPosition = SavedPosition(Pair(middleX, middleY), cursorPos) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt deleted file mode 100644 index a50d8fb..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt +++ /dev/null @@ -1,203 +0,0 @@ - - -@file:UseSerializers(DashlessUUIDSerializer::class) - -package moe.nea.firmament.features.inventory - -import com.mojang.blaze3d.systems.RenderSystem -import java.util.UUID -import org.lwjgl.glfw.GLFW -import kotlinx.serialization.Serializable -import kotlinx.serialization.UseSerializers -import kotlinx.serialization.serializer -import net.minecraft.client.gui.screen.ingame.HandledScreen -import net.minecraft.entity.player.PlayerInventory -import net.minecraft.screen.GenericContainerScreenHandler -import net.minecraft.screen.slot.SlotActionType -import net.minecraft.util.Identifier -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.HandledScreenKeyPressedEvent -import moe.nea.firmament.events.IsSlotProtectedEvent -import moe.nea.firmament.events.SlotRenderEvents -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.keybindings.SavedKeyBinding -import moe.nea.firmament.mixins.accessor.AccessorHandledScreen -import moe.nea.firmament.util.CommonSoundEffects -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.SBData -import moe.nea.firmament.util.SkyBlockIsland -import moe.nea.firmament.util.data.ProfileSpecificDataHolder -import moe.nea.firmament.util.item.displayNameAccordingToNbt -import moe.nea.firmament.util.item.loreAccordingToNbt -import moe.nea.firmament.util.json.DashlessUUIDSerializer -import moe.nea.firmament.util.skyblockUUID -import moe.nea.firmament.util.unformattedString - -object SlotLocking : FirmamentFeature { - override val identifier: String - get() = "slot-locking" - - @Serializable - data class Data( - val lockedSlots: MutableSet<Int> = mutableSetOf(), - val lockedSlotsRift: MutableSet<Int> = mutableSetOf(), - - val lockedUUIDs: MutableSet<UUID> = mutableSetOf(), - ) - - object TConfig : ManagedConfig(identifier) { - val lockSlot by keyBinding("lock") { GLFW.GLFW_KEY_L } - val lockUUID by keyBindingWithOutDefaultModifiers("lock-uuid") { - SavedKeyBinding(GLFW.GLFW_KEY_L, shift = true) - } - } - - override val config: TConfig - get() = TConfig - - object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "locked-slots", ::Data) - - val lockedUUIDs get() = DConfig.data?.lockedUUIDs - - val lockedSlots - get() = when (SBData.skyblockLocation) { - SkyBlockIsland.RIFT -> DConfig.data?.lockedSlotsRift - null -> null - else -> DConfig.data?.lockedSlots - } - - fun isSalvageScreen(screen: HandledScreen<*>?): Boolean { - if (screen == null) return false - return screen.title.unformattedString.contains("Salvage Item") - } - - fun isTradeScreen(screen: HandledScreen<*>?): Boolean { - if (screen == null) return false - val handler = screen.screenHandler as? GenericContainerScreenHandler ?: return false - if (handler.inventory.size() < 9) return false - val middlePane = handler.inventory.getStack(handler.inventory.size() - 5) - if (middlePane == null) return false - return middlePane.displayNameAccordingToNbt?.unformattedString == "⇦ Your stuff" - } - - fun isNpcShop(screen: HandledScreen<*>?): Boolean { - if (screen == null) return false - val handler = screen.screenHandler as? GenericContainerScreenHandler ?: return false - if (handler.inventory.size() < 9) return false - val sellItem = handler.inventory.getStack(handler.inventory.size() - 5) - if (sellItem == null) return false - if (sellItem.displayNameAccordingToNbt?.unformattedString == "Sell Item") return true - val lore = sellItem.loreAccordingToNbt - return (lore.lastOrNull() ?: return false).unformattedString == "Click to buyback!" - } - - @Subscribe - fun onSalvageProtect(event: IsSlotProtectedEvent) { - if (event.slot == null) return - if (!event.slot.hasStack()) return - if (event.slot.stack.displayNameAccordingToNbt?.unformattedString != "Salvage Items") return - val inv = event.slot.inventory - var anyBlocked = false - for (i in 0 until event.slot.index) { - val stack = inv.getStack(i) - if (IsSlotProtectedEvent.shouldBlockInteraction(null, SlotActionType.THROW, stack)) - anyBlocked = true - } - if (anyBlocked) { - event.protectSilent() - } - } - - @Subscribe - fun onProtectUuidItems(event: IsSlotProtectedEvent) { - val doesNotDeleteItem = event.actionType == SlotActionType.SWAP - || event.actionType == SlotActionType.PICKUP - || event.actionType == SlotActionType.QUICK_MOVE - || event.actionType == SlotActionType.QUICK_CRAFT - || event.actionType == SlotActionType.CLONE - || event.actionType == SlotActionType.PICKUP_ALL - val isSellOrTradeScreen = - isNpcShop(MC.handledScreen) || isTradeScreen(MC.handledScreen) || isSalvageScreen(MC.handledScreen) - if ((!isSellOrTradeScreen || event.slot?.inventory !is PlayerInventory) - && doesNotDeleteItem - ) return - val stack = event.itemStack ?: return - val uuid = stack.skyblockUUID ?: return - if (uuid in (lockedUUIDs ?: return)) { - event.protect() - } - } - - @Subscribe - fun onProtectSlot(it: IsSlotProtectedEvent) { - if (it.slot != null && it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) { - it.protect() - } - } - - @Subscribe - fun onLockUUID(it: HandledScreenKeyPressedEvent) { - if (!it.matches(TConfig.lockUUID)) return - val inventory = MC.handledScreen ?: return - inventory as AccessorHandledScreen - - val slot = inventory.focusedSlot_Firmament ?: return - val stack = slot.stack ?: return - val uuid = stack.skyblockUUID ?: return - val lockedUUIDs = lockedUUIDs ?: return - if (uuid in lockedUUIDs) { - lockedUUIDs.remove(uuid) - } else { - lockedUUIDs.add(uuid) - } - DConfig.markDirty() - CommonSoundEffects.playSuccess() - it.cancel() - } - - @Subscribe - fun onLockSlot(it: HandledScreenKeyPressedEvent) { - if (!it.matches(TConfig.lockSlot)) return - val inventory = MC.handledScreen ?: return - inventory as AccessorHandledScreen - - val slot = inventory.focusedSlot_Firmament ?: return - val lockedSlots = lockedSlots ?: return - if (slot.inventory is PlayerInventory) { - if (slot.index in lockedSlots) { - lockedSlots.remove(slot.index) - } else { - lockedSlots.add(slot.index) - } - DConfig.markDirty() - CommonSoundEffects.playSuccess() - } - } - - @Subscribe - fun onRenderSlotOverlay(it: SlotRenderEvents.After) { - val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf()) - val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf()) - if (isSlotLocked || isUUIDLocked) { - RenderSystem.disableDepthTest() - it.context.drawSprite( - it.slot.x, it.slot.y, 0, - 16, 16, - MC.guiAtlasManager.getSprite( - when { - isSlotLocked -> - (Identifier.of("firmament:slot_locked")) - - isUUIDLocked -> - (Identifier.of("firmament:uuid_locked")) - - else -> - error("unreachable") - } - ) - ) - RenderSystem.enableDepthTest() - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButton.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButton.kt deleted file mode 100644 index 539edf2..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButton.kt +++ /dev/null @@ -1,85 +0,0 @@ - - -package moe.nea.firmament.features.inventory.buttons - -import com.mojang.brigadier.StringReader -import me.shedaniel.math.Dimension -import me.shedaniel.math.Point -import me.shedaniel.math.Rectangle -import kotlinx.serialization.Serializable -import net.minecraft.client.gui.DrawContext -import net.minecraft.command.CommandRegistryAccess -import net.minecraft.command.argument.ItemStackArgumentType -import net.minecraft.item.ItemStack -import net.minecraft.resource.featuretoggle.FeatureFlags -import net.minecraft.util.Identifier -import moe.nea.firmament.repo.ItemCache.asItemStack -import moe.nea.firmament.repo.RepoManager -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.SkyblockId -import moe.nea.firmament.util.memoize - -@Serializable -data class InventoryButton( - var x: Int, - var y: Int, - var anchorRight: Boolean, - var anchorBottom: Boolean, - var icon: String? = "", - var command: String? = "", -) { - companion object { - val itemStackParser by lazy { - ItemStackArgumentType.itemStack(CommandRegistryAccess.of(MC.defaultRegistries, - FeatureFlags.VANILLA_FEATURES)) - } - val dimensions = Dimension(18, 18) - val getItemForName = ::getItemForName0.memoize(1024) - fun getItemForName0(icon: String): ItemStack { - val repoItem = RepoManager.getNEUItem(SkyblockId(icon)) - var itemStack = repoItem.asItemStack(idHint = SkyblockId(icon)) - if (repoItem == null) { - val giveSyntaxItem = if (icon.startsWith("/give") || icon.startsWith("give")) - icon.split(" ", limit = 3).getOrNull(2) ?: icon - else icon - val componentItem = - runCatching { - itemStackParser.parse(StringReader(giveSyntaxItem)).createStack(1, false) - }.getOrNull() - if (componentItem != null) - itemStack = componentItem - } - return itemStack - } - } - - fun render(context: DrawContext) { - context.drawSprite( - 0, - 0, - 0, - dimensions.width, - dimensions.height, - MC.guiAtlasManager.getSprite(Identifier.of("firmament:inventory_button_background")) - ) - context.drawItem(getItem(), 1, 1) - } - - fun isValid() = !icon.isNullOrBlank() && !command.isNullOrBlank() - - fun getPosition(guiRect: Rectangle): Point { - return Point( - (if (anchorRight) guiRect.maxX else guiRect.minX) + x, - (if (anchorBottom) guiRect.maxY else guiRect.minY) + y, - ) - } - - fun getBounds(guiRect: Rectangle): Rectangle { - return Rectangle(getPosition(guiRect), dimensions) - } - - fun getItem(): ItemStack { - return getItemForName(icon ?: "") - } - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonEditor.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonEditor.kt deleted file mode 100644 index c57563e..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonEditor.kt +++ /dev/null @@ -1,184 +0,0 @@ - - -package moe.nea.firmament.features.inventory.buttons - -import io.github.notenoughupdates.moulconfig.common.IItemStack -import io.github.notenoughupdates.moulconfig.platform.ModernItemStack -import io.github.notenoughupdates.moulconfig.xml.Bind -import me.shedaniel.math.Point -import me.shedaniel.math.Rectangle -import org.lwjgl.glfw.GLFW -import net.minecraft.client.gui.DrawContext -import net.minecraft.client.gui.widget.ButtonWidget -import net.minecraft.client.util.InputUtil -import net.minecraft.text.Text -import net.minecraft.util.math.MathHelper -import net.minecraft.util.math.Vec2f -import moe.nea.firmament.util.ClipboardUtils -import moe.nea.firmament.util.FragmentGuiScreen -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.MoulConfigUtils - -class InventoryButtonEditor( - val lastGuiRect: Rectangle, -) : FragmentGuiScreen() { - inner class Editor(val originalButton: InventoryButton) { - @field:Bind - var command: String = originalButton.command ?: "" - - @field:Bind - var icon: String = originalButton.icon ?: "" - - @Bind - fun getItemIcon(): IItemStack { - save() - return ModernItemStack.of(InventoryButton.getItemForName(icon)) - } - - @Bind - fun delete() { - buttons.removeIf { it === originalButton } - popup = null - } - - fun save() { - originalButton.icon = icon - originalButton.command = command - } - } - - var buttons: MutableList<InventoryButton> = - InventoryButtons.DConfig.data.buttons.map { it.copy() }.toMutableList() - - override fun close() { - InventoryButtons.DConfig.data.buttons = buttons - InventoryButtons.DConfig.markDirty() - super.close() - } - - override fun init() { - super.init() - addDrawableChild( - ButtonWidget.builder(Text.translatable("firmament.inventory-buttons.load-preset")) { - val t = ClipboardUtils.getTextContents() - val newButtons = InventoryButtonTemplates.loadTemplate(t) - if (newButtons != null) - buttons = newButtons.toMutableList() - } - .position(lastGuiRect.minX + 10, lastGuiRect.minY + 35) - .width(lastGuiRect.width - 20) - .build() - ) - addDrawableChild( - ButtonWidget.builder(Text.translatable("firmament.inventory-buttons.save-preset")) { - ClipboardUtils.setTextContent(InventoryButtonTemplates.saveTemplate(buttons)) - } - .position(lastGuiRect.minX + 10, lastGuiRect.minY + 60) - .width(lastGuiRect.width - 20) - .build() - ) - } - - override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { - super.render(context, mouseX, mouseY, delta) - context.matrices.push() - context.matrices.translate(0f, 0f, -10f) - context.fill(lastGuiRect.minX, lastGuiRect.minY, lastGuiRect.maxX, lastGuiRect.maxY, -1) - context.setShaderColor(1f, 1f, 1f, 1f) - context.matrices.pop() - for (button in buttons) { - val buttonPosition = button.getBounds(lastGuiRect) - context.matrices.push() - context.matrices.translate(buttonPosition.minX.toFloat(), buttonPosition.minY.toFloat(), 0F) - button.render(context) - context.matrices.pop() - } - } - - override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - if (super.keyPressed(keyCode, scanCode, modifiers)) return true - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { - close() - return true - } - return false - } - - override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { - if (super.mouseReleased(mouseX, mouseY, button)) return true - val clickedButton = buttons.firstOrNull { it.getBounds(lastGuiRect).contains(Point(mouseX, mouseY)) } - if (clickedButton != null && !justPerformedAClickAction) { - createPopup(MoulConfigUtils.loadGui("button_editor_fragment", Editor(clickedButton)), Point(mouseX, mouseY)) - return true - } - justPerformedAClickAction = false - lastDraggedButton = null - return false - } - - override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean { - if (super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) return true - - if (initialDragMousePosition.distanceSquared(Vec2f(mouseX.toFloat(), mouseY.toFloat())) >= 4 * 4) { - initialDragMousePosition = Vec2f(-10F, -10F) - lastDraggedButton?.let { dragging -> - justPerformedAClickAction = true - val (anchorRight, anchorBottom, offsetX, offsetY) = getCoordsForMouse(mouseX.toInt(), mouseY.toInt()) - ?: return true - dragging.x = offsetX - dragging.y = offsetY - dragging.anchorRight = anchorRight - dragging.anchorBottom = anchorBottom - } - } - return false - } - - var lastDraggedButton: InventoryButton? = null - var justPerformedAClickAction = false - var initialDragMousePosition = Vec2f(-10F, -10F) - - data class AnchoredCoords( - val anchorRight: Boolean, - val anchorBottom: Boolean, - val offsetX: Int, - val offsetY: Int, - ) - - fun getCoordsForMouse(mx: Int, my: Int): AnchoredCoords? { - if (lastGuiRect.contains(mx, my) || lastGuiRect.contains( - Point( - mx + InventoryButton.dimensions.width, - my + InventoryButton.dimensions.height, - ) - ) - ) return null - - val anchorRight = mx > lastGuiRect.maxX - val anchorBottom = my > lastGuiRect.maxY - var offsetX = mx - if (anchorRight) lastGuiRect.maxX else lastGuiRect.minX - var offsetY = my - if (anchorBottom) lastGuiRect.maxY else lastGuiRect.minY - if (InputUtil.isKeyPressed(MC.window.handle, InputUtil.GLFW_KEY_LEFT_SHIFT)) { - offsetX = MathHelper.floor(offsetX / 20F) * 20 - offsetY = MathHelper.floor(offsetY / 20F) * 20 - } - return AnchoredCoords(anchorRight, anchorBottom, offsetX, offsetY) - } - - override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { - if (super.mouseClicked(mouseX, mouseY, button)) return true - val clickedButton = buttons.firstOrNull { it.getBounds(lastGuiRect).contains(Point(mouseX, mouseY)) } - if (clickedButton != null) { - lastDraggedButton = clickedButton - initialDragMousePosition = Vec2f(mouseX.toFloat(), mouseY.toFloat()) - return true - } - val mx = mouseX.toInt() - val my = mouseY.toInt() - val (anchorRight, anchorBottom, offsetX, offsetY) = getCoordsForMouse(mx, my) ?: return true - buttons.add(InventoryButton(offsetX, offsetY, anchorRight, anchorBottom, null, null)) - justPerformedAClickAction = true - return true - } - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonTemplates.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonTemplates.kt deleted file mode 100644 index 99b544b..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonTemplates.kt +++ /dev/null @@ -1,35 +0,0 @@ - - -package moe.nea.firmament.features.inventory.buttons - -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import net.minecraft.text.Text -import moe.nea.firmament.Firmament -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.TemplateUtil - -object InventoryButtonTemplates { - - val legacyPrefix = "NEUBUTTONS/" - val modernPrefix = "MAYBEONEDAYIWILLHAVEMYOWNFORMAT" - - fun loadTemplate(t: String): List<InventoryButton>? { - val buttons = TemplateUtil.maybeDecodeTemplate<List<String>>(legacyPrefix, t) ?: return null - return buttons.mapNotNull { - try { - Firmament.json.decodeFromString<InventoryButton>(it).also { - if (it.icon?.startsWith("extra:") == true || it.command?.any { it.isLowerCase() } == true) { - MC.sendChat(Text.translatable("firmament.inventory-buttons.import-failed")) - } - } - } catch (e: Exception) { - null - } - } - } - - fun saveTemplate(buttons: List<InventoryButton>): String { - return TemplateUtil.encodeTemplate(legacyPrefix, buttons.map { Firmament.json.encodeToString(it) }) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtons.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtons.kt deleted file mode 100644 index fa90d21..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtons.kt +++ /dev/null @@ -1,88 +0,0 @@ - - -package moe.nea.firmament.features.inventory.buttons - -import me.shedaniel.math.Rectangle -import kotlinx.serialization.Serializable -import kotlinx.serialization.serializer -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.HandledScreenClickEvent -import moe.nea.firmament.events.HandledScreenForegroundEvent -import moe.nea.firmament.events.HandledScreenPushREIEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.ScreenUtil -import moe.nea.firmament.util.data.DataHolder -import moe.nea.firmament.util.getRectangle - -object InventoryButtons : FirmamentFeature { - override val identifier: String - get() = "inventory-buttons" - - object TConfig : ManagedConfig(identifier) { - val _openEditor by button("open-editor") { - openEditor() - } - } - - object DConfig : DataHolder<Data>(serializer(), identifier, ::Data) - - @Serializable - data class Data( - var buttons: MutableList<InventoryButton> = mutableListOf() - ) - - - override val config: ManagedConfig - get() = TConfig - - fun getValidButtons() = DConfig.data.buttons.asSequence().filter { it.isValid() } - - @Subscribe - fun onRectangles(it: HandledScreenPushREIEvent) { - val bounds = it.screen.getRectangle() - for (button in getValidButtons()) { - val buttonBounds = button.getBounds(bounds) - it.block(buttonBounds) - } - } - - @Subscribe - fun onClickScreen(it: HandledScreenClickEvent) { - val bounds = it.screen.getRectangle() - for (button in getValidButtons()) { - val buttonBounds = button.getBounds(bounds) - if (buttonBounds.contains(it.mouseX, it.mouseY)) { - MC.sendCommand(button.command!! /* non null invariant covered by getValidButtons */) - break - } - } - } - - @Subscribe - fun onRenderForeground(it: HandledScreenForegroundEvent) { - val bounds = it.screen.getRectangle() - for (button in getValidButtons()) { - val buttonBounds = button.getBounds(bounds) - it.context.matrices.push() - it.context.matrices.translate(buttonBounds.minX.toFloat(), buttonBounds.minY.toFloat(), 0F) - button.render(it.context) - it.context.matrices.pop() - } - lastRectangle = bounds - } - - var lastRectangle: Rectangle? = null - fun openEditor() { - ScreenUtil.setScreenLater( - InventoryButtonEditor( - lastRectangle ?: Rectangle( - MC.window.scaledWidth / 2 - 100, - MC.window.scaledHeight / 2 - 100, - 200, 200, - ) - ) - ) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageBackingHandle.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageBackingHandle.kt deleted file mode 100644 index 1015578..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageBackingHandle.kt +++ /dev/null @@ -1,53 +0,0 @@ - - -package moe.nea.firmament.features.inventory.storageoverlay - -import net.minecraft.client.gui.screen.Screen -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen -import net.minecraft.screen.GenericContainerScreenHandler -import moe.nea.firmament.util.ifMatches -import moe.nea.firmament.util.unformattedString - -/** - * A handle representing the state of the "server side" screens. - */ -sealed interface StorageBackingHandle { - - sealed interface HasBackingScreen { - val handler: GenericContainerScreenHandler - } - - /** - * The main storage overview is open. Clicking on a slot will open that page. This page is accessible via `/storage` - */ - data class Overview(override val handler: GenericContainerScreenHandler) : StorageBackingHandle, HasBackingScreen - - /** - * An individual storage page is open. This may be a backpack or an enderchest page. This page is accessible via - * the [Overview] or via `/ec <index + 1>` for enderchest pages. - */ - data class Page(override val handler: GenericContainerScreenHandler, val storagePageSlot: StoragePageSlot) : - StorageBackingHandle, HasBackingScreen - - companion object { - private val enderChestName = "^Ender Chest \\(([1-9])/[1-9]\\)$".toRegex() - private val backPackName = "^.+Backpack \\(Slot #([0-9]+)\\)$".toRegex() - - /** - * Parse a screen into a [StorageBackingHandle]. If this returns null it means that the screen is not - * representable as a [StorageBackingHandle], meaning another screen is open, for example the enderchest icon - * selection screen. - */ - fun fromScreen(screen: Screen?): StorageBackingHandle? { - if (screen == null) return null - if (screen !is GenericContainerScreen) return null - val title = screen.title.unformattedString - if (title == "Storage") return Overview(screen.screenHandler) - return title.ifMatches(enderChestName) { - Page(screen.screenHandler, StoragePageSlot.ofEnderChestPage(it.groupValues[1].toInt())) - } ?: title.ifMatches(backPackName) { - Page(screen.screenHandler, StoragePageSlot.ofBackPackPage(it.groupValues[1].toInt())) - } - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageData.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageData.kt deleted file mode 100644 index 7555c56..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageData.kt +++ /dev/null @@ -1,21 +0,0 @@ - - -@file:UseSerializers(SortedMapSerializer::class) -package moe.nea.firmament.features.inventory.storageoverlay - -import java.util.SortedMap -import kotlinx.serialization.Serializable -import kotlinx.serialization.UseSerializers -import moe.nea.firmament.util.SortedMapSerializer - -@Serializable -data class StorageData( - val storageInventories: SortedMap<StoragePageSlot, StorageInventory> = sortedMapOf() -) { - @Serializable - data class StorageInventory( - var title: String, - val slot: StoragePageSlot, - var inventory: VirtualInventory?, - ) -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlay.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlay.kt deleted file mode 100644 index b615c73..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlay.kt +++ /dev/null @@ -1,154 +0,0 @@ - - -package moe.nea.firmament.features.inventory.storageoverlay - -import java.util.SortedMap -import kotlinx.serialization.serializer -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen -import net.minecraft.client.gui.screen.ingame.HandledScreen -import net.minecraft.entity.player.PlayerInventory -import net.minecraft.item.Items -import net.minecraft.network.packet.c2s.play.CloseHandledScreenC2SPacket -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.ScreenChangeEvent -import moe.nea.firmament.events.SlotClickEvent -import moe.nea.firmament.events.TickEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.customgui.customGui -import moe.nea.firmament.util.data.ProfileSpecificDataHolder - -object StorageOverlay : FirmamentFeature { - - - object Data : ProfileSpecificDataHolder<StorageData>(serializer(), "storage-data", ::StorageData) - - override val identifier: String - get() = "storage-overlay" - - object TConfig : ManagedConfig(identifier) { - val alwaysReplace by toggle("always-replace") { true } - val columns by integer("rows", 1, 10) { 3 } - val scrollSpeed by integer("scroll-speed", 1, 50) { 10 } - val inverseScroll by toggle("inverse-scroll") { false } - val padding by integer("padding", 1, 20) { 5 } - val margin by integer("margin", 1, 60) { 20 } - } - - fun adjustScrollSpeed(amount: Double): Double { - return amount * TConfig.scrollSpeed * (if (TConfig.inverseScroll) 1 else -1) - } - - override val config: TConfig - get() = TConfig - - var lastStorageOverlay: StorageOverviewScreen? = null - var skipNextStorageOverlayBackflip = false - var currentHandler: StorageBackingHandle? = null - - @Subscribe - fun onTick(event: TickEvent) { - rememberContent(currentHandler ?: return) - } - - @Subscribe - fun onClick(event: SlotClickEvent) { - if (lastStorageOverlay != null && event.slot.inventory !is PlayerInventory && event.slot.index < 9 - && event.stack.item != Items.BLACK_STAINED_GLASS_PANE - ) { - skipNextStorageOverlayBackflip = true - } - } - - @Subscribe - fun onScreenChange(it: ScreenChangeEvent) { - if (it.old == null && it.new == null) return - val storageOverlayScreen = it.old as? StorageOverlayScreen - ?: ((it.old as? HandledScreen<*>)?.customGui as? StorageOverlayCustom)?.overview - var storageOverviewScreen = it.old as? StorageOverviewScreen - val screen = it.new as? GenericContainerScreen - val oldHandler = currentHandler - currentHandler = StorageBackingHandle.fromScreen(screen) - rememberContent(currentHandler) - if (storageOverviewScreen != null && oldHandler is StorageBackingHandle.HasBackingScreen) { - val player = MC.player - assert(player != null) - player?.networkHandler?.sendPacket(CloseHandledScreenC2SPacket(oldHandler.handler.syncId)) - if (player?.currentScreenHandler === oldHandler.handler) { - player.currentScreenHandler = player.playerScreenHandler - } - } - storageOverviewScreen = storageOverviewScreen ?: lastStorageOverlay - if (it.new == null && storageOverlayScreen != null && !storageOverlayScreen.isExiting) { - it.overrideScreen = storageOverlayScreen - return - } - if (storageOverviewScreen != null - && !storageOverviewScreen.isClosing - && (currentHandler is StorageBackingHandle.Overview || currentHandler == null) - ) { - if (skipNextStorageOverlayBackflip) { - skipNextStorageOverlayBackflip = false - } else { - it.overrideScreen = storageOverviewScreen - lastStorageOverlay = null - } - return - } - screen ?: return - screen.customGui = StorageOverlayCustom( - currentHandler ?: return, - screen, - storageOverlayScreen ?: (if (TConfig.alwaysReplace) StorageOverlayScreen() else return)) - } - - fun rememberContent(handler: StorageBackingHandle?) { - handler ?: return - // TODO: Make all of these functions work on deltas / updates instead of the entire contents - val data = Data.data?.storageInventories ?: return - when (handler) { - is StorageBackingHandle.Overview -> rememberStorageOverview(handler, data) - is StorageBackingHandle.Page -> rememberPage(handler, data) - } - Data.markDirty() - } - - private fun rememberStorageOverview( - handler: StorageBackingHandle.Overview, - data: SortedMap<StoragePageSlot, StorageData.StorageInventory> - ) { - for ((index, stack) in handler.handler.stacks.withIndex()) { - // Ignore unloaded item stacks - if (stack.isEmpty) continue - val slot = StoragePageSlot.fromOverviewSlotIndex(index) ?: continue - val isEmpty = stack.item in StorageOverviewScreen.emptyStorageSlotItems - if (slot in data) { - if (isEmpty) - data.remove(slot) - continue - } - if (!isEmpty) { - data[slot] = StorageData.StorageInventory(slot.defaultName(), slot, null) - } - } - } - - private fun rememberPage( - handler: StorageBackingHandle.Page, - data: SortedMap<StoragePageSlot, StorageData.StorageInventory> - ) { - // TODO: FIXME: FIXME NOW: Definitely don't copy all of this every tick into persistence - val newStacks = - VirtualInventory(handler.handler.stacks.take(handler.handler.rows * 9).drop(9).map { it.copy() }) - data.compute(handler.storagePageSlot) { slot, existingInventory -> - (existingInventory ?: StorageData.StorageInventory( - slot.defaultName(), - slot, - null - )).also { - it.inventory = newStacks - } - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayCustom.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayCustom.kt deleted file mode 100644 index d0d9114..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayCustom.kt +++ /dev/null @@ -1,98 +0,0 @@ - -package moe.nea.firmament.features.inventory.storageoverlay - -import me.shedaniel.math.Point -import me.shedaniel.math.Rectangle -import net.minecraft.client.MinecraftClient -import net.minecraft.client.gui.DrawContext -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen -import net.minecraft.entity.player.PlayerInventory -import net.minecraft.screen.slot.Slot -import moe.nea.firmament.mixins.accessor.AccessorHandledScreen -import moe.nea.firmament.util.customgui.CustomGui - -class StorageOverlayCustom( - val handler: StorageBackingHandle, - val screen: GenericContainerScreen, - val overview: StorageOverlayScreen, -) : CustomGui() { - override fun onVoluntaryExit(): Boolean { - overview.isExiting = true - return super.onVoluntaryExit() - } - - override fun getBounds(): List<Rectangle> { - return overview.getBounds() - } - - override fun afterSlotRender(context: DrawContext, slot: Slot) { - if (slot.inventory !is PlayerInventory) - context.disableScissor() - } - - override fun beforeSlotRender(context: DrawContext, slot: Slot) { - if (slot.inventory !is PlayerInventory) - overview.createScissors(context) - } - - override fun onInit() { - overview.init(MinecraftClient.getInstance(), screen.width, screen.height) - overview.init() - screen as AccessorHandledScreen - screen.x_Firmament = overview.measurements.x - screen.y_Firmament = overview.measurements.y - screen.backgroundWidth_Firmament = overview.measurements.totalWidth - screen.backgroundHeight_Firmament = overview.measurements.totalHeight - } - - override fun isPointOverSlot(slot: Slot, xOffset: Int, yOffset: Int, pointX: Double, pointY: Double): Boolean { - if (!super.isPointOverSlot(slot, xOffset, yOffset, pointX, pointY)) - return false - if (slot.inventory !is PlayerInventory) { - if (!overview.getScrollPanelInner().contains(pointX, pointY)) - return false - } - return true - } - - override fun shouldDrawForeground(): Boolean { - return false - } - - override fun mouseClick(mouseX: Double, mouseY: Double, button: Int): Boolean { - return overview.mouseClicked(mouseX, mouseY, button, (handler as? StorageBackingHandle.Page)?.storagePageSlot) - } - - override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) { - overview.drawBackgrounds(drawContext) - overview.drawPages(drawContext, - mouseX, - mouseY, - delta, - (handler as? StorageBackingHandle.Page)?.storagePageSlot, - screen.screenHandler.slots.take(screen.screenHandler.rows * 9).drop(9), - Point((screen as AccessorHandledScreen).x_Firmament, screen.y_Firmament)) - overview.drawScrollBar(drawContext) - } - - override fun moveSlot(slot: Slot) { - val index = slot.index - if (index in 0..<36) { - val (x, y) = overview.getPlayerInventorySlotPosition(index) - slot.x = x - (screen as AccessorHandledScreen).x_Firmament - slot.y = y - screen.y_Firmament - } else { - slot.x = -100000 - slot.y = -100000 - } - } - - override fun mouseScrolled( - mouseX: Double, - mouseY: Double, - horizontalAmount: Double, - verticalAmount: Double - ): Boolean { - return overview.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayScreen.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayScreen.kt deleted file mode 100644 index 13c6974..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayScreen.kt +++ /dev/null @@ -1,296 +0,0 @@ - -package moe.nea.firmament.features.inventory.storageoverlay - -import me.shedaniel.math.Point -import me.shedaniel.math.Rectangle -import net.minecraft.client.gui.DrawContext -import net.minecraft.client.gui.screen.Screen -import net.minecraft.screen.slot.Slot -import net.minecraft.text.Text -import net.minecraft.util.Identifier -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.CommandEvent -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.ScreenUtil -import moe.nea.firmament.util.assertTrueOr - -class StorageOverlayScreen : Screen(Text.literal("")) { - - companion object { - val PLAYER_WIDTH = 184 - val PLAYER_HEIGHT = 91 - val PLAYER_Y_INSET = 3 - val SLOT_SIZE = 18 - val PADDING = 10 - val PAGE_WIDTH = SLOT_SIZE * 9 - val HOTBAR_X = 12 - val HOTBAR_Y = 67 - val MAIN_INVENTORY_Y = 9 - val SCROLL_BAR_WIDTH = 8 - val SCROLL_BAR_HEIGHT = 16 - } - - var isExiting: Boolean = false - var scroll: Float = 0F - var pageWidthCount = StorageOverlay.TConfig.columns - - inner class Measurements { - val innerScrollPanelWidth = PAGE_WIDTH * pageWidthCount + (pageWidthCount - 1) * PADDING - val overviewWidth = innerScrollPanelWidth + 3 * PADDING + SCROLL_BAR_WIDTH - val x = width / 2 - overviewWidth / 2 - val overviewHeight = minOf(3 * 18 * 6, height - PLAYER_HEIGHT - minOf(80, height / 10)) - val innerScrollPanelHeight = overviewHeight - PADDING * 2 - val y = height / 2 - (overviewHeight + PLAYER_HEIGHT) / 2 - val playerX = width / 2 - PLAYER_WIDTH / 2 - val playerY = y + overviewHeight - PLAYER_Y_INSET - val totalWidth = overviewWidth - val totalHeight = overviewHeight - PLAYER_Y_INSET + PLAYER_HEIGHT - } - - var measurements = Measurements() - - var lastRenderedInnerHeight = 0 - public override fun init() { - super.init() - pageWidthCount = StorageOverlay.TConfig.columns - .coerceAtMost((width - PADDING) / (PAGE_WIDTH + PADDING)) - .coerceAtLeast(1) - measurements = Measurements() - } - - override fun mouseScrolled( - mouseX: Double, - mouseY: Double, - horizontalAmount: Double, - verticalAmount: Double - ): Boolean { - scroll = (scroll + StorageOverlay.adjustScrollSpeed(verticalAmount)).toFloat() - .coerceAtMost(getMaxScroll()) - .coerceAtLeast(0F) - return true - } - - fun getMaxScroll() = lastRenderedInnerHeight.toFloat() - getScrollPanelInner().height - - val playerInventorySprite = Identifier.of("firmament:storageoverlay/player_inventory") - val upperBackgroundSprite = Identifier.of("firmament:storageoverlay/upper_background") - val slotRowSprite = Identifier.of("firmament:storageoverlay/storage_row") - val scrollbarBackground = Identifier.of("firmament:storageoverlay/scroll_bar_background") - val scrollbarKnob = Identifier.of("firmament:storageoverlay/scroll_bar_knob") - - override fun close() { - isExiting = true - super.close() - } - - override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { - super.render(context, mouseX, mouseY, delta) - drawBackgrounds(context) - drawPages(context, mouseX, mouseY, delta, null, null, Point()) - drawScrollBar(context) - drawPlayerInventory(context, mouseX, mouseY, delta) - } - - fun getScrollbarPercentage(): Float { - return scroll / getMaxScroll() - } - - fun drawScrollBar(context: DrawContext) { - val sbRect = getScrollBarRect() - context.drawGuiTexture( - scrollbarBackground, - sbRect.minX, sbRect.minY, - sbRect.width, sbRect.height, - ) - context.drawGuiTexture( - scrollbarKnob, - sbRect.minX, sbRect.minY + (getScrollbarPercentage() * (sbRect.height - SCROLL_BAR_HEIGHT)).toInt(), - SCROLL_BAR_WIDTH, SCROLL_BAR_HEIGHT - ) - } - - fun drawBackgrounds(context: DrawContext) { - context.drawGuiTexture(upperBackgroundSprite, - measurements.x, - measurements.y, - 0, - measurements.overviewWidth, - measurements.overviewHeight) - context.drawGuiTexture(playerInventorySprite, - measurements.playerX, - measurements.playerY, - 0, - PLAYER_WIDTH, - PLAYER_HEIGHT) - } - - fun getPlayerInventorySlotPosition(int: Int): Pair<Int, Int> { - if (int < 9) { - return Pair(measurements.playerX + int * SLOT_SIZE + HOTBAR_X, HOTBAR_Y + measurements.playerY) - } - return Pair( - measurements.playerX + (int % 9) * SLOT_SIZE + HOTBAR_X, - measurements.playerY + (int / 9 - 1) * SLOT_SIZE + MAIN_INVENTORY_Y - ) - } - - fun drawPlayerInventory(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { - val items = MC.player?.inventory?.main ?: return - items.withIndex().forEach { (index, item) -> - val (x, y) = getPlayerInventorySlotPosition(index) - context.drawItem(item, x, y, 0) - context.drawItemInSlot(textRenderer, item, x, y) - } - } - - fun getScrollBarRect(): Rectangle { - return Rectangle(measurements.x + PADDING + measurements.innerScrollPanelWidth + PADDING, - measurements.y + PADDING, - SCROLL_BAR_WIDTH, - measurements.innerScrollPanelHeight) - } - - fun getScrollPanelInner(): Rectangle { - return Rectangle(measurements.x + PADDING, - measurements.y + PADDING, - measurements.innerScrollPanelWidth, - measurements.innerScrollPanelHeight) - } - - fun createScissors(context: DrawContext) { - val rect = getScrollPanelInner() - context.enableScissor( - rect.minX, rect.minY, - rect.maxX, rect.maxY - ) - } - - fun drawPages( - context: DrawContext, mouseX: Int, mouseY: Int, delta: Float, - excluding: StoragePageSlot?, - slots: List<Slot>?, - slotOffset: Point - ) { - createScissors(context) - val data = StorageOverlay.Data.data ?: StorageData() - layoutedForEach(data) { rect, page, inventory -> - drawPage(context, - rect.x, - rect.y, - page, inventory, - if (excluding == page) slots else null, - slotOffset - ) - } - context.disableScissor() - } - - override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { - return mouseClicked(mouseX, mouseY, button, null) - } - - fun mouseClicked(mouseX: Double, mouseY: Double, button: Int, activePage: StoragePageSlot?): Boolean { - if (getScrollPanelInner().contains(mouseX, mouseY)) { - val data = StorageOverlay.Data.data ?: StorageData() - layoutedForEach(data) { rect, page, _ -> - if (rect.contains(mouseX, mouseY) && activePage != page && button == 0) { - page.navigateTo() - return true - } - } - return false - } - val sbRect = getScrollBarRect() - if (sbRect.contains(mouseX, mouseY)) { - // TODO: support dragging of the mouse and such - val percentage = (mouseY - sbRect.getY()) / sbRect.getHeight() - scroll = (getMaxScroll() * percentage).toFloat() - mouseScrolled(0.0, 0.0, 0.0, 0.0) - return true - } - return false - } - - private inline fun layoutedForEach( - data: StorageData, - func: ( - rectangle: Rectangle, - page: StoragePageSlot, inventory: StorageData.StorageInventory, - ) -> Unit - ) { - var yOffset = -scroll.toInt() - var xOffset = 0 - var maxHeight = 0 - for ((page, inventory) in data.storageInventories.entries) { - val currentHeight = inventory.inventory?.let { it.rows * SLOT_SIZE + 4 + textRenderer.fontHeight } - ?: 18 - maxHeight = maxOf(maxHeight, currentHeight) - val rect = Rectangle( - measurements.x + PADDING + (PAGE_WIDTH + PADDING) * xOffset, - yOffset + measurements.y + PADDING, - PAGE_WIDTH, - currentHeight - ) - func(rect, page, inventory) - xOffset++ - if (xOffset >= pageWidthCount) { - yOffset += maxHeight - xOffset = 0 - maxHeight = 0 - } - } - lastRenderedInnerHeight = maxHeight + yOffset + scroll.toInt() - } - - fun drawPage( - context: DrawContext, - x: Int, - y: Int, - page: StoragePageSlot, - inventory: StorageData.StorageInventory, - slots: List<Slot>?, - slotOffset: Point, - ): Int { - val inv = inventory.inventory - if (inv == null) { - context.drawGuiTexture(upperBackgroundSprite, x, y, PAGE_WIDTH, 18) - context.drawText(textRenderer, - Text.literal("TODO: open this page"), - x + 4, - y + 4, - -1, - true) - return 18 - } - assertTrueOr(slots == null || slots.size == inv.stacks.size) { return 0 } - val name = page.defaultName() - context.drawText(textRenderer, Text.literal(name), x + 4, y + 2, - if (slots == null) 0xFFFFFFFF.toInt() else 0xFFFFFF00.toInt(), true) - context.drawGuiTexture(slotRowSprite, x, y + 4 + textRenderer.fontHeight, PAGE_WIDTH, inv.rows * SLOT_SIZE) - inv.stacks.forEachIndexed { index, stack -> - val slotX = (index % 9) * SLOT_SIZE + x + 1 - val slotY = (index / 9) * SLOT_SIZE + y + 4 + textRenderer.fontHeight + 1 - if (slots == null) { - context.drawItem(stack, slotX, slotY) - context.drawItemInSlot(textRenderer, stack, slotX, slotY) - } else { - val slot = slots[index] - slot.x = slotX - slotOffset.x - slot.y = slotY - slotOffset.y - } - } - return inv.rows * SLOT_SIZE + 4 + textRenderer.fontHeight - } - - fun getBounds(): List<Rectangle> { - return listOf( - Rectangle(measurements.x, - measurements.y, - measurements.overviewWidth, - measurements.overviewHeight), - Rectangle(measurements.playerX, - measurements.playerY, - PLAYER_WIDTH, - PLAYER_HEIGHT)) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverviewScreen.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverviewScreen.kt deleted file mode 100644 index 2cbd54e..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverviewScreen.kt +++ /dev/null @@ -1,123 +0,0 @@ - - -package moe.nea.firmament.features.inventory.storageoverlay - -import org.lwjgl.glfw.GLFW -import kotlin.math.max -import net.minecraft.block.Blocks -import net.minecraft.client.gui.DrawContext -import net.minecraft.client.gui.screen.Screen -import net.minecraft.item.Item -import net.minecraft.item.Items -import net.minecraft.text.Text -import net.minecraft.util.DyeColor -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.toShedaniel - -class StorageOverviewScreen() : Screen(Text.empty()) { - companion object { - val emptyStorageSlotItems = listOf<Item>( - Blocks.RED_STAINED_GLASS_PANE.asItem(), - Blocks.BROWN_STAINED_GLASS_PANE.asItem(), - Items.GRAY_DYE - ) - val pageWidth get() = 19 * 9 - } - - val content = StorageOverlay.Data.data ?: StorageData() - var isClosing = false - - var scroll = 0 - var lastRenderedHeight = 0 - - override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { - super.render(context, mouseX, mouseY, delta) - context.fill(0, 0, width, height, 0x90000000.toInt()) - layoutedForEach { (key, value), offsetX, offsetY -> - context.matrices.push() - context.matrices.translate(offsetX.toFloat(), offsetY.toFloat(), 0F) - renderStoragePage(context, value, mouseX - offsetX, mouseY - offsetY) - context.matrices.pop() - } - } - - inline fun layoutedForEach(onEach: (data: Pair<StoragePageSlot, StorageData.StorageInventory>, offsetX: Int, offsetY: Int) -> Unit) { - var offsetY = 0 - var currentMaxHeight = StorageOverlay.config.margin - StorageOverlay.config.padding - scroll - var totalHeight = -currentMaxHeight - content.storageInventories.onEachIndexed { index, (key, value) -> - val pageX = (index % StorageOverlay.config.columns) - if (pageX == 0) { - currentMaxHeight += StorageOverlay.config.padding - offsetY += currentMaxHeight - totalHeight += currentMaxHeight - currentMaxHeight = 0 - } - val xPosition = - width / 2 - (StorageOverlay.config.columns * (pageWidth + StorageOverlay.config.padding) - StorageOverlay.config.padding) / 2 + pageX * (pageWidth + StorageOverlay.config.padding) - onEach(Pair(key, value), xPosition, offsetY) - val height = getStorePageHeight(value) - currentMaxHeight = max(currentMaxHeight, height) - } - lastRenderedHeight = totalHeight + currentMaxHeight - } - - override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { - layoutedForEach { (k, p), x, y -> - val rx = mouseX - x - val ry = mouseY - y - if (rx in (0.0..pageWidth.toDouble()) && ry in (0.0..getStorePageHeight(p).toDouble())) { - close() - StorageOverlay.lastStorageOverlay = this - k.navigateTo() - return true - } - } - return super.mouseClicked(mouseX, mouseY, button) - } - - fun getStorePageHeight(page: StorageData.StorageInventory): Int { - return page.inventory?.rows?.let { it * 19 + MC.font.fontHeight + 2 } ?: 60 - } - - override fun mouseScrolled( - mouseX: Double, - mouseY: Double, - horizontalAmount: Double, - verticalAmount: Double - ): Boolean { - scroll = - (scroll + StorageOverlay.adjustScrollSpeed(verticalAmount)).toInt() - .coerceAtMost(lastRenderedHeight - height + 2 * StorageOverlay.config.margin).coerceAtLeast(0) - return true - } - - private fun renderStoragePage(context: DrawContext, page: StorageData.StorageInventory, mouseX: Int, mouseY: Int) { - context.drawText(MC.font, page.title, 2, 2, -1, true) - val inventory = page.inventory - if (inventory == null) { - // TODO: Missing texture - context.fill(0, 0, pageWidth, 60, DyeColor.RED.toShedaniel().darker(4.0).color) - context.drawCenteredTextWithShadow(MC.font, Text.literal("Not loaded yet"), pageWidth / 2, 30, -1) - return - } - - for ((index, stack) in inventory.stacks.withIndex()) { - val x = (index % 9) * 19 - val y = (index / 9) * 19 + MC.font.fontHeight + 2 - if (((mouseX - x) in 0 until 18) && ((mouseY - y) in 0 until 18)) { - context.fill(x, y, x + 18, y + 18, 0x80808080.toInt()) - } else { - context.fill(x, y, x + 18, y + 18, 0x40808080.toInt()) - } - context.drawItem(stack, x + 1, y + 1) - context.drawItemInSlot(MC.font, stack, x + 1, y + 1) - } - } - - override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) - isClosing = true - return super.keyPressed(keyCode, scanCode, modifiers) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StoragePageSlot.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StoragePageSlot.kt deleted file mode 100644 index 9259415..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StoragePageSlot.kt +++ /dev/null @@ -1,66 +0,0 @@ - - -package moe.nea.firmament.features.inventory.storageoverlay - -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import moe.nea.firmament.util.MC - -@Serializable(with = StoragePageSlot.Serializer::class) -data class StoragePageSlot(val index: Int) : Comparable<StoragePageSlot> { - object Serializer : KSerializer<StoragePageSlot> { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("StoragePageSlot", PrimitiveKind.INT) - - override fun deserialize(decoder: Decoder): StoragePageSlot { - return StoragePageSlot(decoder.decodeInt()) - } - - override fun serialize(encoder: Encoder, value: StoragePageSlot) { - encoder.encodeInt(value.index) - } - } - - init { - assert(index in 0 until (3 * 9)) - } - - val isEnderChest get() = index < 9 - val isBackPack get() = !isEnderChest - val slotIndexInOverviewPage get() = if (isEnderChest) index + 9 else index + 18 - fun defaultName(): String = if (isEnderChest) "Ender Chest #${index + 1}" else "Backpack #${index - 9 + 1}" - - fun navigateTo() { - if (isBackPack) { - MC.sendCommand("backpack ${index - 9 + 1}") - } else { - MC.sendCommand("enderchest ${index + 1}") - } - } - - companion object { - fun fromOverviewSlotIndex(slot: Int): StoragePageSlot? { - if (slot in 9 until 18) return StoragePageSlot(slot - 9) - if (slot in 27 until 45) return StoragePageSlot(slot - 27 + 9) - return null - } - - fun ofEnderChestPage(slot: Int): StoragePageSlot { - assert(slot in 1..9) - return StoragePageSlot(slot - 1) - } - - fun ofBackPackPage(slot: Int): StoragePageSlot { - assert(slot in 1..18) - return StoragePageSlot(slot - 1 + 9) - } - } - - override fun compareTo(other: StoragePageSlot): Int { - return this.index - other.index - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt deleted file mode 100644 index e07df8a..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt +++ /dev/null @@ -1,65 +0,0 @@ - - -package moe.nea.firmament.features.inventory.storageoverlay - -import io.ktor.util.decodeBase64Bytes -import io.ktor.util.encodeBase64 -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import net.minecraft.item.ItemStack -import net.minecraft.nbt.NbtCompound -import net.minecraft.nbt.NbtIo -import net.minecraft.nbt.NbtList -import net.minecraft.nbt.NbtOps -import net.minecraft.nbt.NbtSizeTracker - -@Serializable(with = VirtualInventory.Serializer::class) -data class VirtualInventory( - val stacks: List<ItemStack> -) { - val rows = stacks.size / 9 - - init { - assert(stacks.size % 9 == 0) - assert(stacks.size / 9 in 1..5) - } - - - object Serializer : KSerializer<VirtualInventory> { - const val INVENTORY = "INVENTORY" - override val descriptor: SerialDescriptor - get() = PrimitiveSerialDescriptor("VirtualInventory", PrimitiveKind.STRING) - - override fun deserialize(decoder: Decoder): VirtualInventory { - val s = decoder.decodeString() - val n = NbtIo.readCompressed(ByteArrayInputStream(s.decodeBase64Bytes()), NbtSizeTracker.of(100_000_000)) - val items = n.getList(INVENTORY, NbtCompound.COMPOUND_TYPE.toInt()) - return VirtualInventory(items.map { - it as NbtCompound - if (it.isEmpty) ItemStack.EMPTY - else runCatching { - ItemStack.CODEC.parse(NbtOps.INSTANCE, it).orThrow - }.getOrElse { ItemStack.EMPTY } - }) - } - - override fun serialize(encoder: Encoder, value: VirtualInventory) { - val list = NbtList() - value.stacks.forEach { - if (it.isEmpty) list.add(NbtCompound()) - else list.add(runCatching { ItemStack.CODEC.encode(it, NbtOps.INSTANCE, NbtCompound()).orThrow } - .getOrElse { NbtCompound() }) - } - val baos = ByteArrayOutputStream() - NbtIo.writeCompressed(NbtCompound().also { it.put(INVENTORY, list) }, baos) - encoder.encodeString(baos.toByteArray().encodeBase64()) - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt b/src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt deleted file mode 100644 index ed48437..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt +++ /dev/null @@ -1,81 +0,0 @@ - -package moe.nea.firmament.features.mining - -import java.util.* -import kotlin.time.Duration -import moe.nea.firmament.util.TimeMark - -class Histogram<T>( - val maxSize: Int, - val maxDuration: Duration, -) { - - data class OrderedTimestamp(val timestamp: TimeMark, val order: Int) : Comparable<OrderedTimestamp> { - override fun compareTo(other: OrderedTimestamp): Int { - val o = timestamp.compareTo(other.timestamp) - if (o != 0) return o - return order.compareTo(other.order) - } - } - - val size: Int get() = dataPoints.size - private val dataPoints: NavigableMap<OrderedTimestamp, T> = TreeMap() - - private var order = Int.MIN_VALUE - - fun record(entry: T, timestamp: TimeMark = TimeMark.now()) { - dataPoints[OrderedTimestamp(timestamp, order++)] = entry - trim() - } - - fun oldestUpdate(): TimeMark { - trim() - return if (dataPoints.isEmpty()) TimeMark.now() else dataPoints.firstKey().timestamp - } - - fun latestUpdate(): TimeMark { - trim() - return if (dataPoints.isEmpty()) TimeMark.farPast() else dataPoints.lastKey().timestamp - } - - fun averagePer(valueExtractor: (T) -> Double, perDuration: Duration): Double? { - return aggregate( - seed = 0.0, - operator = { accumulator, entry, _ -> accumulator + valueExtractor(entry) }, - finish = { sum, beginning, end -> - val timespan = end - beginning - if (timespan > perDuration) - sum / (timespan / perDuration) - else null - }) - } - - fun <V, R> aggregate( - seed: V, - operator: (V, T, TimeMark) -> V, - finish: (V, TimeMark, TimeMark) -> R - ): R? { - trim() - var accumulator = seed - var min: TimeMark? = null - var max: TimeMark? = null - dataPoints.forEach { (key, value) -> - max = key.timestamp - if (min == null) - min = key.timestamp - accumulator = operator(accumulator, value, key.timestamp) - } - if (min == null) - return null - return finish(accumulator, min!!, max!!) - } - - private fun trim() { - while (maxSize < dataPoints.size) { - dataPoints.pollFirstEntry() - } - dataPoints.headMap(OrderedTimestamp(TimeMark.ago(maxDuration), Int.MAX_VALUE)).clear() - } - - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/mining/PickaxeAbility.kt b/src/main/kotlin/moe/nea/firmament/features/mining/PickaxeAbility.kt deleted file mode 100644 index 7879f2d..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/mining/PickaxeAbility.kt +++ /dev/null @@ -1,176 +0,0 @@ - -package moe.nea.firmament.features.mining - -import java.util.regex.Pattern -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds -import net.minecraft.item.ItemStack -import net.minecraft.util.DyeColor -import net.minecraft.util.Hand -import net.minecraft.util.Identifier -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.HudRenderEvent -import moe.nea.firmament.events.ProcessChatEvent -import moe.nea.firmament.events.SlotClickEvent -import moe.nea.firmament.events.WorldReadyEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.DurabilityBarEvent -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.SHORT_NUMBER_FORMAT -import moe.nea.firmament.util.TIME_PATTERN -import moe.nea.firmament.util.TimeMark -import moe.nea.firmament.util.extraAttributes -import moe.nea.firmament.util.item.displayNameAccordingToNbt -import moe.nea.firmament.util.item.loreAccordingToNbt -import moe.nea.firmament.util.parseShortNumber -import moe.nea.firmament.util.parseTimePattern -import moe.nea.firmament.util.render.RenderCircleProgress -import moe.nea.firmament.util.render.lerp -import moe.nea.firmament.util.toShedaniel -import moe.nea.firmament.util.unformattedString -import moe.nea.firmament.util.useMatch - -object PickaxeAbility : FirmamentFeature { - override val identifier: String - get() = "pickaxe-info" - - - object TConfig : ManagedConfig(identifier) { - val cooldownEnabled by toggle("ability-cooldown") { true } - val cooldownScale by integer("ability-scale", 16, 64) { 16 } - val drillFuelBar by toggle("fuel-bar") { true } - } - - var lobbyJoinTime = TimeMark.farPast() - var lastUsage = mutableMapOf<String, TimeMark>() - var abilityOverride: String? = null - var defaultAbilityDurations = mutableMapOf<String, Duration>( - "Mining Speed Boost" to 120.seconds, - "Pickobulus" to 110.seconds, - "Gemstone Infusion" to 140.seconds, - "Hazardous Miner" to 140.seconds, - "Maniac Miner" to 59.seconds, - "Vein Seeker" to 60.seconds - ) - - override val config: ManagedConfig - get() = TConfig - - fun getCooldownPercentage(name: String, cooldown: Duration): Double { - val sinceLastUsage = lastUsage[name]?.passedTime() ?: Duration.INFINITE - if (sinceLastUsage < cooldown) - return sinceLastUsage / cooldown - val sinceLobbyJoin = lobbyJoinTime.passedTime() - val halfCooldown = cooldown / 2 - if (sinceLobbyJoin < halfCooldown) { - return (sinceLobbyJoin / halfCooldown) - } - return 1.0 - } - - @Subscribe - fun onSlotClick(it: SlotClickEvent) { - if (MC.screen?.title?.unformattedString == "Heart of the Mountain") { - val name = it.stack.displayNameAccordingToNbt?.unformattedString ?: return - val cooldown = it.stack.loreAccordingToNbt.firstNotNullOfOrNull { - cooldownPattern.useMatch(it.unformattedString) { - parseTimePattern(group("cooldown")) - } - } ?: return - defaultAbilityDurations[name] = cooldown - } - } - - @Subscribe - fun onDurabilityBar(it: DurabilityBarEvent) { - if (!TConfig.drillFuelBar) return - val lore = it.item.loreAccordingToNbt - if (lore.lastOrNull()?.unformattedString?.contains("DRILL") != true) return - val maxFuel = lore.firstNotNullOfOrNull { - fuelPattern.useMatch(it.unformattedString) { - parseShortNumber(group("maxFuel")) - } - } ?: return - val extra = it.item.extraAttributes - if (!extra.contains("drill_fuel")) return - val fuel = extra.getInt("drill_fuel") - val percentage = fuel / maxFuel.toFloat() - it.barOverride = DurabilityBarEvent.DurabilityBar( - lerp( - DyeColor.RED.toShedaniel(), - DyeColor.GREEN.toShedaniel(), - percentage - ), percentage - ) - } - - @Subscribe - fun onChatMessage(it: ProcessChatEvent) { - abilityUsePattern.useMatch(it.unformattedString) { - lastUsage[group("name")] = TimeMark.now() - } - abilitySwitchPattern.useMatch(it.unformattedString) { - abilityOverride = group("ability") - } - } - - @Subscribe - fun onWorldReady(event: WorldReadyEvent) { - lastUsage.clear() - lobbyJoinTime = TimeMark.now() - abilityOverride = null - } - - val abilityUsePattern = Pattern.compile("You used your (?<name>.*) Pickaxe Ability!") - val fuelPattern = Pattern.compile("Fuel: .*/(?<maxFuel>$SHORT_NUMBER_FORMAT)") - - data class PickaxeAbilityData( - val name: String, - val cooldown: Duration, - ) - - fun getCooldownFromLore(itemStack: ItemStack): PickaxeAbilityData? { - val lore = itemStack.loreAccordingToNbt - if (!lore.any { it.unformattedString.contains("Breaking Power") == true }) - return null - val cooldown = lore.firstNotNullOfOrNull { - cooldownPattern.useMatch(it.unformattedString) { - parseTimePattern(group("cooldown")) - } - } ?: return null - val name = lore.firstNotNullOfOrNull { - abilityPattern.useMatch(it.unformattedString) { - group("name") - } - } ?: return null - return PickaxeAbilityData(name, cooldown) - } - - - val cooldownPattern = Pattern.compile("Cooldown: (?<cooldown>$TIME_PATTERN)") - val abilityPattern = Pattern.compile("Ability: (?<name>.*) {2}RIGHT CLICK") - val abilitySwitchPattern = - Pattern.compile("You selected (?<ability>.*) as your Pickaxe Ability\\. This ability will apply to all of your pickaxes!") - - - @Subscribe - fun renderHud(event: HudRenderEvent) { - if (!TConfig.cooldownEnabled) return - var ability = getCooldownFromLore(MC.player?.getStackInHand(Hand.MAIN_HAND) ?: return) ?: return - defaultAbilityDurations[ability.name] = ability.cooldown - val ao = abilityOverride - if (ao != ability.name && ao != null) { - ability = PickaxeAbilityData(ao, defaultAbilityDurations[ao] ?: 120.seconds) - } - event.context.matrices.push() - event.context.matrices.translate(MC.window.scaledWidth / 2F, MC.window.scaledHeight / 2F, 0F) - event.context.matrices.scale(TConfig.cooldownScale.toFloat(), TConfig.cooldownScale.toFloat(), 1F) - RenderCircleProgress.renderCircle( - event.context, Identifier.of("firmament", "textures/gui/circle.png"), - getCooldownPercentage(ability.name, ability.cooldown).toFloat(), - 0f, 1f, 0f, 1f - ) - event.context.matrices.pop() - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/mining/PristineProfitTracker.kt b/src/main/kotlin/moe/nea/firmament/features/mining/PristineProfitTracker.kt deleted file mode 100644 index f1bc7e5..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/mining/PristineProfitTracker.kt +++ /dev/null @@ -1,133 +0,0 @@ - -package moe.nea.firmament.features.mining - -import io.github.notenoughupdates.moulconfig.xml.Bind -import moe.nea.jarvis.api.Point -import kotlinx.serialization.Serializable -import kotlinx.serialization.serializer -import kotlin.time.Duration.Companion.seconds -import net.minecraft.text.Text -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.ProcessChatEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.gui.hud.MoulConfigHud -import moe.nea.firmament.util.BazaarPriceStrategy -import moe.nea.firmament.util.FirmFormatters.formatCommas -import moe.nea.firmament.util.SkyblockId -import moe.nea.firmament.util.data.ProfileSpecificDataHolder -import moe.nea.firmament.util.formattedString -import moe.nea.firmament.util.parseIntWithComma -import moe.nea.firmament.util.useMatch - -object PristineProfitTracker : FirmamentFeature { - override val identifier: String - get() = "pristine-profit" - - enum class GemstoneKind( - val label: String, - val flawedId: SkyblockId, - ) { - SAPPHIRE("Sapphire", SkyblockId("FLAWED_SAPPHIRE_GEM")), - RUBY("Ruby", SkyblockId("FLAWED_RUBY_GEM")), - AMETHYST("Amethyst", SkyblockId("FLAWED_AMETHYST_GEM")), - AMBER("Amber", SkyblockId("FLAWED_AMBER_GEM")), - TOPAZ("Topaz", SkyblockId("FLAWED_TOPAZ_GEM")), - JADE("Jade", SkyblockId("FLAWED_JADE_GEM")), - JASPER("Jasper", SkyblockId("FLAWED_JASPER_GEM")), - OPAL("Opal", SkyblockId("FLAWED_OPAL_GEM")), - } - - @Serializable - data class Data( - var maxMoneyPerSecond: Double = 1.0, - var maxCollectionPerSecond: Double = 1.0, - ) - - object DConfig : ProfileSpecificDataHolder<Data>(serializer(), identifier, ::Data) - - override val config: ManagedConfig? - get() = TConfig - - object TConfig : ManagedConfig(identifier) { - val timeout by duration("timeout", 0.seconds, 120.seconds) { 30.seconds } - val gui by position("position", 80, 30) { Point(0.05, 0.2) } - } - - val sellingStrategy = BazaarPriceStrategy.SELL_ORDER - - val pristineRegex = - "PRISTINE! You found . Flawed (?<kind>${ - GemstoneKind.entries.joinToString("|") { it.label } - }) Gemstone x(?<count>[0-9,]+)!".toPattern() - - val collectionHistogram = Histogram<Double>(10000, 180.seconds) - val moneyHistogram = Histogram<Double>(10000, 180.seconds) - - object ProfitHud : MoulConfigHud("pristine_profit", TConfig.gui) { - @field:Bind - var moneyCurrent: Double = 0.0 - - @field:Bind - var moneyMax: Double = 1.0 - - @field:Bind - var moneyText = "" - - @field:Bind - var collectionCurrent = 0.0 - - @field:Bind - var collectionMax = 1.0 - - @field:Bind - var collectionText = "" - override fun shouldRender(): Boolean = collectionHistogram.latestUpdate().passedTime() < TConfig.timeout - } - - val SECONDS_PER_HOUR = 3600 - val ROUGHS_PER_FLAWED = 80 - - fun updateUi() { - val collectionPerSecond = collectionHistogram.averagePer({ it }, 1.seconds) - val moneyPerSecond = moneyHistogram.averagePer({ it }, 1.seconds) - if (collectionPerSecond == null || moneyPerSecond == null) return - ProfitHud.collectionCurrent = collectionPerSecond - ProfitHud.collectionText = Text.stringifiedTranslatable("firmament.pristine-profit.collection", - formatCommas(collectionPerSecond * SECONDS_PER_HOUR, - 1)).formattedString() - ProfitHud.moneyCurrent = moneyPerSecond - ProfitHud.moneyText = Text.stringifiedTranslatable("firmament.pristine-profit.money", - formatCommas(moneyPerSecond * SECONDS_PER_HOUR, 1)) - .formattedString() - val data = DConfig.data - if (data != null) { - if (data.maxCollectionPerSecond < collectionPerSecond && collectionHistogram.oldestUpdate() - .passedTime() > 30.seconds - ) { - data.maxCollectionPerSecond = collectionPerSecond - DConfig.markDirty() - } - if (data.maxMoneyPerSecond < moneyPerSecond && moneyHistogram.oldestUpdate().passedTime() > 30.seconds) { - data.maxMoneyPerSecond = moneyPerSecond - DConfig.markDirty() - } - ProfitHud.collectionMax = maxOf(data.maxCollectionPerSecond, collectionPerSecond) - ProfitHud.moneyMax = maxOf(data.maxMoneyPerSecond, moneyPerSecond) - } - } - - - @Subscribe - fun onMessage(it: ProcessChatEvent) { - pristineRegex.useMatch(it.unformattedString) { - val gemstoneKind = GemstoneKind.valueOf(group("kind").uppercase()) - val flawedCount = parseIntWithComma(group("count")) - val moneyAmount = sellingStrategy.getSellPrice(gemstoneKind.flawedId) * flawedCount - moneyHistogram.record(moneyAmount) - val collectionAmount = flawedCount * ROUGHS_PER_FLAWED - collectionHistogram.record(collectionAmount.toDouble()) - updateUi() - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/notifications/Notifications.kt b/src/main/kotlin/moe/nea/firmament/features/notifications/Notifications.kt deleted file mode 100644 index 8d912d1..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/notifications/Notifications.kt +++ /dev/null @@ -1,7 +0,0 @@ - -package moe.nea.firmament.features.notifications - -import moe.nea.firmament.features.FirmamentFeature - -object Notifications { -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/AlwaysPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/AlwaysPredicate.kt deleted file mode 100644 index 4dd28df..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/AlwaysPredicate.kt +++ /dev/null @@ -1,17 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonElement -import net.minecraft.item.ItemStack - -object AlwaysPredicate : FirmamentModelPredicate { - override fun test(stack: ItemStack): Boolean { - return true - } - - object Parser : FirmamentModelPredicateParser { - override fun parse(jsonElement: JsonElement): FirmamentModelPredicate { - return AlwaysPredicate - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/AndPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/AndPredicate.kt deleted file mode 100644 index 55a4f32..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/AndPredicate.kt +++ /dev/null @@ -1,26 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonArray -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import net.minecraft.item.ItemStack - -class AndPredicate(val children: Array<FirmamentModelPredicate>) : FirmamentModelPredicate { - override fun test(stack: ItemStack): Boolean { - return children.all { it.test(stack) } - } - - object Parser : FirmamentModelPredicateParser { - override fun parse(jsonElement: JsonElement): FirmamentModelPredicate { - val children = - (jsonElement as JsonArray) - .flatMap { - CustomModelOverrideParser.parsePredicates(it as JsonObject) - } - .toTypedArray() - return AndPredicate(children) - } - - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/BakedModelExtra.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/BakedModelExtra.kt deleted file mode 100644 index ae1f6d5..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/BakedModelExtra.kt +++ /dev/null @@ -1,9 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import net.minecraft.client.render.model.BakedModel - -interface BakedModelExtra { - fun getHeadModel_firmament(): BakedModel? - fun setHeadModel_firmament(headModel: BakedModel?) -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/BakedOverrideData.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/BakedOverrideData.kt deleted file mode 100644 index c012883..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/BakedOverrideData.kt +++ /dev/null @@ -1,8 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -interface BakedOverrideData { - fun getFirmamentOverrides(): Array<FirmamentModelPredicate>? - fun setFirmamentOverrides(overrides: Array<FirmamentModelPredicate>?) - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt deleted file mode 100644 index 18da54c..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt +++ /dev/null @@ -1,296 +0,0 @@ -@file:UseSerializers(BlockPosSerializer::class, IdentifierSerializer::class) - -package moe.nea.firmament.features.texturepack - -import java.util.concurrent.CompletableFuture -import net.fabricmc.loader.api.FabricLoader -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient -import kotlinx.serialization.UseSerializers -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.json.JsonDecoder -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.serializer -import kotlin.jvm.optionals.getOrNull -import net.minecraft.block.Block -import net.minecraft.block.BlockState -import net.minecraft.client.render.model.BakedModel -import net.minecraft.client.util.ModelIdentifier -import net.minecraft.registry.RegistryKey -import net.minecraft.registry.RegistryKeys -import net.minecraft.resource.ResourceManager -import net.minecraft.resource.SinglePreparationResourceReloader -import net.minecraft.util.Identifier -import net.minecraft.util.math.BlockPos -import net.minecraft.util.profiler.Profiler -import moe.nea.firmament.Firmament -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.compat.SodiumChunkReloader -import moe.nea.firmament.events.BakeExtraModelsEvent -import moe.nea.firmament.events.EarlyResourceReloadEvent -import moe.nea.firmament.events.FinalizeResourceManagerEvent -import moe.nea.firmament.events.SkyblockServerUpdateEvent -import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger -import moe.nea.firmament.util.IdentifierSerializer -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.SBData -import moe.nea.firmament.util.SkyBlockIsland -import moe.nea.firmament.util.json.BlockPosSerializer -import moe.nea.firmament.util.json.SingletonSerializableList - - -object CustomBlockTextures { - @Serializable - data class CustomBlockOverride( - val modes: @Serializable(SingletonSerializableList::class) List<String>, - val area: List<Area>? = null, - val replacements: Map<Identifier, Replacement>, - ) - - @Serializable(with = Replacement.Serializer::class) - data class Replacement( - val block: Identifier, - val sound: Identifier?, - ) { - - @Transient - val blockModelIdentifier get() = ModelIdentifier(block.withPrefixedPath("block/"), "firmament") - - @Transient - val bakedModel: BakedModel by lazy(LazyThreadSafetyMode.NONE) { - MC.instance.bakedModelManager.getModel(blockModelIdentifier) - } - - @OptIn(ExperimentalSerializationApi::class) - @kotlinx.serialization.Serializer(Replacement::class) - object DefaultSerializer : KSerializer<Replacement> - - object Serializer : KSerializer<Replacement> { - val delegate = serializer<JsonElement>() - override val descriptor: SerialDescriptor - get() = delegate.descriptor - - override fun deserialize(decoder: Decoder): Replacement { - val jsonElement = decoder.decodeSerializableValue(delegate) - if (jsonElement is JsonPrimitive) { - require(jsonElement.isString) - return Replacement(Identifier.tryParse(jsonElement.content)!!, null) - } - return (decoder as JsonDecoder).json.decodeFromJsonElement(DefaultSerializer, jsonElement) - } - - override fun serialize(encoder: Encoder, value: Replacement) { - encoder.encodeSerializableValue(DefaultSerializer, value) - } - } - } - - @Serializable - data class Area( - val min: BlockPos, - val max: BlockPos, - ) { - @Transient - val realMin = BlockPos( - minOf(min.x, max.x), - minOf(min.y, max.y), - minOf(min.z, max.z), - ) - - @Transient - val realMax = BlockPos( - maxOf(min.x, max.x), - maxOf(min.y, max.y), - maxOf(min.z, max.z), - ) - - fun roughJoin(other: Area): Area { - return Area( - BlockPos( - minOf(realMin.x, other.realMin.x), - minOf(realMin.y, other.realMin.y), - minOf(realMin.z, other.realMin.z), - ), - BlockPos( - maxOf(realMax.x, other.realMax.x), - maxOf(realMax.y, other.realMax.y), - maxOf(realMax.z, other.realMax.z), - ) - ) - } - - fun contains(blockPos: BlockPos): Boolean { - return (blockPos.x in realMin.x..realMax.x) && - (blockPos.y in realMin.y..realMax.y) && - (blockPos.z in realMin.z..realMax.z) - } - } - - data class LocationReplacements( - val lookup: Map<Block, List<BlockReplacement>> - ) - - data class BlockReplacement( - val checks: List<Area>?, - val replacement: Replacement, - ) { - val roughCheck by lazy(LazyThreadSafetyMode.NONE) { - if (checks == null || checks.size < 3) return@lazy null - checks.reduce { acc, next -> acc.roughJoin(next) } - } - } - - data class BakedReplacements(val data: Map<SkyBlockIsland, LocationReplacements>) - - var allLocationReplacements: BakedReplacements = BakedReplacements(mapOf()) - var currentIslandReplacements: LocationReplacements? = null - - fun refreshReplacements() { - val location = SBData.skyblockLocation - val replacements = - if (CustomSkyBlockTextures.TConfig.enableBlockOverrides) location?.let(allLocationReplacements.data::get) - else null - val lastReplacements = currentIslandReplacements - currentIslandReplacements = replacements - if (lastReplacements != replacements) { - MC.nextTick { - MC.worldRenderer.chunks?.chunks?.forEach { - // false schedules rebuilds outside a 27 block radius to happen async - it.scheduleRebuild(false) - } - sodiumReloadTask?.run() - } - } - } - - private val sodiumReloadTask = runCatching { - SodiumChunkReloader() - }.getOrElse { - if (FabricLoader.getInstance().isModLoaded("sodium")) - logger.error("Could not create sodium chunk reloader") - null - } - - - fun matchesPosition(replacement: BlockReplacement, blockPos: BlockPos?): Boolean { - if (blockPos == null) return true - val rc = replacement.roughCheck - if (rc != null && !rc.contains(blockPos)) return false - val areas = replacement.checks - if (areas != null && !areas.any { it.contains(blockPos) }) return false - return true - } - - @JvmStatic - fun getReplacementModel(block: BlockState, blockPos: BlockPos?): BakedModel? { - return getReplacement(block, blockPos)?.bakedModel - } - - @JvmStatic - fun getReplacement(block: BlockState, blockPos: BlockPos?): Replacement? { - if (isInFallback() && blockPos == null) return null - val replacements = currentIslandReplacements?.lookup?.get(block.block) ?: return null - for (replacement in replacements) { - if (replacement.checks == null || matchesPosition(replacement, blockPos)) - return replacement.replacement - } - return null - } - - - @Subscribe - fun onLocation(event: SkyblockServerUpdateEvent) { - refreshReplacements() - } - - @Volatile - var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture(BakedReplacements( - mapOf())) - - val insideFallbackCall = ThreadLocal.withInitial { 0 } - - @JvmStatic - fun enterFallbackCall() { - insideFallbackCall.set(insideFallbackCall.get() + 1) - } - - fun isInFallback() = insideFallbackCall.get() > 0 - - @JvmStatic - fun exitFallbackCall() { - insideFallbackCall.set(insideFallbackCall.get() - 1) - } - - @Subscribe - fun onEarlyReload(event: EarlyResourceReloadEvent) { - preparationFuture = CompletableFuture - .supplyAsync( - { prepare(event.resourceManager) }, event.preparationExecutor) - } - - @Subscribe - fun bakeExtraModels(event: BakeExtraModelsEvent) { - preparationFuture.join().data.values - .flatMap { it.lookup.values } - .flatten() - .mapTo(mutableSetOf()) { it.replacement.blockModelIdentifier } - .forEach { event.addNonItemModel(it) } - } - - private fun prepare(manager: ResourceManager): BakedReplacements { - val resources = manager.findResources("overrides/blocks") { - it.namespace == "firmskyblock" && it.path.endsWith(".json") - } - val map = mutableMapOf<SkyBlockIsland, MutableMap<Block, MutableList<BlockReplacement>>>() - for ((file, resource) in resources) { - val json = - Firmament.tryDecodeJsonFromStream<CustomBlockOverride>(resource.inputStream) - .getOrElse { ex -> - logger.error("Failed to load block texture override at $file", ex) - continue - } - for (mode in json.modes) { - val island = SkyBlockIsland.forMode(mode) - val islandMpa = map.getOrPut(island, ::mutableMapOf) - for ((blockId, replacement) in json.replacements) { - val block = MC.defaultRegistries.getWrapperOrThrow(RegistryKeys.BLOCK) - .getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId)) - .getOrNull() - if (block == null) { - logger.error("Failed to load block texture override at ${file}: unknown block '$blockId'") - continue - } - val replacements = islandMpa.getOrPut(block.value(), ::mutableListOf) - replacements.add(BlockReplacement(json.area, replacement)) - } - } - } - - return BakedReplacements(map.mapValues { LocationReplacements(it.value) }) - } - - @JvmStatic - fun patchIndigo(orig: BakedModel, pos: BlockPos, state: BlockState): BakedModel { - return getReplacementModel(state, pos) ?: orig - } - - @Subscribe - fun onStart(event: FinalizeResourceManagerEvent) { - event.resourceManager.registerReloader(object : - SinglePreparationResourceReloader<BakedReplacements>() { - override fun prepare(manager: ResourceManager, profiler: Profiler): BakedReplacements { - return preparationFuture.join() - } - - override fun apply(prepared: BakedReplacements, manager: ResourceManager, profiler: Profiler?) { - allLocationReplacements = prepared - refreshReplacements() - } - }) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt deleted file mode 100644 index 23577ee..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt +++ /dev/null @@ -1,106 +0,0 @@ - -@file:UseSerializers(IdentifierSerializer::class) - -package moe.nea.firmament.features.texturepack - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient -import kotlinx.serialization.UseSerializers -import net.minecraft.item.ArmorMaterial -import net.minecraft.item.ItemStack -import net.minecraft.resource.ResourceManager -import net.minecraft.resource.SinglePreparationResourceReloader -import net.minecraft.util.Identifier -import net.minecraft.util.profiler.Profiler -import moe.nea.firmament.Firmament -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.FinalizeResourceManagerEvent -import moe.nea.firmament.events.subscription.SubscriptionOwner -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger -import moe.nea.firmament.util.IdentifierSerializer -import moe.nea.firmament.util.IdentityCharacteristics -import moe.nea.firmament.util.computeNullableFunction -import moe.nea.firmament.util.skyBlockId - -object CustomGlobalArmorOverrides : SubscriptionOwner { - @Serializable - data class ArmorOverride( - @SerialName("item_ids") - val itemIds: List<String>, - val layers: List<ArmorOverrideLayer>, - val overrides: List<ArmorOverrideOverride> = listOf(), - ) { - @Transient - val bakedLayers = bakeLayers(layers) - } - - fun bakeLayers(layers: List<ArmorOverrideLayer>): List<ArmorMaterial.Layer> { - return layers.map { ArmorMaterial.Layer(it.identifier, it.suffix, it.tint) } - } - - @Serializable - data class ArmorOverrideLayer( - val tint: Boolean = false, - val identifier: Identifier, - val suffix: String = "", - ) - - @Serializable - data class ArmorOverrideOverride( - val predicate: FirmamentModelPredicate, - val layers: List<ArmorOverrideLayer>, - ) { - @Transient - val bakedLayers = bakeLayers(layers) - } - - override val delegateFeature: FirmamentFeature - get() = CustomSkyBlockTextures - - val overrideCache = mutableMapOf<IdentityCharacteristics<ItemStack>, Any>() - - @JvmStatic - fun overrideArmor(stack: ItemStack): List<ArmorMaterial.Layer>? { - if (!CustomSkyBlockTextures.TConfig.enableArmorOverrides) return null - return overrideCache.computeNullableFunction(IdentityCharacteristics(stack)) { - val id = stack.skyBlockId ?: return@computeNullableFunction null - val override = overrides[id.neuItem] ?: return@computeNullableFunction null - for (suboverride in override.overrides) { - if (suboverride.predicate.test(stack)) { - return@computeNullableFunction suboverride.bakedLayers - } - } - return@computeNullableFunction override.bakedLayers - } - } - - var overrides: Map<String, ArmorOverride> = mapOf() - - @Subscribe - fun onStart(event: FinalizeResourceManagerEvent) { - event.resourceManager.registerReloader(object : - SinglePreparationResourceReloader<Map<String, ArmorOverride>>() { - override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> { - val overrideFiles = manager.findResources("overrides/armor_models") { - it.namespace == "firmskyblock" && it.path.endsWith(".json") - } - val overrides = overrideFiles.mapNotNull { - Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex -> - logger.error("Failed to load armor texture override at ${it.key}", ex) - null - } - } - val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } } - .toMap() - return associatedMap - } - - override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) { - overrides = prepared - } - }) - } - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt deleted file mode 100644 index d64c844..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt +++ /dev/null @@ -1,167 +0,0 @@ - -@file:UseSerializers(IdentifierSerializer::class, CustomModelOverrideParser.FirmamentRootPredicateSerializer::class) - -package moe.nea.firmament.features.texturepack - - -import java.util.concurrent.CompletableFuture -import org.slf4j.LoggerFactory -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable -import kotlinx.serialization.Serializable -import kotlinx.serialization.UseSerializers -import kotlin.jvm.optionals.getOrNull -import net.minecraft.client.render.item.ItemModels -import net.minecraft.client.render.model.BakedModel -import net.minecraft.client.util.ModelIdentifier -import net.minecraft.item.ItemStack -import net.minecraft.resource.ResourceManager -import net.minecraft.resource.SinglePreparationResourceReloader -import net.minecraft.text.Text -import net.minecraft.util.Identifier -import net.minecraft.util.profiler.Profiler -import moe.nea.firmament.Firmament -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.BakeExtraModelsEvent -import moe.nea.firmament.events.EarlyResourceReloadEvent -import moe.nea.firmament.events.FinalizeResourceManagerEvent -import moe.nea.firmament.events.ScreenChangeEvent -import moe.nea.firmament.events.subscription.SubscriptionOwner -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.util.IdentifierSerializer -import moe.nea.firmament.util.IdentityCharacteristics -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.computeNullableFunction -import moe.nea.firmament.util.json.SingletonSerializableList -import moe.nea.firmament.util.runNull - -object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalTextures.CustomGuiTextureOverride>(), - SubscriptionOwner { - override val delegateFeature: FirmamentFeature - get() = CustomSkyBlockTextures - - class CustomGuiTextureOverride( - val classes: List<ItemOverrideCollection> - ) - - @Serializable - data class GlobalItemOverride( - val screen: @Serializable(SingletonSerializableList::class) List<Identifier>, - val model: Identifier, - val predicate: FirmamentModelPredicate, - ) - - @Serializable - data class ScreenFilter( - val title: StringMatcher, - ) - - data class ItemOverrideCollection( - val screenFilter: ScreenFilter, - val overrides: List<GlobalItemOverride>, - ) - - @Subscribe - fun onStart(event: FinalizeResourceManagerEvent) { - MC.resourceManager.registerReloader(this) - } - - @Subscribe - fun onEarlyReload(event: EarlyResourceReloadEvent) { - preparationFuture = CompletableFuture - .supplyAsync( - { - prepare(event.resourceManager) - }, event.preparationExecutor) - } - - @Subscribe - fun onBakeModels(event: BakeExtraModelsEvent) { - for (guiClassOverride in preparationFuture.join().classes) { - for (override in guiClassOverride.overrides) { - event.addItemModel(ModelIdentifier(override.model, "inventory")) - } - } - } - - @Volatile - var preparationFuture: CompletableFuture<CustomGuiTextureOverride> = CompletableFuture.completedFuture( - CustomGuiTextureOverride(listOf())) - - override fun prepare(manager: ResourceManager?, profiler: Profiler?): CustomGuiTextureOverride { - return preparationFuture.join() - } - - override fun apply(prepared: CustomGuiTextureOverride, manager: ResourceManager?, profiler: Profiler?) { - this.guiClassOverrides = prepared - } - - val logger = LoggerFactory.getLogger(CustomGlobalTextures::class.java) - fun prepare(manager: ResourceManager): CustomGuiTextureOverride { - val overrideResources = - manager.findResources("overrides/item") { it.namespace == "firmskyblock" && it.path.endsWith(".json") } - .mapNotNull { - Firmament.tryDecodeJsonFromStream<GlobalItemOverride>(it.value.inputStream).getOrElse { ex -> - logger.error("Failed to load global item override at ${it.key}", ex) - null - } - } - - val byGuiClass = overrideResources.flatMap { override -> override.screen.toSet().map { it to override } } - .groupBy { it.first } - val guiClasses = byGuiClass.entries - .mapNotNull { - val key = it.key - val guiClassResource = - manager.getResource(Identifier.of(key.namespace, "filters/screen/${key.path}.json")) - .getOrNull() - ?: return@mapNotNull runNull { - logger.error("Failed to locate screen filter at $key") - } - val screenFilter = - Firmament.tryDecodeJsonFromStream<ScreenFilter>(guiClassResource.inputStream) - .getOrElse { ex -> - logger.error("Failed to load screen filter at $key", ex) - return@mapNotNull null - } - ItemOverrideCollection(screenFilter, it.value.map { it.second }) - } - logger.info("Loaded ${overrideResources.size} global item overrides") - return CustomGuiTextureOverride(guiClasses) - } - - var guiClassOverrides = CustomGuiTextureOverride(listOf()) - - var matchingOverrides: Set<ItemOverrideCollection> = setOf() - - @Subscribe - fun onOpenGui(event: ScreenChangeEvent) { - val newTitle = event.new?.title ?: Text.empty() - matchingOverrides = guiClassOverrides.classes - .filterTo(mutableSetOf()) { it.screenFilter.title.matches(newTitle) } - } - - val overrideCache = mutableMapOf<IdentityCharacteristics<ItemStack>, Any>() - - @JvmStatic - fun replaceGlobalModel( - models: ItemModels, - stack: ItemStack, - cir: CallbackInfoReturnable<BakedModel> - ) { - val value = overrideCache.computeNullableFunction(IdentityCharacteristics(stack)) { - for (guiClassOverride in matchingOverrides) { - for (override in guiClassOverride.overrides) { - if (override.predicate.test(stack)) { - return@computeNullableFunction models.modelManager.getModel( - ModelIdentifier(override.model, "inventory")) - } - } - } - null - } - if (value != null) - cir.returnValue = value - } - - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomModelOverrideParser.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomModelOverrideParser.kt deleted file mode 100644 index a4e7c02..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomModelOverrideParser.kt +++ /dev/null @@ -1,74 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonObject -import kotlinx.serialization.KSerializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import net.minecraft.item.ItemStack -import net.minecraft.util.Identifier - -object CustomModelOverrideParser { - object FirmamentRootPredicateSerializer : KSerializer<FirmamentModelPredicate> { - val delegateSerializer = kotlinx.serialization.json.JsonObject.serializer() - override val descriptor: SerialDescriptor - get() = SerialDescriptor("FirmamentModelRootPredicate", delegateSerializer.descriptor) - - override fun deserialize(decoder: Decoder): FirmamentModelPredicate { - val json = decoder.decodeSerializableValue(delegateSerializer).intoGson() as JsonObject - return AndPredicate(parsePredicates(json).toTypedArray()) - } - - override fun serialize(encoder: Encoder, value: FirmamentModelPredicate) { - TODO("Cannot serialize firmament predicates") - } - } - - val predicateParsers = mutableMapOf<Identifier, FirmamentModelPredicateParser>() - - - fun registerPredicateParser(name: String, parser: FirmamentModelPredicateParser) { - predicateParsers[Identifier.of("firmament", name)] = parser - } - - init { - registerPredicateParser("display_name", DisplayNamePredicate.Parser) - registerPredicateParser("lore", LorePredicate.Parser) - registerPredicateParser("all", AndPredicate.Parser) - registerPredicateParser("any", OrPredicate.Parser) - registerPredicateParser("not", NotPredicate.Parser) - registerPredicateParser("item", ItemPredicate.Parser) - registerPredicateParser("extra_attributes", ExtraAttributesPredicate.Parser) - registerPredicateParser("pet", PetPredicate.Parser) - } - - private val neverPredicate = listOf( - object : FirmamentModelPredicate { - override fun test(stack: ItemStack): Boolean { - return false - } - } - ) - - fun parsePredicates(predicates: JsonObject): List<FirmamentModelPredicate> { - val parsedPredicates = mutableListOf<FirmamentModelPredicate>() - for (predicateName in predicates.keySet()) { - if (!predicateName.startsWith("firmament:")) continue - val identifier = Identifier.of(predicateName) - val parser = predicateParsers[identifier] ?: return neverPredicate - val parsedPredicate = parser.parse(predicates[predicateName]) ?: return neverPredicate - parsedPredicates.add(parsedPredicate) - } - return parsedPredicates - } - - @JvmStatic - fun parseCustomModelOverrides(jsonObject: JsonObject): Array<FirmamentModelPredicate>? { - val predicates = (jsonObject["predicate"] as? JsonObject) ?: return null - val parsedPredicates = parsePredicates(predicates) - if (parsedPredicates.isEmpty()) - return null - return parsedPredicates.toTypedArray() - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt deleted file mode 100644 index dec6046..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt +++ /dev/null @@ -1,114 +0,0 @@ -package moe.nea.firmament.features.texturepack - -import com.mojang.authlib.minecraft.MinecraftProfileTexture -import com.mojang.authlib.properties.Property -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable -import net.minecraft.block.SkullBlock -import net.minecraft.client.MinecraftClient -import net.minecraft.client.render.RenderLayer -import net.minecraft.client.util.ModelIdentifier -import net.minecraft.component.type.ProfileComponent -import net.minecraft.util.Identifier -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.BakeExtraModelsEvent -import moe.nea.firmament.events.CustomItemModelEvent -import moe.nea.firmament.events.TickEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.IdentityCharacteristics -import moe.nea.firmament.util.item.decodeProfileTextureProperty -import moe.nea.firmament.util.skyBlockId - -object CustomSkyBlockTextures : FirmamentFeature { - override val identifier: String - get() = "custom-skyblock-textures" - - object TConfig : ManagedConfig(identifier) { - val enabled by toggle("enabled") { true } - val skullsEnabled by toggle("skulls-enabled") { true } - val cacheDuration by integer("cache-duration", 0, 20) { 1 } - val enableModelOverrides by toggle("model-overrides") { true } - val enableArmorOverrides by toggle("armor-overrides") { true } - val enableBlockOverrides by toggle("block-overrides") { true } - } - - override val config: ManagedConfig - get() = TConfig - - @Subscribe - fun onTick(it: TickEvent) { - if (TConfig.cacheDuration < 1 || it.tickCount % TConfig.cacheDuration == 0) { - // TODO: unify all of those caches somehow - CustomItemModelEvent.clearCache() - skullTextureCache.clear() - CustomGlobalTextures.overrideCache.clear() - CustomGlobalArmorOverrides.overrideCache.clear() - } - } - - @Subscribe - fun bakeCustomFirmModels(event: BakeExtraModelsEvent) { - val resources = - MinecraftClient.getInstance().resourceManager.findResources("models/item" - ) { it: Identifier -> - "firmskyblock" == it.namespace && it.path - .endsWith(".json") - } - for (identifier in resources.keys) { - val modelId = ModelIdentifier.ofInventoryVariant( - Identifier.of( - "firmskyblock", - identifier.path.substring( - "models/item/".length, - identifier.path.length - ".json".length), - )) - event.addItemModel(modelId) - } - } - - @Subscribe - fun onCustomModelId(it: CustomItemModelEvent) { - if (!TConfig.enabled) return - val id = it.itemStack.skyBlockId ?: return - it.overrideModel = ModelIdentifier.ofInventoryVariant(Identifier.of("firmskyblock", id.identifier.path)) - } - - private val skullTextureCache = mutableMapOf<IdentityCharacteristics<ProfileComponent>, Any>() - private val sentinelPresentInvalid = Object() - - private val mcUrlRegex = "https?://textures.minecraft.net/texture/([a-fA-F0-9]+)".toRegex() - - fun getSkullId(textureProperty: Property): String? { - val texture = decodeProfileTextureProperty(textureProperty) ?: return null - val textureUrl = - texture.textures[MinecraftProfileTexture.Type.SKIN]?.url ?: return null - val mcUrlData = mcUrlRegex.matchEntire(textureUrl) ?: return null - return mcUrlData.groupValues[1] - } - - fun getSkullTexture(profile: ProfileComponent): Identifier? { - val id = getSkullId(profile.properties["textures"].firstOrNull() ?: return null) ?: return null - return Identifier.of("firmskyblock", "textures/placedskull/$id.png") - } - - fun modifySkullTexture( - type: SkullBlock.SkullType?, - component: ProfileComponent?, - cir: CallbackInfoReturnable<RenderLayer> - ) { - if (type != SkullBlock.Type.PLAYER) return - if (!TConfig.skullsEnabled) return - if (component == null) return - val ic = IdentityCharacteristics(component) - - val n = skullTextureCache.getOrPut(ic) { - val id = getSkullTexture(component) ?: return@getOrPut sentinelPresentInvalid - if (!MinecraftClient.getInstance().resourceManager.getResource(id).isPresent) { - return@getOrPut sentinelPresentInvalid - } - return@getOrPut id - } - if (n === sentinelPresentInvalid) return - cir.returnValue = RenderLayer.getEntityTranslucent(n as Identifier) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt deleted file mode 100644 index c89931e..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt +++ /dev/null @@ -1,22 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonElement -import net.minecraft.item.ItemStack -import net.minecraft.nbt.NbtElement -import net.minecraft.nbt.NbtString -import moe.nea.firmament.util.item.displayNameAccordingToNbt -import moe.nea.firmament.util.item.loreAccordingToNbt - -data class DisplayNamePredicate(val stringMatcher: StringMatcher) : FirmamentModelPredicate { - override fun test(stack: ItemStack): Boolean { - val display = stack.displayNameAccordingToNbt - return stringMatcher.matches(display) - } - - object Parser : FirmamentModelPredicateParser { - override fun parse(jsonElement: JsonElement): FirmamentModelPredicate { - return DisplayNamePredicate(StringMatcher.parse(jsonElement)) - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/ExtraAttributesPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/ExtraAttributesPredicate.kt deleted file mode 100644 index 4114f45..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/ExtraAttributesPredicate.kt +++ /dev/null @@ -1,268 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonArray -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import com.google.gson.JsonPrimitive -import net.minecraft.item.ItemStack -import net.minecraft.nbt.NbtByte -import net.minecraft.nbt.NbtCompound -import net.minecraft.nbt.NbtDouble -import net.minecraft.nbt.NbtElement -import net.minecraft.nbt.NbtFloat -import net.minecraft.nbt.NbtInt -import net.minecraft.nbt.NbtList -import net.minecraft.nbt.NbtLong -import net.minecraft.nbt.NbtShort -import net.minecraft.nbt.NbtString -import moe.nea.firmament.util.extraAttributes - -fun interface NbtMatcher { - fun matches(nbt: NbtElement): Boolean - - object Parser { - fun parse(jsonElement: JsonElement): NbtMatcher? { - if (jsonElement is JsonPrimitive) { - if (jsonElement.isString) { - val string = jsonElement.asString - return MatchStringExact(string) - } - if (jsonElement.isNumber) { - return MatchNumberExact(jsonElement.asLong) //TODO: parse generic number - } - } - if (jsonElement is JsonObject) { - var encounteredParser: NbtMatcher? = null - for (entry in ExclusiveParserType.entries) { - val data = jsonElement[entry.key] ?: continue - if (encounteredParser != null) { - // TODO: warn - return null - } - encounteredParser = entry.parse(data) ?: return null - } - return encounteredParser - } - return null - } - - enum class ExclusiveParserType(val key: String) { - STRING("string") { - override fun parse(element: JsonElement): NbtMatcher? { - return MatchString(StringMatcher.parse(element)) - } - }, - INT("int") { - override fun parse(element: JsonElement): NbtMatcher? { - return parseGenericNumber(element, - { it.asInt }, - { (it as? NbtInt)?.intValue() }, - { a, b -> - if (a == b) Comparison.EQUAL - else if (a < b) Comparison.LESS_THAN - else Comparison.GREATER - }) - } - }, - FLOAT("float") { - override fun parse(element: JsonElement): NbtMatcher? { - return parseGenericNumber(element, - { it.asFloat }, - { (it as? NbtFloat)?.floatValue() }, - { a, b -> - if (a == b) Comparison.EQUAL - else if (a < b) Comparison.LESS_THAN - else Comparison.GREATER - }) - } - }, - DOUBLE("double") { - override fun parse(element: JsonElement): NbtMatcher? { - return parseGenericNumber(element, - { it.asDouble }, - { (it as? NbtDouble)?.doubleValue() }, - { a, b -> - if (a == b) Comparison.EQUAL - else if (a < b) Comparison.LESS_THAN - else Comparison.GREATER - }) - } - }, - LONG("long") { - override fun parse(element: JsonElement): NbtMatcher? { - return parseGenericNumber(element, - { it.asLong }, - { (it as? NbtLong)?.longValue() }, - { a, b -> - if (a == b) Comparison.EQUAL - else if (a < b) Comparison.LESS_THAN - else Comparison.GREATER - }) - } - }, - SHORT("short") { - override fun parse(element: JsonElement): NbtMatcher? { - return parseGenericNumber(element, - { it.asShort }, - { (it as? NbtShort)?.shortValue() }, - { a, b -> - if (a == b) Comparison.EQUAL - else if (a < b) Comparison.LESS_THAN - else Comparison.GREATER - }) - } - }, - BYTE("byte") { - override fun parse(element: JsonElement): NbtMatcher? { - return parseGenericNumber(element, - { it.asByte }, - { (it as? NbtByte)?.byteValue() }, - { a, b -> - if (a == b) Comparison.EQUAL - else if (a < b) Comparison.LESS_THAN - else Comparison.GREATER - }) - } - }, - ; - - abstract fun parse(element: JsonElement): NbtMatcher? - } - - enum class Comparison { - LESS_THAN, EQUAL, GREATER - } - - inline fun <T : Any> parseGenericNumber( - jsonElement: JsonElement, - primitiveExtractor: (JsonPrimitive) -> T?, - crossinline nbtExtractor: (NbtElement) -> T?, - crossinline compare: (T, T) -> Comparison - ): NbtMatcher? { - if (jsonElement is JsonPrimitive) { - val expected = primitiveExtractor(jsonElement) ?: return null - return NbtMatcher { - val actual = nbtExtractor(it) ?: return@NbtMatcher false - compare(actual, expected) == Comparison.EQUAL - } - } - if (jsonElement is JsonObject) { - val minElement = jsonElement.getAsJsonPrimitive("min") - val min = if (minElement != null) primitiveExtractor(minElement) ?: return null else null - val minExclusive = jsonElement.get("minExclusive")?.asBoolean ?: false - val maxElement = jsonElement.getAsJsonPrimitive("max") - val max = if (maxElement != null) primitiveExtractor(maxElement) ?: return null else null - val maxExclusive = jsonElement.get("maxExclusive")?.asBoolean ?: true - if (min == null && max == null) return null - return NbtMatcher { - val actual = nbtExtractor(it) ?: return@NbtMatcher false - if (max != null) { - val comp = compare(actual, max) - if (comp == Comparison.GREATER) return@NbtMatcher false - if (comp == Comparison.EQUAL && maxExclusive) return@NbtMatcher false - } - if (min != null) { - val comp = compare(actual, min) - if (comp == Comparison.LESS_THAN) return@NbtMatcher false - if (comp == Comparison.EQUAL && minExclusive) return@NbtMatcher false - } - return@NbtMatcher true - } - } - return null - - } - } - - class MatchNumberExact(val number: Long) : NbtMatcher { - override fun matches(nbt: NbtElement): Boolean { - return when (nbt) { - is NbtByte -> nbt.byteValue().toLong() == number - is NbtInt -> nbt.intValue().toLong() == number - is NbtShort -> nbt.shortValue().toLong() == number - is NbtLong -> nbt.longValue().toLong() == number - else -> false - } - } - - } - - class MatchStringExact(val string: String) : NbtMatcher { - override fun matches(nbt: NbtElement): Boolean { - return nbt is NbtString && nbt.asString() == string - } - - override fun toString(): String { - return "MatchNbtStringExactly($string)" - } - } - - class MatchString(val string: StringMatcher) : NbtMatcher { - override fun matches(nbt: NbtElement): Boolean { - return nbt is NbtString && string.matches(nbt.asString()) - } - - override fun toString(): String { - return "MatchNbtString($string)" - } - } -} - -data class ExtraAttributesPredicate( - val path: NbtPrism, - val matcher: NbtMatcher, -) : FirmamentModelPredicate { - - object Parser : FirmamentModelPredicateParser { - override fun parse(jsonElement: JsonElement): FirmamentModelPredicate? { - if (jsonElement !is JsonObject) return null - val path = jsonElement.get("path") ?: return null - val pathSegments = if (path is JsonArray) { - path.map { (it as JsonPrimitive).asString } - } else if (path is JsonPrimitive && path.isString) { - path.asString.split(".") - } else return null - val matcher = NbtMatcher.Parser.parse(jsonElement.get("match") ?: jsonElement) - ?: return null - return ExtraAttributesPredicate(NbtPrism(pathSegments), matcher) - } - } - - override fun test(stack: ItemStack): Boolean { - return path.access(stack.extraAttributes) - .any { matcher.matches(it) } - } -} - -class NbtPrism(val path: List<String>) { - override fun toString(): String { - return "Prism($path)" - } - fun access(root: NbtElement): Collection<NbtElement> { - var rootSet = mutableListOf(root) - var switch = mutableListOf<NbtElement>() - for (pathSegment in path) { - if (pathSegment == ".") continue - for (element in rootSet) { - if (element is NbtList) { - if (pathSegment == "*") - switch.addAll(element) - val index = pathSegment.toIntOrNull() ?: continue - if (index !in element.indices) continue - switch.add(element[index]) - } - if (element is NbtCompound) { - if (pathSegment == "*") - element.keys.mapTo(switch) { element.get(it)!! } - switch.add(element.get(pathSegment) ?: continue) - } - } - val temp = switch - switch = rootSet - rootSet = temp - switch.clear() - } - return rootSet - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicate.kt deleted file mode 100644 index d11fec0..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicate.kt +++ /dev/null @@ -1,8 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import net.minecraft.item.ItemStack - -interface FirmamentModelPredicate { - fun test(stack: ItemStack): Boolean -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicateParser.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicateParser.kt deleted file mode 100644 index 3ed0c67..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicateParser.kt +++ /dev/null @@ -1,8 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonElement - -interface FirmamentModelPredicateParser { - fun parse(jsonElement: JsonElement): FirmamentModelPredicate? -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/ItemPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/ItemPredicate.kt deleted file mode 100644 index 4302b53..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/ItemPredicate.kt +++ /dev/null @@ -1,32 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonElement -import com.google.gson.JsonPrimitive -import kotlin.jvm.optionals.getOrNull -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.registry.RegistryKey -import net.minecraft.registry.RegistryKeys -import net.minecraft.util.Identifier -import moe.nea.firmament.util.MC - -class ItemPredicate( - val item: Item -) : FirmamentModelPredicate { - override fun test(stack: ItemStack): Boolean { - return stack.item == item - } - - object Parser : FirmamentModelPredicateParser { - override fun parse(jsonElement: JsonElement): ItemPredicate? { - if (jsonElement is JsonPrimitive && jsonElement.isString) { - val itemKey = RegistryKey.of(RegistryKeys.ITEM, - Identifier.tryParse(jsonElement.asString) - ?: return null) - return ItemPredicate(MC.defaultItems.getOptional(itemKey).getOrNull()?.value() ?: return null) - } - return null - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/JsonUnbakedModelFirmExtra.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/JsonUnbakedModelFirmExtra.kt deleted file mode 100644 index ab9e27d..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/JsonUnbakedModelFirmExtra.kt +++ /dev/null @@ -1,10 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import net.minecraft.util.Identifier - -interface JsonUnbakedModelFirmExtra { - - fun setHeadModel_firmament(identifier: Identifier?) - fun getHeadModel_firmament(): Identifier? -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt deleted file mode 100644 index 13e3974..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt +++ /dev/null @@ -1,19 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonElement -import net.minecraft.item.ItemStack -import moe.nea.firmament.util.item.loreAccordingToNbt - -class LorePredicate(val matcher: StringMatcher) : FirmamentModelPredicate { - object Parser : FirmamentModelPredicateParser { - override fun parse(jsonElement: JsonElement): FirmamentModelPredicate { - return LorePredicate(StringMatcher.parse(jsonElement)) - } - } - - override fun test(stack: ItemStack): Boolean { - val lore = stack.loreAccordingToNbt - return lore.any { matcher.matches(it) } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideData.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideData.kt deleted file mode 100644 index 1585bd7..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideData.kt +++ /dev/null @@ -1,7 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -interface ModelOverrideData { - fun getFirmamentOverrides(): Array<FirmamentModelPredicate>? - fun setFirmamentOverrides(overrides: Array<FirmamentModelPredicate>?) -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideFilterSet.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideFilterSet.kt deleted file mode 100644 index 4ef8d06..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideFilterSet.kt +++ /dev/null @@ -1,19 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonElement -import moe.nea.firmament.util.filter.IteratorFilterSet - -class ModelOverrideFilterSet(original: java.util.Set<Map.Entry<String, JsonElement>>) : - IteratorFilterSet<Map.Entry<String, JsonElement>>(original) { - companion object { - @JvmStatic - fun createFilterSet(set: java.util.Set<*>): java.util.Set<*> { - return ModelOverrideFilterSet(set as java.util.Set<Map.Entry<String, JsonElement>>) as java.util.Set<*> - } - } - - override fun shouldKeepElement(element: Map.Entry<String, JsonElement>): Boolean { - return !element.key.startsWith("firmament:") - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/NotPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/NotPredicate.kt deleted file mode 100644 index ecd67c3..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/NotPredicate.kt +++ /dev/null @@ -1,18 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import net.minecraft.item.ItemStack - -class NotPredicate(val children: Array<FirmamentModelPredicate>) : FirmamentModelPredicate { - override fun test(stack: ItemStack): Boolean { - return children.none { it.test(stack) } - } - - object Parser : FirmamentModelPredicateParser { - override fun parse(jsonElement: JsonElement): FirmamentModelPredicate { - return NotPredicate(CustomModelOverrideParser.parsePredicates(jsonElement as JsonObject).toTypedArray()) - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/NumberMatcher.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/NumberMatcher.kt deleted file mode 100644 index 7e6665f..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/NumberMatcher.kt +++ /dev/null @@ -1,125 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonElement -import com.google.gson.JsonPrimitive -import moe.nea.firmament.util.useMatch - -abstract class NumberMatcher { - abstract fun test(number: Number): Boolean - - - companion object { - fun parse(jsonElement: JsonElement): NumberMatcher? { - if (jsonElement is JsonPrimitive) { - if (jsonElement.isString) { - val string = jsonElement.asString - return parseRange(string) ?: parseOperator(string) - } - if (jsonElement.isNumber) { - val number = jsonElement.asNumber - val hasDecimals = (number.toString().contains(".")) - return MatchNumberExact(if (hasDecimals) number.toLong() else number.toDouble()) - } - } - return null - } - - private val intervalSpec = - "(?<beginningOpen>[\\[\\(])(?<beginning>[0-9.]+)?,(?<ending>[0-9.]+)?(?<endingOpen>[\\]\\)])" - .toPattern() - - fun parseRange(string: String): RangeMatcher? { - intervalSpec.useMatch<Nothing>(string) { - // Open in the set-theory sense, meaning does not include its end. - val beginningOpen = group("beginningOpen") == "(" - val endingOpen = group("endingOpen") == ")" - val beginning = group("beginning")?.toDouble() - val ending = group("ending")?.toDouble() - return RangeMatcher(beginning, !beginningOpen, ending, !endingOpen) - } - return null - } - - enum class Operator(val operator: String) { - LESS("<") { - override fun matches(comparisonResult: Int): Boolean { - return comparisonResult < 0 - } - }, - LESS_EQUALS("<=") { - override fun matches(comparisonResult: Int): Boolean { - return comparisonResult <= 0 - } - }, - GREATER(">") { - override fun matches(comparisonResult: Int): Boolean { - return comparisonResult > 0 - } - }, - GREATER_EQUALS(">=") { - override fun matches(comparisonResult: Int): Boolean { - return comparisonResult >= 0 - } - }, - ; - - abstract fun matches(comparisonResult: Int): Boolean - } - - private val operatorPattern = "(?<operator>${Operator.entries.joinToString("|") {it.operator}})(?<value>[0-9.]+)".toPattern() - - fun parseOperator(string: String): OperatorMatcher? { - operatorPattern.useMatch<Nothing>(string) { - val operatorName = group("operator") - val operator = Operator.entries.find { it.operator == operatorName }!! - val value = group("value").toDouble() - return OperatorMatcher(operator, value) - } - return null - } - - data class OperatorMatcher(val operator: Operator, val value: Double) : NumberMatcher() { - override fun test(number: Number): Boolean { - return operator.matches(number.toDouble().compareTo(value)) - } - } - - - data class MatchNumberExact(val number: Number) : NumberMatcher() { - override fun test(number: Number): Boolean { - return when (this.number) { - is Double -> number.toDouble() == this.number.toDouble() - else -> number.toLong() == this.number.toLong() - } - } - } - - data class RangeMatcher( - val beginning: Double?, - val beginningInclusive: Boolean, - val ending: Double?, - val endingInclusive: Boolean, - ) : NumberMatcher() { - override fun test(number: Number): Boolean { - val value = number.toDouble() - if (beginning != null) { - if (beginningInclusive) { - if (value < beginning) return false - } else { - if (value <= beginning) return false - } - } - if (ending != null) { - if (endingInclusive) { - if (value > ending) return false - } else { - if (value >= ending) return false - } - } - return true - } - } - } - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/OrPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/OrPredicate.kt deleted file mode 100644 index 32f556b..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/OrPredicate.kt +++ /dev/null @@ -1,26 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonArray -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import net.minecraft.item.ItemStack - -class OrPredicate(val children: Array<FirmamentModelPredicate>) : FirmamentModelPredicate { - override fun test(stack: ItemStack): Boolean { - return children.any { it.test(stack) } - } - - object Parser : FirmamentModelPredicateParser { - override fun parse(jsonElement: JsonElement): FirmamentModelPredicate { - val children = - (jsonElement as JsonArray) - .flatMap { - CustomModelOverrideParser.parsePredicates(it as JsonObject) - } - .toTypedArray() - return OrPredicate(children) - } - - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/PetPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/PetPredicate.kt deleted file mode 100644 index 5e5d750..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/PetPredicate.kt +++ /dev/null @@ -1,66 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import net.minecraft.item.ItemStack -import moe.nea.firmament.repo.ExpLadders -import moe.nea.firmament.util.petData - -class PetPredicate( - val petId: StringMatcher?, - val tier: RarityMatcher?, - val exp: NumberMatcher?, - val candyUsed: NumberMatcher?, - val level: NumberMatcher?, -) : FirmamentModelPredicate { - - override fun test(stack: ItemStack): Boolean { - val petData = stack.petData ?: return false - if (petId != null) { - if (!petId.matches(petData.type)) return false - } - if (exp != null) { - if (!exp.test(petData.exp)) return false - } - if (candyUsed != null) { - if (!candyUsed.test(petData.candyUsed)) return false - } - if (tier != null) { - if (!tier.match(petData.tier)) return false - } - val levelData by lazy(LazyThreadSafetyMode.NONE) { - ExpLadders.getExpLadder(petData.type, petData.tier) - .getPetLevel(petData.exp) - } - if (level != null) { - if (!level.test(levelData.currentLevel)) return false - } - return true - } - - object Parser : FirmamentModelPredicateParser { - override fun parse(jsonElement: JsonElement): FirmamentModelPredicate? { - if (jsonElement.isJsonPrimitive) { - return PetPredicate(StringMatcher.Equals(jsonElement.asString, false), null, null, null, null) - } - if (jsonElement !is JsonObject) return null - val idMatcher = jsonElement["id"]?.let(StringMatcher::parse) - val expMatcher = jsonElement["exp"]?.let(NumberMatcher::parse) - val levelMatcher = jsonElement["level"]?.let(NumberMatcher::parse) - val candyMatcher = jsonElement["candyUsed"]?.let(NumberMatcher::parse) - val tierMatcher = jsonElement["tier"]?.let(RarityMatcher::parse) - return PetPredicate( - idMatcher, - tierMatcher, - expMatcher, - candyMatcher, - levelMatcher, - ) - } - } - - override fun toString(): String { - return super.toString() - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/RarityMatcher.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/RarityMatcher.kt deleted file mode 100644 index 634a171..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/RarityMatcher.kt +++ /dev/null @@ -1,69 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonElement -import io.github.moulberry.repo.data.Rarity -import moe.nea.firmament.util.useMatch - -abstract class RarityMatcher { - abstract fun match(rarity: Rarity): Boolean - - companion object { - fun parse(jsonElement: JsonElement): RarityMatcher { - val string = jsonElement.asString - val range = parseRange(string) - if (range != null) return range - return Exact(Rarity.valueOf(string)) - } - - private val allRarities = Rarity.entries.joinToString("|", "(?:", ")") - private val intervalSpec = - "(?<beginningOpen>[\\[\\(])(?<beginning>$allRarities)?,(?<ending>$allRarities)?(?<endingOpen>[\\]\\)])" - .toPattern() - - fun parseRange(string: String): RangeMatcher? { - intervalSpec.useMatch<Nothing>(string) { - // Open in the set-theory sense, meaning does not include its end. - val beginningOpen = group("beginningOpen") == "(" - val endingOpen = group("endingOpen") == ")" - val beginning = group("beginning")?.let(Rarity::valueOf) - val ending = group("ending")?.let(Rarity::valueOf) - return RangeMatcher(beginning, !beginningOpen, ending, !endingOpen) - } - return null - } - - } - - data class Exact(val expected: Rarity) : RarityMatcher() { - override fun match(rarity: Rarity): Boolean { - return rarity == expected - } - } - - data class RangeMatcher( - val beginning: Rarity?, - val beginningInclusive: Boolean, - val ending: Rarity?, - val endingInclusive: Boolean, - ) : RarityMatcher() { - override fun match(rarity: Rarity): Boolean { - if (beginning != null) { - if (beginningInclusive) { - if (rarity < beginning) return false - } else { - if (rarity <= beginning) return false - } - } - if (ending != null) { - if (endingInclusive) { - if (rarity > ending) return false - } else { - if (rarity >= ending) return false - } - } - return true - } - } - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt deleted file mode 100644 index 5eb86ac..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt +++ /dev/null @@ -1,159 +0,0 @@ - -package moe.nea.firmament.features.texturepack - -import com.google.gson.JsonArray -import com.google.gson.JsonElement -import com.google.gson.JsonNull -import com.google.gson.JsonObject -import com.google.gson.JsonPrimitive -import com.google.gson.internal.LazilyParsedNumber -import java.util.function.Predicate -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import net.minecraft.nbt.NbtString -import net.minecraft.text.Text -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.removeColorCodes - -@Serializable(with = StringMatcher.Serializer::class) -interface StringMatcher { - fun matches(string: String): Boolean - fun matches(text: Text): Boolean { - return matches(text.string) - } - - fun matches(nbt: NbtString): Boolean { - val string = nbt.asString() - val jsonStart = string.indexOf('{') - val stringStart = string.indexOf('"') - val isString = stringStart >= 0 && string.subSequence(0, stringStart).isBlank() - val isJson = jsonStart >= 0 && string.subSequence(0, jsonStart).isBlank() - if (isString || isJson) - return matches(Text.Serialization.fromJson(string, MC.defaultRegistries) ?: return false) - return matches(string) - } - - class Equals(input: String, val stripColorCodes: Boolean) : StringMatcher { - private val expected = if (stripColorCodes) input.removeColorCodes() else input - override fun matches(string: String): Boolean { - return expected == (if (stripColorCodes) string.removeColorCodes() else string) - } - - override fun toString(): String { - return "Equals($expected, stripColorCodes = $stripColorCodes)" - } - } - - class Pattern(val patternWithColorCodes: String, val stripColorCodes: Boolean) : StringMatcher { - private val regex: Predicate<String> = patternWithColorCodes.toPattern().asMatchPredicate() - override fun matches(string: String): Boolean { - return regex.test(if (stripColorCodes) string.removeColorCodes() else string) - } - - override fun toString(): String { - return "Pattern($patternWithColorCodes, stripColorCodes = $stripColorCodes)" - } - } - - object Serializer : KSerializer<StringMatcher> { - val delegateSerializer = kotlinx.serialization.json.JsonElement.serializer() - override val descriptor: SerialDescriptor - get() = SerialDescriptor("StringMatcher", delegateSerializer.descriptor) - - override fun deserialize(decoder: Decoder): StringMatcher { - val delegate = decoder.decodeSerializableValue(delegateSerializer) - val gsonDelegate = delegate.intoGson() - return parse(gsonDelegate) - } - - override fun serialize(encoder: Encoder, value: StringMatcher) { - encoder.encodeSerializableValue(delegateSerializer, Companion.serialize(value).intoKotlinJson()) - } - - } - - companion object { - fun serialize(stringMatcher: StringMatcher): JsonElement { - TODO("Cannot serialize string matchers rn") - } - - fun parse(jsonElement: JsonElement): StringMatcher { - if (jsonElement is JsonPrimitive) { - return Equals(jsonElement.asString, true) - } - if (jsonElement is JsonObject) { - val regex = jsonElement["regex"] as JsonPrimitive? - val text = jsonElement["equals"] as JsonPrimitive? - val shouldStripColor = when (val color = (jsonElement["color"] as JsonPrimitive?)?.asString) { - "preserve" -> false - "strip", null -> true - else -> error("Unknown color preservation mode: $color") - } - if ((regex == null) == (text == null)) error("Could not parse $jsonElement as string matcher") - if (regex != null) - return Pattern(regex.asString, shouldStripColor) - if (text != null) - return Equals(text.asString, shouldStripColor) - } - error("Could not parse $jsonElement as a string matcher") - } - } -} - -fun JsonElement.intoKotlinJson(): kotlinx.serialization.json.JsonElement { - when (this) { - is JsonNull -> return kotlinx.serialization.json.JsonNull - is JsonObject -> { - return kotlinx.serialization.json.JsonObject(this.entrySet() - .associate { it.key to it.value.intoKotlinJson() }) - } - - is JsonArray -> { - return kotlinx.serialization.json.JsonArray(this.map { it.intoKotlinJson() }) - } - - is JsonPrimitive -> { - if (this.isString) - return kotlinx.serialization.json.JsonPrimitive(this.asString) - if (this.isBoolean) - return kotlinx.serialization.json.JsonPrimitive(this.asBoolean) - return kotlinx.serialization.json.JsonPrimitive(this.asNumber) - } - - else -> error("Unknown json variant $this") - } -} - -fun kotlinx.serialization.json.JsonElement.intoGson(): JsonElement { - when (this) { - is kotlinx.serialization.json.JsonNull -> return JsonNull.INSTANCE - is kotlinx.serialization.json.JsonPrimitive -> { - if (this.isString) - return JsonPrimitive(this.content) - if (this.content == "true") - return JsonPrimitive(true) - if (this.content == "false") - return JsonPrimitive(false) - return JsonPrimitive(LazilyParsedNumber(this.content)) - } - - is kotlinx.serialization.json.JsonObject -> { - val obj = JsonObject() - for ((k, v) in this) { - obj.add(k, v.intoGson()) - } - return obj - } - - is kotlinx.serialization.json.JsonArray -> { - val arr = JsonArray() - for (v in this) { - arr.add(v.intoGson()) - } - return arr - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt b/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt deleted file mode 100644 index 8a8291a..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt +++ /dev/null @@ -1,131 +0,0 @@ - - -package moe.nea.firmament.features.world - -import io.github.moulberry.repo.data.Coordinate -import kotlinx.serialization.Serializable -import kotlinx.serialization.serializer -import net.minecraft.text.Text -import net.minecraft.util.math.Vec3d -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.ProcessChatEvent -import moe.nea.firmament.events.SkyblockServerUpdateEvent -import moe.nea.firmament.events.WorldRenderLastEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.repo.RepoManager -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.SBData -import moe.nea.firmament.util.SkyBlockIsland -import moe.nea.firmament.util.blockPos -import moe.nea.firmament.util.data.ProfileSpecificDataHolder -import moe.nea.firmament.util.render.RenderInWorldContext -import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorld -import moe.nea.firmament.util.unformattedString - - -object FairySouls : FirmamentFeature { - - - @Serializable - data class Data( - val foundSouls: MutableMap<SkyBlockIsland, MutableSet<Int>> = mutableMapOf() - ) - - override val config: ManagedConfig - get() = TConfig - - object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "found-fairysouls", ::Data) - - - object TConfig : ManagedConfig("fairy-souls") { - val displaySouls by toggle("show") { false } - val resetSouls by button("reset") { - DConfig.data?.foundSouls?.clear() != null - updateMissingSouls() - } - } - - - override val identifier: String get() = "fairy-souls" - - val playerReach = 5 - val playerReachSquared = playerReach * playerReach - - var currentLocationName: SkyBlockIsland? = null - var currentLocationSouls: List<Coordinate> = emptyList() - var currentMissingSouls: List<Coordinate> = emptyList() - - fun updateMissingSouls() { - currentMissingSouls = emptyList() - val c = DConfig.data ?: return - val fi = c.foundSouls[currentLocationName] ?: setOf() - val cms = currentLocationSouls.toMutableList() - fi.asSequence().sortedDescending().filter { it in cms.indices }.forEach { cms.removeAt(it) } - currentMissingSouls = cms - } - - fun updateWorldSouls() { - currentLocationSouls = emptyList() - val loc = currentLocationName ?: return - currentLocationSouls = RepoManager.neuRepo.constants.fairySouls.soulLocations[loc.locrawMode] ?: return - } - - fun findNearestClickableSoul(): Coordinate? { - val player = MC.player ?: return null - val pos = player.pos - val location = SBData.skyblockLocation ?: return null - val soulLocations: List<Coordinate> = - RepoManager.neuRepo.constants.fairySouls.soulLocations[location.locrawMode] ?: return null - return soulLocations - .map { it to it.blockPos.getSquaredDistance(pos) } - .filter { it.second < playerReachSquared } - .minByOrNull { it.second } - ?.first - } - - private fun markNearestSoul() { - val nearestSoul = findNearestClickableSoul() ?: return - val c = DConfig.data ?: return - val loc = currentLocationName ?: return - val idx = currentLocationSouls.indexOf(nearestSoul) - c.foundSouls.computeIfAbsent(loc) { mutableSetOf() }.add(idx) - DConfig.markDirty() - updateMissingSouls() - } - - @Subscribe - fun onWorldRender(it: WorldRenderLastEvent) { - if (!TConfig.displaySouls) return - renderInWorld(it) { - color(1F, 1F, 0F, 0.8F) - currentMissingSouls.forEach { - block(it.blockPos) - } - color(1f, 0f, 1f, 1f) - currentLocationSouls.forEach { - wireframeCube(it.blockPos) - } - } - } - - @Subscribe - fun onProcessChat(it: ProcessChatEvent) { - when (it.text.unformattedString) { - "You have already found that Fairy Soul!" -> { - markNearestSoul() - } - - "SOUL! You found a Fairy Soul!" -> { - markNearestSoul() - } - } - } - - @Subscribe - fun onLocationChange(it: SkyblockServerUpdateEvent) { - currentLocationName = it.newLocraw?.skyblockLocation - updateWorldSouls() - updateMissingSouls() - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/world/NPCWaypoints.kt b/src/main/kotlin/moe/nea/firmament/features/world/NPCWaypoints.kt deleted file mode 100644 index 592b8fa..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/world/NPCWaypoints.kt +++ /dev/null @@ -1,40 +0,0 @@ -package moe.nea.firmament.features.world - -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.commands.thenExecute -import moe.nea.firmament.events.CommandEvent -import moe.nea.firmament.events.ReloadRegistrationEvent -import moe.nea.firmament.util.MoulConfigUtils -import moe.nea.firmament.util.ScreenUtil - -object NPCWaypoints { - - var allNpcWaypoints = listOf<NavigableWaypoint>() - - @Subscribe - fun onRepoReloadRegistration(event: ReloadRegistrationEvent) { - event.repo.registerReloadListener { - allNpcWaypoints = it.items.items.values - .asSequence() - .filter { !it.island.isNullOrBlank() } - .map { - NavigableWaypoint.NPCWaypoint(it) - } - .toList() - } - } - - @Subscribe - fun onOpenGui(event: CommandEvent.SubCommand) { - event.subcommand("npcs") { - thenExecute { - ScreenUtil.setScreenLater(MoulConfigUtils.loadScreen( - "npc_waypoints", - NpcWaypointGui(allNpcWaypoints), - null)) - } - } - } - - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/world/NavigableWaypoint.kt b/src/main/kotlin/moe/nea/firmament/features/world/NavigableWaypoint.kt deleted file mode 100644 index 28a517f..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/world/NavigableWaypoint.kt +++ /dev/null @@ -1,22 +0,0 @@ -package moe.nea.firmament.features.world - -import io.github.moulberry.repo.data.NEUItem -import net.minecraft.util.math.BlockPos -import moe.nea.firmament.util.SkyBlockIsland - -abstract class NavigableWaypoint { - abstract val name: String - abstract val position: BlockPos - abstract val island: SkyBlockIsland - - data class NPCWaypoint( - val item: NEUItem, - ) : NavigableWaypoint() { - override val name: String - get() = item.displayName - override val position: BlockPos - get() = BlockPos(item.x, item.y, item.z) - override val island: SkyBlockIsland - get() = SkyBlockIsland.forMode(item.island) - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/world/NavigationHelper.kt b/src/main/kotlin/moe/nea/firmament/features/world/NavigationHelper.kt deleted file mode 100644 index acdfb86..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/world/NavigationHelper.kt +++ /dev/null @@ -1,121 +0,0 @@ -package moe.nea.firmament.features.world - -import io.github.moulberry.repo.constants.Islands -import net.minecraft.text.Text -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Position -import net.minecraft.util.math.Vec3i -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.SkyblockServerUpdateEvent -import moe.nea.firmament.events.TickEvent -import moe.nea.firmament.events.WorldRenderLastEvent -import moe.nea.firmament.repo.RepoManager -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.SBData -import moe.nea.firmament.util.SkyBlockIsland -import moe.nea.firmament.util.WarpUtil -import moe.nea.firmament.util.render.RenderInWorldContext - -object NavigationHelper { - var targetWaypoint: NavigableWaypoint? = null - set(value) { - field = value - recalculateRoute() - } - - var nextTeleporter: Islands.Teleporter? = null - private set - - val Islands.Teleporter.toIsland get() = SkyBlockIsland.forMode(this.getTo()) - val Islands.Teleporter.fromIsland get() = SkyBlockIsland.forMode(this.getFrom()) - val Islands.Teleporter.blockPos get() = BlockPos(x.toInt(), y.toInt(), z.toInt()) - - @Subscribe - fun onWorldSwitch(event: SkyblockServerUpdateEvent) { - recalculateRoute() - } - - fun recalculateRoute() { - val tp = targetWaypoint - val currentIsland = SBData.skyblockLocation - if (tp == null || currentIsland == null) { - nextTeleporter = null - return - } - val route = findRoute(currentIsland, tp.island, mutableSetOf()) - nextTeleporter = route?.get(0) - } - - private fun findRoute( - fromIsland: SkyBlockIsland, - targetIsland: SkyBlockIsland, - visitedIslands: MutableSet<SkyBlockIsland> - ): MutableList<Islands.Teleporter>? { - var shortestChain: MutableList<Islands.Teleporter>? = null - for (it in RepoManager.neuRepo.constants.islands.teleporters) { - if (it.toIsland in visitedIslands) continue - if (it.fromIsland != fromIsland) continue - if (it.toIsland == targetIsland) return mutableListOf(it) - visitedIslands.add(fromIsland) - val nextRoute = findRoute(it.toIsland, targetIsland, visitedIslands) ?: continue - nextRoute.add(0, it) - if (shortestChain == null || shortestChain.size > nextRoute.size) { - shortestChain = nextRoute - } - visitedIslands.remove(fromIsland) - } - return shortestChain - } - - - @Subscribe - fun onMovement(event: TickEvent) { // TODO: add a movement tick event maybe? - val tp = targetWaypoint ?: return - val p = MC.player ?: return - if (p.squaredDistanceTo(tp.position.toCenterPos()) < 5 * 5) { - targetWaypoint = null - } - } - - @Subscribe - fun drawWaypoint(event: WorldRenderLastEvent) { - val tp = targetWaypoint ?: return - val nt = nextTeleporter - RenderInWorldContext.renderInWorld(event) { - if (nt != null) { - waypoint(nt.blockPos, - Text.literal("Teleporter to " + nt.toIsland.userFriendlyName), - Text.literal("(towards " + tp.name + "§f)")) - } else if (tp.island == SBData.skyblockLocation) { - waypoint(tp.position, - Text.literal(tp.name)) - } - } - } - - fun tryWarpNear() { - val tp = targetWaypoint - if (tp == null) { - MC.sendChat(Text.literal("Could not find a waypoint to warp you to. Select one first.")) - return - } - WarpUtil.teleportToNearestWarp(tp.island, tp.position.asPositionView()) - } - -} - -fun Vec3i.asPositionView(): Position { - return object : Position { - override fun getX(): Double { - return this@asPositionView.x.toDouble() - } - - override fun getY(): Double { - return this@asPositionView.y.toDouble() - } - - override fun getZ(): Double { - return this@asPositionView.z.toDouble() - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/features/world/NpcWaypointGui.kt b/src/main/kotlin/moe/nea/firmament/features/world/NpcWaypointGui.kt deleted file mode 100644 index 6146e50..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/world/NpcWaypointGui.kt +++ /dev/null @@ -1,68 +0,0 @@ -package moe.nea.firmament.features.world - -import io.github.notenoughupdates.moulconfig.observer.ObservableList -import io.github.notenoughupdates.moulconfig.xml.Bind -import moe.nea.firmament.features.events.anniversity.AnniversaryFeatures.atOnce -import moe.nea.firmament.keybindings.SavedKeyBinding - -class NpcWaypointGui( - val allWaypoints: List<NavigableWaypoint>, -) { - - data class NavigableWaypointW(val waypoint: NavigableWaypoint) { - @Bind - fun name() = waypoint.name - - @Bind - fun isSelected() = NavigationHelper.targetWaypoint == waypoint - - @Bind - fun click() { - if (SavedKeyBinding.isShiftDown()) { - NavigationHelper.targetWaypoint = waypoint - NavigationHelper.tryWarpNear() - } else if (isSelected()) { - NavigationHelper.targetWaypoint = null - } else { - NavigationHelper.targetWaypoint = waypoint - } - } - } - - @JvmField - @field:Bind - var search: String = "" - var lastSearch: String? = null - - @Bind("results") - fun results(): ObservableList<NavigableWaypointW> { - return results - } - - @Bind - fun tick() { - if (search != lastSearch) { - updateSearch() - lastSearch = search - } - } - - val results: ObservableList<NavigableWaypointW> = ObservableList(mutableListOf()) - - fun updateSearch() { - val split = search.split(" +".toRegex()) - results.atOnce { - results.clear() - allWaypoints.filter { waypoint -> - if (search.isBlank()) { - true - } else { - split.all { waypoint.name.contains(it, ignoreCase = true) } - } - }.mapTo(results) { - NavigableWaypointW(it) - } - } - } - -} diff --git a/src/main/kotlin/moe/nea/firmament/features/world/Waypoints.kt b/src/main/kotlin/moe/nea/firmament/features/world/Waypoints.kt deleted file mode 100644 index 91a06da..0000000 --- a/src/main/kotlin/moe/nea/firmament/features/world/Waypoints.kt +++ /dev/null @@ -1,297 +0,0 @@ - - -package moe.nea.firmament.features.world - -import com.mojang.brigadier.arguments.IntegerArgumentType -import me.shedaniel.math.Color -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource -import kotlinx.serialization.Serializable -import kotlin.collections.component1 -import kotlin.collections.component2 -import kotlin.collections.set -import kotlin.time.Duration.Companion.hours -import kotlin.time.Duration.Companion.seconds -import net.minecraft.command.argument.BlockPosArgumentType -import net.minecraft.server.command.ServerCommandSource -import net.minecraft.text.Text -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3d -import moe.nea.firmament.Firmament -import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.commands.get -import moe.nea.firmament.commands.thenArgument -import moe.nea.firmament.commands.thenExecute -import moe.nea.firmament.commands.thenLiteral -import moe.nea.firmament.events.CommandEvent -import moe.nea.firmament.events.ProcessChatEvent -import moe.nea.firmament.events.TickEvent -import moe.nea.firmament.events.WorldReadyEvent -import moe.nea.firmament.events.WorldRenderLastEvent -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig -import moe.nea.firmament.util.ClipboardUtils -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.TimeMark -import moe.nea.firmament.util.render.RenderInWorldContext - -object Waypoints : FirmamentFeature { - override val identifier: String - get() = "waypoints" - - object TConfig : ManagedConfig(identifier) { - val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds } - val showIndex by toggle("show-index") { true } - val skipToNearest by toggle("skip-to-nearest") { false } - // TODO: look ahead size - } - - data class TemporaryWaypoint( - val pos: BlockPos, - val postedAt: TimeMark, - ) - - override val config get() = TConfig - - val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>() - val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern() - - val waypoints = mutableListOf<BlockPos>() - var ordered = false - var orderedIndex = 0 - - @Serializable - data class ColeWeightWaypoint( - val x: Int, - val y: Int, - val z: Int, - val r: Int = 0, - val g: Int = 0, - val b: Int = 0, - ) - - @Subscribe - fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) { - if (waypoints.isEmpty()) return - RenderInWorldContext.renderInWorld(event) { - if (!ordered) { - waypoints.withIndex().forEach { - color(0f, 0.3f, 0.7f, 0.5f) - block(it.value) - color(1f, 1f, 1f, 1f) - if (TConfig.showIndex) - withFacingThePlayer(it.value.toCenterPos()) { - text(Text.literal(it.index.toString())) - } - } - } else { - orderedIndex %= waypoints.size - val firstColor = Color.ofRGBA(0, 200, 40, 180) - color(firstColor) - tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f) - waypoints.withIndex().toList() - .wrappingWindow(orderedIndex, 3) - .zip( - listOf( - firstColor, - Color.ofRGBA(180, 200, 40, 150), - Color.ofRGBA(180, 80, 20, 140), - ) - ) - .reversed() - .forEach { (waypoint, col) -> - val (index, pos) = waypoint - color(col) - block(pos) - color(1f, 1f, 1f, 1f) - if (TConfig.showIndex) - withFacingThePlayer(pos.toCenterPos()) { - text(Text.literal(index.toString())) - } - } - } - } - } - - @Subscribe - fun onTick(event: TickEvent) { - if (waypoints.isEmpty() || !ordered) return - orderedIndex %= waypoints.size - val p = MC.player?.pos ?: return - if (TConfig.skipToNearest) { - orderedIndex = - (waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size - } else { - if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) { - orderedIndex = (orderedIndex + 1) % waypoints.size - } - } - } - - @Subscribe - fun onProcessChat(it: ProcessChatEvent) { - val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString) - if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) { - temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint( - BlockPos( - matcher.group(1).toInt(), - matcher.group(2).toInt(), - matcher.group(3).toInt(), - ), - TimeMark.now() - ) - } - } - - @Subscribe - fun onCommand(event: CommandEvent.SubCommand) { - event.subcommand("waypoint") { - thenArgument("pos", BlockPosArgumentType.blockPos()) { pos -> - thenExecute { - val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer()) - waypoints.add(position) - source.sendFeedback( - Text.stringifiedTranslatable( - "firmament.command.waypoint.added", - position.x, - position.y, - position.z - ) - ) - } - } - } - event.subcommand("waypoints") { - thenLiteral("clear") { - thenExecute { - waypoints.clear() - source.sendFeedback(Text.translatable("firmament.command.waypoint.clear")) - } - } - thenLiteral("toggleordered") { - thenExecute { - ordered = !ordered - if (ordered) { - val p = MC.player?.pos ?: Vec3d.ZERO - orderedIndex = - waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0 - } - source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered")) - } - } - thenLiteral("skip") { - thenExecute { - if (ordered && waypoints.isNotEmpty()) { - orderedIndex = (orderedIndex + 1) % waypoints.size - source.sendFeedback(Text.translatable("firmament.command.waypoint.skip")) - } else { - source.sendError(Text.translatable("firmament.command.waypoint.skip.error")) - } - } - } - thenLiteral("remove") { - thenArgument("index", IntegerArgumentType.integer(0)) { indexArg -> - thenExecute { - val index = get(indexArg) - if (index in waypoints.indices) { - waypoints.removeAt(index) - source.sendFeedback(Text.stringifiedTranslatable( - "firmament.command.waypoint.remove", - index)) - } else { - source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error")) - } - } - } - } - thenLiteral("import") { - thenExecute { - val contents = ClipboardUtils.getTextContents() - val data = try { - Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents) - } catch (ex: Exception) { - Firmament.logger.error("Could not load waypoints from clipboard", ex) - source.sendError(Text.translatable("firmament.command.waypoint.import.error")) - return@thenExecute - } - waypoints.clear() - data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) } - source.sendFeedback( - Text.stringifiedTranslatable( - "firmament.command.waypoint.import", - data.size - ) - ) - } - } - } - } - - @Subscribe - fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) { - temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration } - if (temporaryPlayerWaypointList.isEmpty()) return - RenderInWorldContext.renderInWorld(event) { - color(1f, 1f, 0f, 1f) - temporaryPlayerWaypointList.forEach { (player, waypoint) -> - block(waypoint.pos) - } - color(1f, 1f, 1f, 1f) - temporaryPlayerWaypointList.forEach { (player, waypoint) -> - val skin = - MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player } - ?.skinTextures - ?.texture - withFacingThePlayer(waypoint.pos.toCenterPos()) { - waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player)) - if (skin != null) { - matrixStack.translate(0F, -20F, 0F) - // Head front - texture( - skin, 16, 16, - 1 / 8f, 1 / 8f, - 2 / 8f, 2 / 8f, - ) - // Head overlay - texture( - skin, 16, 16, - 5 / 8f, 1 / 8f, - 6 / 8f, 2 / 8f, - ) - } - } - } - } - } - - @Subscribe - fun onWorldReady(event: WorldReadyEvent) { - temporaryPlayerWaypointList.clear() - } -} - -fun <E> List<E>.wrappingWindow(startIndex: Int, windowSize: Int): List<E> { - val result = ArrayList<E>(windowSize) - if (startIndex + windowSize < size) { - result.addAll(subList(startIndex, startIndex + windowSize)) - } else { - result.addAll(subList(startIndex, size)) - result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex))) - } - return result -} - - -fun FabricClientCommandSource.asFakeServer(): ServerCommandSource { - val source = this - return ServerCommandSource( - source.player, - source.position, - source.rotation, - null, - 0, - "FakeServerCommandSource", - Text.literal("FakeServerCommandSource"), - null, - source.player - ) -} |