aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt5
-rw-r--r--src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt17
-rw-r--r--src/compat/rei/java/moe/nea/firmament/compat/rei/SBItemEntryDefinition.kt14
-rw-r--r--src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockCraftingRecipeDynamicGenerator.kt63
-rw-r--r--src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockItemIdFocusedStackProvider.kt3
-rw-r--r--src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt2
-rw-r--r--src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt7
-rw-r--r--src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt2
-rw-r--r--src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBReforgeRecipe.kt41
-rw-r--r--src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBShopRecipe.kt61
-rw-r--r--src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java16
-rw-r--r--src/main/java/moe/nea/firmament/mixins/PlayerDropEventPatch.java20
-rw-r--r--src/main/kotlin/events/CustomItemModelEvent.kt28
-rw-r--r--src/main/kotlin/events/FirmamentEventBus.kt3
-rw-r--r--src/main/kotlin/events/IsSlotProtectedEvent.kt72
-rw-r--r--src/main/kotlin/features/FeatureManager.kt4
-rw-r--r--src/main/kotlin/features/debug/PowerUserTools.kt7
-rw-r--r--src/main/kotlin/features/inventory/SlotLocking.kt24
-rw-r--r--src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt8
-rw-r--r--src/main/kotlin/features/mining/PickaxeAbility.kt12
-rw-r--r--src/main/kotlin/features/world/Waypoints.kt15
-rw-r--r--src/main/kotlin/repo/BetterRepoRecipeCache.kt5
-rw-r--r--src/main/kotlin/repo/ItemCache.kt3
-rw-r--r--src/main/kotlin/repo/SBItemStack.kt68
-rw-r--r--src/main/kotlin/util/FirmFormatters.kt28
-rw-r--r--src/main/kotlin/util/MoulConfigUtils.kt13
-rw-r--r--src/main/kotlin/util/SBData.kt9
-rw-r--r--src/main/kotlin/util/ScoreboardUtil.kt72
-rw-r--r--src/main/kotlin/util/SkyBlockIsland.kt1
-rw-r--r--src/main/kotlin/util/SkyblockId.kt6
-rw-r--r--src/main/kotlin/util/mc/IntrospectableItemModelManager.kt7
-rw-r--r--src/main/kotlin/util/render/TranslatedScissors.kt6
-rw-r--r--src/main/kotlin/util/skyblock/DungeonUtil.kt33
-rw-r--r--src/main/kotlin/util/skyblock/ItemType.kt2
-rw-r--r--src/main/kotlin/util/textutil.kt104
-rw-r--r--src/test/resources/testdata/items/books/feather_falling.snbt36
-rw-r--r--src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt4
-rw-r--r--src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt4
-rw-r--r--src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt5
-rw-r--r--src/texturePacks/java/moe/nea/firmament/features/texturepack/predicates/ItemPredicate.kt2
-rw-r--r--src/texturePacks/java/moe/nea/firmament/mixins/custommodels/PatchLegacyTexturePathsIntoArmorLayers.java38
-rw-r--r--src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java26
-rw-r--r--src/texturePacks/java/moe/nea/firmament/mixins/custommodels/SupplyFakeModelPatch.java10
-rw-r--r--translations/en_us.json4
44 files changed, 641 insertions, 269 deletions
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt
index 92f2cfc..aade59e 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt
@@ -25,6 +25,7 @@ import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe
import moe.nea.firmament.compat.rei.recipes.SBKatRecipe
import moe.nea.firmament.compat.rei.recipes.SBMobDropRecipe
import moe.nea.firmament.compat.rei.recipes.SBReforgeRecipe
+import moe.nea.firmament.compat.rei.recipes.SBShopRecipe
import moe.nea.firmament.events.HandledScreenPushREIEvent
import moe.nea.firmament.features.inventory.CraftingOverlay
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
@@ -81,6 +82,7 @@ class FirmamentReiPlugin : REIClientPlugin {
registry.add(SBKatRecipe.Category)
registry.add(SBReforgeRecipe.Category)
registry.add(SBEssenceUpgradeRecipe.Category)
+ registry.add(SBShopRecipe.Category)
}
override fun registerExclusionZones(zones: ExclusionZones) {
@@ -103,6 +105,9 @@ class FirmamentReiPlugin : REIClientPlugin {
SBMobDropRecipe.Category.categoryIdentifier,
SkyblockMobDropRecipeDynamicGenerator)
registry.registerDisplayGenerator(
+ SBShopRecipe.Category.categoryIdentifier,
+ SkyblockShopRecipeDynamicGenerator)
+ registry.registerDisplayGenerator(
SBKatRecipe.Category.categoryIdentifier,
SkyblockKatRecipeDynamicGenerator)
registry.registerDisplayGenerator(
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt
index a7b4c99..35a1e1b 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt
@@ -18,10 +18,13 @@ import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.DrawContext
import net.minecraft.item.tooltip.TooltipType
+import net.minecraft.text.Text
import moe.nea.firmament.compat.rei.FirmamentReiPlugin.Companion.asItemEntry
import moe.nea.firmament.events.ItemTooltipEvent
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.ErrorUtil
+import moe.nea.firmament.util.FirmFormatters
+import moe.nea.firmament.util.darkGrey
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
@@ -39,15 +42,18 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack> {
context.matrices.push()
context.matrices.translate(bounds.centerX.toFloat(), bounds.centerY.toFloat(), 0F)
context.matrices.scale(bounds.width.toFloat() / 16F, bounds.height.toFloat() / 16F, 1f)
- context.drawItemWithoutEntity(
- entry.asItemEntry().value,
- -8, -8,
+ val item = entry.asItemEntry().value
+ context.drawItemWithoutEntity(item, -8, -8)
+ context.drawStackOverlay(minecraft.textRenderer, item, -8, -8,
+ if (entry.value.getStackSize() > 1000) FirmFormatters.shortFormat(entry.value.getStackSize()
+ .toDouble())
+ else null
)
context.matrices.pop()
}
val minecraft = MinecraftClient.getInstance()
- var canUseVanillaTooltipEvents = false
+ var canUseVanillaTooltipEvents = true
override fun getTooltip(entry: EntryStack<SBItemStack>, tooltipContext: TooltipContext): Tooltip? {
val stack = entry.value.asImmutableItemStack()
@@ -60,6 +66,7 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack> {
stack, tooltipContext.vanillaContext(), TooltipType.BASIC, lore
)
} catch (ex: Exception) {
+ canUseVanillaTooltipEvents = false
ErrorUtil.softError("Failed to use vanilla tooltips", ex)
}
} else {
@@ -70,6 +77,8 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack> {
lore
))
}
+ if (entry.value.getStackSize() > 1000 && lore.isNotEmpty())
+ lore.add(1, Text.literal("${entry.value.getStackSize()}x").darkGrey())
// TODO: tags aren't sent as early now so some tooltip components that use tags will crash the game
// stack.getTooltip(
// Item.TooltipContext.create(
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/SBItemEntryDefinition.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/SBItemEntryDefinition.kt
index 9638281..2b1700d 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/SBItemEntryDefinition.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/SBItemEntryDefinition.kt
@@ -15,12 +15,9 @@ import net.minecraft.registry.tag.TagKey
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import moe.nea.firmament.compat.rei.FirmamentReiPlugin.Companion.asItemEntry
-import moe.nea.firmament.repo.PetData
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.SkyblockId
-import moe.nea.firmament.util.petData
-import moe.nea.firmament.util.skyBlockId
object SBItemEntryDefinition : EntryDefinition<SBItemStack> {
override fun equals(o1: SBItemStack, o2: SBItemStack, context: ComparisonContext): Boolean {
@@ -55,7 +52,7 @@ object SBItemEntryDefinition : EntryDefinition<SBItemStack> {
override fun wildcard(entry: EntryStack<SBItemStack>?, value: SBItemStack): SBItemStack {
return value.copy(stackSize = 1, petData = RepoManager.getPotentialStubPetData(value.skyblockId),
- stars = 0, extraLore = listOf())
+ stars = 0, extraLore = listOf(), reforge = null)
}
override fun normalize(entry: EntryStack<SBItemStack>?, value: SBItemStack): SBItemStack {
@@ -86,12 +83,5 @@ object SBItemEntryDefinition : EntryDefinition<SBItemStack> {
fun getPassthrough(item: ItemConvertible) = getEntry(SBItemStack.passthrough(ItemStack(item.asItem())))
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) }
- )
- )
+ getEntry(SBItemStack(stack))
}
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockCraftingRecipeDynamicGenerator.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockCraftingRecipeDynamicGenerator.kt
index f52f418..e80840f 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockCraftingRecipeDynamicGenerator.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockCraftingRecipeDynamicGenerator.kt
@@ -1,11 +1,10 @@
-
-
package moe.nea.firmament.compat.rei
import io.github.moulberry.repo.data.NEUCraftingRecipe
import io.github.moulberry.repo.data.NEUForgeRecipe
import io.github.moulberry.repo.data.NEUKatUpgradeRecipe
import io.github.moulberry.repo.data.NEUMobDropRecipe
+import io.github.moulberry.repo.data.NEUNpcShopRecipe
import io.github.moulberry.repo.data.NEURecipe
import java.util.Optional
import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator
@@ -17,49 +16,51 @@ import moe.nea.firmament.compat.rei.recipes.SBEssenceUpgradeRecipe
import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe
import moe.nea.firmament.compat.rei.recipes.SBKatRecipe
import moe.nea.firmament.compat.rei.recipes.SBMobDropRecipe
+import moe.nea.firmament.compat.rei.recipes.SBShopRecipe
import moe.nea.firmament.repo.EssenceRecipeProvider
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.repo.SBItemStack
val SkyblockCraftingRecipeDynamicGenerator =
- neuDisplayGenerator<SBCraftingRecipe, NEUCraftingRecipe> { SBCraftingRecipe(it) }
+ neuDisplayGenerator<SBCraftingRecipe, NEUCraftingRecipe> { SBCraftingRecipe(it) }
val SkyblockForgeRecipeDynamicGenerator =
- neuDisplayGenerator<SBForgeRecipe, NEUForgeRecipe> { SBForgeRecipe(it) }
+ neuDisplayGenerator<SBForgeRecipe, NEUForgeRecipe> { SBForgeRecipe(it) }
val SkyblockMobDropRecipeDynamicGenerator =
- neuDisplayGenerator<SBMobDropRecipe, NEUMobDropRecipe> { SBMobDropRecipe(it) }
-
+ neuDisplayGenerator<SBMobDropRecipe, NEUMobDropRecipe> { SBMobDropRecipe(it) }
+val SkyblockShopRecipeDynamicGenerator =
+ neuDisplayGenerator<SBShopRecipe, NEUNpcShopRecipe> { SBShopRecipe(it) }
val SkyblockKatRecipeDynamicGenerator =
- neuDisplayGenerator<SBKatRecipe, NEUKatUpgradeRecipe> { SBKatRecipe(it) }
+ neuDisplayGenerator<SBKatRecipe, NEUKatUpgradeRecipe> { SBKatRecipe(it) }
val SkyblockEssenceRecipeDynamicGenerator =
- neuDisplayGenerator<SBEssenceUpgradeRecipe, EssenceRecipeProvider.EssenceUpgradeRecipe> { SBEssenceUpgradeRecipe(it) }
+ neuDisplayGeneratorWithItem<SBEssenceUpgradeRecipe, EssenceRecipeProvider.EssenceUpgradeRecipe> { item, recipe ->
+ SBEssenceUpgradeRecipe(recipe, item)
+ }
inline fun <D : Display, reified T : NEURecipe> neuDisplayGenerator(crossinline mapper: (T) -> D) =
- object : DynamicDisplayGenerator<D> {
- override fun getRecipeFor(entry: EntryStack<*>): Optional<List<D>> {
- if (entry.type != SBItemEntryDefinition.type) return Optional.empty()
- val item = entry.castValue<SBItemStack>()
- val recipes = RepoManager.getRecipesFor(item.skyblockId)
- val craftingRecipes = recipes.filterIsInstance<T>()
- return Optional.of(craftingRecipes.map(mapper))
- }
+ neuDisplayGeneratorWithItem<D, T> { _, it -> mapper(it) }
- override fun generate(builder: ViewSearchBuilder): Optional<List<D>> {
- if (SBCraftingRecipe.Category.catIdentifier !in builder.categories) return Optional.empty()
- return Optional.of(
- RepoManager.getAllRecipes().filterIsInstance<T>().map { mapper(it) }
- .toList()
- )
- }
+inline fun <D : Display, reified T : NEURecipe> neuDisplayGeneratorWithItem(crossinline mapper: (SBItemStack, T) -> D) =
+ object : DynamicDisplayGenerator<D> {
+ override fun getRecipeFor(entry: EntryStack<*>): Optional<List<D>> {
+ if (entry.type != SBItemEntryDefinition.type) return Optional.empty()
+ val item = entry.castValue<SBItemStack>()
+ val recipes = RepoManager.getRecipesFor(item.skyblockId)
+ val craftingRecipes = recipes.filterIsInstance<T>()
+ return Optional.of(craftingRecipes.map { mapper(item, it) })
+ }
- override fun getUsageFor(entry: EntryStack<*>): Optional<List<D>> {
- if (entry.type != SBItemEntryDefinition.type) return Optional.empty()
- val item = entry.castValue<SBItemStack>()
- val recipes = RepoManager.getUsagesFor(item.skyblockId)
- val craftingRecipes = recipes.filterIsInstance<T>()
- return Optional.of(craftingRecipes.map(mapper))
+ override fun generate(builder: ViewSearchBuilder): Optional<List<D>> {
+ return Optional.empty() // TODO: allows searching without blocking getRecipeFor
+ }
- }
- }
+ override fun getUsageFor(entry: EntryStack<*>): Optional<List<D>> {
+ if (entry.type != SBItemEntryDefinition.type) return Optional.empty()
+ val item = entry.castValue<SBItemStack>()
+ val recipes = RepoManager.getUsagesFor(item.skyblockId)
+ val craftingRecipes = recipes.filterIsInstance<T>()
+ return Optional.of(craftingRecipes.map { mapper(item, it) })
+ }
+ }
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockItemIdFocusedStackProvider.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockItemIdFocusedStackProvider.kt
index cfb6f74..518f7b4 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockItemIdFocusedStackProvider.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/SkyblockItemIdFocusedStackProvider.kt
@@ -17,8 +17,7 @@ object SkyblockItemIdFocusedStackProvider : FocusedStackProvider {
screen as AccessorHandledScreen
val focusedSlot = screen.focusedSlot_Firmament ?: return CompoundEventResult.pass()
val item = focusedSlot.stack ?: return CompoundEventResult.pass()
- val skyblockId = item.skyBlockId ?: return CompoundEventResult.pass()
- return CompoundEventResult.interruptTrue(SBItemEntryDefinition.getEntry(skyblockId))
+ return CompoundEventResult.interruptTrue(SBItemEntryDefinition.getEntry(item))
}
override fun getPriority(): Double = 1_000_000.0
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt
index 8db3d75..c02e078 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt
@@ -22,7 +22,7 @@ class SBCraftingRecipe(override val neuRecipe: NEUCraftingRecipe) : SBRecipe() {
override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.catIdentifier
object Category : DisplayCategory<SBCraftingRecipe> {
- val catIdentifier = CategoryIdentifier.of<SBCraftingRecipe>(Firmament.MOD_ID, "crafing_recipe")
+ val catIdentifier = CategoryIdentifier.of<SBCraftingRecipe>(Firmament.MOD_ID, "crafting_recipe")
override fun getCategoryIdentifier(): CategoryIdentifier<out SBCraftingRecipe> = catIdentifier
override fun getTitle(): Text = Text.literal("SkyBlock Crafting")
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt
index ec71ec8..e0a3784 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt
@@ -14,7 +14,8 @@ import moe.nea.firmament.repo.EssenceRecipeProvider
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.SkyblockId
-class SBEssenceUpgradeRecipe(override val neuRecipe: EssenceRecipeProvider.EssenceUpgradeRecipe) : SBRecipe() {
+class SBEssenceUpgradeRecipe(override val neuRecipe: EssenceRecipeProvider.EssenceUpgradeRecipe,
+ val sourceItem: SBItemStack) : SBRecipe() {
object Category : DisplayCategory<SBEssenceUpgradeRecipe> {
override fun getCategoryIdentifier(): CategoryIdentifier<SBEssenceUpgradeRecipe> =
CategoryIdentifier.of(Firmament.MOD_ID, "essence_upgrade")
@@ -33,13 +34,13 @@ class SBEssenceUpgradeRecipe(override val neuRecipe: EssenceRecipeProvider.Essen
list.add(Widgets.createRecipeBase(bounds))
list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 - 18 / 2))
.markInput()
- .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter - 1))))
+ .entry(SBItemEntryDefinition.getEntry(display.sourceItem.copy(stars = recipe.starCountAfter - 1))))
list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 + 18 / 2))
.markInput()
.entry(SBItemEntryDefinition.getEntry(recipe.essenceIngredient)))
list.add(Widgets.createSlot(Point(bounds.maxX - 12 - 16, bounds.centerY - 8))
.markOutput()
- .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter))))
+ .entry(SBItemEntryDefinition.getEntry(display.sourceItem.copy(stars = recipe.starCountAfter))))
val extraItems = recipe.extraItems
list.add(Widgets.createArrow(Point(bounds.centerX - 24 / 2,
if (extraItems.isEmpty()) bounds.centerY - 17 / 2
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt
index 92b2f3f..7a0ec78 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt
@@ -8,7 +8,6 @@ import me.shedaniel.rei.api.client.gui.widgets.Widget
import me.shedaniel.rei.api.client.gui.widgets.Widgets
import me.shedaniel.rei.api.client.registry.display.DisplayCategory
import me.shedaniel.rei.api.common.category.CategoryIdentifier
-import me.shedaniel.rei.api.common.util.EntryStacks
import kotlin.math.cos
import kotlin.math.sin
import kotlin.time.Duration.Companion.seconds
@@ -41,6 +40,7 @@ class SBForgeRecipe(override val neuRecipe: NEUForgeRecipe) : SBRecipe() {
Text.stringifiedTranslatable("firmament.recipe.forge.time",
display.neuRecipe.duration.seconds)))
val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8)
+ add(Widgets.createBurningFire(ingredientsCenter).animationDurationTicks(25.0))
val count = display.neuRecipe.inputs.size
if (count == 1) {
add(
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBReforgeRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBReforgeRecipe.kt
index 4d00a4f..b8313a6 100644
--- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBReforgeRecipe.kt
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBReforgeRecipe.kt
@@ -44,7 +44,7 @@ import moe.nea.firmament.util.tr
class SBReforgeRecipe(
val reforge: Reforge,
- val limitToItem: SkyblockId?,
+ val limitToItem: SBItemStack?,
) : Display {
companion object {
val catIdentifier = CategoryIdentifier.of<SBReforgeRecipe>(Firmament.MOD_ID, "reforge_recipe")
@@ -132,10 +132,10 @@ class SBReforgeRecipe(
fun getRecipesForSBItemStack(item: SBItemStack): Optional<List<SBReforgeRecipe>> {
val reforgeRecipes = mutableListOf<SBReforgeRecipe>()
for (reforge in ReforgeStore.findEligibleForInternalName(item.skyblockId)) {
- reforgeRecipes.add(SBReforgeRecipe(reforge, item.skyblockId))
+ reforgeRecipes.add(SBReforgeRecipe(reforge, item))
}
for (reforge in ReforgeStore.findEligibleForItem(item.itemType ?: ItemType.NIL)) {
- reforgeRecipes.add(SBReforgeRecipe(reforge, item.skyblockId))
+ reforgeRecipes.add(SBReforgeRecipe(reforge, item))
}
if (reforgeRecipes.isEmpty()) return Optional.empty()
return Optional.of(reforgeRecipes)
@@ -162,26 +162,27 @@ class SBReforgeRecipe(
}
}
- private val eligibleItems =
- if (limitToItem != null) listOfNotNull(RepoManager.getNEUItem(limitToItem))
- else reforge.eligibleItems.flatMap {
+ private val inputItems = run {
+ if (limitToItem != null) return@run listOf(SBItemEntryDefinition.getEntry(limitToItem))
+ val eligibleItems = reforge.eligibleItems.flatMap {
when (it) {
- is Reforge.ReforgeEligibilityFilter.AllowsInternalName ->
- listOfNotNull(RepoManager.getNEUItem(it.internalName))
-
- is Reforge.ReforgeEligibilityFilter.AllowsItemType ->
- ReforgeStore.resolveItemType(it.itemType)
- .flatMapTo(mutableSetOf()) {
- (RepoItemTypeCache.byItemType[it] ?: listOf()) +
- (RepoItemTypeCache.byItemType[it.dungeonVariant] ?: listOf())
- }.toList()
-
- is Reforge.ReforgeEligibilityFilter.AllowsVanillaItemType -> {
- listOf() // TODO: add filter support for this and potentially rework this to search for the declared item type in repo, instead of remapped item type
+ is Reforge.ReforgeEligibilityFilter.AllowsInternalName ->
+ listOfNotNull(RepoManager.getNEUItem(it.internalName))
+
+ is Reforge.ReforgeEligibilityFilter.AllowsItemType ->
+ ReforgeStore.resolveItemType(it.itemType)
+ .flatMapTo(mutableSetOf()) {
+ (RepoItemTypeCache.byItemType[it] ?: listOf()) +
+ (RepoItemTypeCache.byItemType[it.dungeonVariant] ?: listOf())
+ }.toList()
+
+ is Reforge.ReforgeEligibilityFilter.AllowsVanillaItemType -> {
+ listOf() // TODO: add filter support for this and potentially rework this to search for the declared item type in repo, instead of remapped item type
+ }
}
- }
}
- private val inputItems = eligibleItems.map { SBItemEntryDefinition.getEntry(it.skyblockId) }
+ eligibleItems.map { SBItemEntryDefinition.getEntry(it.skyblockId) }
+ }
private val outputItems =
inputItems.map { SBItemEntryDefinition.getEntry(it.value.copy(reforge = reforge.reforgeId)) }
private val reforgeStone = reforge.reforgeStone?.let(SBItemEntryDefinition::getEntry)
diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBShopRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBShopRecipe.kt
new file mode 100644
index 0000000..a252802
--- /dev/null
+++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBShopRecipe.kt
@@ -0,0 +1,61 @@
+package moe.nea.firmament.compat.rei.recipes
+
+import io.github.moulberry.repo.data.NEUNpcShopRecipe
+import me.shedaniel.math.Point
+import me.shedaniel.math.Rectangle
+import me.shedaniel.rei.api.client.gui.Renderer
+import me.shedaniel.rei.api.client.gui.widgets.Widget
+import me.shedaniel.rei.api.client.gui.widgets.Widgets
+import me.shedaniel.rei.api.client.registry.display.DisplayCategory
+import me.shedaniel.rei.api.common.category.CategoryIdentifier
+import me.shedaniel.rei.api.common.entry.EntryIngredient
+import net.minecraft.item.Items
+import net.minecraft.text.Text
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.compat.rei.SBItemEntryDefinition
+import moe.nea.firmament.util.skyblockId
+
+class SBShopRecipe(override val neuRecipe: NEUNpcShopRecipe) : SBRecipe() {
+ override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.catIdentifier
+ val merchant = SBItemEntryDefinition.getEntry(neuRecipe.isSoldBy.skyblockId)
+ override fun getInputEntries(): List<EntryIngredient> {
+ return listOf(EntryIngredient.of(merchant)) + super.getInputEntries()
+ }
+
+ object Category : DisplayCategory<SBShopRecipe> {
+ val catIdentifier = CategoryIdentifier.of<SBShopRecipe>(Firmament.MOD_ID, "npc_shopping")
+ override fun getCategoryIdentifier(): CategoryIdentifier<SBShopRecipe> = catIdentifier
+
+ override fun getTitle(): Text = Text.literal("SkyBlock NPC Shopping")
+
+ override fun getIcon(): Renderer = SBItemEntryDefinition.getPassthrough(Items.EMERALD)
+ override fun setupDisplay(display: SBShopRecipe, bounds: Rectangle): List<Widget> {
+ val point = Point(bounds.centerX, bounds.centerY)
+ return buildList {
+ add(Widgets.createRecipeBase(bounds))
+ add(Widgets.createSlot(Point(point.x - 2 - 18 / 2, point.y - 18 - 6))
+ .unmarkInputOrOutput()
+ .entry(display.merchant)
+ .disableBackground())
+ add(Widgets.createArrow(Point(point.x - 2 - 24 / 2, point.y - 6)))
+ val cost = display.neuRecipe.cost
+ for ((i, item) in cost.withIndex()) {
+ add(Widgets.createSlot(Point(
+ point.x - 14 - 18,
+ point.y + i * 18 - 18 * cost.size / 2))
+ .entry(SBItemEntryDefinition.getEntry(item))
+ .markInput())
+ // TODO: fix frame clipping
+ }
+ add(Widgets.createResultSlotBackground(Point(point.x + 18, point.y - 18 / 2)))
+ add(
+ Widgets.createSlot(Point(point.x + 18, point.y - 18 / 2))
+ .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.result))
+ .disableBackground().markOutput()
+ )
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java
index e607ba3..43aec40 100644
--- a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java
+++ b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java
@@ -4,8 +4,11 @@ package moe.nea.firmament.mixins;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
-import com.llamalad7.mixinextras.sugar.Local;
-import moe.nea.firmament.events.*;
+import moe.nea.firmament.events.HandledScreenClickEvent;
+import moe.nea.firmament.events.HandledScreenForegroundEvent;
+import moe.nea.firmament.events.HandledScreenKeyPressedEvent;
+import moe.nea.firmament.events.IsSlotProtectedEvent;
+import moe.nea.firmament.events.SlotRenderEvents;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.entity.player.PlayerInventory;
@@ -22,9 +25,6 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
-
-import java.util.Iterator;
@Mixin(value = HandledScreen.class, priority = 990)
public abstract class MixinHandledScreen<T extends ScreenHandler> {
@@ -74,17 +74,17 @@ public abstract class MixinHandledScreen<T extends ScreenHandler> {
public void onMouseClickedSlot(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) {
if (slotId == -999 && getScreenHandler() != null && actionType == SlotActionType.PICKUP) { // -999 is code for "clicked outside the main window"
ItemStack cursorStack = getScreenHandler().getCursorStack();
- if (cursorStack != null && IsSlotProtectedEvent.shouldBlockInteraction(slot, SlotActionType.THROW, cursorStack)) {
+ if (cursorStack != null && IsSlotProtectedEvent.shouldBlockInteraction(slot, SlotActionType.THROW, IsSlotProtectedEvent.MoveOrigin.INVENTORY_MOVE, cursorStack)) {
ci.cancel();
return;
}
}
- if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType)) {
+ if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType, IsSlotProtectedEvent.MoveOrigin.INVENTORY_MOVE)) {
ci.cancel();
return;
}
if (actionType == SlotActionType.SWAP && 0 <= button && button < 9) {
- if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0), actionType)) {
+ if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0), actionType, IsSlotProtectedEvent.MoveOrigin.INVENTORY_MOVE)) {
ci.cancel();
}
}
diff --git a/src/main/java/moe/nea/firmament/mixins/PlayerDropEventPatch.java b/src/main/java/moe/nea/firmament/mixins/PlayerDropEventPatch.java
index 9a4626f..b20c223 100644
--- a/src/main/java/moe/nea/firmament/mixins/PlayerDropEventPatch.java
+++ b/src/main/java/moe/nea/firmament/mixins/PlayerDropEventPatch.java
@@ -14,15 +14,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ClientPlayerEntity.class)
public abstract class PlayerDropEventPatch extends PlayerEntity {
- public PlayerDropEventPatch() {
- super(null, null, 0, null);
- }
+ public PlayerDropEventPatch() {
+ super(null, null, 0, null);
+ }
- @Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true)
- public void onDropSelectedItem(boolean entireStack, CallbackInfoReturnable<Boolean> cir) {
- Slot fakeSlot = new Slot(getInventory(), getInventory().selectedSlot, 0, 0);
- if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot, SlotActionType.THROW)) {
- cir.setReturnValue(false);
- }
- }
+ @Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true)
+ public void onDropSelectedItem(boolean entireStack, CallbackInfoReturnable<Boolean> cir) {
+ Slot fakeSlot = new Slot(getInventory(), getInventory().selectedSlot, 0, 0);
+ if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot, SlotActionType.THROW, IsSlotProtectedEvent.MoveOrigin.DROP_FROM_HOTBAR)) {
+ cir.setReturnValue(false);
+ }
+ }
}
diff --git a/src/main/kotlin/events/CustomItemModelEvent.kt b/src/main/kotlin/events/CustomItemModelEvent.kt
index 11528fd..21ee326 100644
--- a/src/main/kotlin/events/CustomItemModelEvent.kt
+++ b/src/main/kotlin/events/CustomItemModelEvent.kt
@@ -1,23 +1,43 @@
package moe.nea.firmament.events
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
import net.minecraft.item.ItemStack
import net.minecraft.util.Identifier
+import moe.nea.firmament.util.collections.WeakCache
+import moe.nea.firmament.util.mc.IntrospectableItemModelManager
// TODO: assert an order on these events
data class CustomItemModelEvent(
val itemStack: ItemStack,
+ val itemModelManager: IntrospectableItemModelManager,
var overrideModel: Identifier? = null,
) : FirmamentEvent() {
companion object : FirmamentEventBus<CustomItemModelEvent>() {
+ val cache = WeakCache.memoize("ItemModelIdentifier", ::getModelIdentifier0)
+
@JvmStatic
- fun getModelIdentifier(itemStack: ItemStack?): Identifier? {
- // TODO: Re-add memoization and add an error / warning if the model does not exist
+ fun getModelIdentifier(itemStack: ItemStack?, itemModelManager: IntrospectableItemModelManager): Identifier? {
if (itemStack == null) return null
- return publish(CustomItemModelEvent(itemStack)).overrideModel
+ return cache.invoke(itemStack, itemModelManager).getOrNull()
+ }
+
+ fun getModelIdentifier0(
+ itemStack: ItemStack,
+ itemModelManager: IntrospectableItemModelManager
+ ): Optional<Identifier> {
+ // TODO: add an error / warning if the model does not exist
+ return Optional.ofNullable(publish(CustomItemModelEvent(itemStack, itemModelManager)).overrideModel)
}
}
fun overrideIfExists(overrideModel: Identifier) {
- this.overrideModel = overrideModel
+ if (itemModelManager.hasModel_firmament(overrideModel))
+ this.overrideModel = overrideModel
+ }
+
+ fun overrideIfEmpty(identifier: Identifier) {
+ if (overrideModel == null)
+ overrideModel = identifier
}
}
diff --git a/src/main/kotlin/events/FirmamentEventBus.kt b/src/main/kotlin/events/FirmamentEventBus.kt
index 71331d1..af4e16a 100644
--- a/src/main/kotlin/events/FirmamentEventBus.kt
+++ b/src/main/kotlin/events/FirmamentEventBus.kt
@@ -3,6 +3,7 @@ package moe.nea.firmament.events
import java.util.concurrent.CopyOnWriteArrayList
import org.apache.commons.lang3.reflect.TypeUtils
import moe.nea.firmament.Firmament
+import moe.nea.firmament.util.ErrorUtil
import moe.nea.firmament.util.MC
/**
@@ -48,7 +49,7 @@ open class FirmamentEventBus<T : FirmamentEvent> {
val klass = e.javaClass
if (!function.knownErrors.contains(klass) || Firmament.DEBUG) {
function.knownErrors.add(klass)
- Firmament.logger.error("Caught exception during processing event $event by $function", e)
+ ErrorUtil.softError("Caught exception during processing event $event by $function", e)
}
}
}
diff --git a/src/main/kotlin/events/IsSlotProtectedEvent.kt b/src/main/kotlin/events/IsSlotProtectedEvent.kt
index cd2b676..eac2d9b 100644
--- a/src/main/kotlin/events/IsSlotProtectedEvent.kt
+++ b/src/main/kotlin/events/IsSlotProtectedEvent.kt
@@ -1,5 +1,3 @@
-
-
package moe.nea.firmament.events
import net.minecraft.item.ItemStack
@@ -10,37 +8,49 @@ import moe.nea.firmament.util.CommonSoundEffects
import moe.nea.firmament.util.MC
data class IsSlotProtectedEvent(
- val slot: Slot?,
- val actionType: SlotActionType,
- var isProtected: Boolean,
- val itemStackOverride: ItemStack?,
- var silent: Boolean = false,
+ val slot: Slot?,
+ val actionType: SlotActionType,
+ var isProtected: Boolean,
+ val itemStackOverride: ItemStack?,
+ val origin: MoveOrigin,
+ var silent: Boolean = false,
) : FirmamentEvent() {
- val itemStack get() = itemStackOverride ?: slot!!.stack
+ val itemStack get() = itemStackOverride ?: slot!!.stack
- fun protect() {
- isProtected = true
- }
+ fun protect() {
+ isProtected = true
+ silent = false
+ }
- fun protectSilent() {
- if (!isProtected) {
- silent = true
- }
- isProtected = true
- }
+ fun protectSilent() {
+ if (!isProtected) {
+ silent = true
+ }
+ isProtected = true
+ }
- companion object : FirmamentEventBus<IsSlotProtectedEvent>() {
- @JvmStatic
- @JvmOverloads
- fun shouldBlockInteraction(slot: Slot?, action: SlotActionType, itemStackOverride: ItemStack? = null): Boolean {
- if (slot == null && itemStackOverride == null) return false
- val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride)
- publish(event)
- if (event.isProtected && !event.silent) {
- MC.sendChat(Text.translatable("firmament.protectitem").append(event.itemStack.name))
- CommonSoundEffects.playFailure()
- }
- return event.isProtected
- }
- }
+ enum class MoveOrigin {
+ DROP_FROM_HOTBAR,
+ SALVAGE,
+ INVENTORY_MOVE
+ ;
+ }
+ companion object : FirmamentEventBus<IsSlotProtectedEvent>() {
+ @JvmStatic
+ @JvmOverloads
+ fun shouldBlockInteraction(
+ slot: Slot?, action: SlotActionType,
+ origin: MoveOrigin,
+ itemStackOverride: ItemStack? = null,
+ ): Boolean {
+ if (slot == null && itemStackOverride == null) return false
+ val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride, origin)
+ publish(event)
+ if (event.isProtected && !event.silent) {
+ MC.sendChat(Text.translatable("firmament.protectitem").append(event.itemStack.name))
+ CommonSoundEffects.playFailure()
+ }
+ return event.isProtected
+ }
+ }
}
diff --git a/src/main/kotlin/features/FeatureManager.kt b/src/main/kotlin/features/FeatureManager.kt
index 0f5ebf8..9a3cbf8 100644
--- a/src/main/kotlin/features/FeatureManager.kt
+++ b/src/main/kotlin/features/FeatureManager.kt
@@ -45,10 +45,6 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
private var hasAutoloaded = false
- init {
- autoload()
- }
-
fun autoload() {
synchronized(this) {
if (hasAutoloaded) return
diff --git a/src/main/kotlin/features/debug/PowerUserTools.kt b/src/main/kotlin/features/debug/PowerUserTools.kt
index 225bc13..ac7a6fb 100644
--- a/src/main/kotlin/features/debug/PowerUserTools.kt
+++ b/src/main/kotlin/features/debug/PowerUserTools.kt
@@ -30,6 +30,7 @@ import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
import moe.nea.firmament.util.ClipboardUtils
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.focusedItemStack
+import moe.nea.firmament.util.mc.IntrospectableItemModelManager
import moe.nea.firmament.util.mc.SNbtFormatter.Companion.toPrettyString
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
@@ -119,7 +120,11 @@ object PowerUserTools : FirmamentFeature {
lastCopiedStack =
Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skyblockid", sbId.neuItem))
} else if (it.matches(TConfig.copyTexturePackId)) {
- val model = CustomItemModelEvent.getModelIdentifier(item) // TODO: remove global texture overrides, maybe
+ val model = CustomItemModelEvent.getModelIdentifier0(item, object : IntrospectableItemModelManager {
+ override fun hasModel_firmament(identifier: Identifier): Boolean {
+ return true
+ }
+ }).getOrNull() // TODO: remove global texture overrides, maybe
if (model == null) {
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.modelid.fail"))
return
diff --git a/src/main/kotlin/features/inventory/SlotLocking.kt b/src/main/kotlin/features/inventory/SlotLocking.kt
index 99130d5..7a3a152 100644
--- a/src/main/kotlin/features/inventory/SlotLocking.kt
+++ b/src/main/kotlin/features/inventory/SlotLocking.kt
@@ -15,6 +15,7 @@ import net.minecraft.screen.slot.SlotActionType
import net.minecraft.util.Identifier
import net.minecraft.util.StringIdentifiable
import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.events.FeaturesInitializedEvent
import moe.nea.firmament.events.HandledScreenForegroundEvent
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
import moe.nea.firmament.events.HandledScreenKeyReleasedEvent
@@ -37,6 +38,7 @@ import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.render.GuiRenderLayers
import moe.nea.firmament.util.render.drawLine
+import moe.nea.firmament.util.skyblock.DungeonUtil
import moe.nea.firmament.util.skyblockUUID
import moe.nea.firmament.util.unformattedString
@@ -60,6 +62,7 @@ object SlotLocking : FirmamentFeature {
val slotBind by keyBinding("bind") { GLFW.GLFW_KEY_L }
val slotBindRequireShift by toggle("require-quick-move") { true }
val slotRenderLines by choice("bind-render") { SlotRenderLinesMode.ONLY_BOXES }
+ val allowDroppingInDungeons by toggle("drop-in-dungeons") { true }
}
enum class SlotRenderLinesMode : StringIdentifiable {
@@ -120,7 +123,11 @@ object SlotLocking : FirmamentFeature {
var anyBlocked = false
for (i in 0 until event.slot.index) {
val stack = inv.getStack(i)
- if (IsSlotProtectedEvent.shouldBlockInteraction(null, SlotActionType.THROW, stack))
+ if (IsSlotProtectedEvent.shouldBlockInteraction(null,
+ SlotActionType.THROW,
+ IsSlotProtectedEvent.MoveOrigin.SALVAGE,
+ stack)
+ )
anyBlocked = true
}
if (anyBlocked) {
@@ -156,6 +163,19 @@ object SlotLocking : FirmamentFeature {
}
@Subscribe
+ fun onEvent(event: FeaturesInitializedEvent) {
+ IsSlotProtectedEvent.subscribe(receivesCancelled = true, "SlotLocking:unlockInDungeons") {
+ if (it.isProtected
+ && it.origin == IsSlotProtectedEvent.MoveOrigin.DROP_FROM_HOTBAR
+ && DungeonUtil.isInActiveDungeon
+ && TConfig.allowDroppingInDungeons
+ ) {
+ it.isProtected = false
+ }
+ }
+ }
+
+ @Subscribe
fun onQuickMoveBoundSlot(it: IsSlotProtectedEvent) {
val boundSlots = DConfig.data?.boundSlots ?: mapOf()
val isValidAction =
@@ -245,7 +265,7 @@ object SlotLocking : FirmamentFeature {
val (hotX, hotY) = hotbarSlot.lineCenter()
val (invX, invY) = inventorySlot.lineCenter()
val anyHovered = accScreen.focusedSlot_Firmament === hotbarSlot
- || accScreen.focusedSlot_Firmament === inventorySlot
+ || accScreen.focusedSlot_Firmament === inventorySlot
if (!anyHovered && TConfig.slotRenderLines == SlotRenderLinesMode.NOTHING)
continue
val color = if (anyHovered)
diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt
index cf1cf1d..633a8fe 100644
--- a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt
+++ b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt
@@ -35,6 +35,7 @@ import moe.nea.firmament.util.mc.FakeSlot
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.render.drawGuiTexture
+import moe.nea.firmament.util.render.enableScissorWithoutTranslation
import moe.nea.firmament.util.tr
import moe.nea.firmament.util.unformattedString
@@ -241,9 +242,9 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
fun createScissors(context: DrawContext) {
val rect = getScrollPanelInner()
- context.enableScissor(
- rect.minX, rect.minY,
- rect.maxX, rect.maxY
+ context.enableScissorWithoutTranslation(
+ rect.minX.toFloat(), rect.minY.toFloat(),
+ rect.maxX.toFloat(), rect.maxY.toFloat(),
)
}
@@ -302,6 +303,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
}
fun mouseClicked(mouseX: Double, mouseY: Double, button: Int, activePage: StoragePageSlot?): Boolean {
+ guiContext.setFocusedElement(null) // Blur all elements. They will be refocused by clickMCComponentInPlace if in doubt, and we don't have any double click components.
if (getScrollPanelInner().contains(mouseX, mouseY)) {
val data = StorageOverlay.Data.data ?: StorageData()
layoutedForEach(data) { rect, page, _ ->
diff --git a/src/main/kotlin/features/mining/PickaxeAbility.kt b/src/main/kotlin/features/mining/PickaxeAbility.kt
index 94b49f9..1737969 100644
--- a/src/main/kotlin/features/mining/PickaxeAbility.kt
+++ b/src/main/kotlin/features/mining/PickaxeAbility.kt
@@ -156,10 +156,21 @@ object PickaxeAbility : FirmamentFeature {
fun onChatMessage(it: ProcessChatEvent) {
abilityUsePattern.useMatch(it.unformattedString) {
lastUsage[group("name")] = TimeMark.now()
+ abilityOverride = group("name")
}
abilitySwitchPattern.useMatch(it.unformattedString) {
abilityOverride = group("ability")
}
+ pickaxeAbilityCooldownPattern.useMatch(it.unformattedString) {
+ val ability = abilityOverride ?: return@useMatch
+ val remainingCooldown = parseTimePattern(group("remainingCooldown"))
+ val length = defaultAbilityDurations[ability] ?: return@useMatch
+ lastUsage[ability] = TimeMark.ago(length - remainingCooldown)
+ }
+ nowAvailable.useMatch(it.unformattedString) {
+ val ability = group("name")
+ lastUsage[ability] = TimeMark.farPast()
+ }
}
@Subscribe
@@ -179,6 +190,7 @@ object PickaxeAbility : FirmamentFeature {
val fuelPattern = Pattern.compile("Fuel: .*/(?<maxFuel>$SHORT_NUMBER_FORMAT)")
val pickaxeAbilityCooldownPattern =
Pattern.compile("Your pickaxe ability is on cooldown for (?<remainingCooldown>$TIME_PATTERN)\\.")
+ val nowAvailable = Pattern.compile("(?<name>[a-zA-Z0-9 ]+) is now available!")
data class PickaxeAbilityData(
val name: String,
diff --git a/src/main/kotlin/features/world/Waypoints.kt b/src/main/kotlin/features/world/Waypoints.kt
index 16db059..3ebfe70 100644
--- a/src/main/kotlin/features/world/Waypoints.kt
+++ b/src/main/kotlin/features/world/Waypoints.kt
@@ -4,6 +4,7 @@ import com.mojang.brigadier.arguments.IntegerArgumentType
import me.shedaniel.math.Color
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
import kotlinx.serialization.Serializable
+import kotlinx.serialization.encodeToString
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
@@ -32,6 +33,7 @@ import moe.nea.firmament.util.ClipboardUtils
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.render.RenderInWorldContext
+import moe.nea.firmament.util.tr
object Waypoints : FirmamentFeature {
override val identifier: String
@@ -198,11 +200,22 @@ object Waypoints : FirmamentFeature {
}
}
}
+ thenLiteral("export") {
+ thenExecute {
+ val data = Firmament.tightJson.encodeToString<List<ColeWeightWaypoint>>(waypoints.map {
+ ColeWeightWaypoint(it.x,
+ it.y,
+ it.z)
+ })
+ ClipboardUtils.setTextContent(data)
+ source.sendFeedback(tr("firmament.command.waypoint.export", "Copied ${waypoints.size} waypoints to clipboard"))
+ }
+ }
thenLiteral("import") {
thenExecute {
val contents = ClipboardUtils.getTextContents()
val data = try {
- Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents)
+ Firmament.tightJson.decodeFromString<List<ColeWeightWaypoint>>(contents)
} catch (ex: Exception) {
Firmament.logger.error("Could not load waypoints from clipboard", ex)
source.sendError(Text.translatable("firmament.command.waypoint.import.error"))
diff --git a/src/main/kotlin/repo/BetterRepoRecipeCache.kt b/src/main/kotlin/repo/BetterRepoRecipeCache.kt
index 4b32e57..6d18223 100644
--- a/src/main/kotlin/repo/BetterRepoRecipeCache.kt
+++ b/src/main/kotlin/repo/BetterRepoRecipeCache.kt
@@ -2,8 +2,10 @@ package moe.nea.firmament.repo
import io.github.moulberry.repo.IReloadable
import io.github.moulberry.repo.NEURepository
+import io.github.moulberry.repo.data.NEUNpcShopRecipe
import io.github.moulberry.repo.data.NEURecipe
import moe.nea.firmament.util.SkyblockId
+import moe.nea.firmament.util.skyblockId
class BetterRepoRecipeCache(vararg val extraProviders: ExtraRecipeProvider) : IReloadable {
var usages: Map<SkyblockId, Set<NEURecipe>> = mapOf()
@@ -17,6 +19,9 @@ class BetterRepoRecipeCache(vararg val extraProviders: ExtraRecipeProvider) : IR
.flatMap { it.recipes }
(baseRecipes + extraProviders.flatMap { it.provideExtraRecipes() })
.forEach { recipe ->
+ if (recipe is NEUNpcShopRecipe) {
+ usages.getOrPut(recipe.isSoldBy.skyblockId, ::mutableSetOf).add(recipe)
+ }
recipe.allInputs.forEach { usages.getOrPut(SkyblockId(it.itemId), ::mutableSetOf).add(recipe) }
recipe.allOutputs.forEach { recipes.getOrPut(SkyblockId(it.itemId), ::mutableSetOf).add(recipe) }
}
diff --git a/src/main/kotlin/repo/ItemCache.kt b/src/main/kotlin/repo/ItemCache.kt
index e140dd8..9fa0083 100644
--- a/src/main/kotlin/repo/ItemCache.kt
+++ b/src/main/kotlin/repo/ItemCache.kt
@@ -24,6 +24,7 @@ import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.NbtString
+import net.minecraft.text.MutableText
import net.minecraft.text.Style
import net.minecraft.text.Text
import moe.nea.firmament.Firmament
@@ -100,7 +101,7 @@ object ItemCache : IReloadable {
}
}
- fun un189Lore(lore: String): Text {
+ fun un189Lore(lore: String): MutableText {
val base = Text.literal("")
base.setStyle(Style.EMPTY.withItalic(false))
var lastColorCode = Style.EMPTY
diff --git a/src/main/kotlin/repo/SBItemStack.kt b/src/main/kotlin/repo/SBItemStack.kt
index 4d07801..da34707 100644
--- a/src/main/kotlin/repo/SBItemStack.kt
+++ b/src/main/kotlin/repo/SBItemStack.kt
@@ -17,8 +17,10 @@ import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.ItemCache.withFallback
import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.LegacyFormattingCode
+import moe.nea.firmament.util.MC
import moe.nea.firmament.util.ReforgeId
import moe.nea.firmament.util.SkyblockId
+import moe.nea.firmament.util.blue
import moe.nea.firmament.util.directLiteralStringContent
import moe.nea.firmament.util.extraAttributes
import moe.nea.firmament.util.getReforgeId
@@ -27,12 +29,16 @@ import moe.nea.firmament.util.grey
import moe.nea.firmament.util.mc.appendLore
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
+import moe.nea.firmament.util.mc.modifyLore
+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.skyBlockId
import moe.nea.firmament.util.skyblock.ItemType
import moe.nea.firmament.util.skyblock.Rarity
import moe.nea.firmament.util.skyblockId
+import moe.nea.firmament.util.unformattedString
import moe.nea.firmament.util.useMatch
import moe.nea.firmament.util.withColor
@@ -99,6 +105,13 @@ data class SBItemStack constructor(
return SBItemStack(SkyblockId.NULL, null, itemStack.count, null, fallback = itemStack)
}
+ fun parseStatBlock(itemStack: ItemStack): List<StatLine> {
+ return itemStack.loreAccordingToNbt
+ .map { parseStatLine(it) }
+ .takeWhile { it != null }
+ .filterNotNull()
+ }
+
fun appendEnhancedStats(
itemStack: ItemStack,
reforgeStats: Map<String, Double>,
@@ -135,6 +148,7 @@ data class SBItemStack constructor(
data class StatFormatting(
val postFix: String,
val color: Formatting,
+ val isStarAffected: Boolean = true,
)
val formattingOverrides = mapOf(
@@ -142,7 +156,7 @@ data class SBItemStack constructor(
"Strength" to StatFormatting("", Formatting.RED),
"Damage" to StatFormatting("", Formatting.RED),
"Bonus Attack Speed" to StatFormatting("%", Formatting.RED),
- "Shot Cooldown" to StatFormatting("s", Formatting.RED),
+ "Shot Cooldown" to StatFormatting("s", Formatting.GREEN, false),
"Ability Damage" to StatFormatting("%", Formatting.RED),
"Crit Damage" to StatFormatting("%", Formatting.RED),
"Crit Chance" to StatFormatting("%", Formatting.RED),
@@ -184,9 +198,11 @@ data class SBItemStack constructor(
val color: Formatting,
val prefix: String,
val postFix: String,
+ val isHidden: Boolean,
) {
- REFORGE(Formatting.BLUE, "(", ")"),
-
+ REFORGE(Formatting.BLUE, "(", ")", false),
+ STAR_BUFF(Formatting.RESET, "", "", true),
+ CATA_STAR_BUFF(Formatting.DARK_GRAY, "(", ")", false),
;
}
@@ -194,7 +210,7 @@ data class SBItemStack constructor(
val statName: String,
val value: Text?,
val rest: List<Text> = listOf(),
- val valueNum: Double? = value?.directLiteralStringContent?.trim(' ', '%', '+')?.toDoubleOrNull()
+ val valueNum: Double? = value?.directLiteralStringContent?.trim(' ', 's', '%', '+')?.toDoubleOrNull()
) {
fun addStat(amount: Double, buffKind: BuffKind): StatLine {
val formattedAmount = FirmFormatters.formatCommas(amount, 1, includeSign = true)
@@ -202,7 +218,8 @@ data class SBItemStack constructor(
valueNum = (valueNum ?: 0.0) + amount,
value = null,
rest = rest +
- listOf(
+ if (buffKind.isHidden) emptyList()
+ else listOf(
Text.literal(
buffKind.prefix + formattedAmount +
statFormatting.postFix +
@@ -308,6 +325,22 @@ data class SBItemStack constructor(
data.putString("modifier", reforgeId.id)
itemStack.extraAttributes = data
appendEnhancedStats(itemStack, reforgeStats, BuffKind.REFORGE)
+ reforge.reforgeAbility?.get(rarity)?.let { reforgeAbility ->
+ val formattedReforgeAbility = ItemCache.un189Lore(reforgeAbility)
+ .grey()
+ itemStack.modifyLore {
+ val lastBlank = it.indexOfLast { it.unformattedString.isBlank() }
+ val newList = mutableListOf<Text>()
+ newList.addAll(it.subList(0, lastBlank))
+ newList.add(Text.literal(""))
+ newList.add(Text.literal("${reforge.reforgeName} Bonus").blue())
+ MC.font.textHandler.wrapLines(formattedReforgeAbility, 180, Style.EMPTY).mapTo(newList) {
+ it.reconstitute()
+ }
+ newList.addAll(it.subList(lastBlank, it.size))
+ return@modifyLore newList
+ }
+ }
}
// TODO: avoid instantiating the item stack here
@@ -325,12 +358,14 @@ data class SBItemStack constructor(
return@run ItemStack.EMPTY
val replacementData = mutableMapOf<String, String>()
injectReplacementDataForPets(replacementData)
- return@run neuItem.asItemStack(idHint = skyblockId, replacementData)
+ val baseItem = neuItem.asItemStack(idHint = skyblockId, replacementData)
.withFallback(fallback)
.copyWithCount(stackSize)
- .also { appendReforgeInfo(it) }
- .also { it.appendLore(extraLore) }
- .also { enhanceStatsByStars(it, stars) }
+ val baseStats = parseStatBlock(baseItem)
+ appendReforgeInfo(baseItem)
+ baseItem.appendLore(extraLore)
+ enhanceStatsByStars(baseItem, stars, baseStats)
+ return@run baseItem
}
if (itemStack_ == null)
itemStack_ = itemStack
@@ -340,6 +375,7 @@ data class SBItemStack constructor(
private fun starString(stars: Int): Text {
if (stars <= 0) return Text.empty()
+ // TODO: idk master stars
val tiers = listOf(
LegacyFormattingCode.GOLD,
LegacyFormattingCode.LIGHT_PURPLE,
@@ -359,11 +395,23 @@ data class SBItemStack constructor(
return starString
}
- private fun enhanceStatsByStars(itemStack: ItemStack, stars: Int) {
+ private fun enhanceStatsByStars(itemStack: ItemStack, stars: Int, baseStats: List<StatLine>) {
if (stars == 0) return
// TODO: increase stats and add the star level into the nbt data so star displays work
+ itemStack.modifyExtraAttributes {
+ it.putInt("upgrade_level", stars)
+ }
itemStack.displayNameAccordingToNbt = itemStack.displayNameAccordingToNbt.copy()
.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)
}
fun asImmutableItemStack(): ItemStack {
diff --git a/src/main/kotlin/util/FirmFormatters.kt b/src/main/kotlin/util/FirmFormatters.kt
index 4b32c2a..acb7102 100644
--- a/src/main/kotlin/util/FirmFormatters.kt
+++ b/src/main/kotlin/util/FirmFormatters.kt
@@ -9,11 +9,39 @@ import kotlin.io.path.isReadable
import kotlin.io.path.isRegularFile
import kotlin.io.path.listDirectoryEntries
import kotlin.math.absoluteValue
+import kotlin.math.roundToInt
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import net.minecraft.text.Text
object FirmFormatters {
+
+ private inline fun shortIf(
+ value: Double, breakpoint: Double, char: String,
+ return_: (String) -> Nothing
+ ) {
+ if (value >= breakpoint) {
+ val broken = (value / breakpoint * 10).roundToInt()
+ if (broken > 99)
+ return_((broken / 10).toString() + char)
+ val decimals = broken.toString()
+ decimals.singleOrNull()?.let {
+ return_("0.$it$char")
+ }
+ return_("${decimals[0]}.${decimals[1]}$char")
+ }
+ }
+
+ fun shortFormat(double: Double): String {
+ if (double < 0) return "-" + shortFormat(-double)
+ shortIf(double, 1_000_000_000_000.0, "t") { return it }
+ shortIf(double, 1_000_000_000.0, "b") { return it }
+ shortIf(double, 1_000_000.0, "m") { return it }
+ shortIf(double, 1_000.0, "k") { return it }
+ shortIf(double, 1.0, "") { return it }
+ return double.toString()
+ }
+
fun formatCommas(int: Int, segments: Int = 3): String = formatCommas(int.toLong(), segments)
fun formatCommas(long: Long, segments: Int = 3, includeSign: Boolean = false): String {
if (long < 0 && long != Long.MIN_VALUE) {
diff --git a/src/main/kotlin/util/MoulConfigUtils.kt b/src/main/kotlin/util/MoulConfigUtils.kt
index 62bf3dd..362a4d9 100644
--- a/src/main/kotlin/util/MoulConfigUtils.kt
+++ b/src/main/kotlin/util/MoulConfigUtils.kt
@@ -25,6 +25,7 @@ import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.screen.Screen
+import net.minecraft.client.util.InputUtil
import moe.nea.firmament.gui.BarComponent
import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.gui.FirmHoverComponent
@@ -257,7 +258,17 @@ object MoulConfigUtils {
keyboardEvent: KeyboardEvent
): Boolean {
val immContext = createInPlaceFullContext(null, IMinecraft.instance.mouseX, IMinecraft.instance.mouseY)
- return component.keyboardEvent(keyboardEvent, immContext.translated(x, y, w, h))
+ if (component.keyboardEvent(keyboardEvent, immContext.translated(x, y, w, h)))
+ return true
+ if (component.context.getFocusedElement() != null) {
+ if (keyboardEvent is KeyboardEvent.KeyPressed
+ && keyboardEvent.pressed && keyboardEvent.keycode == InputUtil.GLFW_KEY_ESCAPE
+ ) {
+ component.context.setFocusedElement(null)
+ }
+ return true
+ }
+ return false
}
fun clickMCComponentInPlace(
diff --git a/src/main/kotlin/util/SBData.kt b/src/main/kotlin/util/SBData.kt
index e785ff6..b2f9449 100644
--- a/src/main/kotlin/util/SBData.kt
+++ b/src/main/kotlin/util/SBData.kt
@@ -19,6 +19,11 @@ object SBData {
"CLICK THIS TO SUGGEST IT IN CHAT [NO DASHES]",
)
var profileId: UUID? = null
+ get() {
+ // TODO: allow unfiltered access to this somehow
+ if (!isOnSkyblock) return null
+ return field
+ }
/**
* Source: https://hypixel-skyblock.fandom.com/wiki/Time_Systems
@@ -37,11 +42,15 @@ object SBData {
HypixelModAPI.getInstance().createHandler(ClientboundLocationPacket::class.java) {
MC.onMainThread {
val lastLocraw = locraw
+ val oldProfileId = profileId
locraw = Locraw(it.serverName,
it.serverType.getOrNull()?.name?.uppercase(),
it.mode.getOrNull(),
it.map.getOrNull())
SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, locraw))
+ if(oldProfileId != profileId) {
+ ProfileSwitchEvent.publish(ProfileSwitchEvent(oldProfileId, profileId))
+ }
profileIdCommandDebounce = TimeMark.now()
}
}
diff --git a/src/main/kotlin/util/ScoreboardUtil.kt b/src/main/kotlin/util/ScoreboardUtil.kt
index 4311971..0970892 100644
--- a/src/main/kotlin/util/ScoreboardUtil.kt
+++ b/src/main/kotlin/util/ScoreboardUtil.kt
@@ -1,8 +1,6 @@
-
-
package moe.nea.firmament.util
-import java.util.*
+import java.util.Optional
import net.minecraft.client.gui.hud.InGameHud
import net.minecraft.scoreboard.ScoreboardDisplaySlot
import net.minecraft.scoreboard.Team
@@ -10,36 +8,48 @@ import net.minecraft.text.StringVisitable
import net.minecraft.text.Style
import net.minecraft.text.Text
import net.minecraft.util.Formatting
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.events.TickEvent
-fun getScoreboardLines(): List<Text> {
- val scoreboard = MC.player?.scoreboard ?: return listOf()
- val activeObjective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.SIDEBAR) ?: return listOf()
- return scoreboard.getScoreboardEntries(activeObjective)
- .filter { !it.hidden() }
- .sortedWith(InGameHud.SCOREBOARD_ENTRY_COMPARATOR)
- .take(15).map {
- val team = scoreboard.getScoreHolderTeam(it.owner)
- val text = it.name()
- Team.decorateName(team, text)
- }
-}
+object ScoreboardUtil {
+ var scoreboardLines: List<Text> = listOf()
+ var simplifiedScoreboardLines: List<String> = listOf()
+ @Subscribe
+ fun onTick(event: TickEvent) {
+ scoreboardLines = getScoreboardLinesUncached()
+ simplifiedScoreboardLines = scoreboardLines.map { it.unformattedString }
+ }
+
+ private fun getScoreboardLinesUncached(): List<Text> {
+ val scoreboard = MC.player?.scoreboard ?: return listOf()
+ val activeObjective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.SIDEBAR) ?: return listOf()
+ return scoreboard.getScoreboardEntries(activeObjective)
+ .filter { !it.hidden() }
+ .sortedWith(InGameHud.SCOREBOARD_ENTRY_COMPARATOR)
+ .take(15).map {
+ val team = scoreboard.getScoreHolderTeam(it.owner)
+ val text = it.name()
+ Team.decorateName(team, text)
+ }
+ }
+}
fun Text.formattedString(): String {
- val sb = StringBuilder()
- visit(StringVisitable.StyledVisitor<Unit> { style, string ->
- val c = Formatting.byName(style.color?.name)
- if (c != null) {
- sb.append("§${c.code}")
- }
- if (style.isUnderlined) {
- sb.append("§n")
- }
- if (style.isBold) {
- sb.append("§l")
- }
- sb.append(string)
- Optional.empty()
- }, Style.EMPTY)
- return sb.toString().replace("§[^a-f0-9]".toRegex(), "")
+ val sb = StringBuilder()
+ visit(StringVisitable.StyledVisitor<Unit> { style, string ->
+ val c = Formatting.byName(style.color?.name)
+ if (c != null) {
+ sb.append("§${c.code}")
+ }
+ if (style.isUnderlined) {
+ sb.append("§n")
+ }
+ if (style.isBold) {
+ sb.append("§l")
+ }
+ sb.append(string)
+ Optional.empty()
+ }, Style.EMPTY)
+ return sb.toString().replace("§[^a-f0-9]".toRegex(), "")
}
diff --git a/src/main/kotlin/util/SkyBlockIsland.kt b/src/main/kotlin/util/SkyBlockIsland.kt
index f15cadb..a86543c 100644
--- a/src/main/kotlin/util/SkyBlockIsland.kt
+++ b/src/main/kotlin/util/SkyBlockIsland.kt
@@ -36,6 +36,7 @@ private constructor(
val RIFT = forMode("rift")
val MINESHAFT = forMode("mineshaft")
val GARDEN = forMode("garden")
+ val DUNGEON = forMode("dungeon")
}
val userFriendlyName
diff --git a/src/main/kotlin/util/SkyblockId.kt b/src/main/kotlin/util/SkyblockId.kt
index 631b444..a99afda 100644
--- a/src/main/kotlin/util/SkyblockId.kt
+++ b/src/main/kotlin/util/SkyblockId.kt
@@ -119,6 +119,12 @@ var ItemStack.extraAttributes: NbtCompound
return customData.nbt
}
+fun ItemStack.modifyExtraAttributes(block: (NbtCompound) -> Unit) {
+ val baseNbt = get(DataComponentTypes.CUSTOM_DATA)?.copyNbt() ?: NbtCompound()
+ block(baseNbt)
+ set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(baseNbt))
+}
+
val ItemStack.skyblockUUIDString: String?
get() = extraAttributes.getString("uuid")?.takeIf { it.isNotBlank() }
diff --git a/src/main/kotlin/util/mc/IntrospectableItemModelManager.kt b/src/main/kotlin/util/mc/IntrospectableItemModelManager.kt
new file mode 100644
index 0000000..e546fd3
--- /dev/null
+++ b/src/main/kotlin/util/mc/IntrospectableItemModelManager.kt
@@ -0,0 +1,7 @@
+package moe.nea.firmament.util.mc
+
+import net.minecraft.util.Identifier
+
+interface IntrospectableItemModelManager {
+ fun hasModel_firmament(identifier: Identifier): Boolean
+}
diff --git a/src/main/kotlin/util/render/TranslatedScissors.kt b/src/main/kotlin/util/render/TranslatedScissors.kt
index c1e6544..8f8bdcf 100644
--- a/src/main/kotlin/util/render/TranslatedScissors.kt
+++ b/src/main/kotlin/util/render/TranslatedScissors.kt
@@ -1,11 +1,15 @@
package moe.nea.firmament.util.render
+import org.joml.Matrix4f
import org.joml.Vector4f
import net.minecraft.client.gui.DrawContext
fun DrawContext.enableScissorWithTranslation(x1: Float, y1: Float, x2: Float, y2: Float) {
- val pMat = matrices.peek().positionMatrix
+ enableScissor(x1.toInt(), y1.toInt(), x2.toInt(), y2.toInt())
+}
+fun DrawContext.enableScissorWithoutTranslation(x1: Float, y1: Float, x2: Float, y2: Float) {
+ val pMat = matrices.peek().positionMatrix.invert(Matrix4f())
val target = Vector4f()
target.set(x1, y1, 0f, 1f)
diff --git a/src/main/kotlin/util/skyblock/DungeonUtil.kt b/src/main/kotlin/util/skyblock/DungeonUtil.kt
new file mode 100644
index 0000000..488b158
--- /dev/null
+++ b/src/main/kotlin/util/skyblock/DungeonUtil.kt
@@ -0,0 +1,33 @@
+package moe.nea.firmament.util.skyblock
+
+import moe.nea.firmament.util.SBData
+import moe.nea.firmament.util.ScoreboardUtil
+import moe.nea.firmament.util.SkyBlockIsland
+import moe.nea.firmament.util.TIME_PATTERN
+
+object DungeonUtil {
+ val isInDungeonIsland get() = SBData.skyblockLocation == SkyBlockIsland.DUNGEON
+ private val timeElapsedRegex = "Time Elapsed: $TIME_PATTERN".toRegex()
+ val isInActiveDungeon get() = isInDungeonIsland && ScoreboardUtil.simplifiedScoreboardLines.any { it.matches(
+ timeElapsedRegex) }
+
+/*Title:
+
+§f§lSKYBLOCK§B§L CO-OP
+
+' Late Spring 7th'
+' §75:20am'
+' §7⏣ §cThe Catacombs §7(M3)'
+' §7♲ §7Ironman'
+' '
+'Keys: §c■ §c✗ §8■ §a1x'
+'Time Elapsed: §a46s'
+'Cleared: §660% §8(105)'
+' '
+'§e[B] §b151_Dragon §e2,062§c❤'
+'§e[A] §6Lennart0312 §a17,165§c'
+'§e[T] §b187i §a14,581§c❤'
+'§e[H] §bFlameeke §a8,998§c❤'
+' '
+'§ewww.hypixel.net'*/
+}
diff --git a/src/main/kotlin/util/skyblock/ItemType.kt b/src/main/kotlin/util/skyblock/ItemType.kt
index 6c7096c..7a776b5 100644
--- a/src/main/kotlin/util/skyblock/ItemType.kt
+++ b/src/main/kotlin/util/skyblock/ItemType.kt
@@ -67,6 +67,8 @@ value class ItemType private constructor(val name: String) {
val dungeonVariant get() = ofName("DUNGEON $name")
+ val isDungeon get() = name.startsWith("DUNGEON ")
+
override fun toString(): String {
return name
}
diff --git a/src/main/kotlin/util/textutil.kt b/src/main/kotlin/util/textutil.kt
index ab3de43..c295ae0 100644
--- a/src/main/kotlin/util/textutil.kt
+++ b/src/main/kotlin/util/textutil.kt
@@ -1,71 +1,17 @@
package moe.nea.firmament.util
+import java.util.Optional
import net.minecraft.text.ClickEvent
import net.minecraft.text.MutableText
+import net.minecraft.text.OrderedText
import net.minecraft.text.PlainTextContent
+import net.minecraft.text.StringVisitable
+import net.minecraft.text.Style
import net.minecraft.text.Text
import net.minecraft.text.TextColor
import net.minecraft.text.TranslatableTextContent
import net.minecraft.util.Formatting
-import moe.nea.firmament.Firmament
-
-
-class TextMatcher(text: Text) {
- data class State(
- var iterator: MutableList<Text>,
- var currentText: Text?,
- var offset: Int,
- var textContent: String,
- )
-
- var state = State(
- mutableListOf(text),
- null,
- 0,
- ""
- )
-
- fun pollChunk(): Boolean {
- val firstOrNull = state.iterator.removeFirstOrNull() ?: return false
- state.offset = 0
- state.currentText = firstOrNull
- state.textContent = when (val content = firstOrNull.content) {
- is PlainTextContent.Literal -> content.string
- else -> {
- Firmament.logger.warn("TextContent of type ${content.javaClass} not understood.")
- return false
- }
- }
- state.iterator.addAll(0, firstOrNull.siblings)
- return true
- }
- fun pollChunks(): Boolean {
- while (state.offset !in state.textContent.indices) {
- if (!pollChunk()) {
- return false
- }
- }
- return true
- }
-
- fun pollChar(): Char? {
- if (!pollChunks()) return null
- return state.textContent[state.offset++]
- }
-
-
- fun expectString(string: String): Boolean {
- var found = ""
- while (found.length < string.length) {
- if (!pollChunks()) return false
- val takeable = state.textContent.drop(state.offset).take(string.length - found.length)
- state.offset += takeable.length
- found += takeable
- }
- return found == string
- }
-}
val formattingChars = "kmolnrKMOLNR".toSet()
fun CharSequence.removeColorCodes(keepNonColorCodes: Boolean = false): String {
@@ -89,6 +35,47 @@ fun CharSequence.removeColorCodes(keepNonColorCodes: Boolean = false): String {
return stringBuffer.toString()
}
+fun OrderedText.reconstitute(): MutableText {
+ val base = Text.literal("")
+ base.setStyle(Style.EMPTY.withItalic(false))
+ var lastColorCode = Style.EMPTY
+ val text = StringBuilder()
+ this.accept { index, style, codePoint ->
+ if (style != lastColorCode) {
+ if (text.isNotEmpty())
+ base.append(Text.literal(text.toString()).setStyle(lastColorCode))
+ lastColorCode = style
+ text.clear()
+ }
+ text.append(codePoint.toChar())
+ true
+ }
+ if (text.isNotEmpty())
+ base.append(Text.literal(text.toString()).setStyle(lastColorCode))
+ return base
+
+}
+fun StringVisitable.reconstitute(): MutableText {
+ val base = Text.literal("")
+ base.setStyle(Style.EMPTY.withItalic(false))
+ var lastColorCode = Style.EMPTY
+ val text = StringBuilder()
+ this.visit({ style, string ->
+ if (style != lastColorCode) {
+ if (text.isNotEmpty())
+ base.append(Text.literal(text.toString()).setStyle(lastColorCode))
+ lastColorCode = style
+ text.clear()
+ }
+ text.append(string)
+ Optional.empty<Unit>()
+ }, Style.EMPTY)
+ if (text.isNotEmpty())
+ base.append(Text.literal(text.toString()).setStyle(lastColorCode))
+ return base
+
+}
+
val Text.unformattedString: String
get() = string.removeColorCodes() // TODO: maybe shortcircuit this with .visit
@@ -135,6 +122,7 @@ fun MutableText.pink() = withColor(Formatting.LIGHT_PURPLE)
fun MutableText.yellow() = withColor(Formatting.YELLOW)
fun MutableText.gold() = withColor(Formatting.GOLD)
fun MutableText.grey() = withColor(Formatting.GRAY)
+fun MutableText.darkGrey() = withColor(Formatting.DARK_GRAY)
fun MutableText.red() = withColor(Formatting.RED)
fun MutableText.white() = withColor(Formatting.WHITE)
fun MutableText.bold(): MutableText = styled { it.withBold(true) }
diff --git a/src/test/resources/testdata/items/books/feather_falling.snbt b/src/test/resources/testdata/items/books/feather_falling.snbt
new file mode 100644
index 0000000..1de4632
--- /dev/null
+++ b/src/test/resources/testdata/items/books/feather_falling.snbt
@@ -0,0 +1,36 @@
+{
+ components: {
+ "minecraft:attribute_modifiers": {
+ modifiers: [
+ ],
+ show_in_tooltip: 0b
+ },
+ "minecraft:custom_data": {
+ enchantments: {
+ feather_falling: 6
+ },
+ id: "ENCHANTED_BOOK",
+ timestamp: 1737123521091L,
+ uuid: "b8128489-9ed0-4a1a-94c0-d3279ffe45ac"
+ },
+ "minecraft:custom_name": '{"extra":[{"color":"blue","text":"Enchanted Book"}],"italic":false,"text":""}',
+ "minecraft:hide_additional_tooltip": {
+ },
+ "minecraft:lore": [
+ '{"extra":[{"color":"blue","text":"Feather Falling VI"}],"italic":false,"text":""}',
+ '{"extra":[{"color":"gray","text":"Increases how high you can fall"}],"italic":false,"text":""}',
+ '{"extra":[{"color":"gray","text":"before taking fall damage by "},{"color":"green","text":"6"},{"color":"gray","text":" and"}],"italic":false,"text":""}',
+ '{"extra":[{"color":"gray","text":"reduces fall damage by "},{"color":"green","text":"30%"},{"color":"gray","text":"."}],"italic":false,"text":""}',
+ '{"italic":false,"text":""}',
+ '{"extra":[{"color":"gray","text":"Applicable on: "},{"color":"blue","text":"Boots"}],"italic":false,"text":""}',
+ '{"extra":[{"color":"gray","text":""},{"color":"gray","text":"Apply Cost: "},{"color":"dark_aqua","text":"60 Exp Levels"}],"italic":false,"text":""}',
+ '{"italic":false,"text":""}',
+ '{"extra":[{"color":"gray","text":"Use this on an item in an Anvil to"}],"italic":false,"text":""}',
+ '{"extra":[{"color":"gray","text":"apply it!"}],"italic":false,"text":""}',
+ '{"italic":false,"text":""}',
+ '{"extra":[{"bold":true,"color":"blue","text":"RARE"}],"italic":false,"text":""}'
+ ]
+ },
+ count: 1,
+ id: "minecraft:enchanted_book"
+}
diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt
index 85dfa32..aafc85a 100644
--- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt
+++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt
@@ -145,6 +145,7 @@ object CustomGlobalArmorOverrides {
null
}
}
+ bakedOverrides.clear()
val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } }
.toMap()
associatedMap.forEach { it.value.bake(manager) }
@@ -152,7 +153,6 @@ object CustomGlobalArmorOverrides {
}
override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) {
- bakedOverrides.clear()
overrides = prepared
}
})
@@ -160,11 +160,13 @@ object CustomGlobalArmorOverrides {
@JvmStatic
fun overrideArmor(itemStack: ItemStack, slot: EquipmentSlot): Optional<EquippableComponent> {
+ if (!CustomSkyBlockTextures.TConfig.enableArmorOverrides) return Optional.empty()
return overrideCache.invoke(itemStack, slot)
}
@JvmStatic
fun overrideArmorLayer(id: Identifier): EquipmentModel? {
+ if (!CustomSkyBlockTextures.TConfig.enableArmorOverrides) return null
return bakedOverrides[id]
}
diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt
index 02c0714..20f1fb6 100644
--- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt
+++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt
@@ -100,12 +100,12 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
manager.getResource(Identifier.of(key.namespace, "filters/screen/${key.path}.json"))
.getOrNull()
?: return@mapNotNull runNull {
- ErrorUtil.softError("Failed to locate screen filter at $key")
+ ErrorUtil.softError("Failed to locate screen filter at $key used by ${it.value.map { it.first }}")
}
val screenFilter =
Firmament.tryDecodeJsonFromStream<ScreenFilter>(guiClassResource.inputStream)
.getOrElse { ex ->
- ErrorUtil.softError("Failed to load screen filter at $key", ex)
+ ErrorUtil.softError("Failed to load screen filter at $key used by ${it.value.map { it.first }}", ex)
return@mapNotNull null
}
ItemOverrideCollection(screenFilter, it.value.map { it.second })
diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt
index d9ca5b4..bef52d2 100644
--- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt
+++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt
@@ -3,7 +3,6 @@ package moe.nea.firmament.features.texturepack
import com.mojang.authlib.minecraft.MinecraftProfileTexture
import com.mojang.authlib.properties.Property
import java.util.Optional
-import org.jetbrains.annotations.Nullable
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
import kotlin.jvm.optionals.getOrNull
import net.minecraft.block.SkullBlock
@@ -34,6 +33,7 @@ object CustomSkyBlockTextures : FirmamentFeature {
val enableModelOverrides by toggle("model-overrides") { true }
val enableArmorOverrides by toggle("armor-overrides") { true }
val enableBlockOverrides by toggle("block-overrides") { true }
+ val enableLegacyMinecraftCompat by toggle("legacy-minecraft-path-support") { true }
val enableLegacyCIT by toggle("legacy-cit") { true }
val allowRecoloringUiText by toggle("recolor-text") { true }
}
@@ -44,6 +44,7 @@ object CustomSkyBlockTextures : FirmamentFeature {
val allItemCaches by lazy {
listOf(
skullTextureCache.cache,
+ CustomItemModelEvent.cache.cache,
CustomGlobalArmorOverrides.overrideCache.cache
)
}
@@ -75,7 +76,7 @@ object CustomSkyBlockTextures : FirmamentFeature {
fun onCustomModelId(it: CustomItemModelEvent) {
if (!TConfig.enabled) return
val id = it.itemStack.skyBlockId ?: return
- it.overrideIfExists(Identifier.of("firmskyblock", id.identifier.path))
+ it.overrideIfEmpty(Identifier.of("firmskyblock", id.identifier.path))
}
private val skullTextureCache =
diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/predicates/ItemPredicate.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/predicates/ItemPredicate.kt
index 3cb80c7..4833dc0 100644
--- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/predicates/ItemPredicate.kt
+++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/predicates/ItemPredicate.kt
@@ -17,7 +17,7 @@ class ItemPredicate(
val item: Item
) : FirmamentModelPredicate {
override fun test(stack: ItemStack): Boolean {
- return stack.item == item
+ return stack.isOf(item)
}
object Parser : FirmamentModelPredicateParser {
diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/PatchLegacyTexturePathsIntoArmorLayers.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/PatchLegacyTexturePathsIntoArmorLayers.java
new file mode 100644
index 0000000..f829da0
--- /dev/null
+++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/PatchLegacyTexturePathsIntoArmorLayers.java
@@ -0,0 +1,38 @@
+package moe.nea.firmament.mixins.custommodels;
+
+
+import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
+import moe.nea.firmament.util.MC;
+import net.minecraft.client.render.entity.equipment.EquipmentModel;
+import net.minecraft.util.Identifier;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(EquipmentModel.Layer.class)
+public class PatchLegacyTexturePathsIntoArmorLayers {
+ @Shadow
+ @Final
+ private Identifier textureId;
+
+ @Inject(method = "getFullTextureId", at = @At("HEAD"), cancellable = true)
+ private void replaceWith1201TextureIfExists(EquipmentModel.LayerType layerType, CallbackInfoReturnable<Identifier> cir) {
+ if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableLegacyMinecraftCompat())
+ return;
+ var resourceManager = MC.INSTANCE.getResourceManager();
+ // legacy format: "assets/{identifier.namespace}/textures/models/armor/{identifier.path}_layer_{isLegs ? 2 : 1}{suffix}.png"
+ // suffix is sadly not available to us here. this means leather armor will look a bit shite
+ var legacyIdentifier = this.textureId.withPath((textureName) -> {
+ String var10000 = layerType.asString();
+ return "textures/models/armor/" + textureName + "_layer_" +
+ (layerType == EquipmentModel.LayerType.HUMANOID_LEGGINGS ? 2 : 1)
+ + ".png";
+ });
+ if (resourceManager.getResource(legacyIdentifier).isPresent()) {
+ cir.setReturnValue(legacyIdentifier);
+ }
+ }
+}
diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java
index dfc87a0..97abd1f 100644
--- a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java
+++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java
@@ -4,46 +4,40 @@ package moe.nea.firmament.mixins.custommodels;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import moe.nea.firmament.events.CustomItemModelEvent;
+import moe.nea.firmament.util.mc.IntrospectableItemModelManager;
import net.minecraft.client.item.ItemModelManager;
import net.minecraft.client.render.item.model.ItemModel;
import net.minecraft.client.render.item.model.MissingItemModel;
-import net.minecraft.client.render.model.BakedModelManager;
import net.minecraft.component.ComponentType;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
+import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.function.Function;
@Mixin(ItemModelManager.class)
-public class ReplaceItemModelPatch {
+public class ReplaceItemModelPatch implements IntrospectableItemModelManager {
@Shadow
@Final
private Function<Identifier, ItemModel> modelGetter;
- @Inject(method = "<init>", at = @At("TAIL"))
- private void saveMissingModel(BakedModelManager bakedModelManager, CallbackInfo ci) {
- }
-
- @Unique
- private boolean hasModel(Identifier identifier) {
- return !(modelGetter.apply(identifier) instanceof MissingItemModel);
- }
-
@WrapOperation(
method = "update(Lnet/minecraft/client/render/item/ItemRenderState;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;Lnet/minecraft/world/World;Lnet/minecraft/entity/LivingEntity;I)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;get(Lnet/minecraft/component/ComponentType;)Ljava/lang/Object;"))
private Object replaceItemModelByIdentifier(ItemStack instance, ComponentType componentType, Operation<Object> original) {
- var override = CustomItemModelEvent.getModelIdentifier(instance);
- if (override != null && hasModel(override)) {
+ var override = CustomItemModelEvent.getModelIdentifier(instance, this);
+ if (override != null && hasModel_firmament(override)) {
return override;
}
return original.call(instance, componentType);
}
+
+ @Override
+ public boolean hasModel_firmament(@NotNull Identifier identifier) {
+ return !(modelGetter.apply(identifier) instanceof MissingItemModel);
+ }
}
diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/SupplyFakeModelPatch.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/SupplyFakeModelPatch.java
index 8d3b3f8..b22920c 100644
--- a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/SupplyFakeModelPatch.java
+++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/SupplyFakeModelPatch.java
@@ -4,6 +4,7 @@ import com.google.gson.JsonObject;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.llamalad7.mixinextras.sugar.Local;
import moe.nea.firmament.Firmament;
+import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;
import moe.nea.firmament.features.texturepack.PredicateModel;
import moe.nea.firmament.util.ErrorUtil;
import net.minecraft.client.item.ItemAsset;
@@ -17,10 +18,7 @@ import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
-import java.io.IOException;
import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -46,12 +44,12 @@ public class SupplyFakeModelPatch {
}
private static ItemAssetsLoader.Result supplyExtraModels(ResourceManager resourceManager, ItemAssetsLoader.Result oldModels) {
+ if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableLegacyMinecraftCompat()) return oldModels;
Map<Identifier, ItemAsset> newModels = new HashMap<>(oldModels.contents());
var resources = resourceManager.findResources(
"models/item",
- id -> id.getNamespace().equals("firmskyblock")
- && id.getPath().endsWith(".json")
- && !id.getPath().substring("models/item/".length()).contains("/"));
+ id -> (id.getNamespace().equals("firmskyblock") || id.getNamespace().equals("cittofirmgenerated"))
+ && id.getPath().endsWith(".json"));
for (Map.Entry<Identifier, Resource> model : resources.entrySet()) {
var resource = model.getValue();
var itemModelId = model.getKey().withPath(it -> it.substring("models/item/".length(), it.length() - ".json".length()));
diff --git a/translations/en_us.json b/translations/en_us.json
index 82dcd45..1432332 100644
--- a/translations/en_us.json
+++ b/translations/en_us.json
@@ -81,6 +81,8 @@
"firmament.config.custom-skyblock-textures.enabled.description": "Allow replacing items for texture packs. Turning this off does not disable custom predicates",
"firmament.config.custom-skyblock-textures.legacy-cit": "Enable legacy CIT Resewn compat",
"firmament.config.custom-skyblock-textures.legacy-cit.description": "Allow CIT resewn texture packs written for 1.20.4 to be loaded on newer versions.",
+ "firmament.config.custom-skyblock-textures.legacy-minecraft-path-support": "Enable Legacy Paths",
+ "firmament.config.custom-skyblock-textures.legacy-minecraft-path-support.description": "Allow texture packs to load textures from some legacy paths. I.e.: Allows loading 1.21.0 armor textures on 1.21.4.",
"firmament.config.custom-skyblock-textures.model-overrides": "Enable model overrides/predicates",
"firmament.config.custom-skyblock-textures.model-overrides.description": "Enable Firmament's model predicates. This will apply to vanilla models as well, if that vanilla model has Firmament predicates.",
"firmament.config.custom-skyblock-textures.recolor-text": "Allow packs to recolor text",
@@ -231,6 +233,8 @@
"firmament.config.slot-locking.bind-render.choice.only_boxes": "Only boxes",
"firmament.config.slot-locking.bind-render.description": "Disable rendering of the slot binding lines (or all of the slot binding rendering), unless the relevant slot is being hovered.",
"firmament.config.slot-locking.bind.description": "Bind a hotbar slot to another slot. This allows quick switching between the slots by shift clicking on either slot.",
+ "firmament.config.slot-locking.drop-in-dungeons": "Allow Dungeon Abilities",
+ "firmament.config.slot-locking.drop-in-dungeons.description": "Allow dropping items in dungeons, to use your dungeon ultimate abilities.",
"firmament.config.slot-locking.lock": "Lock Slot",
"firmament.config.slot-locking.lock-uuid": "Lock UUID (Lock Item)",
"firmament.config.slot-locking.lock-uuid.description": "Lock a SkyBlock item by it's UUID. This blocks a specific item from being dropped/sold, but still allows moving it around.",