Add crafting overlay

This commit is contained in:
nea
2023-06-12 17:46:41 +02:00
parent cd87be6cb3
commit d4410a67fb
6 changed files with 130 additions and 14 deletions

View File

@@ -15,8 +15,6 @@
- Dwarven Mines - Dwarven Mines
- Experimentation Solver - Experimentation Solver
- Capes - Capes
- Dungeon Profit
- Dungeon Map
Priority 2: Priority 2:
- missing talismans / pets in pv - missing talismans / pets in pv
@@ -33,7 +31,6 @@ Priority 2:
Priority 3: Priority 3:
- Item rarity halo - Item rarity halo
- Zealot Counter using Combat XP popups - Zealot Counter using Combat XP popups
- Use REIs "Move Item" functionality to higlight slots
- client sided stat tracker that does not need the api - client sided stat tracker that does not need the api
- skills - skills
- coins - coins
@@ -43,3 +40,10 @@ Priority 3:
- and much more that i will add as i go along - and much more that i will add as i go along
Dungeons (planned in another mod):
- Dungeon Profit
- Dungeon Map
- Dungeon waypoints
- Dungeon puzzle solvers
- Talk with the big D

View File

@@ -23,6 +23,7 @@ import kotlinx.serialization.serializer
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.features.debug.DeveloperFeatures import moe.nea.firmament.features.debug.DeveloperFeatures
import moe.nea.firmament.features.fishing.FishingWarning import moe.nea.firmament.features.fishing.FishingWarning
import moe.nea.firmament.features.inventory.CraftingOverlay
import moe.nea.firmament.features.inventory.SlotLocking import moe.nea.firmament.features.inventory.SlotLocking
import moe.nea.firmament.features.world.FairySouls import moe.nea.firmament.features.world.FairySouls
import moe.nea.firmament.util.data.DataHolder import moe.nea.firmament.util.data.DataHolder
@@ -49,6 +50,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
loadFeature(FairySouls) loadFeature(FairySouls)
loadFeature(FishingWarning) loadFeature(FishingWarning)
loadFeature(SlotLocking) loadFeature(SlotLocking)
loadFeature(CraftingOverlay)
if (Firmament.DEBUG) if (Firmament.DEBUG)
loadFeature(DeveloperFeatures) loadFeature(DeveloperFeatures)
hasAutoloaded = true hasAutoloaded = true

View File

@@ -0,0 +1,66 @@
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.events.SlotRenderEvents
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.recipes.SBCraftingRecipe
import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry
import moe.nea.firmament.rei.SBItemEntryDefinition
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 name: String
get() = "Crafting Overlay"
override val identifier: String
get() = "crafting-overlay"
override fun onLoad() {
SlotRenderEvents.After.subscribe { event ->
val slot = event.slot
val recipe = this.recipe ?: return@subscribe
if (slot.inventory != screen?.screenHandler?.inventory) return@subscribe
val recipeIndex = craftingOverlayIndices.indexOf(slot.index)
if (recipeIndex < 0) return@subscribe
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.stackSize < 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()}"
)
}
}
}
}

View File

