aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea/firmament/features
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-08-28 19:04:24 +0200
committerLinnea Gräf <nea@nea.moe>2024-08-28 19:04:24 +0200
commitd2f240ff0ca0d27f417f837e706c781a98c31311 (patch)
tree0db7aff6cc14deaf36eed83889d59fd6b3a6f599 /src/main/kotlin/moe/nea/firmament/features
parenta6906308163aa3b2d18fa1dc1aa71ac9bbcc83ab (diff)
downloadfirmament-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')
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt120
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt23
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/chat/AutoCompletions.kt57
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/chat/ChatLinks.kt161
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/chat/QuickCommands.kt100
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/debug/DebugLogger.kt13
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/debug/DebugView.kt38
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/debug/DeveloperFeatures.kt55
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/debug/MinorTrolling.kt27
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/debug/PowerUserTools.kt193
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt131
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/diana/DianaWaypoints.kt35
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/diana/NearbyBurrowsSolver.kt144
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/events/anniversity/AnniversaryFeatures.kt224
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/events/carnival/CarnivalFeatures.kt17
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/events/carnival/MinesweeperHelper.kt276
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/fixes/CompatibliltyFeatures.kt51
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/fixes/Fixes.kt71
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/CraftingOverlay.kt66
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/ItemRarityCosmetics.kt85
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/PriceData.kt51
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/SaveCursorPosition.kt66
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt203
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButton.kt85
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonEditor.kt184
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtonTemplates.kt35
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/buttons/InventoryButtons.kt88
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageBackingHandle.kt53
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageData.kt21
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlay.kt154
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayCustom.kt98
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverlayScreen.kt296
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StorageOverviewScreen.kt123
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/StoragePageSlot.kt66
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/storageoverlay/VirtualInventory.kt65
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/mining/Histogram.kt81
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/mining/PickaxeAbility.kt176
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/mining/PristineProfitTracker.kt133
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/notifications/Notifications.kt7
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/AlwaysPredicate.kt17
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/AndPredicate.kt26
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/BakedModelExtra.kt9
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/BakedOverrideData.kt8
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt296
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt106
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt167
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/CustomModelOverrideParser.kt74
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt114
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt22
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/ExtraAttributesPredicate.kt268
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicate.kt8
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicateParser.kt8
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/ItemPredicate.kt32
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/JsonUnbakedModelFirmExtra.kt10
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt19
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideData.kt7
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideFilterSet.kt19
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/NotPredicate.kt18
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/NumberMatcher.kt125
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/OrPredicate.kt26
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/PetPredicate.kt66
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/RarityMatcher.kt69
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt159
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/world/FairySouls.kt131
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/world/NPCWaypoints.kt40
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/world/NavigableWaypoint.kt22
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/world/NavigationHelper.kt121
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/world/NpcWaypointGui.kt68
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/world/Waypoints.kt297
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
- )
-}