aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/repo
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/repo')
-rw-r--r--src/main/kotlin/repo/ExpLadder.kt3
-rw-r--r--src/main/kotlin/repo/ExpensiveItemCacheApi.kt8
-rw-r--r--src/main/kotlin/repo/HypixelStaticData.kt25
-rw-r--r--src/main/kotlin/repo/ItemCache.kt113
-rw-r--r--src/main/kotlin/repo/MiningRepoData.kt2
-rw-r--r--src/main/kotlin/repo/ModernOverlaysData.kt41
-rw-r--r--src/main/kotlin/repo/RepoManager.kt19
-rw-r--r--src/main/kotlin/repo/SBItemStack.kt54
-rw-r--r--src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt4
-rw-r--r--src/main/kotlin/repo/recipes/RecipeLayouter.kt7
-rw-r--r--src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt24
-rw-r--r--src/main/kotlin/repo/recipes/SBEssenceUpgradeRecipeRenderer.kt76
-rw-r--r--src/main/kotlin/repo/recipes/SBForgeRecipeRenderer.kt83
13 files changed, 407 insertions, 52 deletions
diff --git a/src/main/kotlin/repo/ExpLadder.kt b/src/main/kotlin/repo/ExpLadder.kt
index fbc9eb8..25a74de 100644
--- a/src/main/kotlin/repo/ExpLadder.kt
+++ b/src/main/kotlin/repo/ExpLadder.kt
@@ -19,7 +19,8 @@ object ExpLadders : IReloadable {
val expInCurrentLevel: Float,
var expTotal: Float,
) {
- val percentageToNextLevel: Float = expInCurrentLevel / expRequiredForNextLevel
+ val percentageToNextLevel: Float = expInCurrentLevel / expRequiredForNextLevel
+ val percentageToMaxLevel: Float = expTotal / expRequiredForMaxLevel
}
data class ExpLadder(
diff --git a/src/main/kotlin/repo/ExpensiveItemCacheApi.kt b/src/main/kotlin/repo/ExpensiveItemCacheApi.kt
new file mode 100644
index 0000000..eef95a6
--- /dev/null
+++ b/src/main/kotlin/repo/ExpensiveItemCacheApi.kt
@@ -0,0 +1,8 @@
+package moe.nea.firmament.repo
+
+/**
+ * Marker for functions that could potentially invoke DFU. Please do not call on a lot of objects at once, or try to make sure the item is cached and fall back to a more gentle function call using [SBItemStack.isWarm] and similar functions.
+ */
+@RequiresOptIn
+@Retention(AnnotationRetention.BINARY)
+annotation class ExpensiveItemCacheApi
diff --git a/src/main/kotlin/repo/HypixelStaticData.kt b/src/main/kotlin/repo/HypixelStaticData.kt
index 181aa70..b0ada77 100644
--- a/src/main/kotlin/repo/HypixelStaticData.kt
+++ b/src/main/kotlin/repo/HypixelStaticData.kt
@@ -3,21 +3,17 @@ package moe.nea.firmament.repo
import io.ktor.client.call.body
import io.ktor.client.request.get
import org.apache.logging.log4j.LogManager
-import org.lwjgl.glfw.GLFW
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlin.time.Duration.Companion.minutes
import moe.nea.firmament.Firmament
import moe.nea.firmament.apis.CollectionResponse
import moe.nea.firmament.apis.CollectionSkillData
-import moe.nea.firmament.keybindings.IKeyBinding
import moe.nea.firmament.util.SkyblockId
-import moe.nea.firmament.util.async.waitForInput
object HypixelStaticData {
private val logger = LogManager.getLogger("Firmament.HypixelStaticData")
@@ -25,7 +21,13 @@ object HypixelStaticData {
private val hypixelApiBaseUrl = "https://api.hypixel.net"
var lowestBin: Map<SkyblockId, Double> = mapOf()
private set
- var bazaarData: Map<SkyblockId, BazaarData> = mapOf()
+ var avg1dlowestBin: Map<SkyblockId, Double> = mapOf()
+ private set
+ var avg3dlowestBin: Map<SkyblockId, Double> = mapOf()
+ private set
+ var avg7dlowestBin: Map<SkyblockId, Double> = mapOf()
+ private set
+ var bazaarData: Map<SkyblockId.BazaarStock, BazaarData> = mapOf()
private set
var collectionData: Map<String, CollectionSkillData> = mapOf()
private set
@@ -56,9 +58,10 @@ object HypixelStaticData {
val products: Map<SkyblockId.BazaarStock, BazaarData> = mapOf(),
)
- fun getPriceOfItem(item: SkyblockId): Double? = bazaarData[item]?.quickStatus?.buyPrice ?: lowestBin[item]
- fun hasBazaarStock(item: SkyblockId): Boolean {
+ fun getPriceOfItem(item: SkyblockId): Double? = bazaarData[SkyblockId.BazaarStock.fromSkyBlockId(item)]?.quickStatus?.buyPrice ?: lowestBin[item]
+
+ fun hasBazaarStock(item: SkyblockId.BazaarStock): Boolean {
return item in bazaarData
}
@@ -90,6 +93,12 @@ object HypixelStaticData {
private suspend fun fetchPricesFromMoulberry() {
lowestBin = Firmament.httpClient.get("$moulberryBaseUrl/lowestbin.json")
.body<Map<SkyblockId, Double>>()
+ avg1dlowestBin = Firmament.httpClient.get("$moulberryBaseUrl/auction_averages_lbin/1day.json")
+ .body<Map<SkyblockId, Double>>()
+ avg3dlowestBin = Firmament.httpClient.get("$moulberryBaseUrl/auction_averages_lbin/3day.json")
+ .body<Map<SkyblockId, Double>>()
+ avg7dlowestBin = Firmament.httpClient.get("$moulberryBaseUrl/auction_averages_lbin/7day.json")
+ .body<Map<SkyblockId, Double>>()
}
private suspend fun fetchBazaarPrices() {
@@ -97,7 +106,7 @@ object HypixelStaticData {
if (!response.success) {
logger.warn("Retrieved unsuccessful bazaar data")
}
- bazaarData = response.products.mapKeys { it.key.toRepoId() }
+ bazaarData = response.products
}
private suspend fun updateCollectionData() {
diff --git a/src/main/kotlin/repo/ItemCache.kt b/src/main/kotlin/repo/ItemCache.kt
index 09eedac..14decd8 100644
--- a/src/main/kotlin/repo/ItemCache.kt
+++ b/src/main/kotlin/repo/ItemCache.kt
@@ -8,8 +8,15 @@ import java.text.NumberFormat
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import org.apache.logging.log4j.LogManager
-import kotlinx.coroutines.Job
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import kotlin.io.path.readText
import kotlin.jvm.optionals.getOrNull
import net.minecraft.SharedConstants
import net.minecraft.component.DataComponentTypes
@@ -22,14 +29,18 @@ import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.NbtString
+import net.minecraft.nbt.StringNbtReader
import net.minecraft.text.MutableText
import net.minecraft.text.Style
import net.minecraft.text.Text
+import net.minecraft.util.Identifier
import moe.nea.firmament.Firmament
+import moe.nea.firmament.features.debug.ExportedTestConstantMeta
import moe.nea.firmament.repo.RepoManager.initialize
import moe.nea.firmament.util.LegacyFormattingCode
import moe.nea.firmament.util.LegacyTagParser
import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.MinecraftDispatcher
import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.TestUtil
import moe.nea.firmament.util.directLiteralStringContent
@@ -40,6 +51,7 @@ import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.mc.modifyLore
import moe.nea.firmament.util.mc.setCustomName
import moe.nea.firmament.util.mc.setSkullOwner
+import moe.nea.firmament.util.skyblockId
import moe.nea.firmament.util.transformEachRecursively
object ItemCache : IReloadable {
@@ -56,14 +68,18 @@ object ItemCache : IReloadable {
putShort("Damage", damage.toShort())
}
+ @ExpensiveItemCacheApi
private fun NbtCompound.transformFrom10809ToModern() = convert189ToModern(this@transformFrom10809ToModern)
+ val currentSaveVersion = SharedConstants.getGameVersion().saveVersion.id
+
+ @ExpensiveItemCacheApi
fun convert189ToModern(nbtComponent: NbtCompound): NbtCompound? =
try {
df.update(
TypeReferences.ITEM_STACK,
Dynamic(NbtOps.INSTANCE, nbtComponent),
-1,
- SharedConstants.getGameVersion().saveVersion.id
+ currentSaveVersion
).value as NbtCompound
} catch (e: Exception) {
isFlawless = false
@@ -126,19 +142,48 @@ object ItemCache : IReloadable {
return base
}
+ fun tryFindFromModernFormat(skyblockId: SkyblockId): NbtCompound? {
+ val overlayFile =
+ RepoManager.overlayData.getMostModernReadableOverlay(skyblockId, currentSaveVersion) ?: return null
+ val overlay = StringNbtReader.readCompound(overlayFile.path.readText())
+ val result = ExportedTestConstantMeta.SOURCE_CODEC.decode(
+ NbtOps.INSTANCE, overlay
+ ).result().getOrNull() ?: return null
+ val meta = result.first
+ return df.update(
+ TypeReferences.ITEM_STACK,
+ Dynamic(NbtOps.INSTANCE, result.second),
+ meta.dataVersion,
+ currentSaveVersion
+ ).value as NbtCompound
+ }
+
+ @ExpensiveItemCacheApi
private fun NEUItem.asItemStackNow(): ItemStack {
+
try {
+ var modernItemTag = tryFindFromModernFormat(this.skyblockId)
val oldItemTag = get10809CompoundTag()
- val modernItemTag = oldItemTag.transformFrom10809ToModern()
- ?: return brokenItemStack(this)
+ var usedOldNbt = false
+ if (modernItemTag == null) {
+ usedOldNbt = true
+ modernItemTag = oldItemTag.transformFrom10809ToModern()
+ ?: return brokenItemStack(this)
+ }
val itemInstance =
ItemStack.fromNbt(MC.defaultRegistries, modernItemTag).getOrNull() ?: return brokenItemStack(this)
+ if (usedOldNbt) {
+ val tag = oldItemTag.getCompound("tag")
+ val extraAttributes = tag.flatMap { it.getCompound("ExtraAttributes") }
+ .getOrNull()
+ if (extraAttributes != null)
+ itemInstance.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(extraAttributes))
+ val itemModel = tag.flatMap { it.getString("ItemModel") }.getOrNull()
+ if (itemModel != null)
+ itemInstance.set(DataComponentTypes.ITEM_MODEL, Identifier.of(itemModel))
+ }
itemInstance.loreAccordingToNbt = lore.map { un189Lore(it) }
itemInstance.displayNameAccordingToNbt = un189Lore(displayName)
- val extraAttributes = oldItemTag.getCompound("tag").flatMap { it.getCompound("ExtraAttributes") }
- .getOrNull()
- if (extraAttributes != null)
- itemInstance.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(extraAttributes))
return itemInstance
} catch (e: Exception) {
e.printStackTrace()
@@ -146,6 +191,11 @@ object ItemCache : IReloadable {
}
}
+ fun hasCacheFor(skyblockId: SkyblockId): Boolean {
+ return skyblockId.neuItem in cache
+ }
+
+ @ExpensiveItemCacheApi
fun NEUItem?.asItemStack(idHint: SkyblockId? = null, loreReplacements: Map<String, String>? = null): ItemStack {
if (this == null) return brokenItemStack(null, idHint)
var s = cache[this.skyblockItemId]
@@ -179,22 +229,49 @@ object ItemCache : IReloadable {
}
}
- var job: Job? = null
+ var itemRecacheScope: CoroutineScope? = null
- override fun reload(repository: NEURepository) {
- val j = job
- if (j != null && j.isActive) {
- j.cancel()
+ private var recacheSoonSubmitted = mutableSetOf<SkyblockId>()
+
+ @OptIn(ExpensiveItemCacheApi::class)
+ fun recacheSoon(neuItem: NEUItem) {
+ itemRecacheScope?.launch {
+ if (!withContext(MinecraftDispatcher) {
+ recacheSoonSubmitted.add(neuItem.skyblockId)
+ }) {
+ return@launch
+ }
+ neuItem.asItemStack()
}
+ }
+
+ @OptIn(ExpensiveItemCacheApi::class)
+ override fun reload(repository: NEURepository) {
+ val j = itemRecacheScope
+ j?.cancel("New reload invoked")
cache.clear()
isFlawless = true
if (TestUtil.isInTest) return
- job = Firmament.coroutineScope.launch {
- val items = repository.items?.items ?: return@launch
- items.values.forEach {
- it.asItemStack() // Rebuild cache
- }
+ val newScope =
+ CoroutineScope(
+ Firmament.coroutineScope.coroutineContext +
+ SupervisorJob(Firmament.globalJob) +
+ Dispatchers.Default.limitedParallelism(
+ (Runtime.getRuntime().availableProcessors() / 4).coerceAtLeast(1)
+ )
+ )
+ val items = repository.items?.items
+ newScope.launch {
+ val items = items ?: return@launch
+ items.values.chunked(500).map { chunk ->
+ async {
+ chunk.forEach {
+ it.asItemStack() // Rebuild cache
+ }
+ }
+ }.awaitAll()
}
+ itemRecacheScope = newScope
}
fun coinItem(coinAmount: Int): ItemStack {
diff --git a/src/main/kotlin/repo/MiningRepoData.kt b/src/main/kotlin/repo/MiningRepoData.kt
index e40292d..e96a241 100644
--- a/src/main/kotlin/repo/MiningRepoData.kt
+++ b/src/main/kotlin/repo/MiningRepoData.kt
@@ -81,6 +81,7 @@ class MiningRepoData : IReloadable {
) {
@Transient
val dropItem = baseDrop?.let(::SBItemStack)
+ @OptIn(ExpensiveItemCacheApi::class)
private val labeledStack by lazy {
dropItem?.asCopiedItemStack()?.also(::markItemStack)
}
@@ -110,6 +111,7 @@ class MiningRepoData : IReloadable {
fun isActiveIn(location: SkyBlockIsland) = onlyIn == null || location in onlyIn
+ @OptIn(ExpensiveItemCacheApi::class)
private fun convertToModernBlock(): Block? {
// TODO: this should be in a shared util, really
val newCompound = ItemCache.convert189ToModern(NbtCompound().apply {
diff --git a/src/main/kotlin/repo/ModernOverlaysData.kt b/src/main/kotlin/repo/ModernOverlaysData.kt
new file mode 100644
index 0000000..543b800
--- /dev/null
+++ b/src/main/kotlin/repo/ModernOverlaysData.kt
@@ -0,0 +1,41 @@
+package moe.nea.firmament.repo
+
+import io.github.moulberry.repo.IReloadable
+import io.github.moulberry.repo.NEURepository
+import java.nio.file.Path
+import kotlin.io.path.extension
+import kotlin.io.path.isDirectory
+import kotlin.io.path.listDirectoryEntries
+import kotlin.io.path.nameWithoutExtension
+import moe.nea.firmament.util.SkyblockId
+
+// TODO: move this over to the repo parser
+class ModernOverlaysData : IReloadable {
+ data class OverlayFile(
+ val version: Int,
+ val path: Path,
+ )
+
+ var overlays: Map<SkyblockId, List<OverlayFile>> = mapOf()
+ override fun reload(repo: NEURepository) {
+ val items = mutableMapOf<SkyblockId, MutableList<OverlayFile>>()
+ repo.baseFolder.resolve("itemsOverlay")
+ .takeIf { it.isDirectory() }
+ ?.listDirectoryEntries()
+ ?.forEach { versionFolder ->
+ val version = versionFolder.fileName.toString().toIntOrNull() ?: return@forEach
+ versionFolder.listDirectoryEntries()
+ .forEach { item ->
+ if (item.extension != "snbt") return@forEach
+ val itemId = item.nameWithoutExtension
+ items.getOrPut(SkyblockId(itemId)) { mutableListOf() }.add(OverlayFile(version, item))
+ }
+ }
+ this.overlays = items
+ }
+
+ fun getOverlayFiles(skyblockId: SkyblockId) = overlays[skyblockId] ?: listOf()
+ fun getMostModernReadableOverlay(skyblockId: SkyblockId, version: Int) = getOverlayFiles(skyblockId)
+ .filter { it.version <= version }
+ .maxByOrNull { it.version }
+}
diff --git a/src/main/kotlin/repo/RepoManager.kt b/src/main/kotlin/repo/RepoManager.kt
index cc36fba..df89092 100644
--- a/src/main/kotlin/repo/RepoManager.kt
+++ b/src/main/kotlin/repo/RepoManager.kt
@@ -11,6 +11,7 @@ import kotlinx.coroutines.launch
import net.minecraft.client.MinecraftClient
import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket
import net.minecraft.recipe.display.CuttingRecipeDisplay
+import net.minecraft.util.StringIdentifiable
import moe.nea.firmament.Firmament
import moe.nea.firmament.Firmament.logger
import moe.nea.firmament.events.ReloadRegistrationEvent
@@ -46,6 +47,16 @@ object RepoManager {
}
val alwaysSuperCraft by toggle("enable-super-craft") { true }
var warnForMissingItemListMod by toggle("warn-for-missing-item-list-mod") { true }
+ val perfectRenders by choice("perfect-renders") { PerfectRender.RENDER }
+ }
+
+ enum class PerfectRender(val label: String) : StringIdentifiable {
+ NOTHING("nothing"),
+ RENDER("render"),
+ RENDER_AND_TEXT("text"),
+ ;
+
+ override fun asString(): String? = label
}
val currentDownloadedSha by RepoDownloadManager::latestSavedVersionHash
@@ -55,9 +66,11 @@ object RepoManager {
val essenceRecipeProvider = EssenceRecipeProvider()
val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider, ReforgeStore)
val miningData = MiningRepoData()
+ val overlayData = ModernOverlaysData()
fun makeNEURepository(path: Path): NEURepository {
return NEURepository.of(path).apply {
+ registerReloadListener(overlayData)
registerReloadListener(ItemCache)
registerReloadListener(RepoItemTypeCache)
registerReloadListener(ExpLadders)
@@ -135,8 +148,10 @@ object RepoManager {
} catch (exc: NEURepositoryException) {
ErrorUtil.softError("Failed to reload repository", exc)
MC.sendChat(
- tr("firmament.repo.reloadfail",
- "Failed to reload repository. This will result in some mod features not working.")
+ tr(
+ "firmament.repo.reloadfail",
+ "Failed to reload repository. This will result in some mod features not working."
+ )
)
}
}
diff --git a/src/main/kotlin/repo/SBItemStack.kt b/src/main/kotlin/repo/SBItemStack.kt
index 3690866..01d1c4d 100644
--- a/src/main/kotlin/repo/SBItemStack.kt
+++ b/src/main/kotlin/repo/SBItemStack.kt
@@ -225,14 +225,21 @@ data class SBItemStack constructor(
Text.literal(
buffKind.prefix + formattedAmount +
statFormatting.postFix +
- buffKind.postFix + " ")
- .withColor(buffKind.color)))
+ buffKind.postFix + " "
+ )
+ .withColor(buffKind.color)
+ )
+ )
}
fun formatValue() =
- Text.literal(FirmFormatters.formatCommas(valueNum ?: 0.0,
- 1,
- includeSign = true) + statFormatting.postFix + " ")
+ Text.literal(
+ FirmFormatters.formatCommas(
+ valueNum ?: 0.0,
+ 1,
+ includeSign = true
+ ) + statFormatting.postFix + " "
+ )
.setStyle(Style.EMPTY.withColor(statFormatting.color))
val statFormatting = formattingOverrides[statName] ?: StatFormatting("", Formatting.GREEN)
@@ -256,7 +263,7 @@ data class SBItemStack constructor(
return segments.joinToString(" ") { it.replaceFirstChar { it.uppercaseChar() } }
}
- private fun parseStatLine(line: Text): StatLine? {
+ fun parseStatLine(line: Text): StatLine? {
val sibs = line.siblings
val stat = sibs.firstOrNull() ?: return null
if (stat.style.color != TextColor.fromFormatting(Formatting.GRAY)) return null
@@ -346,7 +353,9 @@ data class SBItemStack constructor(
}
// TODO: avoid instantiating the item stack here
+ @ExpensiveItemCacheApi
val itemType: ItemType? get() = ItemType.fromItemStack(asImmutableItemStack())
+ @ExpensiveItemCacheApi
val rarity: Rarity? get() = Rarity.fromItem(asImmutableItemStack())
private var itemStack_: ItemStack? = null
@@ -357,6 +366,7 @@ data class SBItemStack constructor(
group("power").toInt()
} ?: 0
+ @ExpensiveItemCacheApi
private val itemStack: ItemStack
get() {
val itemStack = itemStack_ ?: run {
@@ -413,19 +423,35 @@ data class SBItemStack constructor(
.append(starString(stars))
val isDungeon = ItemType.fromItemStack(itemStack)?.isDungeon ?: true
val truncatedStarCount = if (isDungeon) minOf(5, stars) else stars
- appendEnhancedStats(itemStack,
- baseStats
- .filter { it.statFormatting.isStarAffected }
- .associate {
- it.statName to ((it.valueNum ?: 0.0) * (truncatedStarCount * 0.02))
- },
- BuffKind.STAR_BUFF)
+ appendEnhancedStats(
+ itemStack,
+ baseStats
+ .filter { it.statFormatting.isStarAffected }
+ .associate {
+ it.statName to ((it.valueNum ?: 0.0) * (truncatedStarCount * 0.02))
+ },
+ BuffKind.STAR_BUFF
+ )
+ }
+
+ fun isWarm(): Boolean {
+ if (itemStack_ != null) return true
+ if (ItemCache.hasCacheFor(skyblockId)) return true
+ return false
+ }
+
+ @OptIn(ExpensiveItemCacheApi::class)
+ fun asLazyImmutableItemStack(): ItemStack? {
+ if (isWarm()) return asImmutableItemStack()
+ return null
}
- fun asImmutableItemStack(): ItemStack {
+ @ExpensiveItemCacheApi
+ fun asImmutableItemStack(): ItemStack { // TODO: add a "or fallback to painting" option to asLazyImmutableItemStack to be used in more places.
return itemStack
}
+ @ExpensiveItemCacheApi
fun asCopiedItemStack(): ItemStack {
return itemStack.copy()
}
diff --git a/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt b/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt
index 9a1aea5..3774f26 100644
--- a/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt
+++ b/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt
@@ -9,11 +9,13 @@ import net.minecraft.util.Identifier
import moe.nea.firmament.repo.SBItemStack
interface GenericRecipeRenderer<T : NEURecipe> {
- fun render(recipe: T, bounds: Rectangle, layouter: RecipeLayouter)
+ fun render(recipe: T, bounds: Rectangle, layouter: RecipeLayouter, mainItem: SBItemStack?)
fun getInputs(recipe: T): Collection<SBItemStack>
fun getOutputs(recipe: T): Collection<SBItemStack>
val icon: ItemStack
val title: Text
val identifier: Identifier
fun findAllRecipes(neuRepository: NEURepository): Iterable<T>
+ val displayHeight: Int get() = 66
+ val typ: Class<T>
}
diff --git a/src/main/kotlin/repo/recipes/RecipeLayouter.kt b/src/main/kotlin/repo/recipes/RecipeLayouter.kt
index 109bff5..ed0dca2 100644
--- a/src/main/kotlin/repo/recipes/RecipeLayouter.kt
+++ b/src/main/kotlin/repo/recipes/RecipeLayouter.kt
@@ -1,6 +1,8 @@
package moe.nea.firmament.repo.recipes
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
+import me.shedaniel.math.Point
+import me.shedaniel.math.Rectangle
import net.minecraft.text.Text
import moe.nea.firmament.repo.SBItemStack
@@ -21,13 +23,16 @@ interface RecipeLayouter {
slotKind: SlotKind,
)
+ fun createTooltip(rectangle: Rectangle, label: Text)
+
fun createLabel(
x: Int, y: Int,
text: Text
)
- fun createArrow(x: Int, y: Int)
+ fun createArrow(x: Int, y: Int): Rectangle
fun createMoulConfig(x: Int, y: Int, w: Int, h: Int, component: GuiComponent)
+ fun createFire(ingredientsCenter: Point, animationTicks: Int)
}
diff --git a/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt b/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt
index 679aec8..e38380c 100644
--- a/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt
+++ b/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt
@@ -12,17 +12,24 @@ import moe.nea.firmament.Firmament
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.tr
-class SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> {
- override fun render(recipe: NEUCraftingRecipe, bounds: Rectangle, layouter: RecipeLayouter) {
+object SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> {
+ override fun render(
+ recipe: NEUCraftingRecipe,
+ bounds: Rectangle,
+ layouter: RecipeLayouter,
+ mainItem: SBItemStack?,
+ ) {
val point = Point(bounds.centerX - 58, bounds.centerY - 27)
layouter.createArrow(point.x + 60, point.y + 18)
for (i in 0 until 3) {
for (j in 0 until 3) {
val item = recipe.inputs[i + j * 3]
- layouter.createItemSlot(point.x + 1 + i * 18,
- point.y + 1 + j * 18,
- SBItemStack(item),
- RecipeLayouter.SlotKind.SMALL_INPUT)
+ layouter.createItemSlot(
+ point.x + 1 + i * 18,
+ point.y + 1 + j * 18,
+ SBItemStack(item),
+ RecipeLayouter.SlotKind.SMALL_INPUT
+ )
}
}
layouter.createItemSlot(
@@ -32,6 +39,9 @@ class SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> {
)
}
+ override val typ: Class<NEUCraftingRecipe>
+ get() = NEUCraftingRecipe::class.java
+
override fun getInputs(recipe: NEUCraftingRecipe): Collection<SBItemStack> {
return recipe.allInputs.mapNotNull { SBItemStack(it) }
}
@@ -45,6 +55,6 @@ class SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> {
}
override val icon: ItemStack = ItemStack(Blocks.CRAFTING_TABLE)
- override val title: Text = tr("firmament.category.crafting", "SkyBlock Crafting") // TODO: fix tr not being included in jars
+ override val title: Text = tr("firmament.category.crafting", "SkyBlock Crafting")
override val identifier: Identifier = Firmament.identifier("crafting_recipe")
}
diff --git a/src/main/kotlin/repo/recipes/SBEssenceUpgradeRecipeRenderer.kt b/src/main/kotlin/repo/recipes/SBEssenceUpgradeRecipeRenderer.kt
new file mode 100644
index 0000000..d358e6a
--- /dev/null
+++ b/src/main/kotlin/repo/recipes/SBEssenceUpgradeRecipeRenderer.kt
@@ -0,0 +1,76 @@
+package moe.nea.firmament.repo.recipes
+
+import io.github.moulberry.repo.NEURepository
+import io.github.moulberry.repo.data.NEUForgeRecipe
+import me.shedaniel.math.Point
+import me.shedaniel.math.Rectangle
+import net.minecraft.item.ItemStack
+import net.minecraft.text.Text
+import net.minecraft.util.Identifier
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.repo.EssenceRecipeProvider
+import moe.nea.firmament.repo.ExpensiveItemCacheApi
+import moe.nea.firmament.repo.RepoManager
+import moe.nea.firmament.repo.SBItemStack
+import moe.nea.firmament.util.SkyblockId
+import moe.nea.firmament.util.tr
+
+object SBEssenceUpgradeRecipeRenderer : GenericRecipeRenderer<EssenceRecipeProvider.EssenceUpgradeRecipe> {
+ override fun render(
+ recipe: EssenceRecipeProvider.EssenceUpgradeRecipe,
+ bounds: Rectangle,
+ layouter: RecipeLayouter,
+ mainItem: SBItemStack?
+ ) {
+ val sourceItem = mainItem ?: SBItemStack(recipe.itemId)
+ layouter.createItemSlot(
+ bounds.minX + 12,
+ bounds.centerY - 8 - 18 / 2,
+ sourceItem.copy(stars = recipe.starCountAfter - 1),
+ RecipeLayouter.SlotKind.SMALL_INPUT
+ )
+ layouter.createItemSlot(
+ bounds.minX + 12, bounds.centerY - 8 + 18 / 2,
+ SBItemStack(recipe.essenceIngredient),
+ RecipeLayouter.SlotKind.SMALL_INPUT
+ )
+ layouter.createItemSlot(
+ bounds.maxX - 12 - 16, bounds.centerY - 8,
+ sourceItem.copy(stars = recipe.starCountAfter),
+ RecipeLayouter.SlotKind.SMALL_OUTPUT
+ )
+ val extraItems = recipe.extraItems
+ layouter.createArrow(
+ bounds.centerX - 24 / 2,
+ if (extraItems.isEmpty()) bounds.centerY - 17 / 2
+ else bounds.centerY + 18 / 2
+ )
+ for ((index, item) in extraItems.withIndex()) {
+ layouter.createItemSlot(
+ bounds.centerX - extraItems.size * 16 / 2 - 2 / 2 + index * 18,
+ bounds.centerY - 18 / 2,
+ SBItemStack(item),
+ RecipeLayouter.SlotKind.SMALL_INPUT,
+ )
+ }
+ }
+
+ override fun getInputs(recipe: EssenceRecipeProvider.EssenceUpgradeRecipe): Collection<SBItemStack> {
+ return recipe.allInputs.mapNotNull { SBItemStack(it) }
+ }
+
+ override fun getOutputs(recipe: EssenceRecipeProvider.EssenceUpgradeRecipe): Collection<SBItemStack> {
+ return listOfNotNull(SBItemStack(recipe.itemId))
+ }
+
+ @OptIn(ExpensiveItemCacheApi::class)
+ override val icon: ItemStack get() = SBItemStack(SkyblockId("ESSENCE_WITHER")).asImmutableItemStack()
+ override val title: Text = tr("firmament.category.essence", "Essence Upgrades")
+ override val identifier: Identifier = Firmament.identifier("essence_upgrade")
+ override fun findAllRecipes(neuRepository: NEURepository): Iterable<EssenceRecipeProvider.EssenceUpgradeRecipe> {
+ return RepoManager.essenceRecipeProvider.recipes
+ }
+
+ override val typ: Class<EssenceRecipeProvider.EssenceUpgradeRecipe>
+ get() = EssenceRecipeProvider.EssenceUpgradeRecipe::class.java
+}
diff --git a/src/main/kotlin/repo/recipes/SBForgeRecipeRenderer.kt b/src/main/kotlin/repo/recipes/SBForgeRecipeRenderer.kt
new file mode 100644
index 0000000..9fdb756
--- /dev/null
+++ b/src/main/kotlin/repo/recipes/SBForgeRecipeRenderer.kt
@@ -0,0 +1,83 @@
+package moe.nea.firmament.repo.recipes
+
+import io.github.moulberry.repo.NEURepository
+import io.github.moulberry.repo.data.NEUCraftingRecipe
+import io.github.moulberry.repo.data.NEUForgeRecipe
+import me.shedaniel.math.Point
+import me.shedaniel.math.Rectangle
+import kotlin.math.cos
+import kotlin.math.sin
+import kotlin.time.Duration.Companion.seconds
+import net.minecraft.block.Blocks
+import net.minecraft.item.ItemStack
+import net.minecraft.text.Text
+import net.minecraft.util.Identifier
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.repo.SBItemStack
+import moe.nea.firmament.util.tr
+
+object SBForgeRecipeRenderer : GenericRecipeRenderer<NEUForgeRecipe> {
+ override fun render(
+ recipe: NEUForgeRecipe,
+ bounds: Rectangle,
+ layouter: RecipeLayouter,
+ mainItem: SBItemStack?,
+ ) {
+ val arrow = layouter.createArrow(bounds.minX + 90, bounds.minY + 54 - 18 / 2)
+ layouter.createTooltip(
+ arrow,
+ Text.stringifiedTranslatable(
+ "firmament.recipe.forge.time",
+ recipe.duration.seconds
+ )
+ )
+
+ val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8)
+ layouter.createFire(ingredientsCenter, 25)
+ val count = recipe.inputs.size
+ if (count == 1) {
+ layouter.createItemSlot(
+ ingredientsCenter.x, ingredientsCenter.y - 18,
+ SBItemStack(recipe.inputs.single()),
+ RecipeLayouter.SlotKind.SMALL_INPUT,
+ )
+ } else {
+ recipe.inputs.forEachIndexed { idx, ingredient ->
+ val rad = Math.PI * 2 * idx / count
+ layouter.createItemSlot(
+ (ingredientsCenter.x + cos(rad) * 30).toInt(), (ingredientsCenter.y + sin(rad) * 30).toInt(),
+ SBItemStack(ingredient),
+ RecipeLayouter.SlotKind.SMALL_INPUT,
+ )
+ }
+ }
+ layouter.createItemSlot(
+ bounds.minX + 124, bounds.minY + 46,
+ SBItemStack(recipe.outputStack),
+ RecipeLayouter.SlotKind.BIG_OUTPUT
+ )
+ }
+
+ override val displayHeight: Int
+ get() = 104
+
+ override fun getInputs(recipe: NEUForgeRecipe): Collection<SBItemStack> {
+ return recipe.inputs.mapNotNull { SBItemStack(it) }
+ }
+
+ override fun getOutputs(recipe: NEUForgeRecipe): Collection<SBItemStack> {
+ return listOfNotNull(SBItemStack(recipe.outputStack))
+ }
+
+ override val icon: ItemStack = ItemStack(Blocks.ANVIL)
+ override val title: Text = tr("firmament.category.forge", "Forge Recipes")
+ override val identifier: Identifier = Firmament.identifier("forge_recipe")
+
+ override fun findAllRecipes(neuRepository: NEURepository): Iterable<NEUForgeRecipe> {
+ // TODO: theres gotta be an index for these tbh.
+ return neuRepository.items.items.values.flatMap { it.recipes }.filterIsInstance<NEUForgeRecipe>()
+ }
+
+ override val typ: Class<NEUForgeRecipe>
+ get() = NEUForgeRecipe::class.java
+}