@@ -24,18 +24,22 @@ import me.shedaniel.rei.api.client.registry.display.DisplayRegistry
import me.shedaniel.rei.api.client.registry.entry.CollapsibleEntryRegistry import me.shedaniel.rei.api.client.registry.entry.CollapsibleEntryRegistry
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry import me.shedaniel.rei.api.client.registry.entry.EntryRegistry
import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry
import me.shedaniel.rei.api.client.registry.transfer.TransferHandler
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry
import me.shedaniel.rei.api.common.entry.EntryStack import me.shedaniel.rei.api.common.entry.EntryStack
import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import moe.nea.firmament.features.inventory.CraftingOverlay
import moe.nea.firmament.recipes.SBCraftingRecipe import moe.nea.firmament.recipes.SBCraftingRecipe
import moe.nea.firmament.recipes.SBForgeRecipe import moe.nea.firmament.recipes.SBForgeRecipe
import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.skyblockId import moe.nea.firmament.util.skyblockId
import moe.nea.firmament.util.unformattedString
class FirmamentReiPlugin : REIClientPlugin { class FirmamentReiPlugin : REIClientPlugin {
@@ -48,6 +52,19 @@ class FirmamentReiPlugin : REIClientPlugin {
val SKYBLOCK_ITEM_TYPE_ID = Identifier("firmament", "skyblockitems") val SKYBLOCK_ITEM_TYPE_ID = Identifier("firmament", "skyblockitems")
} }
override fun registerTransferHandlers(registry: TransferHandlerRegistry) {
registry.register(TransferHandler { context ->
val screen = context.containerScreen
val display = context.display
if (display !is SBCraftingRecipe || screen !is GenericContainerScreen || screen.title?.unformattedString != "Craft Item") {
return@TransferHandler TransferHandler.Result.createNotApplicable()
}
if (context.isActuallyCrafting)
CraftingOverlay.setOverlay(screen, display)
return@TransferHandler TransferHandler.Result.createSuccessful()
})
}
override fun registerEntryTypes(registry: EntryTypeRegistry) { override fun registerEntryTypes(registry: EntryTypeRegistry) {
registry.register(SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition) registry.register(SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition)
} }

View File

@@ -39,7 +39,10 @@ import moe.nea.firmament.repo.ItemCache
import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.FirmFormatters import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.HypixelPetInfo
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.petData
import moe.nea.firmament.util.skyBlockId
// TODO: add in extra data like pet info, into this structure // TODO: add in extra data like pet info, into this structure
data class PetData( data class PetData(
@@ -47,6 +50,12 @@ data class PetData(
val petId: String, val petId: String,
val exp: Double, val exp: Double,
) { ) {
companion object {
fun fromHypixel(petInfo: HypixelPetInfo) = PetData(
petInfo.tier, petInfo.type, petInfo.exp,
)
}
val levelData by lazy { ExpLadders.getExpLadder(petId, rarity).getPetLevel(exp) } val levelData by lazy { ExpLadders.getExpLadder(petId, rarity).getPetLevel(exp) }
} }
@@ -155,5 +164,13 @@ object SBItemEntryDefinition : EntryDefinition<SBItemStack> {
fun getEntry(ingredient: NEUIngredient): EntryStack<SBItemStack> = fun getEntry(ingredient: NEUIngredient): EntryStack<SBItemStack> =
getEntry(SkyblockId(ingredient.itemId), count = ingredient.amount.toInt()) getEntry(SkyblockId(ingredient.itemId), count = ingredient.amount.toInt())
fun getEntry(stack: ItemStack): EntryStack<SBItemStack> =
getEntry(
SBItemStack(
stack.skyBlockId ?: SkyblockId.NULL,
RepoManager.getNEUItem(stack.skyBlockId ?: SkyblockId.NULL),
stack.count,
petData = stack.petData?.let { PetData.fromHypixel(it) }
)
)
} }

View File

@@ -15,17 +15,20 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
@file:UseSerializers(DashlessUUIDSerializer::class)
package moe.nea.firmament.util package moe.nea.firmament.util
import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEUItem
import io.github.moulberry.repo.data.Rarity import io.github.moulberry.repo.data.Rarity
import java.util.UUID
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtCompound
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import moe.nea.firmament.util.json.DashlessUUIDSerializer
/** /**
* A skyblock item id, as used by the NEU repo. * A skyblock item id, as used by the NEU repo.
@@ -59,6 +62,7 @@ value class SkyblockId(val neuItem: String) {
val COINS: SkyblockId = SkyblockId("SKYBLOCK_COIN") val COINS: SkyblockId = SkyblockId("SKYBLOCK_COIN")
private val bazaarEnchantmentRegex = "ENCHANTMENT_(\\D*)_(\\d+)".toRegex() private val bazaarEnchantmentRegex = "ENCHANTMENT_(\\D*)_(\\d+)".toRegex()
val NULL: SkyblockId = SkyblockId("null") val NULL: SkyblockId = SkyblockId("null")
val PET_NULL: SkyblockId = SkyblockId("null_pet")
} }
} }
@@ -68,6 +72,9 @@ val NEUItem.skyblockId get() = SkyblockId(skyblockItemId)
data class HypixelPetInfo( data class HypixelPetInfo(
val type: String, val type: String,
val tier: Rarity, val tier: Rarity,
val exp: Double = 0.0,
val candyUsed: Int = 0,
val uuid: UUID? = null,
) { ) {
val skyblockId get() = SkyblockId("${type.uppercase()};${tier.ordinal}") val skyblockId get() = SkyblockId("${type.uppercase()};${tier.ordinal}")
} }
@@ -77,20 +84,23 @@ private val jsonparser = Json { ignoreUnknownKeys = true }
val ItemStack.extraAttributes: NbtCompound val ItemStack.extraAttributes: NbtCompound
get() = getOrCreateSubNbt("ExtraAttributes") get() = getOrCreateSubNbt("ExtraAttributes")
val ItemStack.petData: HypixelPetInfo?
get() {
val jsonString = extraAttributes.getString("petInfo")
if (jsonString.isNullOrBlank()) return null
return runCatching { jsonparser.decodeFromString<HypixelPetInfo>(jsonString) }
.getOrElse { return null }
}
val ItemStack.skyBlockId: SkyblockId? val ItemStack.skyBlockId: SkyblockId?
get() { get() {
when (val id = extraAttributes.getString("id")) { return when (val id = extraAttributes.getString("id")) {
"PET" -> { "PET" -> {
val jsonString = extraAttributes.getString("petInfo") petData?.skyblockId ?: SkyblockId.PET_NULL
if (jsonString.isNullOrBlank()) return null
val petInfo =
runCatching { jsonparser.decodeFromString<HypixelPetInfo>(jsonString) }
.getOrElse { return null }
return petInfo.skyblockId
} }
// TODO: RUNE, ENCHANTED_BOOK, PARTY_HAT_CRAB{,_ANIMATED}, ABICASE // TODO: RUNE, ENCHANTED_BOOK, PARTY_HAT_CRAB{,_ANIMATED}, ABICASE
else -> { else -> {
return SkyblockId(id) SkyblockId(id)
} }
} }
} }