diff options
Diffstat (limited to 'src/main/kotlin/repo')
-rw-r--r-- | src/main/kotlin/repo/ExpLadder.kt | 3 | ||||
-rw-r--r-- | src/main/kotlin/repo/ExpensiveItemCacheApi.kt | 8 | ||||
-rw-r--r-- | src/main/kotlin/repo/HypixelStaticData.kt | 25 | ||||
-rw-r--r-- | src/main/kotlin/repo/ItemCache.kt | 113 | ||||
-rw-r--r-- | src/main/kotlin/repo/MiningRepoData.kt | 2 | ||||
-rw-r--r-- | src/main/kotlin/repo/ModernOverlaysData.kt | 41 | ||||
-rw-r--r-- | src/main/kotlin/repo/RepoManager.kt | 19 | ||||
-rw-r--r-- | src/main/kotlin/repo/SBItemStack.kt | 54 | ||||
-rw-r--r-- | src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt | 4 | ||||
-rw-r--r-- | src/main/kotlin/repo/recipes/RecipeLayouter.kt | 7 | ||||
-rw-r--r-- | src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt | 24 | ||||
-rw-r--r-- | src/main/kotlin/repo/recipes/SBEssenceUpgradeRecipeRenderer.kt | 76 | ||||
-rw-r--r-- | src/main/kotlin/repo/recipes/SBForgeRecipeRenderer.kt | 83 |
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 +} |