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/ItemCache.kt50
-rw-r--r--src/main/kotlin/repo/MiningRepoData.kt131
-rw-r--r--src/main/kotlin/repo/RepoDownloadManager.kt193
-rw-r--r--src/main/kotlin/repo/RepoManager.kt25
-rw-r--r--src/main/kotlin/repo/RepoModResourcePack.kt7
-rw-r--r--src/main/kotlin/repo/SBItemStack.kt8
-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.kt74
-rw-r--r--src/main/kotlin/repo/recipes/SBForgeRecipeRenderer.kt83
12 files changed, 441 insertions, 168 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/ItemCache.kt b/src/main/kotlin/repo/ItemCache.kt
index 9fa0083..09eedac 100644
--- a/src/main/kotlin/repo/ItemCache.kt
+++ b/src/main/kotlin/repo/ItemCache.kt
@@ -4,7 +4,6 @@ import com.mojang.serialization.Dynamic
import io.github.moulberry.repo.IReloadable
import io.github.moulberry.repo.NEURepository
import io.github.moulberry.repo.data.NEUItem
-import io.github.notenoughupdates.moulconfig.xml.Bind
import java.text.NumberFormat
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
@@ -13,7 +12,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlin.jvm.optionals.getOrNull
import net.minecraft.SharedConstants
-import net.minecraft.client.resource.language.I18n
import net.minecraft.component.DataComponentTypes
import net.minecraft.component.type.NbtComponent
import net.minecraft.datafixer.Schemas
@@ -28,9 +26,6 @@ import net.minecraft.text.MutableText
import net.minecraft.text.Style
import net.minecraft.text.Text
import moe.nea.firmament.Firmament
-import moe.nea.firmament.gui.config.HudMeta
-import moe.nea.firmament.gui.config.HudPosition
-import moe.nea.firmament.gui.hud.MoulConfigHud
import moe.nea.firmament.repo.RepoManager.initialize
import moe.nea.firmament.util.LegacyFormattingCode
import moe.nea.firmament.util.LegacyTagParser
@@ -61,11 +56,12 @@ object ItemCache : IReloadable {
putShort("Damage", damage.toShort())
}
- private fun NbtCompound.transformFrom10809ToModern(): NbtCompound? =
+ private fun NbtCompound.transformFrom10809ToModern() = convert189ToModern(this@transformFrom10809ToModern)
+ fun convert189ToModern(nbtComponent: NbtCompound): NbtCompound? =
try {
df.update(
TypeReferences.ITEM_STACK,
- Dynamic(NbtOps.INSTANCE, this),
+ Dynamic(NbtOps.INSTANCE, nbtComponent),
-1,
SharedConstants.getGameVersion().saveVersion.id
).value as NbtCompound
@@ -139,7 +135,8 @@ object ItemCache : IReloadable {
ItemStack.fromNbt(MC.defaultRegistries, modernItemTag).getOrNull() ?: return brokenItemStack(this)
itemInstance.loreAccordingToNbt = lore.map { un189Lore(it) }
itemInstance.displayNameAccordingToNbt = un189Lore(displayName)
- val extraAttributes = oldItemTag.getCompound("tag").getCompound("ExtraAttributes")
+ val extraAttributes = oldItemTag.getCompound("tag").flatMap { it.getCompound("ExtraAttributes") }
+ .getOrNull()
if (extraAttributes != null)
itemInstance.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(extraAttributes))
return itemInstance
@@ -184,31 +181,6 @@ object ItemCache : IReloadable {
var job: Job? = null
- object ReloadProgressHud : MoulConfigHud(
- "repo_reload", HudMeta(HudPosition(0.0, 0.0, 1F), Text.literal("Repo Reload"), 180, 18)) {
-
-
- var isEnabled = false
- override fun shouldRender(): Boolean {
- return isEnabled
- }
-
- @get:Bind("current")
- var current: Double = 0.0
-
- @get:Bind("label")
- var label: String = ""
-
- @get:Bind("max")
- var max: Double = 0.0
-
- fun reportProgress(label: String, current: Int, max: Int) {
- this.label = label
- this.current = current.toDouble()
- this.max = max.toDouble()
- }
- }
-
override fun reload(repository: NEURepository) {
val j = job
if (j != null && j.isActive) {
@@ -218,20 +190,10 @@ object ItemCache : IReloadable {
isFlawless = true
if (TestUtil.isInTest) return
job = Firmament.coroutineScope.launch {
- val items = repository.items?.items
- if (items == null) {
- ReloadProgressHud.isEnabled = false
- return@launch
- }
- val recacheItems = I18n.translate("firmament.repo.cache")
- ReloadProgressHud.reportProgress(recacheItems, 0, items.size)
- ReloadProgressHud.isEnabled = true
- var i = 0
+ val items = repository.items?.items ?: return@launch
items.values.forEach {
it.asItemStack() // Rebuild cache
- ReloadProgressHud.reportProgress(recacheItems, i++, items.size)
}
- ReloadProgressHud.isEnabled = false
}
}
diff --git a/src/main/kotlin/repo/MiningRepoData.kt b/src/main/kotlin/repo/MiningRepoData.kt
new file mode 100644
index 0000000..e40292d
--- /dev/null
+++ b/src/main/kotlin/repo/MiningRepoData.kt
@@ -0,0 +1,131 @@
+package moe.nea.firmament.repo
+
+import io.github.moulberry.repo.IReloadable
+import io.github.moulberry.repo.NEURepository
+import io.github.moulberry.repo.data.NEUItem
+import java.util.Collections
+import java.util.NavigableMap
+import java.util.TreeMap
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.Transient
+import kotlinx.serialization.serializer
+import kotlin.jvm.optionals.getOrNull
+import kotlin.streams.asSequence
+import net.minecraft.block.Block
+import net.minecraft.item.BlockItem
+import net.minecraft.item.ItemStack
+import net.minecraft.nbt.NbtCompound
+import net.minecraft.text.Text
+import moe.nea.firmament.repo.ReforgeStore.kJson
+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.mc.FirmamentDataComponentTypes
+import moe.nea.firmament.util.mc.displayNameAccordingToNbt
+import moe.nea.firmament.util.skyblockId
+
+class MiningRepoData : IReloadable {
+ var customMiningAreas: Map<SkyBlockIsland, CustomMiningArea> = mapOf()
+ private set
+ var customMiningBlocks: List<CustomMiningBlock> = listOf()
+ private set
+ var toolsByBreakingPower: NavigableMap<BreakingPowerKey, SBItemStack> = Collections.emptyNavigableMap()
+ private set
+
+
+ data class BreakingPowerKey(
+ val breakingPower: Int,
+ val itemId: SkyblockId? = null
+ ) {
+ companion object {
+ val COMPARATOR: Comparator<BreakingPowerKey> =
+ Comparator
+ .comparingInt<BreakingPowerKey> { it.breakingPower }
+ .thenComparing(Comparator.comparing(
+ { it.itemId },
+ nullsFirst(Comparator.comparing<SkyblockId, Boolean> { "PICK" in it.neuItem || "BING" in it.neuItem }.thenComparing(Comparator.naturalOrder<SkyblockId>()))))
+ }
+ }
+
+ override fun reload(repo: NEURepository) {
+ customMiningAreas = repo.file("mining/custom_mining_areas.json")
+ ?.kJson(serializer()) ?: mapOf()
+ customMiningBlocks = repo.tree("mining/blocks")
+ .asSequence()
+ .filter { it.path.endsWith(".json") }
+ .map { it.kJson(serializer<CustomMiningBlock>()) }
+ .toList()
+ toolsByBreakingPower = Collections.unmodifiableNavigableMap(
+ repo.items.items
+ .values
+ .asSequence()
+ .map { SBItemStack(it.skyblockId) }
+ .filter { it.breakingPower > 0 }
+ .associateTo(TreeMap<BreakingPowerKey, SBItemStack>(BreakingPowerKey.COMPARATOR)) {
+ BreakingPowerKey(it.breakingPower, it.skyblockId) to it
+ })
+ }
+
+ fun getToolsThatCanBreak(breakingPower: Int): Collection<SBItemStack> {
+ return toolsByBreakingPower.tailMap(BreakingPowerKey(breakingPower, null), true).values
+ }
+
+ @Serializable
+ data class CustomMiningBlock(
+ val breakingPower: Int = 0,
+ val blockStrength: Int = 0,
+ val name: String? = null,
+ val baseDrop: SkyblockId? = null,
+ val blocks189: List<Block189> = emptyList()
+ ) {
+ @Transient
+ val dropItem = baseDrop?.let(::SBItemStack)
+ private val labeledStack by lazy {
+ dropItem?.asCopiedItemStack()?.also(::markItemStack)
+ }
+
+ private fun markItemStack(itemStack: ItemStack) {
+ itemStack.set(FirmamentDataComponentTypes.CUSTOM_MINING_BLOCK_DATA, this)
+ if (name != null)
+ itemStack.displayNameAccordingToNbt = Text.literal(name)
+ }
+
+ fun getDisplayItem(block: Block): ItemStack {
+ return labeledStack ?: ItemStack(block).also(::markItemStack)
+ }
+ }
+
+ @Serializable
+ data class Block189(
+ val itemId: String,
+ val damage: Short = 0,
+ val onlyIn: List<SkyBlockIsland>? = null,
+ ) {
+ @Transient
+ val block = convertToModernBlock()
+
+ val isCurrentlyActive: Boolean
+ get() = isActiveIn(SBData.skyblockLocation ?: SkyBlockIsland.NIL)
+
+ fun isActiveIn(location: SkyBlockIsland) = onlyIn == null || location in onlyIn
+
+ private fun convertToModernBlock(): Block? {
+ // TODO: this should be in a shared util, really
+ val newCompound = ItemCache.convert189ToModern(NbtCompound().apply {
+ putString("id", itemId)
+ putShort("Damage", damage)
+ }) ?: return null
+ val itemStack = ItemStack.fromNbt(MC.defaultRegistries, newCompound).getOrNull() ?: return null
+ val blockItem = itemStack.item as? BlockItem ?: return null
+ return blockItem.block
+ }
+ }
+
+ @Serializable
+ data class CustomMiningArea(
+ val isSpecialMining: Boolean = true
+ )
+
+
+}
diff --git a/src/main/kotlin/repo/RepoDownloadManager.kt b/src/main/kotlin/repo/RepoDownloadManager.kt
index 3efd83b..888248d 100644
--- a/src/main/kotlin/repo/RepoDownloadManager.kt
+++ b/src/main/kotlin/repo/RepoDownloadManager.kt
@@ -1,5 +1,3 @@
-
-
package moe.nea.firmament.repo
import io.ktor.client.call.body
@@ -28,101 +26,102 @@ import moe.nea.firmament.util.iterate
object RepoDownloadManager {
- val repoSavedLocation = Firmament.DATA_DIR.resolve("repo-extracted")
- val repoMetadataLocation = Firmament.DATA_DIR.resolve("loaded-repo-sha.txt")
-
- private fun loadSavedVersionHash(): String? =
- if (repoSavedLocation.exists()) {
- if (repoMetadataLocation.exists()) {
- try {
- repoMetadataLocation.readText().trim()
- } catch (e: IOException) {
- null
- }
- } else {
- null
- }
- } else null
-
- private fun saveVersionHash(versionHash: String) {
- latestSavedVersionHash = versionHash
- repoMetadataLocation.writeText(versionHash)
- }
-
- var latestSavedVersionHash: String? = loadSavedVersionHash()
- private set
-
- @Serializable
- private class GithubCommitsResponse(val sha: String)
-
- private suspend fun requestLatestGithubSha(): String? {
- if (RepoManager.Config.branch == "prerelease") {
- RepoManager.Config.branch = "master"
- }
- val response =
- Firmament.httpClient.get("https://api.github.com/repos/${RepoManager.Config.username}/${RepoManager.Config.reponame}/commits/${RepoManager.Config.branch}")
- if (response.status.value != 200) {
- return null
- }
- return response.body<GithubCommitsResponse>().sha
- }
-
- private suspend fun downloadGithubArchive(url: String): Path = withContext(IO) {
- val response = Firmament.httpClient.get(url)
- val targetFile = Files.createTempFile("firmament-repo", ".zip")
- val outputChannel = Files.newByteChannel(targetFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE)
- response.bodyAsChannel().copyTo(outputChannel)
- targetFile
- }
-
- /**
- * Downloads the latest repository from github, setting [latestSavedVersionHash].
- * @return true, if an update was performed, false, otherwise (no update needed, or wasn't able to complete update)
- */
- suspend fun downloadUpdate(force: Boolean): Boolean = withContext(CoroutineName("Repo Update Check")) {
- val latestSha = requestLatestGithubSha()
- if (latestSha == null) {
- logger.warn("Could not request github API to retrieve latest REPO sha.")
- return@withContext false
- }
- val currentSha = loadSavedVersionHash()
- if (latestSha != currentSha || force) {
- val requestUrl =
- "https://github.com/${RepoManager.Config.username}/${RepoManager.Config.reponame}/archive/$latestSha.zip"
- logger.info("Planning to upgrade repository from $currentSha to $latestSha from $requestUrl")
- val zipFile = downloadGithubArchive(requestUrl)
- logger.info("Download repository zip file to $zipFile. Deleting old repository")
- withContext(IO) { repoSavedLocation.toFile().deleteRecursively() }
- logger.info("Extracting new repository")
- withContext(IO) { extractNewRepository(zipFile) }
- logger.info("Repository loaded on disk.")
- saveVersionHash(latestSha)
- return@withContext true
- } else {
- logger.debug("Repository on latest sha $currentSha. Not performing update")
- return@withContext false
- }
- }
-
- private fun extractNewRepository(zipFile: Path) {
- repoSavedLocation.createDirectories()
- ZipInputStream(zipFile.inputStream()).use { cis ->
- while (true) {
- val entry = cis.nextEntry ?: break
- if (entry.isDirectory) continue
- val extractedLocation =
- repoSavedLocation.resolve(
- entry.name.substringAfter('/', missingDelimiterValue = "")
- )
- if (repoSavedLocation !in extractedLocation.iterate { it.parent }) {
- logger.error("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.")
- throw RuntimeException("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.")
- }
- extractedLocation.parent.createDirectories()
- extractedLocation.outputStream().use { cis.copyTo(it) }
- }
- }
- }
+ val repoSavedLocation = Firmament.DATA_DIR.resolve("repo-extracted")
+ val repoMetadataLocation = Firmament.DATA_DIR.resolve("loaded-repo-sha.txt")
+
+ private fun loadSavedVersionHash(): String? =
+ if (repoSavedLocation.exists()) {
+ if (repoMetadataLocation.exists()) {
+ try {
+ repoMetadataLocation.readText().trim()
+ } catch (e: IOException) {
+ null
+ }
+ } else {
+ null
+ }
+ } else null
+
+ private fun saveVersionHash(versionHash: String) {
+ latestSavedVersionHash = versionHash
+ repoMetadataLocation.writeText(versionHash)
+ }
+
+ var latestSavedVersionHash: String? = loadSavedVersionHash()
+ private set
+
+ @Serializable
+ private class GithubCommitsResponse(val sha: String)
+
+ private suspend fun requestLatestGithubSha(branchOverride: String?): String? {
+ if (RepoManager.Config.branch == "prerelease") {
+ RepoManager.Config.branch = "master"
+ }
+ val response =
+ Firmament.httpClient.get("https://api.github.com/repos/${RepoManager.Config.username}/${RepoManager.Config.reponame}/commits/${branchOverride ?: RepoManager.Config.branch}")
+ if (response.status.value != 200) {
+ return null
+ }
+ return response.body<GithubCommitsResponse>().sha
+ }
+
+ private suspend fun downloadGithubArchive(url: String): Path = withContext(IO) {
+ val response = Firmament.httpClient.get(url)
+ val targetFile = Files.createTempFile("firmament-repo", ".zip")
+ val outputChannel = Files.newByteChannel(targetFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE)
+ response.bodyAsChannel().copyTo(outputChannel)
+ targetFile
+ }
+
+ /**
+ * Downloads the latest repository from github, setting [latestSavedVersionHash].
+ * @return true, if an update was performed, false, otherwise (no update needed, or wasn't able to complete update)
+ */
+ suspend fun downloadUpdate(force: Boolean, branch: String? = null): Boolean =
+ withContext(CoroutineName("Repo Update Check")) {
+ val latestSha = requestLatestGithubSha(branch)
+ if (latestSha == null) {
+ logger.warn("Could not request github API to retrieve latest REPO sha.")
+ return@withContext false
+ }
+ val currentSha = loadSavedVersionHash()
+ if (latestSha != currentSha || force) {
+ val requestUrl =
+ "https://github.com/${RepoManager.Config.username}/${RepoManager.Config.reponame}/archive/$latestSha.zip"
+ logger.info("Planning to upgrade repository from $currentSha to $latestSha from $requestUrl")
+ val zipFile = downloadGithubArchive(requestUrl)
+ logger.info("Download repository zip file to $zipFile. Deleting old repository")
+ withContext(IO) { repoSavedLocation.toFile().deleteRecursively() }
+ logger.info("Extracting new repository")
+ withContext(IO) { extractNewRepository(zipFile) }
+ logger.info("Repository loaded on disk.")
+ saveVersionHash(latestSha)
+ return@withContext true
+ } else {
+ logger.debug("Repository on latest sha $currentSha. Not performing update")
+ return@withContext false
+ }
+ }
+
+ private fun extractNewRepository(zipFile: Path) {
+ repoSavedLocation.createDirectories()
+ ZipInputStream(zipFile.inputStream()).use { cis ->
+ while (true) {
+ val entry = cis.nextEntry ?: break
+ if (entry.isDirectory) continue
+ val extractedLocation =
+ repoSavedLocation.resolve(
+ entry.name.substringAfter('/', missingDelimiterValue = "")
+ )
+ if (repoSavedLocation !in extractedLocation.iterate { it.parent }) {
+ logger.error("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.")
+ throw RuntimeException("Firmament detected an invalid zip file. This is a potential security risk, please report this in the Firmament discord.")
+ }
+ extractedLocation.parent.createDirectories()
+ extractedLocation.outputStream().use { cis.copyTo(it) }
+ }
+ }
+ }
}
diff --git a/src/main/kotlin/repo/RepoManager.kt b/src/main/kotlin/repo/RepoManager.kt
index 6d9ba14..cc36fba 100644
--- a/src/main/kotlin/repo/RepoManager.kt
+++ b/src/main/kotlin/repo/RepoManager.kt
@@ -54,6 +54,7 @@ object RepoManager {
val essenceRecipeProvider = EssenceRecipeProvider()
val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider, ReforgeStore)
+ val miningData = MiningRepoData()
fun makeNEURepository(path: Path): NEURepository {
return NEURepository.of(path).apply {
@@ -63,6 +64,8 @@ object RepoManager {
registerReloadListener(ItemNameLookup)
registerReloadListener(ReforgeStore)
registerReloadListener(essenceRecipeProvider)
+ registerReloadListener(recipeCache)
+ registerReloadListener(miningData)
ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this))
registerReloadListener {
if (TestUtil.isInTest) return@registerReloadListener
@@ -73,7 +76,6 @@ object RepoManager {
}
}
}
- registerReloadListener(recipeCache)
}
}
@@ -100,16 +102,16 @@ object RepoManager {
fun getNEUItem(skyblockId: SkyblockId): NEUItem? = neuRepo.items.getItemBySkyblockId(skyblockId.neuItem)
+ fun downloadOverridenBranch(branch: String) {
+ Firmament.coroutineScope.launch {
+ RepoDownloadManager.downloadUpdate(true, branch)
+ reload()
+ }
+ }
+
fun launchAsyncUpdate(force: Boolean = false) {
Firmament.coroutineScope.launch {
- ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper bouncy bar
- ItemCache.ReloadProgressHud.isEnabled = true
- try {
- RepoDownloadManager.downloadUpdate(force)
- ItemCache.ReloadProgressHud.reportProgress("Download complete", 1, 1)
- } finally {
- ItemCache.ReloadProgressHud.isEnabled = false
- }
+ RepoDownloadManager.downloadUpdate(force)
reload()
}
}
@@ -127,10 +129,6 @@ object RepoManager {
return
}
try {
- ItemCache.ReloadProgressHud.reportProgress("Reloading from Disk",
- 0,
- -1) // TODO: replace with a proper bouncy bar
- ItemCache.ReloadProgressHud.isEnabled = true
logger.info("Repo reload started.")
neuRepo.reload()
logger.info("Repo reload completed.")
@@ -140,7 +138,6 @@ object RepoManager {
tr("firmament.repo.reloadfail",
"Failed to reload repository. This will result in some mod features not working.")
)
- ItemCache.ReloadProgressHud.isEnabled = false
}
}
diff --git a/src/main/kotlin/repo/RepoModResourcePack.kt b/src/main/kotlin/repo/RepoModResourcePack.kt
index 617efec..4fec14a 100644
--- a/src/main/kotlin/repo/RepoModResourcePack.kt
+++ b/src/main/kotlin/repo/RepoModResourcePack.kt
@@ -5,6 +5,7 @@ import java.nio.file.Files
import java.nio.file.Path
import java.util.*
import net.fabricmc.fabric.api.resource.ModResourcePack
+import net.fabricmc.fabric.impl.resource.loader.ModResourcePackSorter
import net.fabricmc.loader.api.FabricLoader
import net.fabricmc.loader.api.metadata.ModMetadata
import kotlin.io.path.exists
@@ -23,14 +24,14 @@ import net.minecraft.resource.metadata.ResourceMetadata
import net.minecraft.resource.metadata.ResourceMetadataSerializer
import net.minecraft.text.Text
import net.minecraft.util.Identifier
-import net.minecraft.util.PathUtil
+import net.minecraft.util.path.PathUtil
import moe.nea.firmament.Firmament
class RepoModResourcePack(val basePath: Path) : ModResourcePack {
companion object {
- fun append(packs: MutableList<in ModResourcePack>) {
+ fun append(packs: ModResourcePackSorter) {
Firmament.logger.info("Registering mod resource pack")
- packs.add(RepoModResourcePack(RepoDownloadManager.repoSavedLocation))
+ packs.addPack(RepoModResourcePack(RepoDownloadManager.repoSavedLocation))
}
fun createResourceDirectly(identifier: Identifier): Optional<Resource> {
diff --git a/src/main/kotlin/repo/SBItemStack.kt b/src/main/kotlin/repo/SBItemStack.kt
index da34707..3690866 100644
--- a/src/main/kotlin/repo/SBItemStack.kt
+++ b/src/main/kotlin/repo/SBItemStack.kt
@@ -34,6 +34,7 @@ import moe.nea.firmament.util.modifyExtraAttributes
import moe.nea.firmament.util.petData
import moe.nea.firmament.util.prepend
import moe.nea.firmament.util.reconstitute
+import moe.nea.firmament.util.removeColorCodes
import moe.nea.firmament.util.skyBlockId
import moe.nea.firmament.util.skyblock.ItemType
import moe.nea.firmament.util.skyblock.Rarity
@@ -81,6 +82,7 @@ data class SBItemStack constructor(
}
val EMPTY = SBItemStack(SkyblockId.NULL, 0)
+ private val BREAKING_POWER_REGEX = "Breaking Power (?<power>[0-9]+)".toPattern()
operator fun invoke(itemStack: ItemStack): SBItemStack {
val skyblockId = itemStack.skyBlockId ?: SkyblockId.NULL
return SBItemStack(
@@ -349,6 +351,12 @@ data class SBItemStack constructor(
private var itemStack_: ItemStack? = null
+ val breakingPower: Int
+ get() =
+ BREAKING_POWER_REGEX.useMatch(neuItem?.lore?.firstOrNull()?.removeColorCodes()) {
+ group("power").toInt()
+ } ?: 0
+
private val itemStack: ItemStack
get() {
val itemStack = itemStack_ ?: run {
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..0f5271f
--- /dev/null
+++ b/src/main/kotlin/repo/recipes/SBEssenceUpgradeRecipeRenderer.kt
@@ -0,0 +1,74 @@
+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.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))
+ }
+
+ 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
+}