diff options
Diffstat (limited to 'src/compat')
56 files changed, 1361 insertions, 1185 deletions
diff --git a/src/compat/citresewn/java/ConditionDirectAccessHelper.kt b/src/compat/citresewn/java/ConditionDirectAccessHelper.kt deleted file mode 100644 index af97a40..0000000 --- a/src/compat/citresewn/java/ConditionDirectAccessHelper.kt +++ /dev/null @@ -1,59 +0,0 @@ -package moe.nea.firmament.compat.citresewn - -import java.lang.invoke.MethodHandles -import java.util.function.BiPredicate -import java.util.function.Function -import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionNBT - -object ConditionNBTMixin { - class Helper<StringMatcher> { - // TODO: make lambdametafactory work by way of modifying the actual modifiers - - val stringMatcherType = ConditionNBT::class.java.getDeclaredField("matchString").type - - val accessMatcher = run { - val matchStringF = ConditionNBT::class.java.getDeclaredField("matchString"); - matchStringF.isAccessible = true - val l = MethodHandles.privateLookupIn(ConditionNBT::class.java, MethodHandles.lookup()) -// val mt = MethodType.methodType(stringMatcherType, ConditionNBT::class.java) -// val callsite = LambdaMetafactory.metafactory( -// l, "apply", -// MethodType.methodType(Function::class.java), -// MethodType.methodType(java.lang.Object::class.java, java.lang.Object::class.java), -// l.unreflectGetter(matchStringF), -// mt -// ) - val getter = l.unreflectGetter(matchStringF) - Function<ConditionNBT, StringMatcher> { getter.invoke(it) as StringMatcher } - } - val directCaller = run { - val matchM = stringMatcherType.getDeclaredMethod("matches", String::class.java); - matchM.isAccessible = true - val l = MethodHandles.privateLookupIn(ConditionNBT::class.java, MethodHandles.lookup()) -// val mt = MethodType.methodType(java.lang.Boolean.TYPE, stringMatcherType, String::class.java) -// val callsite = LambdaMetafactory.metafactory( -// l, "test", -// MethodType.methodType(BiPredicate::class.java), -// mt, -// l.unreflect(matchM), -// mt -// ) - val func = l.unreflect(matchM) - BiPredicate<StringMatcher, String> { a, b -> func.invoke(a, b) as Boolean } - } - - fun test(condition: ConditionNBT, text: String): Boolean { - return directCaller.test(accessMatcher.apply(condition), text) as Boolean - } - } - - val helper = Helper<Any>() - - @JvmStatic - fun invokeDirectConditionNBTStringMatch( - nbt: ConditionNBT, - text: String, - ): Boolean { - return helper.test(nbt, text) - } -} diff --git a/src/compat/citresewn/java/moe/nea/firmament/mixins/compat/citresewn/MixinConditionComponents.java b/src/compat/citresewn/java/moe/nea/firmament/mixins/compat/citresewn/MixinConditionComponents.java deleted file mode 100644 index 0743d40..0000000 --- a/src/compat/citresewn/java/moe/nea/firmament/mixins/compat/citresewn/MixinConditionComponents.java +++ /dev/null @@ -1,123 +0,0 @@ -package moe.nea.firmament.mixins.compat.citresewn; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; -import com.llamalad7.mixinextras.sugar.ref.LocalRef; -import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.DynamicOps; -import moe.nea.firmament.compat.citresewn.ConditionNBTMixin; -import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures; -import net.minecraft.component.ComponentType; -import net.minecraft.component.DataComponentTypes; -import net.minecraft.component.type.NbtComponent; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Pseudo; -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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import shcm.shsupercm.fabric.citresewn.CITResewn; -import shcm.shsupercm.fabric.citresewn.cit.CITContext; -import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionComponents; -import shcm.shsupercm.fabric.citresewn.defaults.cit.conditions.ConditionNBT; -import shcm.shsupercm.fabric.citresewn.pack.format.PropertyGroup; -import shcm.shsupercm.fabric.citresewn.pack.format.PropertyKey; -import shcm.shsupercm.fabric.citresewn.pack.format.PropertyValue; - -// People are complaining but this really is not my place to fix things - -@Mixin(ConditionComponents.class) -@Pseudo -public class MixinConditionComponents { - @Shadow - private ComponentType<?> componentType; - - @Shadow(remap = false) - private ConditionNBT fallbackNBTCheck; - @Unique - private String[] pathCheck; - @Unique - private int loreInt = -1; - - @Inject(method = "load", at = @At("HEAD"), remap = false) - public void addExtraAttributeSupport(PropertyKey key, PropertyValue value, PropertyGroup properties, CallbackInfo ci, - @Local(argsOnly = true) LocalRef<PropertyKey> keyRef, - @Local(argsOnly = true) LocalRef<PropertyValue> valueRef) { - if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableLegacyCIT()) return; - if (!"nbt".equals(key.path())) return; - if (!value.keyMetadata().startsWith("ExtraAttributes.")) return; - keyRef.set(new PropertyKey(key.namespace(), "component")); - valueRef.set(new PropertyValue( - "minecraft:custom_data" + value.keyMetadata().substring("ExtraAttributes".length()), - value.value(), - value.separator(), - value.position(), - value.propertiesIdentifier(), - value.packName() - )); - CITResewn.logWarnLoading(properties.messageWithDescriptorOf("NBT condition is not supported since 1.21. THIS IS A FIRMAMENT SPECIAL FEATURE ALLOWING YOU TO CONTINUE TO USE nbt.ExtraAttributes.* PROPERTIES FOR A LIMITED TIME! UPDATE YOUR .PROPERTIES FILES OR SWITCH TO FIRMAMENT CIT (https://github.com/FirmamentMC/CitToFirm)", - value.position())); - } - - @Inject(method = "load", - at = @At(value = "INVOKE", remap = false, target = "Lshcm/shsupercm/fabric/citresewn/defaults/cit/conditions/ConditionNBT;loadNbtCondition(Lshcm/shsupercm/fabric/citresewn/pack/format/PropertyValue;Lshcm/shsupercm/fabric/citresewn/pack/format/PropertyGroup;[Ljava/lang/String;Ljava/lang/String;)V"), - remap = false) - private void onLoadSavePath(PropertyKey key, PropertyValue value, PropertyGroup properties, CallbackInfo ci, - @Local String[] path) { - this.pathCheck = path; - this.loreInt = -1; - } - - @Unique - private boolean matchStringDirect(String directString, CITContext context) { - return ConditionNBTMixin.invokeDirectConditionNBTStringMatch(fallbackNBTCheck, directString); - } - - @WrapOperation(method = "test", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/Codec;encodeStart(Lcom/mojang/serialization/DynamicOps;Ljava/lang/Object;)Lcom/mojang/serialization/DataResult;"), remap = false) - DataResult fastPathUnsafeNbtComponent( - Codec instance, - DynamicOps dynamicOps, - Object o, - Operation<DataResult> original) { - if (o instanceof NbtComponent nbtComponent) { - return DataResult.success(nbtComponent.getNbt()); - } - return original.call(instance, dynamicOps, o); - } - - @Inject(method = "test", at = @At("HEAD"), cancellable = true, remap = false) - void fastPathDisplayName(CITContext context, CallbackInfoReturnable<Boolean> cir) { - if (this.componentType == DataComponentTypes.CUSTOM_NAME && pathCheck.length == 0) { - var displayName = context.stack.getComponents().get(DataComponentTypes.CUSTOM_NAME); - if (displayName != null) { - cir.setReturnValue(matchStringDirect((displayName.getString()), context)); - } - } - if (this.componentType == DataComponentTypes.LORE && pathCheck.length == 1) { - var lore = context.stack.getComponents().get(DataComponentTypes.LORE); - if (lore != null) { - var loreLines = lore.lines(); - if (pathCheck[0].equals("*")) { - for (var loreLine : loreLines) { - if (matchStringDirect((loreLine.getString()), context)) { - cir.setReturnValue(true); - return; - } - } - cir.setReturnValue(false); - } else { - if (loreInt < 0) - loreInt = Integer.parseInt(pathCheck[0]); - cir.setReturnValue(0 <= loreInt && loreInt < loreLines.size() && - matchStringDirect((loreLines.get(loreInt).getString()), context)); - } - } - } - } - - -} diff --git a/src/compat/configured/java/BaseConfigNode.kt b/src/compat/configured/java/BaseConfigNode.kt deleted file mode 100644 index afe0375..0000000 --- a/src/compat/configured/java/BaseConfigNode.kt +++ /dev/null @@ -1,40 +0,0 @@ -package moe.nea.firmament.compat.configured - -import com.mrcrayfish.configured.api.IConfigEntry -import com.mrcrayfish.configured.api.IConfigValue -import net.minecraft.text.Text -import moe.nea.firmament.gui.config.AllConfigsGui -import moe.nea.firmament.gui.config.ManagedConfig - -object BaseConfigNode : IConfigEntry { - override fun getChildren(): List<IConfigEntry> { - return ManagedConfig.allManagedConfigs.getAll().map { - ConfigNode(it) // TODO: fix add categories here - } - } - - override fun isRoot(): Boolean { - return true - } - - override fun isLeaf(): Boolean { - return false - } - - override fun getValue(): IConfigValue<*>? { - return null - } - - override fun getEntryName(): String { - return "Firmament" - } - - override fun getTooltip(): Text? { - return null - } - - override fun getTranslationKey(): String? { - return null - } - -} diff --git a/src/compat/configured/java/ConfigCategory.kt b/src/compat/configured/java/ConfigCategory.kt deleted file mode 100644 index 4e33b8b..0000000 --- a/src/compat/configured/java/ConfigCategory.kt +++ /dev/null @@ -1,48 +0,0 @@ -package moe.nea.firmament.compat.configured - -import com.mrcrayfish.configured.api.ConfigType -import com.mrcrayfish.configured.api.IConfigEntry -import com.mrcrayfish.configured.api.IModConfig -import com.mrcrayfish.configured.util.ConfigHelper -import java.nio.file.Path -import java.util.function.Consumer -import moe.nea.firmament.Firmament -import moe.nea.firmament.gui.config.ManagedConfig - -class ConfigCategory( - val category: ManagedConfig -) : BaseConfig() { - - override fun getRoot(): IConfigEntry { - return ConfigNode(category) - } - - override fun getTranslationKey(): String? { - return category.translationKey - } -} - -abstract class BaseConfig : IModConfig { - override fun update(p0: IConfigEntry) { - ConfigHelper.getChangedValues(p0).forEach { - it as ConfigValue - it.saveValue() - } - } - - override fun getType(): ConfigType { - return ConfigType.CLIENT - } - - override fun getFileName(): String { - return "" - } - - override fun getModId(): String { - return Firmament.MOD_ID - } - - override fun loadWorldConfig(p0: Path?, p1: Consumer<IModConfig>?) { - } - -} diff --git a/src/compat/configured/java/ConfigNode.kt b/src/compat/configured/java/ConfigNode.kt deleted file mode 100644 index 16e54a6..0000000 --- a/src/compat/configured/java/ConfigNode.kt +++ /dev/null @@ -1,39 +0,0 @@ -package moe.nea.firmament.compat.configured - -import com.mrcrayfish.configured.api.IConfigEntry -import com.mrcrayfish.configured.api.IConfigValue -import net.minecraft.text.Text -import moe.nea.firmament.gui.config.ManagedConfig - -class ConfigNode(val config: ManagedConfig) : IConfigEntry { - override fun getChildren(): List<IConfigEntry> { - return config.allOptions.map { - ConfigValueNode(it.value) - } - } - - override fun isRoot(): Boolean { - return false - } - - override fun isLeaf(): Boolean { - return false - } - - override fun getValue(): IConfigValue<*>? { - return null - } - - override fun getEntryName(): String { - return config.translationKey - } - - override fun getTooltip(): Text? { - return null - } - - override fun getTranslationKey(): String { - return config.translationKey - } - -} diff --git a/src/compat/configured/java/ConfigValue.kt b/src/compat/configured/java/ConfigValue.kt deleted file mode 100644 index e16c51c..0000000 --- a/src/compat/configured/java/ConfigValue.kt +++ /dev/null @@ -1,72 +0,0 @@ -package moe.nea.firmament.compat.configured - -import com.mrcrayfish.configured.api.IConfigValue -import net.minecraft.text.Text -import moe.nea.firmament.gui.config.ManagedOption - -class ConfigValue<T: Any>(val option: ManagedOption<T>) : IConfigValue<T> { - var value = option.get() - var initialValue = option.get() - - override fun get(): T { - return value - } - - override fun set(p0: T) { - this.value = p0 - } - - override fun getDefault(): T { - return option.default() - } - - override fun isDefault(): Boolean { - // TODO: should this be an option in handlers? - return option == option.default() - } - - override fun isChanged(): Boolean { - return value != initialValue - } - - override fun restore() { - this.value = option.default() - } - - override fun getComment(): Text? { - return null - } - - override fun getTranslationKey(): String? { - return option.rawLabelText - } - - override fun getValidationHint(): Text? { - return null - } - - override fun getName(): String { - return "" - } - - override fun cleanCache() { - - } - - override fun requiresWorldRestart(): Boolean { - return false - } - - override fun requiresGameRestart(): Boolean { - return false - } - - override fun isValid(p0: T): Boolean { - // TODO: should this be validated? - return true - } - - fun saveValue() { - option.set(value) - } -} diff --git a/src/compat/configured/java/ConfigValueNode.kt b/src/compat/configured/java/ConfigValueNode.kt deleted file mode 100644 index df59739..0000000 --- a/src/compat/configured/java/ConfigValueNode.kt +++ /dev/null @@ -1,37 +0,0 @@ -package moe.nea.firmament.compat.configured - -import com.mrcrayfish.configured.api.IConfigEntry -import com.mrcrayfish.configured.api.IConfigValue -import net.minecraft.text.Text -import moe.nea.firmament.gui.config.ManagedOption - -class ConfigValueNode(val option: ManagedOption<*>) : IConfigEntry { - override fun getChildren(): List<IConfigEntry> { - return listOf() - } - - override fun isRoot(): Boolean { - return false - } - - override fun isLeaf(): Boolean { - return true - } - - val value = ConfigValue(option) - override fun getValue(): IConfigValue<*>? { - return value - } - - override fun getEntryName(): String { - return option.propertyName - } - - override fun getTooltip(): Text? { - return null - } - - override fun getTranslationKey(): String? { - return option.rawLabelText - } -} diff --git a/src/compat/configured/java/ConfiguredCompat.kt b/src/compat/configured/java/ConfiguredCompat.kt deleted file mode 100644 index 8e8b022..0000000 --- a/src/compat/configured/java/ConfiguredCompat.kt +++ /dev/null @@ -1,30 +0,0 @@ -package moe.nea.firmament.compat.configured - -import com.mrcrayfish.configured.api.IConfigEntry -import com.mrcrayfish.configured.api.IModConfig -import com.mrcrayfish.configured.api.IModConfigProvider -import com.mrcrayfish.configured.api.ModContext -import moe.nea.firmament.Firmament -import moe.nea.firmament.gui.config.AllConfigsGui -import moe.nea.firmament.gui.config.ManagedConfig - -/** - * Registered in `fabric.mod.json` at `custom.configured.providers` - */ -class ConfiguredCompat : IModConfigProvider { - override fun getConfigurationsForMod(modContext: ModContext): Set<IModConfig> { - if (modContext.modId != Firmament.MOD_ID) return emptySet() - return buildSet { - add(object : BaseConfig() { - override fun getRoot(): IConfigEntry { - return BaseConfigNode - } - - override fun getTranslationKey(): String? { - return "firmament.config.all-configs" - } - }) - ManagedConfig.allManagedConfigs.getAll().mapTo(this) { ConfigCategory(it) } - } - } -} diff --git a/src/compat/configured/java/ConfiguredConfigScreenProvider.kt b/src/compat/configured/java/ConfiguredConfigScreenProvider.kt deleted file mode 100644 index c0095bf..0000000 --- a/src/compat/configured/java/ConfiguredConfigScreenProvider.kt +++ /dev/null @@ -1,22 +0,0 @@ -package moe.nea.firmament.compat.configured - -import com.google.auto.service.AutoService -import com.mrcrayfish.configured.integration.CatalogueConfigFactory -import net.fabricmc.loader.api.FabricLoader -import net.minecraft.client.gui.screen.Screen -import moe.nea.firmament.Firmament -import moe.nea.firmament.gui.config.FirmamentConfigScreenProvider - -@AutoService(FirmamentConfigScreenProvider::class) -class ConfiguredConfigScreenProvider : FirmamentConfigScreenProvider { - override val key: String - get() = "configured" - override val isEnabled: Boolean - get() = FabricLoader.getInstance().isModLoaded("configured") - - override fun open(parent: Screen?): Screen { - return CatalogueConfigFactory.createConfigScreen( - parent, - FabricLoader.getInstance().getModContainer(Firmament.MOD_ID).get()) - } -} diff --git a/src/compat/explosiveEnhancement/java/ExplosiveEnhancementSpawner.kt b/src/compat/explosiveEnhancement/java/ExplosiveEnhancementSpawner.kt deleted file mode 100644 index caf16a5..0000000 --- a/src/compat/explosiveEnhancement/java/ExplosiveEnhancementSpawner.kt +++ /dev/null @@ -1,17 +0,0 @@ -package moe.nea.firmament.compat.explosiveenhancement - -import com.google.auto.service.AutoService -import net.superkat.explosiveenhancement.api.ExplosiveApi -import net.minecraft.util.math.Vec3d -import moe.nea.firmament.features.fixes.CompatibliltyFeatures -import moe.nea.firmament.features.fixes.CompatibliltyFeatures.TConfig -import moe.nea.firmament.util.MC -import moe.nea.firmament.util.compatloader.CompatLoader - -@AutoService(CompatibliltyFeatures.ExplosiveApiWrapper::class) -@CompatLoader.RequireMod("explosiveenhancement") -class ExplosiveEnhancementSpawner : CompatibliltyFeatures.ExplosiveApiWrapper { - override fun spawnParticle(vec3d: Vec3d, power: Float) { - ExplosiveApi.spawnParticles(MC.world, vec3d.x, vec3d.y, vec3d.z, TConfig.explosionSize / 10F) - } -} diff --git a/src/compat/iris/java/moe/nea/firmament/compat/iris/Compat.kt b/src/compat/iris/java/moe/nea/firmament/compat/iris/Compat.kt new file mode 100644 index 0000000..6d04842 --- /dev/null +++ b/src/compat/iris/java/moe/nea/firmament/compat/iris/Compat.kt @@ -0,0 +1,12 @@ +package moe.nea.firmament.compat.iris + +import net.fabricmc.loader.api.FabricLoader +import moe.nea.firmament.util.compatloader.CompatMeta +import moe.nea.firmament.util.compatloader.ICompatMeta + +@CompatMeta +object Compat : ICompatMeta { + override fun shouldLoad(): Boolean { + return FabricLoader.getInstance().isModLoaded("iris") + } +} diff --git a/src/compat/iris/java/moe/nea/firmament/compat/iris/IrisPipelineAssigner.kt b/src/compat/iris/java/moe/nea/firmament/compat/iris/IrisPipelineAssigner.kt new file mode 100644 index 0000000..b7b3e42 --- /dev/null +++ b/src/compat/iris/java/moe/nea/firmament/compat/iris/IrisPipelineAssigner.kt @@ -0,0 +1,19 @@ +package moe.nea.firmament.compat.iris + +import net.irisshaders.iris.api.v0.IrisApi +import net.irisshaders.iris.api.v0.IrisProgram +import util.render.CustomRenderPipelines +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.ClientInitEvent + +object IrisPipelineAssigner { + @Subscribe + fun initIrisAssignments(event: ClientInitEvent) { + val api = IrisApi.getInstance() + api.assignPipeline(CustomRenderPipelines.GUI_TEXTURED_NO_DEPTH_TRIS, IrisProgram.TEXTURED) + api.assignPipeline(CustomRenderPipelines.OMNIPRESENT_LINES, IrisProgram.LINES) + api.assignPipeline(CustomRenderPipelines.COLORED_OMNIPRESENT_QUADS, IrisProgram.BASIC) + api.assignPipeline(CustomRenderPipelines.CIRCLE_FILTER_TRANSLUCENT_GUI_TRIS, IrisProgram.TEXTURED) + api.assignPipeline(CustomRenderPipelines.PARALLAX_CAPE_SHADER, IrisProgram.ENTITIES) + } +} diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/Compat.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/Compat.kt new file mode 100644 index 0000000..d1cfef4 --- /dev/null +++ b/src/compat/jade/java/moe/nea/firmament/compat/jade/Compat.kt @@ -0,0 +1,12 @@ +package moe.nea.firmament.compat.jade + +import net.fabricmc.loader.api.FabricLoader +import moe.nea.firmament.util.compatloader.CompatMeta +import moe.nea.firmament.util.compatloader.ICompatMeta + +@CompatMeta +object Compat : ICompatMeta { + override fun shouldLoad(): Boolean { + return FabricLoader.getInstance().isModLoaded("jade") + } +} diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/CustomFakeBlockProvider.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/CustomFakeBlockProvider.kt new file mode 100644 index 0000000..58344ad --- /dev/null +++ b/src/compat/jade/java/moe/nea/firmament/compat/jade/CustomFakeBlockProvider.kt @@ -0,0 +1,39 @@ +package moe.nea.firmament.compat.jade + +import snownee.jade.api.Accessor +import snownee.jade.api.BlockAccessor +import snownee.jade.api.IWailaClientRegistration +import snownee.jade.api.callback.JadeRayTraceCallback +import net.minecraft.world.phys.HitResult +import moe.nea.firmament.repo.MiningRepoData +import moe.nea.firmament.util.mc.FirmamentDataComponentTypes + +class CustomFakeBlockProvider(val registration: IWailaClientRegistration) : JadeRayTraceCallback { + + override fun onRayTrace( + hitResult: HitResult, + accessor: Accessor<*>?, + originalAccessor: Accessor<*>? + ): Accessor<*>? { + if (!JadeIntegration.TConfig.blockDetection) return accessor + if (accessor !is BlockAccessor) return accessor + val customBlock = JadeIntegration.customBlocks[accessor.block] ?: return accessor + return registration.blockAccessor() + .from(accessor) + .serversideRep(customBlock.getDisplayItem(accessor.block)) + .build() + } + + companion object { + @JvmStatic + fun hasCustomBlock(accessor: BlockAccessor): Boolean { + return getCustomBlock(accessor) != null + } + + @JvmStatic + fun getCustomBlock(accessor: BlockAccessor): MiningRepoData.CustomMiningBlock? { + val item = accessor.serversideRep ?: return null + return item.get(FirmamentDataComponentTypes.CUSTOM_MINING_BLOCK_DATA) + } + } +} diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/CustomMiningHardnessProvider.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/CustomMiningHardnessProvider.kt new file mode 100644 index 0000000..01c06e5 --- /dev/null +++ b/src/compat/jade/java/moe/nea/firmament/compat/jade/CustomMiningHardnessProvider.kt @@ -0,0 +1,97 @@ +package moe.nea.firmament.compat.jade + +import snownee.jade.api.BlockAccessor +import snownee.jade.api.IBlockComponentProvider +import snownee.jade.api.ITooltip +import snownee.jade.api.config.IPluginConfig +import kotlin.time.DurationUnit +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.resources.ResourceLocation +import net.minecraft.core.BlockPos +import moe.nea.firmament.Firmament +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.TickEvent +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.TimeMark +import moe.nea.firmament.util.tr + +object CustomMiningHardnessProvider : IBlockComponentProvider { + + override fun appendTooltip( + tooltip: ITooltip, + block: BlockAccessor, + config: IPluginConfig? + ) { + val customBlock = CustomFakeBlockProvider.getCustomBlock(block) ?: return + if (customBlock.breakingPower > 0) + tooltip.add(tr("firmament.jade.breaking_power", "Required Breaking Power: ${customBlock.breakingPower}")) + } + + override fun getUid(): ResourceLocation = + Firmament.identifier("custom_mining_hardness") + + data class BreakingInfo( + val blockPos: BlockPos, val stage: Int, + val state: BlockState?, + val ts: TimeMark = TimeMark.now() + ) + + var previousBreakingInfo: BreakingInfo? = null + var currentBreakingInfo: BreakingInfo? = null + + @Subscribe + fun clearInfoOnStopBreaking(event: TickEvent) { + val isBreakingBlock = MC.interactionManager?.isDestroying ?: false + if (!isBreakingBlock) { + previousBreakingInfo = null + currentBreakingInfo = null + } + } + + @JvmStatic + fun setBreakingInfo(blockPos: BlockPos, stage: Int) { + previousBreakingInfo = currentBreakingInfo + val state = MC.world?.getBlockState(blockPos) + if (previousBreakingInfo?.let { it.state != state || it.blockPos != blockPos } ?: false) + previousBreakingInfo == null + currentBreakingInfo = BreakingInfo(blockPos.immutable(), stage, state) + // For some reason hypixel initially sends a stage 10 packet, and then fixes it up with a stage 0 packet. + // Ignore the stage 10 packet if we dont have any previous packets for this block. + // This could in theory still have issues if someone perfectly stops breaking a block the tick it finishes and then does not break another block until it respawns, but i deem that to be too much of an edge case. + if (stage == 10 && previousBreakingInfo == null) { + previousBreakingInfo = null + currentBreakingInfo = null + } + } + + @JvmStatic + fun replaceBreakProgress(original: Float): Float { + if (!JadeIntegration.TConfig.miningProgress) return original + if (!isOnMiningIsland()) return original + val pos = MC.interactionManager?.destroyBlockPos ?: return original + val info = currentBreakingInfo + if (info?.blockPos != pos || info.state != MC.world?.getBlockState(pos)) { + currentBreakingInfo = null + previousBreakingInfo = null + return original + } + // TODO: improve this interpolation to work across all stages, to alleviate some of the jittery bar. + // Maybe introduce a proper mining API that tracks the actual progress with some sort of FSM + val interpolatedStage = previousBreakingInfo?.let { prev -> + val timeBetweenTicks = (info.ts - prev.ts).toDouble(DurationUnit.SECONDS) + val stagesPerUpdate = (info.stage - prev.stage).toDouble() + if (stagesPerUpdate < 1) return@let null + val stagesPerSecond = stagesPerUpdate / timeBetweenTicks + info.stage + (info.ts.passedTime().toDouble(DurationUnit.SECONDS) * stagesPerSecond) + .coerceAtMost(stagesPerUpdate) + }?.toFloat() + val stage = interpolatedStage ?: info.stage.toFloat() + return stage / 10F + } + + @JvmStatic + fun replaceBlockBreakSpeed(original: Float): Float { + if (isOnMiningIsland()) return 0F + return original + } +} diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolProvider.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolProvider.kt new file mode 100644 index 0000000..0083d78 --- /dev/null +++ b/src/compat/jade/java/moe/nea/firmament/compat/jade/DrillToolProvider.kt @@ -0,0 +1,72 @@ +package moe.nea.firmament.compat.jade + +import java.util.function.UnaryOperator +import snownee.jade.api.BlockAccessor +import snownee.jade.api.IBlockComponentProvider +import snownee.jade.api.ITooltip +import snownee.jade.api.JadeIds +import snownee.jade.api.config.IPluginConfig +import snownee.jade.api.theme.IThemeHelper +import snownee.jade.api.ui.Element +import snownee.jade.api.ui.JadeUI +import snownee.jade.gui.JadeLinearLayout +import snownee.jade.impl.ui.ItemStackElement +import snownee.jade.impl.ui.TextElementImpl +import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceLocation +import moe.nea.firmament.Firmament +import moe.nea.firmament.repo.ExpensiveItemCacheApi +import moe.nea.firmament.repo.RepoManager +import moe.nea.firmament.repo.SBItemStack +import moe.nea.firmament.util.MC + +class DrillToolProvider : IBlockComponentProvider { + @OptIn(ExpensiveItemCacheApi::class) + override fun appendTooltip( + tooltip: ITooltip, + accessor: BlockAccessor, + p2: IPluginConfig? + ) { + val customBlock = CustomFakeBlockProvider.getCustomBlock(accessor) ?: return + val tool = RepoManager.miningData.getToolsThatCanBreak(customBlock.breakingPower).firstOrNull() + ?.asImmutableItemStack() ?: return + tooltip.replace(JadeIds.MC_HARVEST_TOOL, UnaryOperator { elements -> + elements.map { inner -> + val lastItemIndex = inner.indexOfLast { it is ItemStackElement } + if (lastItemIndex < 0) return@map inner + val innerMut = inner.toMutableList() + val harvestIndicator = innerMut.indexOfLast { + it is TextElementImpl && it.width == 0 && it.string.isNotEmpty() + } + val canHarvest = SBItemStack(MC.stackInHand).breakingPower >= customBlock.breakingPower + val lastItem = innerMut[lastItemIndex] as ItemStackElement + if (harvestIndicator < 0) { + innerMut.add(lastItemIndex + 1, canHarvestIndicator(canHarvest)) + } else { + innerMut.set(harvestIndicator, canHarvestIndicator(canHarvest)) + } + innerMut.set( + lastItemIndex, JadeUI + .item(tool, 0.75f) + ) + innerMut.subList(0, lastItemIndex - 1).removeIf { it is ItemStackElement } + innerMut + } + }) + } + + fun canHarvestIndicator(canHarvest: Boolean): Element { + val t = IThemeHelper.get() + val text = if (canHarvest) t.success(CHECK) else t.danger(X) + return JadeUI.text(text) + .scale(0.75F) + .alignSelfCenter() + } + + private val CHECK: Component = Component.literal("✔") + private val X: Component = Component.literal("✕") + + override fun getUid(): ResourceLocation { + return Firmament.identifier("toolprovider") + } +} diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/FirmamentJadePlugin.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/FirmamentJadePlugin.kt new file mode 100644 index 0000000..a31492e --- /dev/null +++ b/src/compat/jade/java/moe/nea/firmament/compat/jade/FirmamentJadePlugin.kt @@ -0,0 +1,21 @@ +package moe.nea.firmament.compat.jade + +import snownee.jade.api.IWailaClientRegistration +import snownee.jade.api.IWailaCommonRegistration +import snownee.jade.api.IWailaPlugin +import snownee.jade.api.WailaPlugin +import net.minecraft.world.level.block.Block +import moe.nea.firmament.Firmament + +@WailaPlugin +class FirmamentJadePlugin : IWailaPlugin { + override fun register(registration: IWailaCommonRegistration) { + Firmament.logger.debug("Registering Jade integration...") + } + + override fun registerClient(registration: IWailaClientRegistration) { + registration.registerBlockComponent(CustomMiningHardnessProvider, Block::class.java) + registration.registerBlockComponent(DrillToolProvider(), Block::class.java) + registration.addRayTraceCallback(CustomFakeBlockProvider(registration)) + } +} diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/JadeIntegration.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/JadeIntegration.kt new file mode 100644 index 0000000..c289810 --- /dev/null +++ b/src/compat/jade/java/moe/nea/firmament/compat/jade/JadeIntegration.kt @@ -0,0 +1,52 @@ +package moe.nea.firmament.compat.jade + +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.SkyblockServerUpdateEvent +import moe.nea.firmament.repo.MiningRepoData +import moe.nea.firmament.repo.RepoManager +import moe.nea.firmament.util.ErrorUtil +import net.minecraft.world.level.block.Block +import moe.nea.firmament.events.ReloadRegistrationEvent +import moe.nea.firmament.util.data.Config +import moe.nea.firmament.util.data.ManagedConfig + +object JadeIntegration { + @Config + object TConfig : ManagedConfig("jade-integration", Category.INTEGRATIONS) { + val miningProgress by toggle("progress") { true } + val blockDetection by toggle("blocks") { true } + } + + var customBlocks: Map<Block, MiningRepoData.CustomMiningBlock> = mapOf() + + fun refreshBlockInfo() { + if (!isOnMiningIsland()) { + customBlocks = mapOf() + return + } + val blocks = RepoManager.miningData.customMiningBlocks + .flatMap { customBlock -> + // TODO: add a lifted helper method for this + customBlock.blocks189.filter { it.isCurrentlyActive } + .mapNotNull { it.block } + .map { customBlock to it } + } + .groupBy { it.second } + customBlocks = blocks.mapNotNull { (block, customBlocks) -> + val singleMatch = + ErrorUtil.notNullOr(customBlocks.singleOrNull()?.first, + "Two custom blocks both want to supply custom mining behaviour for $block.") { return@mapNotNull null } + block to singleMatch + }.toMap() + } + + @Subscribe + fun onRepoReload(event: ReloadRegistrationEvent) { + event.repo.registerReloadListener { refreshBlockInfo() } + } + + @Subscribe + fun onWorldSwap(event: SkyblockServerUpdateEvent) { + refreshBlockInfo() + } +} diff --git a/src/compat/jade/java/moe/nea/firmament/compat/jade/utils.kt b/src/compat/jade/java/moe/nea/firmament/compat/jade/utils.kt new file mode 100644 index 0000000..364dc02 --- /dev/null +++ b/src/compat/jade/java/moe/nea/firmament/compat/jade/utils.kt @@ -0,0 +1,6 @@ +package moe.nea.firmament.compat.jade + +import moe.nea.firmament.util.SBData + +fun isOnMiningIsland(): Boolean = + SBData.skyblockLocation?.hasCustomMining ?: false diff --git a/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/EnforceToolDisplayForCustomBlocksInHarvestToolProvider.java b/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/EnforceToolDisplayForCustomBlocksInHarvestToolProvider.java new file mode 100644 index 0000000..5fabba8 --- /dev/null +++ b/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/EnforceToolDisplayForCustomBlocksInHarvestToolProvider.java @@ -0,0 +1,33 @@ +package moe.nea.firmament.mixins.compat.jade; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import moe.nea.firmament.compat.jade.CustomFakeBlockProvider; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import snownee.jade.addon.harvest.HarvestToolProvider; +import snownee.jade.api.BlockAccessor; + +import java.util.List; + +@Mixin(HarvestToolProvider.class) +public class EnforceToolDisplayForCustomBlocksInHarvestToolProvider { + @ModifyExpressionValue(method = "getText", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;requiresCorrectToolForDrops()Z")) + private boolean overwriteRequiresTool(boolean original, @Local(argsOnly = true) BlockAccessor accessor) { + if (CustomFakeBlockProvider.hasCustomBlock(accessor)) + return true; + return original; + } + + private static final List<ItemStack> REPLACEABLE_TOOL = List.of(new ItemStack(Blocks.ENCHANTING_TABLE)); + + @ModifyExpressionValue(method = "getText", at = @At(value = "INVOKE", target = "Lcom/google/common/cache/Cache;get(Ljava/lang/Object;Ljava/util/concurrent/Callable;)Ljava/lang/Object;")) + private Object overwriteAvailableTools(Object original, @Local(argsOnly = true) BlockAccessor accessor) { + var orig = (List<ItemStack>) original; + if (orig.isEmpty() && CustomFakeBlockProvider.hasCustomBlock(accessor)) + return REPLACEABLE_TOOL; + return orig; + } +} diff --git a/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/OnUpdateBreakProgress.java b/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/OnUpdateBreakProgress.java new file mode 100644 index 0000000..76084f8 --- /dev/null +++ b/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/OnUpdateBreakProgress.java @@ -0,0 +1,22 @@ +package moe.nea.firmament.mixins.compat.jade; + +import moe.nea.firmament.compat.jade.CustomMiningHardnessProvider; +import moe.nea.firmament.util.MC; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.core.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +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.Objects; + +@Mixin(LevelRenderer.class) +public class OnUpdateBreakProgress { + @Inject(method = "destroyBlockProgress", at = @At("HEAD")) + private void replaceBreakProgress(int entityId, BlockPos pos, int stage, CallbackInfo ci) { + if (entityId == 0 && null != MC.INSTANCE.getInteractionManager() && Objects.equals(MC.INSTANCE.getInteractionManager().destroyBlockPos, pos)) { + CustomMiningHardnessProvider.setBreakingInfo(pos, stage); + } + } +} diff --git a/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/PatchBreakingBarSpeedJade.java b/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/PatchBreakingBarSpeedJade.java new file mode 100644 index 0000000..898923a --- /dev/null +++ b/src/compat/jade/java/moe/nea/firmament/mixins/compat/jade/PatchBreakingBarSpeedJade.java @@ -0,0 +1,25 @@ +package moe.nea.firmament.mixins.compat.jade; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import moe.nea.firmament.compat.jade.CustomMiningHardnessProvider; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import snownee.jade.JadeClient; + +@Mixin(JadeClient.class) +public class PatchBreakingBarSpeedJade { + @ModifyExpressionValue( + method = "drawBreakingProgress", + at = @At(value = "FIELD", target = "Lnet/minecraft/client/multiplayer/MultiPlayerGameMode;destroyProgress:F", opcode = Opcodes.GETFIELD) + ) + private static float replaceBlockBreakingProgress(float original) { + return CustomMiningHardnessProvider.replaceBreakProgress(original); + } + + @ModifyExpressionValue(method = "drawBreakingProgress", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;getDestroyProgress(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)F")) + private static float replacePlayerSpecificBreakingProgress(float original) { + return CustomMiningHardnessProvider.replaceBlockBreakSpeed(original); + } +} diff --git a/src/compat/modmenu/java/moe/nea/firmament/compat/modmenu/FirmamentModMenuPlugin.kt b/src/compat/modmenu/java/moe/nea/firmament/compat/modmenu/FirmamentModMenuPlugin.kt index b734e2c..ff58c20 100644 --- a/src/compat/modmenu/java/moe/nea/firmament/compat/modmenu/FirmamentModMenuPlugin.kt +++ b/src/compat/modmenu/java/moe/nea/firmament/compat/modmenu/FirmamentModMenuPlugin.kt @@ -6,6 +6,6 @@ import moe.nea.firmament.gui.config.AllConfigsGui class FirmamentModMenuPlugin : ModMenuApi { override fun getModConfigScreenFactory(): ConfigScreenFactory<*> { - return ConfigScreenFactory { AllConfigsGui.makeScreen(it) } + return ConfigScreenFactory { AllConfigsGui.makeScreen(parent = it) } } } diff --git a/src/compat/moulconfig/java/MCConfigEditorIntegration.kt b/src/compat/moulconfig/java/MCConfigEditorIntegration.kt index dec2559..6b2df21 100644 --- a/src/compat/moulconfig/java/MCConfigEditorIntegration.kt +++ b/src/compat/moulconfig/java/MCConfigEditorIntegration.kt @@ -1,13 +1,16 @@ package moe.nea.firmament.compat.moulconfig import com.google.auto.service.AutoService +import io.github.notenoughupdates.moulconfig.ChromaColour import io.github.notenoughupdates.moulconfig.Config import io.github.notenoughupdates.moulconfig.DescriptionRendereringBehaviour import io.github.notenoughupdates.moulconfig.Social import io.github.notenoughupdates.moulconfig.common.IMinecraft import io.github.notenoughupdates.moulconfig.common.MyResourceLocation +import io.github.notenoughupdates.moulconfig.common.text.StructuredText import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiElementWrapper +import io.github.notenoughupdates.moulconfig.gui.GuiContext +import io.github.notenoughupdates.moulconfig.gui.GuiElementComponent import io.github.notenoughupdates.moulconfig.gui.GuiOptionEditor import io.github.notenoughupdates.moulconfig.gui.HorizontalAlign import io.github.notenoughupdates.moulconfig.gui.MoulConfigEditor @@ -20,9 +23,12 @@ import io.github.notenoughupdates.moulconfig.gui.editors.ComponentEditor import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorAccordion import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorBoolean import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorButton +import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorColour import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorDropdown import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorText import io.github.notenoughupdates.moulconfig.observer.GetSetter +import io.github.notenoughupdates.moulconfig.platform.MoulConfigPlatform +import io.github.notenoughupdates.moulconfig.platform.MoulConfigScreenComponent import io.github.notenoughupdates.moulconfig.processor.ProcessedCategory import io.github.notenoughupdates.moulconfig.processor.ProcessedOption import java.lang.reflect.Type @@ -30,21 +36,24 @@ import java.net.URI import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import kotlin.time.DurationUnit -import net.minecraft.client.gui.screen.Screen -import net.minecraft.util.Identifier -import net.minecraft.util.StringIdentifiable -import net.minecraft.util.Util +import net.minecraft.client.gui.screens.Screen +import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceLocation +import net.minecraft.util.StringRepresentable +import net.minecraft.Util import moe.nea.firmament.Firmament +import moe.nea.firmament.gui.config.AllConfigsGui import moe.nea.firmament.gui.config.BooleanHandler import moe.nea.firmament.gui.config.ChoiceHandler import moe.nea.firmament.gui.config.ClickHandler +import moe.nea.firmament.gui.config.ColourHandler import moe.nea.firmament.gui.config.DurationHandler import moe.nea.firmament.gui.config.FirmamentConfigScreenProvider import moe.nea.firmament.gui.config.HudMeta import moe.nea.firmament.gui.config.HudMetaHandler import moe.nea.firmament.gui.config.IntegerHandler import moe.nea.firmament.gui.config.KeyBindingHandler -import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.util.data.ManagedConfig import moe.nea.firmament.gui.config.ManagedOption import moe.nea.firmament.gui.config.StringHandler import moe.nea.firmament.gui.toMoulConfig @@ -96,29 +105,31 @@ class MCConfigEditorIntegration : FirmamentConfigScreenProvider { val mappedSetter = setter.xmap(fromT, toT) private val delegateI by lazy { - wrapComponent(RowComponent( - AlignComponent( - TextComponent( - IMinecraft.instance.defaultFontRenderer, - { formatter(setter.get()) }, - 25, - TextComponent.TextAlignment.CENTER, false, false + wrapComponent( + RowComponent( + AlignComponent( + TextComponent( + IMinecraft.INSTANCE.defaultFontRenderer, + { StructuredText.of(formatter(setter.get())) }, + 25, + TextComponent.TextAlignment.CENTER, false, false + ), + GetSetter.constant(HorizontalAlign.CENTER), + GetSetter.constant(VerticalAlign.CENTER) ), - GetSetter.constant(HorizontalAlign.CENTER), - GetSetter.constant(VerticalAlign.CENTER) - ), - SliderComponent( - mappedSetter, - fromT(minValue), - fromT(maxValue), - minStep, - 40 + SliderComponent( + mappedSetter, + fromT(minValue), + fromT(maxValue), + minStep, + 40 + ) ) - )) + ) } } - fun <T> helpRegisterChoice() where T : Enum<T>, T : StringIdentifiable { + fun <T> helpRegisterChoice() where T : Enum<T>, T : StringRepresentable { register(ChoiceHandler::class.java as Class<ChoiceHandler<T>>) { handler, option, categoryAccordionId, configObject -> object : ProcessedEditableOptionFirm<T>(option, categoryAccordionId, configObject) { override fun createEditor(): GuiOptionEditor { @@ -183,10 +194,30 @@ class MCConfigEditorIntegration : FirmamentConfigScreenProvider { } } } + register(ColourHandler::class.java) { handler, option, accordionId, configObject -> + object : ProcessedEditableOptionFirm<ChromaColour>(option, accordionId, configObject) { + override fun fromT(t: ChromaColour): Any { + return t + } + + override fun toT(any: Any?): ChromaColour? { + return any as ChromaColour? + } + + override fun createEditor(): GuiOptionEditor { + return GuiOptionEditorColour(this) + } + + override fun getType(): Type? { + return ChromaColour::class.java + } + } + + } register(ClickHandler::class.java) { handler, option, categoryAccordionId, configObject -> object : ProcessedEditableOptionFirm<Unit>(option, categoryAccordionId, configObject) { override fun createEditor(): GuiOptionEditor { - return GuiOptionEditorButton(this, -1, "Click", configObject) + return GuiOptionEditorButton(this, -1, StructuredText.of("Click"), configObject) } override fun toT(any: Any?): Unit? { @@ -205,7 +236,7 @@ class MCConfigEditorIntegration : FirmamentConfigScreenProvider { register(HudMetaHandler::class.java) { handler, option, categoryAccordionId, configObject -> object : ProcessedEditableOptionFirm<HudMeta>(option, categoryAccordionId, configObject) { override fun createEditor(): GuiOptionEditor { - return GuiOptionEditorButton(this, -1, "Edit HUD", configObject) + return GuiOptionEditorButton(this, -1, StructuredText.of("Edit HUD"), configObject) } override fun fromT(t: HudMeta): Any { @@ -302,101 +333,111 @@ class MCConfigEditorIntegration : FirmamentConfigScreenProvider { } } - override fun open(parent: Screen?): Screen { - val configObject = object : Config() { - override fun saveNow() { - ManagedConfig.allManagedConfigs.getAll().forEach { it.save() } - } + val configObject = object : Config() { + override fun saveNow() { + ManagedConfig.allManagedConfigs.getAll().forEach { it.markDirty() } + } - override fun shouldAutoFocusSearchbar(): Boolean { - return true - } + override fun shouldAutoFocusSearchbar(): Boolean { + return true + } + + override fun getTitle(): StructuredText { + return StructuredText.of("Firmament ${Firmament.version.friendlyString}") + } - override fun getTitle(): String { - return "Firmament" + @Deprecated("Deprecated in java") + override fun executeRunnable(runnableId: Int) { + if (runnableId >= 0) + ErrorUtil.softError("Executed runnable $runnableId") + } + + override fun getDescriptionBehaviour(option: ProcessedOption?): DescriptionRendereringBehaviour { + return DescriptionRendereringBehaviour.EXPAND_PANEL + } + + fun mkSocial(name: String, identifier: ResourceLocation, link: String) = object : Social() { + override fun onClick() { + MC.openUrl(link) } - @Deprecated("Deprecated in java") - override fun executeRunnable(runnableId: Int) { - if (runnableId >= 0) - ErrorUtil.softError("Executed runnable $runnableId") + override fun getTooltip(): List<StructuredText> { + return listOf(StructuredText.of(name)) } - override fun getDescriptionBehaviour(option: ProcessedOption?): DescriptionRendereringBehaviour { - return DescriptionRendereringBehaviour.EXPAND_PANEL + override fun getIcon(): MyResourceLocation { + return identifier.toMoulConfig() } + } - fun mkSocial(name: String, identifier: Identifier, link: String) = object : Social() { - override fun onClick() { - Util.getOperatingSystem().open(URI(link)) + private val socials = listOf<Social>( + mkSocial( + "Discord", Firmament.identifier("textures/socials/discord.png"), + Firmament.modContainer.metadata.contact.get("discord").get() + ), + mkSocial( + "Source Code", Firmament.identifier("textures/socials/git.png"), + Firmament.modContainer.metadata.contact.get("sources").get() + ), + mkSocial( + "Modrinth", Firmament.identifier("textures/socials/modrinth.png"), + Firmament.modContainer.metadata.contact.get("modrinth").get() + ), + ) + + override fun getSocials(): List<Social> { + return socials + } + } + val categories = ManagedConfig.Category.entries.map { + val options = mutableListOf<ProcessedOptionFirm>() + var nextAccordionId = 720 + it.configs.forEach { config -> + val categoryAccordionId = nextAccordionId++ + options.add(object : ProcessedOptionFirm(-1, configObject) { + override fun getDebugDeclarationLocation(): String { + return "FirmamentConfig:${config.name}" } - override fun getTooltip(): List<String> { - return listOf(name) + override fun getName(): StructuredText { + return MoulConfigPlatform.wrap(config.labelText) } - override fun getIcon(): MyResourceLocation { - return identifier.toMoulConfig() + override fun getDescription(): StructuredText { + return StructuredText.of("Missing description") } - } - - private val socials = listOf<Social>( - mkSocial("Discord", Firmament.identifier("textures/socials/discord.png"), - Firmament.modContainer.metadata.contact.get("discord").get()), - mkSocial("Source Code", Firmament.identifier("textures/socials/git.png"), - Firmament.modContainer.metadata.contact.get("sources").get()), - mkSocial("Modrinth", Firmament.identifier("textures/socials/modrinth.png"), - Firmament.modContainer.metadata.contact.get("modrinth").get()), - ) - override fun getSocials(): List<Social> { - return socials - } - } - val categories = ManagedConfig.Category.entries.map { - val options = mutableListOf<ProcessedOptionFirm>() - var nextAccordionId = 720 - it.configs.forEach { config -> - val categoryAccordionId = nextAccordionId++ - options.add(object : ProcessedOptionFirm(-1, configObject) { - override fun getDebugDeclarationLocation(): String { - return "FirmamentConfig:${config.name}" - } - - override fun getName(): String { - return config.labelText.string - } - - override fun getDescription(): String { - return "Missing description" - } - - override fun get(): Any { - return Unit - } + override fun get(): Any { + return Unit + } - override fun getType(): Type { - return Unit.javaClass - } + override fun getType(): Type { + return Unit.javaClass + } - override fun set(value: Any?): Boolean { - return false - } + override fun set(value: Any?): Boolean { + return false + } - override fun createEditor(): GuiOptionEditor { - return GuiOptionEditorAccordion(this, categoryAccordionId) - } - }) - config.allOptions.forEach { (key, option) -> - val processedOption = getHandler(option, categoryAccordionId, configObject) - options.add(processedOption) + override fun createEditor(): GuiOptionEditor { + return GuiOptionEditorAccordion(this, categoryAccordionId) } + }) + config.allOptions.forEach { (key, option) -> + val processedOption = getHandler(option, categoryAccordionId, configObject) + options.add(processedOption) } - - return@map ProcessedCategoryFirm(it, options) } + + return@map ProcessedCategoryFirm(it, options) + } + + override fun open(search: String?, parent: Screen?): Screen { val editor = MoulConfigEditor(ProcessedCategory.collect(categories), configObject) - return GuiElementWrapper(editor) // TODO : add parent support + if (search != null) + editor.search(search) + editor.setWide(AllConfigsGui.ConfigConfig.enableWideMC) + return MoulConfigScreenComponent(Component.empty(), GuiContext(GuiElementComponent(editor)), parent) // TODO : add parent support } } diff --git a/src/compat/moulconfig/java/ProcessedCategoryFirm.kt b/src/compat/moulconfig/java/ProcessedCategoryFirm.kt index 5313441..7c99528 100644 --- a/src/compat/moulconfig/java/ProcessedCategoryFirm.kt +++ b/src/compat/moulconfig/java/ProcessedCategoryFirm.kt @@ -1,9 +1,11 @@ package moe.nea.firmament.compat.moulconfig +import io.github.notenoughupdates.moulconfig.common.text.StructuredText import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorAccordion +import io.github.notenoughupdates.moulconfig.platform.MoulConfigPlatform import io.github.notenoughupdates.moulconfig.processor.ProcessedCategory import io.github.notenoughupdates.moulconfig.processor.ProcessedOption -import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.util.data.ManagedConfig class ProcessedCategoryFirm( val category: ManagedConfig.Category, @@ -21,12 +23,12 @@ class ProcessedCategoryFirm( return "FirmamentCategory.${category.name}" } - override fun getDisplayName(): String { - return category.labelText.string + override fun getDisplayName(): StructuredText { + return MoulConfigPlatform.wrap(category.labelText) } - override fun getDescription(): String { - return category.description.string + override fun getDescription(): StructuredText { + return MoulConfigPlatform.wrap(category.description) } override fun getIdentifier(): String { diff --git a/src/compat/moulconfig/java/ProcessedEditableOptionFirm.kt b/src/compat/moulconfig/java/ProcessedEditableOptionFirm.kt index f0e9aa4..e1ad217 100644 --- a/src/compat/moulconfig/java/ProcessedEditableOptionFirm.kt +++ b/src/compat/moulconfig/java/ProcessedEditableOptionFirm.kt @@ -1,6 +1,8 @@ package moe.nea.firmament.compat.moulconfig import io.github.notenoughupdates.moulconfig.Config +import io.github.notenoughupdates.moulconfig.common.text.StructuredText +import io.github.notenoughupdates.moulconfig.platform.MoulConfigPlatform import moe.nea.firmament.gui.config.ManagedOption import moe.nea.firmament.util.ErrorUtil @@ -14,12 +16,12 @@ abstract class ProcessedEditableOptionFirm<T : Any>( return "FirmamentOption:${managedConfig.name}:${managedOption.propertyName}" } - override fun getName(): String { - return managedOption.labelText.string + override fun getName(): StructuredText { + return MoulConfigPlatform.wrap(managedOption.labelText) } - override fun getDescription(): String { - return managedOption.labelDescription.string + override fun getDescription(): StructuredText { + return MoulConfigPlatform.wrap(managedOption.labelDescription) } abstract fun fromT(t: T): Any @@ -34,11 +36,11 @@ abstract class ProcessedEditableOptionFirm<T : Any>( ErrorUtil.softError("Failed to set value p0 in $this") return false } - managedConfig.save() + managedConfig.markDirty() return true } override fun explicitNotifyChange() { - managedConfig.save() + managedConfig.markDirty() } } diff --git a/src/compat/moulconfig/java/ProcessedOptionFirm.kt b/src/compat/moulconfig/java/ProcessedOptionFirm.kt index 4d0096c..6936048 100644 --- a/src/compat/moulconfig/java/ProcessedOptionFirm.kt +++ b/src/compat/moulconfig/java/ProcessedOptionFirm.kt @@ -10,6 +10,9 @@ abstract class ProcessedOptionFirm( private val accordionId: Int, private val config: Config ) : ProcessedOption { + override fun getPath(): String? { + return "nonsense" + } lateinit var category: ProcessedCategoryFirm override fun getAccordionId(): Int { return accordionId diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/Compat.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/Compat.kt new file mode 100644 index 0000000..9ab4d22 --- /dev/null +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/Compat.kt @@ -0,0 +1,12 @@ +package moe.nea.firmament.compat.rei + +import net.fabricmc.loader.api.FabricLoader +import moe.nea.firmament.util.compatloader.CompatMeta +import moe.nea.firmament.util.compatloader.ICompatMeta + +@CompatMeta +object Compat : ICompatMeta { + override fun shouldLoad(): Boolean { + return FabricLoader.getInstance().isModLoaded("roughlyenoughitems") + } +} diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt index 1097654..cc55fb8 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt @@ -5,9 +5,9 @@ import me.shedaniel.math.FloatingDimension import me.shedaniel.math.Point import me.shedaniel.math.Rectangle import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds -import net.minecraft.client.gui.DrawContext -import net.minecraft.client.gui.Element -import net.minecraft.entity.LivingEntity +import net.minecraft.client.gui.GuiGraphics +import net.minecraft.client.gui.components.events.GuiEventListener +import net.minecraft.world.entity.LivingEntity import moe.nea.firmament.gui.entity.EntityRenderer import moe.nea.firmament.util.ErrorUtil @@ -17,13 +17,13 @@ class EntityWidget( val point: Point, val size: FloatingDimension = FloatingDimension(defaultSize) ) : WidgetWithBounds() { - override fun children(): List<Element> { + override fun children(): List<GuiEventListener> { return emptyList() } var hasErrored = false - override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { + override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) { try { if (!hasErrored) { EntityRenderer.renderEntity( diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt index 98ac276..71e867a 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt @@ -2,9 +2,11 @@ package moe.nea.firmament.compat.rei import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry import me.shedaniel.rei.api.common.plugins.REICommonPlugin +import moe.nea.firmament.repo.RepoManager class FirmamentReiCommonPlugin : REICommonPlugin { override fun registerEntryTypes(registry: EntryTypeRegistry) { + if (!RepoManager.shouldLoadREI()) return registry.register(FirmamentReiPlugin.SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition) } } 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..ad0da89 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 @@ -1,5 +1,6 @@ package moe.nea.firmament.compat.rei +import io.github.moulberry.repo.data.NEUCraftingRecipe import me.shedaniel.rei.api.client.plugins.REIClientPlugin import me.shedaniel.rei.api.client.registry.category.CategoryRegistry import me.shedaniel.rei.api.client.registry.display.DisplayRegistry @@ -12,24 +13,29 @@ 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.type.VanillaEntryTypes -import net.minecraft.client.gui.screen.Screen -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen -import net.minecraft.client.gui.screen.ingame.HandledScreen -import net.minecraft.item.ItemStack -import net.minecraft.text.Text -import net.minecraft.util.ActionResult -import net.minecraft.util.Identifier -import moe.nea.firmament.compat.rei.recipes.SBCraftingRecipe -import moe.nea.firmament.compat.rei.recipes.SBEssenceUpgradeRecipe -import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe +import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.gui.screens.inventory.ContainerScreen +import net.minecraft.client.gui.screens.inventory.MenuAccess +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen +import net.minecraft.world.item.ItemStack +import net.minecraft.network.chat.Component +import net.minecraft.world.InteractionResult +import net.minecraft.resources.ResourceLocation +import moe.nea.firmament.compat.rei.recipes.GenericREIRecipeCategory import moe.nea.firmament.compat.rei.recipes.SBKatRecipe import moe.nea.firmament.compat.rei.recipes.SBMobDropRecipe +import moe.nea.firmament.compat.rei.recipes.SBRecipe 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 +import moe.nea.firmament.repo.ExpensiveItemCacheApi import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.SBItemStack +import moe.nea.firmament.repo.recipes.SBCraftingRecipeRenderer +import moe.nea.firmament.repo.recipes.SBEssenceUpgradeRecipeRenderer +import moe.nea.firmament.repo.recipes.SBForgeRecipeRenderer import moe.nea.firmament.util.MC import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.guessRecipeId @@ -40,30 +46,40 @@ import moe.nea.firmament.util.unformattedString class FirmamentReiPlugin : REIClientPlugin { companion object { + @ExpensiveItemCacheApi fun EntryStack<SBItemStack>.asItemEntry(): EntryStack<ItemStack> { return EntryStack.of(VanillaEntryTypes.ITEM, value.asImmutableItemStack()) } - val SKYBLOCK_ITEM_TYPE_ID = Identifier.of("firmament", "skyblockitems") + val SKYBLOCK_ITEM_TYPE_ID = ResourceLocation.fromNamespaceAndPath("firmament", "skyblockitems") } + @OptIn(ExpensiveItemCacheApi::class) override fun registerTransferHandlers(registry: TransferHandlerRegistry) { + if (!RepoManager.shouldLoadREI()) return registry.register(TransferHandler { context -> val screen = context.containerScreen val display = context.display - if (display !is SBCraftingRecipe) return@TransferHandler TransferHandler.Result.createNotApplicable() - val neuItem = RepoManager.getNEUItem(SkyblockId(display.neuRecipe.output.itemId)) - ?: error("Could not find neu item ${display.neuRecipe.output.itemId} which is used in a recipe output") - val useSuperCraft = context.isStackedCrafting || RepoManager.Config.alwaysSuperCraft - if (neuItem.isVanilla && useSuperCraft) return@TransferHandler TransferHandler.Result.createFailed(Text.translatable( - "firmament.recipe.novanilla")) + if (display !is SBRecipe) return@TransferHandler TransferHandler.Result.createNotApplicable() + val recipe = display.neuRecipe + if (recipe !is NEUCraftingRecipe) return@TransferHandler TransferHandler.Result.createNotApplicable() + val neuItem = RepoManager.getNEUItem(SkyblockId(recipe.output.itemId)) + ?: error("Could not find neu item ${recipe.output.itemId} which is used in a recipe output") + val useSuperCraft = context.isStackedCrafting || RepoManager.TConfig.alwaysSuperCraft + if (neuItem.isVanilla && useSuperCraft) return@TransferHandler TransferHandler.Result.createFailed( + Component.translatable( + "firmament.recipe.novanilla" + ) + ) var shouldReturn = true if (context.isActuallyCrafting && !useSuperCraft) { - if (screen !is GenericContainerScreen || screen.title?.unformattedString != CraftingOverlay.CRAFTING_SCREEN_NAME) { + val craftingScreen = (screen as? ContainerScreen) + ?.takeIf { it.title?.string == CraftingOverlay.CRAFTING_SCREEN_NAME } + if (craftingScreen == null) { MC.sendCommand("craft") shouldReturn = false } - CraftingOverlay.setOverlay(screen as? GenericContainerScreen, display.neuRecipe) + CraftingOverlay.setOverlay(craftingScreen, recipe) } if (context.isActuallyCrafting && useSuperCraft) { shouldReturn = false @@ -74,50 +90,61 @@ class FirmamentReiPlugin : REIClientPlugin { } + val generics = listOf<GenericREIRecipeCategory<*>>( + // Order matters: The order in here is the order in which they show up in REI + GenericREIRecipeCategory(SBCraftingRecipeRenderer), + GenericREIRecipeCategory(SBForgeRecipeRenderer), + GenericREIRecipeCategory(SBEssenceUpgradeRecipeRenderer), + ) + override fun registerCategories(registry: CategoryRegistry) { - registry.add(SBCraftingRecipe.Category) - registry.add(SBForgeRecipe.Category) + if (!RepoManager.shouldLoadREI()) return + + registry.add(generics) registry.add(SBMobDropRecipe.Category) registry.add(SBKatRecipe.Category) registry.add(SBReforgeRecipe.Category) - registry.add(SBEssenceUpgradeRecipe.Category) + registry.add(SBShopRecipe.Category) } override fun registerExclusionZones(zones: ExclusionZones) { - zones.register(HandledScreen::class.java) { HandledScreenPushREIEvent.publish(HandledScreenPushREIEvent(it)).rectangles } + zones.register(AbstractContainerScreen::class.java) { HandledScreenPushREIEvent.publish(HandledScreenPushREIEvent(it)).rectangles } zones.register(StorageOverlayScreen::class.java) { it.getBounds() } } override fun registerDisplays(registry: DisplayRegistry) { - registry.registerDisplayGenerator( - SBCraftingRecipe.Category.catIdentifier, - SkyblockCraftingRecipeDynamicGenerator) + if (!RepoManager.shouldLoadREI()) return + + generics.forEach { + it.registerDynamicGenerator(registry) + } registry.registerDisplayGenerator( SBReforgeRecipe.catIdentifier, SBReforgeRecipe.DynamicGenerator ) registry.registerDisplayGenerator( - SBForgeRecipe.Category.categoryIdentifier, - SkyblockForgeRecipeDynamicGenerator) - registry.registerDisplayGenerator( SBMobDropRecipe.Category.categoryIdentifier, - SkyblockMobDropRecipeDynamicGenerator) + SkyblockMobDropRecipeDynamicGenerator + ) registry.registerDisplayGenerator( - SBKatRecipe.Category.categoryIdentifier, - SkyblockKatRecipeDynamicGenerator) + SBShopRecipe.Category.categoryIdentifier, + SkyblockShopRecipeDynamicGenerator + ) registry.registerDisplayGenerator( - SBEssenceUpgradeRecipe.Category.categoryIdentifier, - SkyblockEssenceRecipeDynamicGenerator + SBKatRecipe.Category.categoryIdentifier, + SkyblockKatRecipeDynamicGenerator ) } override fun registerCollapsibleEntries(registry: CollapsibleEntryRegistry) { - if (!RepoManager.Config.disableItemGroups) + if (!RepoManager.shouldLoadREI()) return + + if (!RepoManager.TConfig.disableItemGroups) RepoManager.neuRepo.constants.parents.parents .forEach { (parent, children) -> registry.group( SkyblockId(parent).identifier, - Text.literal(RepoManager.getNEUItem(SkyblockId(parent))?.displayName ?: parent), + Component.literal(RepoManager.getNEUItem(SkyblockId(parent))?.displayName ?: parent), (children + parent).map { SBItemEntryDefinition.getEntry(SkyblockId(it)) }) } } @@ -128,14 +155,16 @@ class FirmamentReiPlugin : REIClientPlugin { return screen == StorageOverlayScreen::class.java } - override fun <R : Screen?> shouldScreenBeOverlaid(screen: R): ActionResult { - return ActionResult.SUCCESS + override fun <R : Screen?> shouldScreenBeOverlaid(screen: R): InteractionResult { + return InteractionResult.SUCCESS } }) registry.registerFocusedStack(SkyblockItemIdFocusedStackProvider) } override fun registerEntries(registry: EntryRegistry) { + if (!RepoManager.shouldLoadREI()) return + registry.removeEntryIf { true } RepoManager.neuRepo.items?.items?.values?.forEach { neuItem -> registry.addEntry(SBItemEntryDefinition.getEntry(neuItem.skyblockId)) diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt index b917c3e..a19355b 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt @@ -5,17 +5,18 @@ import me.shedaniel.math.impl.PointHelper import me.shedaniel.rei.api.client.REIRuntime import me.shedaniel.rei.api.client.gui.widgets.Slot import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry -import net.minecraft.client.gui.Element -import net.minecraft.client.gui.ParentElement -import net.minecraft.client.gui.screen.ingame.HandledScreen -import net.minecraft.item.ItemStack +import net.minecraft.client.gui.components.events.GuiEventListener +import net.minecraft.client.gui.components.events.ContainerEventHandler +import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen +import net.minecraft.world.item.ItemStack import moe.nea.firmament.util.HoveredItemStackProvider import moe.nea.firmament.util.compatloader.CompatLoader @AutoService(HoveredItemStackProvider::class) @CompatLoader.RequireMod("roughlyenoughitems") class ScreenRegistryHoveredItemStackProvider : HoveredItemStackProvider { - override fun provideHoveredItemStack(screen: HandledScreen<*>): ItemStack? { + override fun provideHoveredItemStack(screen: Screen): ItemStack? { val entryStack = ScreenRegistry.getInstance().getFocusedStack(screen, PointHelper.ofMouse()) ?: return null return entryStack.value as? ItemStack ?: entryStack.cheatsAs().value @@ -25,14 +26,14 @@ class ScreenRegistryHoveredItemStackProvider : HoveredItemStackProvider { @AutoService(HoveredItemStackProvider::class) @CompatLoader.RequireMod("roughlyenoughitems") class OverlayHoveredItemStackProvider : HoveredItemStackProvider { - override fun provideHoveredItemStack(screen: HandledScreen<*>): ItemStack? { - var baseElement: Element? = REIRuntime.getInstance().overlay.orElse(null) + override fun provideHoveredItemStack(screen: Screen): ItemStack? { + var baseElement: GuiEventListener? = REIRuntime.getInstance().overlay.orElse(null) val mx = PointHelper.getMouseFloatingX() val my = PointHelper.getMouseFloatingY() while (true) { if (baseElement is Slot) return baseElement.currentEntry.cheatsAs().value - if (baseElement !is ParentElement) return null - baseElement = baseElement.hoveredElement(mx, my).orElse(null) + if (baseElement !is ContainerEventHandler) return null + baseElement = baseElement.getChildAt(mx, my).orElse(null) } } } 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 c40f00b..0e24f4e 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 @@ -15,40 +15,74 @@ import me.shedaniel.rei.api.client.gui.widgets.Tooltip import me.shedaniel.rei.api.client.gui.widgets.TooltipContext import me.shedaniel.rei.api.common.entry.EntryStack 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 moe.nea.firmament.compat.rei.FirmamentReiPlugin.Companion.asItemEntry +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiGraphics +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraft.world.item.TooltipFlag +import net.minecraft.network.chat.Component import moe.nea.firmament.events.ItemTooltipEvent +import moe.nea.firmament.repo.ExpensiveItemCacheApi +import moe.nea.firmament.repo.ItemCache +import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.SBItemStack import moe.nea.firmament.util.ErrorUtil +import moe.nea.firmament.util.FirmFormatters +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.darkGrey import moe.nea.firmament.util.mc.displayNameAccordingToNbt import moe.nea.firmament.util.mc.loreAccordingToNbt // TODO: make this re implement BatchedEntryRenderer, if possible (likely not, due to no-alloc rendering) // Also it is probably not even that much faster now, with render layers. object NEUItemEntryRenderer : EntryRenderer<SBItemStack> { + @OptIn(ExpensiveItemCacheApi::class) override fun render( entry: EntryStack<SBItemStack>, - context: DrawContext, + context: GuiGraphics, bounds: Rectangle, mouseX: Int, mouseY: Int, delta: Float ) { - 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) - val item = entry.asItemEntry().value - context.drawItemWithoutEntity(item, -8, -8, ) - context.drawStackOverlay(minecraft.textRenderer, item, -8, -8) - context.matrices.pop() + val neuItem = entry.value.neuItem + val itemToRender = if(!RepoManager.TConfig.perfectRenders.rendersPerfectVisuals() && !entry.value.isWarm() && neuItem != null) { + ItemCache.recacheSoon(neuItem) + ItemStack(Items.PAINTING) + } else { + entry.value.asImmutableItemStack() + } + + context.pose().pushMatrix() + context.pose().translate(bounds.centerX.toFloat(), bounds.centerY.toFloat()) + context.pose().scale(bounds.width.toFloat() / 16F, bounds.height.toFloat() / 16F) + context.renderItem(itemToRender, -8, -8) + context.renderItemDecorations( + MC.font, itemToRender, -8, -8, + if (entry.value.getStackSize() > 1000) FirmFormatters.shortFormat( + entry.value.getStackSize() + .toDouble() + ) + else null + ) + context.pose().popMatrix() } - val minecraft = MinecraftClient.getInstance() + val minecraft = Minecraft.getInstance() var canUseVanillaTooltipEvents = true + @OptIn(ExpensiveItemCacheApi::class) override fun getTooltip(entry: EntryStack<SBItemStack>, tooltipContext: TooltipContext): Tooltip? { + if (!entry.value.isWarm() && !RepoManager.TConfig.perfectRenders.rendersPerfectText()) { + val neuItem = entry.value.neuItem + if (neuItem != null) { + val lore = mutableListOf<Component>() + lore.add(Component.literal(neuItem.displayName)) + neuItem.lore.mapTo(mutableListOf()) { Component.literal(it) } + return Tooltip.create(lore) + } + } + val stack = entry.value.asImmutableItemStack() val lore = mutableListOf(stack.displayNameAccordingToNbt) @@ -56,20 +90,24 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack> { if (canUseVanillaTooltipEvents) { try { ItemTooltipCallback.EVENT.invoker().getTooltip( - stack, tooltipContext.vanillaContext(), TooltipType.BASIC, lore + stack, tooltipContext.vanillaContext(), TooltipFlag.Default.NORMAL, lore ) } catch (ex: Exception) { canUseVanillaTooltipEvents = false ErrorUtil.softError("Failed to use vanilla tooltips", ex) } } else { - ItemTooltipEvent.publish(ItemTooltipEvent( - stack, - tooltipContext.vanillaContext(), - TooltipType.BASIC, - lore - )) + ItemTooltipEvent.publish( + ItemTooltipEvent( + stack, + tooltipContext.vanillaContext(), + TooltipFlag.Default.NORMAL, + lore + ) + ) } + if (entry.value.getStackSize() > 1000 && lore.isNotEmpty()) + lore.add(1, Component.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( @@ -77,7 +115,7 @@ object NEUItemEntryRenderer : EntryRenderer<SBItemStack> { // ?: MC.defaultRegistries // ), // MC.player, -// TooltipType.BASIC +// TooltipFlag.Default.NORMAL // ) return Tooltip.create(lore) } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt index 724d193..9f32472 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt @@ -2,8 +2,8 @@ package moe.nea.firmament.compat.rei import com.mojang.serialization.Codec import me.shedaniel.rei.api.common.entry.EntrySerializer -import net.minecraft.network.RegistryByteBuf -import net.minecraft.network.codec.PacketCodec +import net.minecraft.network.RegistryFriendlyByteBuf +import net.minecraft.network.codec.StreamCodec import moe.nea.firmament.repo.SBItemStack object NEUItemEntrySerializer : EntrySerializer<SBItemStack> { @@ -11,7 +11,7 @@ object NEUItemEntrySerializer : EntrySerializer<SBItemStack> { return SBItemStack.CODEC } - override fun streamCodec(): PacketCodec<RegistryByteBuf, SBItemStack> { + override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, SBItemStack> { return SBItemStack.PACKET_CODEC.cast() } } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/REIRecipeLayouter.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/REIRecipeLayouter.kt new file mode 100644 index 0000000..54bc7f0 --- /dev/null +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/REIRecipeLayouter.kt @@ -0,0 +1,82 @@ +package moe.nea.firmament.compat.rei + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent +import me.shedaniel.math.Dimension +import me.shedaniel.math.FloatingDimension +import me.shedaniel.math.Point +import me.shedaniel.math.Rectangle +import me.shedaniel.rei.api.client.gui.widgets.Widget +import me.shedaniel.rei.api.client.gui.widgets.Widgets +import net.minecraft.network.chat.Component +import net.minecraft.world.entity.LivingEntity +import moe.nea.firmament.compat.rei.recipes.wrapWidget +import moe.nea.firmament.repo.SBItemStack +import moe.nea.firmament.repo.recipes.RecipeLayouter + +class REIRecipeLayouter : RecipeLayouter { + val container: MutableList<Widget> = mutableListOf() + fun <T: Widget> add(t: T): T = t.also(container::add) + + override fun createCyclingItemSlot( + x: Int, + y: Int, + content: List<SBItemStack>, + slotKind: RecipeLayouter.SlotKind + ): RecipeLayouter.CyclingItemSlot { + val slot = Widgets.createSlot(Point(x, y)) + if (content.isNotEmpty()) + slot.entries(content.map { SBItemEntryDefinition.getEntry(it) }) + when (slotKind) { + RecipeLayouter.SlotKind.SMALL_INPUT -> slot.markInput() + RecipeLayouter.SlotKind.SMALL_OUTPUT -> slot.markOutput() + RecipeLayouter.SlotKind.BIG_OUTPUT -> { + slot.markOutput().disableBackground() + add(Widgets.createResultSlotBackground(Point(x, y))) + } + RecipeLayouter.SlotKind.DISPLAY -> { + slot.disableBackground() + slot.disableHighlight() + } + } + add(slot) + return object : RecipeLayouter.CyclingItemSlot { + override fun current(): SBItemStack = content.firstOrNull() ?: SBItemStack.EMPTY + override fun update(newValue: SBItemStack) {} + override fun onUpdate(action: () -> Unit) {} + } + } + + override fun createTooltip(rectangle: Rectangle, label: List<Component>) { + add(Widgets.createTooltip(rectangle, *label.toTypedArray())) + } + + override fun createLabel(x: Int, y: Int, text: Component): RecipeLayouter.Updater<Component> { + val label = add(Widgets.createLabel(Point(x, y), text)) + return object : RecipeLayouter.Updater<Component> { + override fun update(newValue: Component) { + label.message = newValue + } + } + } + + override fun createArrow(x: Int, y: Int) = + add(Widgets.createArrow(Point(x, y))).bounds + + override fun createMoulConfig( + x: Int, + y: Int, + w: Int, + h: Int, + component: GuiComponent + ) { + add(wrapWidget(Rectangle(Point(x, y), Dimension(w, h)), component)) + } + + override fun createFire(point: Point, animationTicks: Int) { + add(Widgets.createBurningFire(point).animationDurationTicks(animationTicks.toDouble())) + } + + override fun createEntity(rectangle: Rectangle, entity: LivingEntity) { + add(EntityWidget(entity, rectangle.location, FloatingDimension(rectangle.size))) + } +} 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..fde3e3d 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 @@ -9,24 +9,23 @@ import me.shedaniel.rei.api.common.entry.comparison.ComparisonContext import me.shedaniel.rei.api.common.entry.type.EntryDefinition import me.shedaniel.rei.api.common.entry.type.EntryType import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes -import net.minecraft.item.ItemConvertible -import net.minecraft.item.ItemStack -import net.minecraft.registry.tag.TagKey -import net.minecraft.text.Text -import net.minecraft.util.Identifier +import net.minecraft.world.level.ItemLike +import net.minecraft.world.item.ItemStack +import net.minecraft.tags.TagKey +import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceLocation import moe.nea.firmament.compat.rei.FirmamentReiPlugin.Companion.asItemEntry -import moe.nea.firmament.repo.PetData +import moe.nea.firmament.repo.ExpensiveItemCacheApi import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.SBItemStack import moe.nea.firmament.util.SkyblockId -import moe.nea.firmament.util.petData -import moe.nea.firmament.util.skyBlockId object SBItemEntryDefinition : EntryDefinition<SBItemStack> { override fun equals(o1: SBItemStack, o2: SBItemStack, context: ComparisonContext): Boolean { return o1.skyblockId == o2.skyblockId && o1.getStackSize() == o2.getStackSize() } + @OptIn(ExpensiveItemCacheApi::class) override fun cheatsAs(entry: EntryStack<SBItemStack>?, value: SBItemStack): ItemStack { return value.asCopiedItemStack() } @@ -44,8 +43,14 @@ object SBItemEntryDefinition : EntryDefinition<SBItemStack> { return Stream.empty() } - override fun asFormattedText(entry: EntryStack<SBItemStack>, value: SBItemStack): Text { - return VanillaEntryTypes.ITEM.definition.asFormattedText(entry.asItemEntry(), value.asImmutableItemStack()) + @OptIn(ExpensiveItemCacheApi::class) + override fun asFormattedText(entry: EntryStack<SBItemStack>, value: SBItemStack): Component { + val neuItem = entry.value.neuItem + return if (!RepoManager.TConfig.perfectRenders.rendersPerfectText() || entry.value.isWarm() || neuItem == null) { + VanillaEntryTypes.ITEM.definition.asFormattedText(entry.asItemEntry(), value.asImmutableItemStack()) + } else { + Component.literal(neuItem.displayName) + } } override fun hash(entry: EntryStack<SBItemStack>, value: SBItemStack, context: ComparisonContext): Long { @@ -54,8 +59,10 @@ 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()) + return value.copy( + stackSize = 1, petData = RepoManager.getPotentialStubPetData(value.skyblockId), + stars = 0, extraLore = listOf(), reforge = null + ) } override fun normalize(entry: EntryStack<SBItemStack>?, value: SBItemStack): SBItemStack { @@ -70,7 +77,7 @@ object SBItemEntryDefinition : EntryDefinition<SBItemStack> { return value.getStackSize() == 0 } - override fun getIdentifier(entry: EntryStack<SBItemStack>?, value: SBItemStack): Identifier { + override fun getIdentifier(entry: EntryStack<SBItemStack>?, value: SBItemStack): ResourceLocation { return value.skyblockId.identifier } @@ -83,15 +90,8 @@ object SBItemEntryDefinition : EntryDefinition<SBItemStack> { fun getEntry(ingredient: NEUIngredient): EntryStack<SBItemStack> = getEntry(SkyblockId(ingredient.itemId), count = ingredient.amount.toInt()) - fun getPassthrough(item: ItemConvertible) = getEntry(SBItemStack.passthrough(ItemStack(item.asItem()))) + fun getPassthrough(item: ItemLike) = 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..900ebab 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,65 +1,56 @@ - - 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 import me.shedaniel.rei.api.client.view.ViewSearchBuilder import me.shedaniel.rei.api.common.display.Display import me.shedaniel.rei.api.common.entry.EntryStack -import moe.nea.firmament.compat.rei.recipes.SBCraftingRecipe -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) } - -val SkyblockForgeRecipeDynamicGenerator = - 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) } -val SkyblockEssenceRecipeDynamicGenerator = - neuDisplayGenerator<SBEssenceUpgradeRecipe, EssenceRecipeProvider.EssenceUpgradeRecipe> { SBEssenceUpgradeRecipe(it) } + neuDisplayGenerator<SBKatRecipe, NEUKatUpgradeRecipe> { SBKatRecipe(it) } 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)) - } - - 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() - ) - } - - 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)) - - } - } + neuDisplayGeneratorWithItem<D, T> { _, it -> mapper(it) } + +inline fun <D : Display, reified T : NEURecipe> neuDisplayGeneratorWithItem(crossinline mapper: (SBItemStack, T) -> D) = + neuDisplayGeneratorWithItem(T::class.java, mapper) +inline fun <D : Display, T : NEURecipe> neuDisplayGeneratorWithItem( + filter: Class<T>, + 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>(filter) + return Optional.of(craftingRecipes.map { mapper(item, it) }) + } + + 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>(filter) + 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..1db0376 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 @@ -6,19 +6,17 @@ import dev.architectury.event.CompoundEventResult import me.shedaniel.math.Point import me.shedaniel.rei.api.client.registry.screen.FocusedStackProvider import me.shedaniel.rei.api.common.entry.EntryStack -import net.minecraft.client.gui.screen.Screen -import net.minecraft.client.gui.screen.ingame.HandledScreen +import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen import moe.nea.firmament.mixins.accessor.AccessorHandledScreen -import moe.nea.firmament.util.skyBlockId object SkyblockItemIdFocusedStackProvider : FocusedStackProvider { override fun provide(screen: Screen?, mouse: Point?): CompoundEventResult<EntryStack<*>> { - if (screen !is HandledScreen<*>) return CompoundEventResult.pass() - screen as AccessorHandledScreen + if (screen !is AbstractContainerScreen<*>) return CompoundEventResult.pass() + if (screen !is AccessorHandledScreen) return CompoundEventResult.pass() 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)) + val item = focusedSlot.item ?: return CompoundEventResult.pass() + 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/GenericREIRecipeCategory.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/GenericREIRecipeCategory.kt new file mode 100644 index 0000000..aefb37d --- /dev/null +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/GenericREIRecipeCategory.kt @@ -0,0 +1,67 @@ +package moe.nea.firmament.compat.rei.recipes + +import io.github.moulberry.repo.data.NEURecipe +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.client.registry.display.DisplayRegistry +import me.shedaniel.rei.api.common.category.CategoryIdentifier +import me.shedaniel.rei.api.common.util.EntryStacks +import net.minecraft.network.chat.Component +import moe.nea.firmament.compat.rei.REIRecipeLayouter +import moe.nea.firmament.compat.rei.neuDisplayGeneratorWithItem +import moe.nea.firmament.repo.SBItemStack +import moe.nea.firmament.repo.recipes.GenericRecipeRenderer + +class GenericREIRecipeCategory<T : NEURecipe>( + val renderer: GenericRecipeRenderer<T>, +) : DisplayCategory<GenericRecipe<T>> { + private val dynamicGenerator = + neuDisplayGeneratorWithItem<GenericRecipe<T>, T>(renderer.typ) { item, recipe -> + GenericRecipe( + recipe, + item, + categoryIdentifier + ) + } + + private val categoryIdentifier = CategoryIdentifier.of<GenericRecipe<T>>(renderer.identifier) + override fun getCategoryIdentifier(): CategoryIdentifier<GenericRecipe<T>> { + return categoryIdentifier + } + + override fun getDisplayHeight(): Int { + return renderer.displayHeight + } + + override fun getTitle(): Component? { + return renderer.title + } + + override fun getIcon(): Renderer? { + return EntryStacks.of(renderer.icon) + } + + override fun setupDisplay(display: GenericRecipe<T>, bounds: Rectangle): List<Widget> { + val layouter = REIRecipeLayouter() + layouter.container.add(Widgets.createRecipeBase(bounds)) + renderer.render(display.neuRecipe, bounds, layouter, display.sourceItem) + return layouter.container + } + + fun registerDynamicGenerator(registry: DisplayRegistry) { + registry.registerDisplayGenerator(categoryIdentifier, dynamicGenerator) + } +} + +class GenericRecipe<T : NEURecipe>( + override val neuRecipe: T, + val sourceItem: SBItemStack?, + val id: CategoryIdentifier<GenericRecipe<T>> +) : SBRecipe() { + override fun getCategoryIdentifier(): CategoryIdentifier<*>? { + return id + } +} 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 deleted file mode 100644 index 8db3d75..0000000 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt +++ /dev/null @@ -1,56 +0,0 @@ -package moe.nea.firmament.compat.rei.recipes - -import io.github.moulberry.repo.data.NEUCraftingRecipe -import io.github.moulberry.repo.data.NEUIngredient -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.display.Display -import me.shedaniel.rei.api.common.display.DisplaySerializer -import me.shedaniel.rei.api.common.util.EntryStacks -import net.minecraft.block.Blocks -import net.minecraft.text.Text -import moe.nea.firmament.Firmament -import moe.nea.firmament.compat.rei.SBItemEntryDefinition -import moe.nea.firmament.repo.SBItemStack - -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") - override fun getCategoryIdentifier(): CategoryIdentifier<out SBCraftingRecipe> = catIdentifier - - override fun getTitle(): Text = Text.literal("SkyBlock Crafting") - - override fun getIcon(): Renderer = SBItemEntryDefinition.getPassthrough(Blocks.CRAFTING_TABLE) - override fun setupDisplay(display: SBCraftingRecipe, bounds: Rectangle): List<Widget> { - val point = Point(bounds.centerX - 58, bounds.centerY - 27) - return buildList { - add(Widgets.createRecipeBase(bounds)) - add(Widgets.createArrow(Point(point.x + 60, point.y + 18))) - add(Widgets.createResultSlotBackground(Point(point.x + 95, point.y + 19))) - for (i in 0 until 3) { - for (j in 0 until 3) { - val slot = Widgets.createSlot(Point(point.x + 1 + i * 18, point.y + 1 + j * 18)).markInput() - add(slot) - val item = display.neuRecipe.inputs[i + j * 3] - if (item == NEUIngredient.SENTINEL_EMPTY) continue - slot.entry(SBItemEntryDefinition.getEntry(item)) - } - } - add( - Widgets.createSlot(Point(point.x + 95, point.y + 19)) - .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.output)) - .disableBackground().markOutput() - ) - } - } - - } - -} 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 deleted file mode 100644 index ec71ec8..0000000 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt +++ /dev/null @@ -1,61 +0,0 @@ -package moe.nea.firmament.compat.rei.recipes - -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 net.minecraft.text.Text -import moe.nea.firmament.Firmament -import moe.nea.firmament.compat.rei.SBItemEntryDefinition -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() { - object Category : DisplayCategory<SBEssenceUpgradeRecipe> { - override fun getCategoryIdentifier(): CategoryIdentifier<SBEssenceUpgradeRecipe> = - CategoryIdentifier.of(Firmament.MOD_ID, "essence_upgrade") - - override fun getTitle(): Text { - return Text.literal("Essence Upgrades") - } - - override fun getIcon(): Renderer { - return SBItemEntryDefinition.getEntry(SkyblockId("ESSENCE_WITHER")) - } - - override fun setupDisplay(display: SBEssenceUpgradeRecipe, bounds: Rectangle): List<Widget> { - val recipe = display.neuRecipe - val list = mutableListOf<Widget>() - 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)))) - 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)))) - val extraItems = recipe.extraItems - list.add(Widgets.createArrow(Point(bounds.centerX - 24 / 2, - if (extraItems.isEmpty()) bounds.centerY - 17 / 2 - else bounds.centerY + 18 / 2))) - for ((index, item) in extraItems.withIndex()) { - list.add(Widgets.createSlot( - Point(bounds.centerX - extraItems.size * 16 / 2 - 2 / 2 + index * 18, - bounds.centerY - 18 / 2)) - .markInput() - .entry(SBItemEntryDefinition.getEntry(item))) - } - return list - } - } - - override fun getCategoryIdentifier(): CategoryIdentifier<*> { - return Category.categoryIdentifier - } -} 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 deleted file mode 100644 index 92b2f3f..0000000 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt +++ /dev/null @@ -1,71 +0,0 @@ -package moe.nea.firmament.compat.rei.recipes - -import io.github.moulberry.repo.data.NEUForgeRecipe -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.util.EntryStacks -import kotlin.math.cos -import kotlin.math.sin -import kotlin.time.Duration.Companion.seconds -import net.minecraft.block.Blocks -import net.minecraft.text.Text -import moe.nea.firmament.Firmament -import moe.nea.firmament.compat.rei.SBItemEntryDefinition -import moe.nea.firmament.compat.rei.plus - -class SBForgeRecipe(override val neuRecipe: NEUForgeRecipe) : SBRecipe() { - override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier - - object Category : DisplayCategory<SBForgeRecipe> { - override fun getCategoryIdentifier(): CategoryIdentifier<SBForgeRecipe> = - CategoryIdentifier.of(Firmament.MOD_ID, "forge_recipe") - - override fun getTitle(): Text = Text.literal("Forge Recipes") - override fun getDisplayHeight(): Int { - return 104 - } - - override fun getIcon(): Renderer = SBItemEntryDefinition.getPassthrough(Blocks.ANVIL) - override fun setupDisplay(display: SBForgeRecipe, bounds: Rectangle): List<Widget> { - return buildList { - add(Widgets.createRecipeBase(bounds)) - add(Widgets.createResultSlotBackground(Point(bounds.minX + 124, bounds.minY + 46))) - val arrow = Widgets.createArrow(Point(bounds.minX + 90, bounds.minY + 54 - 18 / 2)) - add(arrow) - add(Widgets.createTooltip(arrow.bounds, - Text.stringifiedTranslatable("firmament.recipe.forge.time", - display.neuRecipe.duration.seconds))) - val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8) - val count = display.neuRecipe.inputs.size - if (count == 1) { - add( - Widgets.createSlot(Point(ingredientsCenter.x, ingredientsCenter.y)).markInput() - .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.inputs.single())) - ) - } else { - display.neuRecipe.inputs.forEachIndexed { idx, ingredient -> - val rad = Math.PI * 2 * idx / count - add( - Widgets.createSlot( - Point( - cos(rad) * 30, - sin(rad) * 30, - ) + ingredientsCenter - ).markInput().entry(SBItemEntryDefinition.getEntry(ingredient)) - ) - } - } - add( - Widgets.createSlot(Point(bounds.minX + 124, bounds.minY + 46)).markOutput().disableBackground() - .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.outputStack)) - ) - } - } - } - -} diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt index cce1465..2ce27a1 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt @@ -8,7 +8,7 @@ import io.github.notenoughupdates.moulconfig.gui.MouseEvent import io.github.notenoughupdates.moulconfig.gui.component.SliderComponent import io.github.notenoughupdates.moulconfig.observer.GetSetter import io.github.notenoughupdates.moulconfig.observer.Property -import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext +import io.github.notenoughupdates.moulconfig.platform.MoulConfigRenderContext import me.shedaniel.math.Point import me.shedaniel.math.Rectangle import me.shedaniel.rei.api.client.gui.Renderer @@ -17,12 +17,13 @@ import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds 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.time.Duration.Companion.seconds -import net.minecraft.client.gui.DrawContext -import net.minecraft.client.gui.Element -import net.minecraft.item.Items -import net.minecraft.text.Text +import net.minecraft.client.gui.navigation.ScreenDirection +import net.minecraft.client.gui.GuiGraphics +import net.minecraft.client.gui.components.events.GuiEventListener +import net.minecraft.client.input.MouseButtonEvent +import net.minecraft.world.item.Items +import net.minecraft.network.chat.Component import moe.nea.firmament.Firmament import moe.nea.firmament.compat.rei.SBItemEntryDefinition import moe.nea.firmament.repo.PetData @@ -38,7 +39,7 @@ class SBKatRecipe(override val neuRecipe: NEUKatUpgradeRecipe) : SBRecipe() { override fun getCategoryIdentifier(): CategoryIdentifier<SBKatRecipe> = CategoryIdentifier.of(Firmament.MOD_ID, "kat_recipe") - override fun getTitle(): Text = Text.literal("Kat Pet Upgrade") + override fun getTitle(): Component = Component.literal("Kat Pet Upgrade") override fun getDisplayHeight(): Int { return 100 } @@ -55,11 +56,13 @@ class SBKatRecipe(override val neuRecipe: NEUKatUpgradeRecipe) : SBRecipe() { val inputLevelLabelCenter = Point(bounds.minX + 30 - 18 + 5 + 8, bounds.minY + 25) val inputLevelLabel = Widgets.createLabel( inputLevelLabelCenter, - Text.literal("")).centered() + Component.literal("") + ).centered() val outputLevelLabelCenter = Point(bounds.maxX - 30 + 8, bounds.minY + 25) val outputLevelLabel = Widgets.createLabel( outputLevelLabelCenter, - Text.literal("")).centered() + Component.literal("") + ).centered() val coinStack = SBItemStack(SkyblockId.COINS, recipe.coins.toInt()) levelValue.whenChanged { oldValue, newValue -> if (oldValue.toInt() == newValue.toInt()) return@whenChanged @@ -69,43 +72,63 @@ class SBKatRecipe(override val neuRecipe: NEUKatUpgradeRecipe) : SBRecipe() { val oldOutput = outputStack.getPetData() ?: return@whenChanged val newOutput = PetData(oldOutput.rarity, oldOutput.petId, newInput.exp) outputStack.setPetData(newOutput) - inputLevelLabel.message = Text.literal(newInput.levelData.currentLevel.toString()) + inputLevelLabel.message = Component.literal(newInput.levelData.currentLevel.toString()) inputLevelLabel.bounds.location = Point( - inputLevelLabelCenter.x - MC.font.getWidth(inputLevelLabel.message) / 2, - inputLevelLabelCenter.y) - outputLevelLabel.message = Text.literal(newOutput.levelData.currentLevel.toString()) + inputLevelLabelCenter.x - MC.font.width(inputLevelLabel.message) / 2, + inputLevelLabelCenter.y + ) + outputLevelLabel.message = Component.literal(newOutput.levelData.currentLevel.toString()) outputLevelLabel.bounds.location = Point( - outputLevelLabelCenter.x - MC.font.getWidth(outputLevelLabel.message) / 2, - outputLevelLabelCenter.y) + outputLevelLabelCenter.x - MC.font.width(outputLevelLabel.message) / 2, + outputLevelLabelCenter.y + ) coinStack.setStackSize((recipe.coins * (1 - 0.3 * newValue / 100)).toInt()) } levelValue.set(1F) add(Widgets.createRecipeBase(bounds)) - add(wrapWidget(Rectangle(bounds.centerX - slider.width / 2, - bounds.maxY - 30, - slider.width, - slider.height), - slider)) - add(Widgets.withTooltip( - Widgets.createArrow(Point(bounds.centerX - arrowWidth / 2, bounds.minY + 40)), - Text.literal("Upgrade time: " + FirmFormatters.formatTimespan(recipe.seconds.seconds)))) + add( + wrapWidget( + Rectangle( + bounds.centerX - slider.width / 2, + bounds.maxY - 30, + slider.width, + slider.height + ), + slider + ) + ) + add( + Widgets.withTooltip( + Widgets.createArrow(Point(bounds.centerX - arrowWidth / 2, bounds.minY + 40)), + Component.literal("Upgrade time: " + FirmFormatters.formatTimespan(recipe.seconds.seconds)) + ) + ) add(Widgets.createResultSlotBackground(Point(bounds.maxX - 30, bounds.minY + 40))) add(inputLevelLabel) add(outputLevelLabel) - add(Widgets.createSlot(Point(bounds.maxX - 30, bounds.minY + 40)).markOutput().disableBackground() - .entry(SBItemEntryDefinition.getEntry(outputStack))) - add(Widgets.createSlot(Point(bounds.minX + 30 - 18 + 5, bounds.minY + 40)).markInput() - .entry(SBItemEntryDefinition.getEntry(inputStack))) + add( + Widgets.createSlot(Point(bounds.maxX - 30, bounds.minY + 40)).markOutput().disableBackground() + .entry(SBItemEntryDefinition.getEntry(outputStack)) + ) + add( + Widgets.createSlot(Point(bounds.minX + 30 - 18 + 5, bounds.minY + 40)).markInput() + .entry(SBItemEntryDefinition.getEntry(inputStack)) + ) val allInputs = recipe.items.map { SBItemEntryDefinition.getEntry(it) } + listOf(SBItemEntryDefinition.getEntry(coinStack)) for ((index, item) in allInputs.withIndex()) { - add(Widgets.createSlot( - Point(bounds.centerX + index * 20 - allInputs.size * 18 / 2 - (allInputs.size - 1) * 2 / 2, - bounds.minY + 20)) - .markInput() - .entry(item)) + add( + Widgets.createSlot( + Point( + bounds.centerX + index * 20 - allInputs.size * 18 / 2 - (allInputs.size - 1) * 2 / 2, + bounds.minY + 20 + ) + ) + .markInput() + .entry(item) + ) } } } @@ -118,85 +141,88 @@ fun wrapWidget(bounds: Rectangle, component: GuiComponent): Widget { return bounds } - override fun children(): List<Element> { + override fun children(): List<GuiEventListener> { return listOf() } - override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { - context.matrices.push() - context.matrices.translate(bounds.minX.toFloat(), bounds.minY.toFloat(), 0F) + override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) { + context.pose().pushMatrix() + context.pose().translate(bounds.minX.toFloat(), bounds.minY.toFloat()) component.render( GuiImmediateContext( - ModernRenderContext(context), + MoulConfigRenderContext(context), bounds.minX, bounds.minY, bounds.width, bounds.height, mouseX - bounds.minX, mouseY - bounds.minY, mouseX, mouseY, mouseX.toFloat(), mouseY.toFloat() - )) - context.matrices.pop() + ) + ) + context.pose().popMatrix() } override fun mouseMoved(mouseX: Double, mouseY: Double) { val mouseXInt = mouseX.toInt() val mouseYInt = mouseY.toInt() - component.mouseEvent(MouseEvent.Move(0F, 0F), - GuiImmediateContext( - IMinecraft.instance.provideTopLevelRenderContext(), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseXInt - bounds.minX, mouseYInt - bounds.minY, - mouseXInt, mouseYInt, - mouseX.toFloat(), mouseY.toFloat() - )) + component.mouseEvent( + MouseEvent.Move(0F, 0F), + GuiImmediateContext( + IMinecraft.INSTANCE.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + ) + ) } - override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { - val mouseXInt = mouseX.toInt() - val mouseYInt = mouseY.toInt() - return component.mouseEvent(MouseEvent.Click(button, true), - GuiImmediateContext( - IMinecraft.instance.provideTopLevelRenderContext(), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseXInt - bounds.minX, mouseYInt - bounds.minY, - mouseXInt, mouseYInt, - mouseX.toFloat(), mouseY.toFloat() - )) + override fun mouseClicked(event: MouseButtonEvent, isDoubleClick: Boolean): Boolean { + val mouseXInt = event.x.toInt() + val mouseYInt = event.y.toInt() + return component.mouseEvent( + MouseEvent.Click(event.button(), true), + GuiImmediateContext( + IMinecraft.INSTANCE.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + event.x.toFloat(), event.y.toFloat() + ) + ) } - override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { - val mouseXInt = mouseX.toInt() - val mouseYInt = mouseY.toInt() - return component.mouseEvent(MouseEvent.Click(button, false), - GuiImmediateContext( - IMinecraft.instance.provideTopLevelRenderContext(), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseXInt - bounds.minX, mouseYInt - bounds.minY, - mouseXInt, mouseYInt, - mouseX.toFloat(), mouseY.toFloat() - )) + override fun mouseReleased(event: MouseButtonEvent): Boolean { + val mouseXInt = event.x.toInt() + val mouseYInt = event.y.toInt() + return component.mouseEvent( + MouseEvent.Click(event.button(), false), + GuiImmediateContext( + IMinecraft.INSTANCE.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + event.x.toFloat(), event.y.toFloat() + ) + ) } - override fun mouseDragged( - mouseX: Double, - mouseY: Double, - button: Int, - deltaX: Double, - deltaY: Double - ): Boolean { - val mouseXInt = mouseX.toInt() - val mouseYInt = mouseY.toInt() - return component.mouseEvent(MouseEvent.Move(deltaX.toFloat(), deltaY.toFloat()), - GuiImmediateContext( - IMinecraft.instance.provideTopLevelRenderContext(), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseXInt - bounds.minX, mouseYInt - bounds.minY, - mouseXInt, mouseYInt, - mouseX.toFloat(), mouseY.toFloat() - )) + override fun mouseDragged(event: MouseButtonEvent, mouseX: Double, mouseY: Double): Boolean { + val mouseXInt = event.x.toInt() + val mouseYInt = event.y.toInt() + return component.mouseEvent( + MouseEvent.Move(0f, 0f), + GuiImmediateContext( + IMinecraft.INSTANCE.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + event.x.toFloat(), event.y.toFloat() + ) + ) } @@ -208,15 +234,17 @@ fun wrapWidget(bounds: Rectangle, component: GuiComponent): Widget { ): Boolean { val mouseXInt = mouseX.toInt() val mouseYInt = mouseY.toInt() - return component.mouseEvent(MouseEvent.Scroll(verticalAmount.toFloat()), - GuiImmediateContext( - IMinecraft.instance.provideTopLevelRenderContext(), - bounds.minX, bounds.minY, - bounds.width, bounds.height, - mouseXInt - bounds.minX, mouseYInt - bounds.minY, - mouseXInt, mouseYInt, - mouseX.toFloat(), mouseY.toFloat() - )) + return component.mouseEvent( + MouseEvent.Scroll(verticalAmount.toFloat()), + GuiImmediateContext( + IMinecraft.INSTANCE.provideTopLevelRenderContext(), + bounds.minX, bounds.minY, + bounds.width, bounds.height, + mouseXInt - bounds.minX, mouseYInt - bounds.minY, + mouseXInt, mouseYInt, + mouseX.toFloat(), mouseY.toFloat() + ) + ) } } } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt index b595c23..927c199 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt @@ -9,9 +9,9 @@ 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 net.minecraft.item.Items -import net.minecraft.text.Text -import net.minecraft.util.Identifier +import net.minecraft.world.item.Items +import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceLocation import moe.nea.firmament.Firmament import moe.nea.firmament.compat.rei.EntityWidget import moe.nea.firmament.compat.rei.SBItemEntryDefinition @@ -24,7 +24,7 @@ class SBMobDropRecipe(override val neuRecipe: NEUMobDropRecipe) : SBRecipe() { override fun getCategoryIdentifier(): CategoryIdentifier<SBMobDropRecipe> = CategoryIdentifier.of(Firmament.MOD_ID, "mob_drop_recipe") - override fun getTitle(): Text = Text.literal("Mob Drops") + override fun getTitle(): Component = Component.literal("Mob Drops") override fun getDisplayHeight(): Int { return 100 } @@ -35,23 +35,23 @@ class SBMobDropRecipe(override val neuRecipe: NEUMobDropRecipe) : SBRecipe() { add(Widgets.createRecipeBase(bounds)) val source = display.neuRecipe.render val entity = if (source.startsWith("@")) { - EntityRenderer.constructEntity(Identifier.of(source.substring(1))) + EntityRenderer.constructEntity(ResourceLocation.parse(source.substring(1))) } else { EntityRenderer.applyModifiers(source, listOf()) } val level = display.neuRecipe.level val fullMobName = - if (level > 0) Text.translatable("firmament.recipe.mobs.name", level, display.neuRecipe.name) - else Text.translatable("firmament.recipe.mobs.name.nolevel", display.neuRecipe.name) - val tt = mutableListOf<Text>() + if (level > 0) Component.translatable("firmament.recipe.mobs.name", level, display.neuRecipe.name) + else Component.translatable("firmament.recipe.mobs.name.nolevel", display.neuRecipe.name) + val tt = mutableListOf<Component>() tt.add((fullMobName)) - tt.add(Text.literal("")) + tt.add(Component.literal("")) if (display.neuRecipe.coins > 0) { - tt.add(Text.stringifiedTranslatable("firmament.recipe.mobs.coins", display.neuRecipe.coins)) + tt.add(Component.translatable("firmament.recipe.mobs.coins", display.neuRecipe.coins)) } if (display.neuRecipe.combatExperience > 0) { tt.add( - Text.stringifiedTranslatable( + Component.translatable( "firmament.recipe.mobs.combat", display.neuRecipe.combatExperience ) @@ -59,14 +59,14 @@ class SBMobDropRecipe(override val neuRecipe: NEUMobDropRecipe) : SBRecipe() { } if (display.neuRecipe.enchantingExperience > 0) { tt.add( - Text.stringifiedTranslatable( + Component.translatable( "firmament.recipe.mobs.exp", display.neuRecipe.enchantingExperience ) ) } if (display.neuRecipe.extra != null) - display.neuRecipe.extra.mapTo(tt) { Text.literal(it) } + display.neuRecipe.extra.mapTo(tt) { Component.literal(it) } if (tt.size == 2) tt.removeAt(1) add( @@ -76,15 +76,15 @@ class SBMobDropRecipe(override val neuRecipe: NEUMobDropRecipe) : SBRecipe() { ) ) add( - Widgets.createLabel(Point(bounds.minX + 15, bounds.minY + 5), Text.literal(display.neuRecipe.name)) + Widgets.createLabel(Point(bounds.minX + 15, bounds.minY + 5), Component.literal(display.neuRecipe.name)) .leftAligned() ) var x = bounds.minX + 60 var y = bounds.minY + 20 for (drop in display.neuRecipe.drops) { - val lore = drop.extra.mapTo(mutableListOf()) { Text.literal(it) } + val lore = drop.extra.mapTo(mutableListOf()) { Component.literal(it) } if (drop.chance != null) { - lore += listOf(Text.translatable("firmament.recipe.mobs.drops", drop.chance)) + lore += listOf(Component.translatable("firmament.recipe.mobs.drops", drop.chance)) } val item = SBItemEntryDefinition.getEntry(drop.dropItem) .value.copy(extraLore = lore) diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt index de7779f..d6ca3f6 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt @@ -6,12 +6,12 @@ import java.util.Optional import me.shedaniel.rei.api.common.display.Display import me.shedaniel.rei.api.common.display.DisplaySerializer import me.shedaniel.rei.api.common.entry.EntryIngredient -import net.minecraft.util.Identifier +import net.minecraft.resources.ResourceLocation import moe.nea.firmament.compat.rei.SBItemEntryDefinition import moe.nea.firmament.util.SkyblockId abstract class SBRecipe : Display { - override fun getDisplayLocation(): Optional<Identifier> { + override fun getDisplayLocation(): Optional<ResourceLocation> { // In theory, we could return a location for the neuRecipe here. (Something along the lines of neurepo:items/item_id.json/0 for the 0th recipe in the items/item_id.json recipes array). return Optional.empty() } 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..ca69782 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 @@ -1,3 +1,5 @@ +@file:OptIn(ExpensiveItemCacheApi::class) + package moe.nea.firmament.compat.rei.recipes import java.util.Optional @@ -17,15 +19,17 @@ import me.shedaniel.rei.api.common.display.Display import me.shedaniel.rei.api.common.display.DisplaySerializer import me.shedaniel.rei.api.common.entry.EntryIngredient import me.shedaniel.rei.api.common.entry.EntryStack -import net.minecraft.entity.EntityType -import net.minecraft.entity.SpawnReason -import net.minecraft.text.Text -import net.minecraft.util.Identifier -import net.minecraft.village.VillagerProfession +import net.minecraft.world.entity.EntityType +import net.minecraft.world.entity.EntitySpawnReason +import net.minecraft.core.Holder +import net.minecraft.network.chat.Component +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.entity.npc.VillagerProfession import moe.nea.firmament.Firmament import moe.nea.firmament.compat.rei.EntityWidget import moe.nea.firmament.compat.rei.SBItemEntryDefinition import moe.nea.firmament.gui.entity.EntityRenderer +import moe.nea.firmament.repo.ExpensiveItemCacheApi import moe.nea.firmament.repo.Reforge import moe.nea.firmament.repo.ReforgeStore import moe.nea.firmament.repo.RepoItemTypeCache @@ -33,6 +37,7 @@ import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.SBItemStack import moe.nea.firmament.util.AprilFoolsUtil import moe.nea.firmament.util.FirmFormatters +import moe.nea.firmament.util.MC import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.gold import moe.nea.firmament.util.grey @@ -44,7 +49,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") @@ -55,7 +60,7 @@ class SBReforgeRecipe( return catIdentifier } - override fun getTitle(): Text { + override fun getTitle(): Component { return tr("firmament.recipecategory.reforge", "Reforge") } @@ -69,37 +74,6 @@ class SBReforgeRecipe( val inputSlot = Widgets.createSlot(Point(bounds.minX + 10, bounds.centerY - 9)) .markInput().entries(display.inputItems) list.add(inputSlot) - if (display.reforgeStone != null) { - list.add(Widgets.createSlot(Point(bounds.minX + 10 + 24, bounds.centerY - 9 - 10)) - .markInput().entry(display.reforgeStone)) - list.add(Widgets.withTooltip( - Widgets.withTranslate(Widgets.wrapRenderer( - Rectangle(Point(bounds.minX + 10 + 24, bounds.centerY - 9 + 10), Dimension(16, 16)), - SBItemEntryDefinition.getEntry(SkyBlockItems.REFORGE_ANVIL)), 0.0, 0.0, 150.0), - Rarity.entries.mapNotNull { rarity -> - display.reforge.reforgeCosts?.get(rarity)?.let { rarity to it } - }.map { (rarity, cost) -> - Text.literal("") - .append(rarity.text) - .append(": ") - .append(Text.literal("${FirmFormatters.formatCommas(cost, 0)} Coins").gold()) - } - )) - } else { - val size = if (AprilFoolsUtil.isAprilFoolsDay) 1.2 else 0.6 - val dimension = - FloatingDimension(EntityWidget.defaultSize.width * size, EntityWidget.defaultSize.height * size) - list.add(Widgets.withTooltip( - EntityWidget( - EntityType.VILLAGER.create(EntityRenderer.fakeWorld, SpawnReason.COMMAND) - ?.also { it.villagerData = it.villagerData.withProfession(VillagerProfession.WEAPONSMITH) }, - Point(bounds.minX + 10 + 24 + 8 - dimension.width / 2, bounds.centerY - dimension.height / 2), - dimension - ), - tr("firmament.recipecategory.reforge.basic", - "This is a basic reforge, available at the Blacksmith.").grey() - )) - } list.add(Widgets.createSlot(Point(bounds.minX + 10 + 24 + 24, bounds.centerY - 9)) .markInput().entries(display.outputItems)) val statToLineMappings = mutableListOf<Pair<String, Label>>() @@ -124,6 +98,37 @@ class SBReforgeRecipe( } updateStatLines() inputSlot.withEntriesListener { updateStatLines() } + if (display.reforgeStone != null) { + list.add(Widgets.createSlot(Point(bounds.minX + 10 + 24, bounds.centerY - 9 - 10)) + .markInput().entry(display.reforgeStone)) + list.add(Widgets.withTooltip( + Widgets.wrapRenderer( + Rectangle(Point(bounds.minX + 10 + 24, bounds.centerY - 9 + 10), Dimension(16, 16)), + SBItemEntryDefinition.getEntry(SkyBlockItems.REFORGE_ANVIL)), + Rarity.entries.mapNotNull { rarity -> + display.reforge.reforgeCosts?.get(rarity)?.let { rarity to it } + }.map { (rarity, cost) -> + Component.literal("") + .append(rarity.text) + .append(": ") + .append(Component.literal("${FirmFormatters.formatCommas(cost, 0)} Coins").gold()) + } + )) + } else { + val size = if (AprilFoolsUtil.isAprilFoolsDay) 1.2 else 0.6 + val dimension = + FloatingDimension(EntityWidget.defaultSize.width * size, EntityWidget.defaultSize.height * size) + list.add(Widgets.withTooltip( + EntityWidget( + EntityType.VILLAGER.create(EntityRenderer.fakeWorld, EntitySpawnReason.COMMAND) + ?.also { it.villagerData = it.villagerData.withProfession(MC.currentOrDefaultRegistries, VillagerProfession.WEAPONSMITH) }, + Point(bounds.minX + 10 + 24 + 8 - dimension.width / 2, bounds.centerY - dimension.height / 2), + dimension + ), + tr("firmament.recipecategory.reforge.basic", + "This is a basic reforge, available at the Blacksmith.").grey() + )) + } return list } } @@ -132,10 +137,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 +167,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) @@ -201,8 +207,8 @@ class SBReforgeRecipe( return catIdentifier } - override fun getDisplayLocation(): Optional<Identifier> { - return Optional.empty() + override fun getDisplayLocation(): Optional<ResourceLocation> { + return Optional.empty<ResourceLocation>() } override fun getSerializer(): DisplaySerializer<out Display>? { 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..87d8bdf --- /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.world.item.Items +import net.minecraft.network.chat.Component +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(): Component = Component.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/compat/sodium/java/moe/nea/firmament/compat/sodium/Compat.kt b/src/compat/sodium/java/moe/nea/firmament/compat/sodium/Compat.kt new file mode 100644 index 0000000..97ae1fc --- /dev/null +++ b/src/compat/sodium/java/moe/nea/firmament/compat/sodium/Compat.kt @@ -0,0 +1,12 @@ +package moe.nea.firmament.compat.sodium + +import moe.nea.firmament.util.compatloader.CompatMeta +import moe.nea.firmament.util.compatloader.ICompatMeta +import net.fabricmc.loader.api.FabricLoader + +@CompatMeta +object Compat : ICompatMeta { + override fun shouldLoad(): Boolean { + return FabricLoader.getInstance().isModLoaded("sodium") + } +} diff --git a/src/compat/sodium/java/SodiumChunkReloader.kt b/src/compat/sodium/java/moe/nea/firmament/compat/sodium/SodiumChunkReloader.kt index 0256b88..e5d0fd9 100644 --- a/src/compat/sodium/java/SodiumChunkReloader.kt +++ b/src/compat/sodium/java/moe/nea/firmament/compat/sodium/SodiumChunkReloader.kt @@ -1,7 +1,7 @@ package moe.nea.firmament.compat.sodium +import moe.nea.firmament.mixins.sodium.accessor.AccessorSodiumWorldRenderer import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer -import moe.nea.firmament.mixins.accessor.sodium.AccessorSodiumWorldRenderer class SodiumChunkReloader : Runnable { override fun run() { diff --git a/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java b/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java deleted file mode 100644 index fe87310..0000000 --- a/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java +++ /dev/null @@ -1,29 +0,0 @@ -package moe.nea.firmament.mixins.custommodels; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; -import moe.nea.firmament.features.texturepack.CustomBlockTextures; -import net.caffeinemc.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask; -import net.minecraft.block.BlockState; -import net.minecraft.client.render.block.BlockModels; -import net.minecraft.client.render.model.BakedModel; -import net.minecraft.util.math.BlockPos; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -@Mixin(ChunkBuilderMeshingTask.class) -public class PatchBlockModelInSodiumChunkGenerator { - @WrapOperation( - method = "execute(Lnet/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildContext;Lnet/caffeinemc/mods/sodium/client/util/task/CancellationToken;)Lnet/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildOutput;", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockModels;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;")) - private BakedModel replaceBlockModel(BlockModels instance, BlockState state, Operation<BakedModel> original, - @Local(name = "blockPos") BlockPos.Mutable pos) { - var replacement = CustomBlockTextures.getReplacementModel(state, pos); - if (replacement != null) return replacement; - CustomBlockTextures.enterFallbackCall(); - var fallback = original.call(instance, state); - CustomBlockTextures.exitFallbackCall(); - return fallback; - } -} diff --git a/src/compat/sodium/java/moe/nea/firmament/mixins/accessor/sodium/AccessorSodiumWorldRenderer.java b/src/compat/sodium/java/moe/nea/firmament/mixins/sodium/accessor/AccessorSodiumWorldRenderer.java index f75874d..bc2210a 100644 --- a/src/compat/sodium/java/moe/nea/firmament/mixins/accessor/sodium/AccessorSodiumWorldRenderer.java +++ b/src/compat/sodium/java/moe/nea/firmament/mixins/sodium/accessor/AccessorSodiumWorldRenderer.java @@ -1,4 +1,4 @@ -package moe.nea.firmament.mixins.accessor.sodium; +package moe.nea.firmament.mixins.sodium.accessor; import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager; diff --git a/src/compat/sodium/java/moe/nea/firmament/mixins/sodium/custommodels/PatchBlockModelInSodiumChunkGenerator.java b/src/compat/sodium/java/moe/nea/firmament/mixins/sodium/custommodels/PatchBlockModelInSodiumChunkGenerator.java new file mode 100644 index 0000000..50dba85 --- /dev/null +++ b/src/compat/sodium/java/moe/nea/firmament/mixins/sodium/custommodels/PatchBlockModelInSodiumChunkGenerator.java @@ -0,0 +1,29 @@ +package moe.nea.firmament.mixins.sodium.custommodels; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import moe.nea.firmament.features.texturepack.CustomBlockTextures; +import net.caffeinemc.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.client.renderer.block.BlockModelShaper; +import net.minecraft.client.renderer.block.model.BlockStateModel; +import net.minecraft.core.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(ChunkBuilderMeshingTask.class) +public class PatchBlockModelInSodiumChunkGenerator { + @WrapOperation( + method = "execute(Lnet/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildContext;Lnet/caffeinemc/mods/sodium/client/util/task/CancellationToken;)Lnet/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildOutput;", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/block/BlockModelShaper;getBlockModel(Lnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/client/renderer/block/model/BlockStateModel;")) + private BlockStateModel replaceBlockModel(BlockModelShaper instance, BlockState state, Operation<BlockStateModel> original, + @Local(name = "blockPos") BlockPos.MutableBlockPos pos) { + var replacement = CustomBlockTextures.getReplacementModel(state, pos); + if (replacement != null) return replacement; + CustomBlockTextures.enterFallbackCall(); + var fallback = original.call(instance, state); + CustomBlockTextures.exitFallbackCall(); + return fallback; + } +} diff --git a/src/compat/wildfireGender/java/moe/nea/firmament/compat/gender/Compat.kt b/src/compat/wildfireGender/java/moe/nea/firmament/compat/gender/Compat.kt new file mode 100644 index 0000000..347dd5d --- /dev/null +++ b/src/compat/wildfireGender/java/moe/nea/firmament/compat/gender/Compat.kt @@ -0,0 +1,13 @@ +package moe.nea.firmament.compat.gender + +import net.fabricmc.loader.api.FabricLoader +import moe.nea.firmament.util.compatloader.CompatMeta +import moe.nea.firmament.util.compatloader.ICompatMeta + +@CompatMeta +object Compat : ICompatMeta { + override fun shouldLoad(): Boolean { + return FabricLoader.getInstance().isModLoaded("wildfire_gender") + } + +} diff --git a/src/compat/wildfireGender/java/moe/nea/firmament/mixins/compat/wildfiregender/PatchArmorTexturesInGenderMod.java b/src/compat/wildfireGender/java/moe/nea/firmament/mixins/compat/wildfiregender/PatchArmorTexturesInGenderMod.java index c3e8950..f8999da 100644 --- a/src/compat/wildfireGender/java/moe/nea/firmament/mixins/compat/wildfiregender/PatchArmorTexturesInGenderMod.java +++ b/src/compat/wildfireGender/java/moe/nea/firmament/mixins/compat/wildfiregender/PatchArmorTexturesInGenderMod.java @@ -4,9 +4,9 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; import com.wildfire.render.GenderArmorLayer; import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides; -import net.minecraft.component.type.EquippableComponent; -import net.minecraft.entity.EquipmentSlot; -import net.minecraft.item.ItemStack; +import net.minecraft.world.item.equipment.Equippable; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; @@ -14,10 +14,10 @@ import org.spongepowered.asm.mixin.injection.At; @Mixin(GenderArmorLayer.class) @Pseudo public class PatchArmorTexturesInGenderMod { - @ModifyExpressionValue(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/BipedEntityRenderState;FF)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;get(Lnet/minecraft/component/ComponentType;)Ljava/lang/Object;")) + @ModifyExpressionValue(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/client/renderer/entity/state/HumanoidRenderState;FF)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;get(Lnet/minecraft/core/component/DataComponentType;)Ljava/lang/Object;")) private Object replaceArmorMaterial(Object original, @Local ItemStack chestplate) { var overrides = CustomGlobalArmorOverrides.overrideArmor(chestplate, EquipmentSlot.CHEST); - return overrides.orElse((EquippableComponent) original); + return overrides.orElse((Equippable) original); } } diff --git a/src/compat/yacl/java/KeybindingController.kt b/src/compat/yacl/java/KeybindingController.kt index 204d521..3714455 100644 --- a/src/compat/yacl/java/KeybindingController.kt +++ b/src/compat/yacl/java/KeybindingController.kt @@ -6,10 +6,13 @@ import dev.isxander.yacl3.api.utils.Dimension import dev.isxander.yacl3.gui.AbstractWidget import dev.isxander.yacl3.gui.YACLScreen import dev.isxander.yacl3.gui.controllers.ControllerWidget -import net.minecraft.text.Text +import net.minecraft.client.input.MouseButtonEvent +import net.minecraft.client.input.KeyEvent +import net.minecraft.network.chat.Component import moe.nea.firmament.gui.config.KeyBindingHandler import moe.nea.firmament.gui.config.KeyBindingStateManager import moe.nea.firmament.gui.config.ManagedOption +import moe.nea.firmament.keybindings.GenericInputButton import moe.nea.firmament.keybindings.SavedKeyBinding class KeybindingController( @@ -21,7 +24,7 @@ class KeybindingController( return option } - override fun formatValue(): Text { + override fun formatValue(): Component { return option.pendingValue().format() } @@ -52,16 +55,16 @@ class KeybindingWidget( return 130 } - override fun getValueText(): Text { + override fun getValueText(): Component { return sm.label } - override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - return sm.keyboardEvent(keyCode, true) + override fun keyPressed(keyEvent: KeyEvent): Boolean { + return sm.keyboardEvent(GenericInputButton.of(keyEvent), true) } - override fun keyReleased(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - return sm.keyboardEvent(keyCode, false) + override fun keyReleased(keyEvent: KeyEvent): Boolean { + return sm.keyboardEvent(GenericInputButton.of(keyEvent), false) } override fun unfocus() { @@ -73,11 +76,11 @@ class KeybindingWidget( if (!focused) sm.onLostFocus() } - override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { - if (button == 0 && isHovered) { - sm.onClick() + override fun mouseClicked(mouseButtonEvent: MouseButtonEvent, doubleClick: Boolean): Boolean { + if (isHovered) { + sm.onClick(mouseButtonEvent.button()) return true } - return super.mouseClicked(mouseX, mouseY, button) + return super.mouseClicked(mouseButtonEvent, doubleClick) } } diff --git a/src/compat/yacl/java/YaclIntegration.kt b/src/compat/yacl/java/YaclIntegration.kt index 45a0d02..e10b8c4 100644 --- a/src/compat/yacl/java/YaclIntegration.kt +++ b/src/compat/yacl/java/YaclIntegration.kt @@ -9,6 +9,7 @@ import dev.isxander.yacl3.api.Option import dev.isxander.yacl3.api.OptionDescription import dev.isxander.yacl3.api.OptionGroup import dev.isxander.yacl3.api.YetAnotherConfigLib +import dev.isxander.yacl3.api.controller.ColorControllerBuilder import dev.isxander.yacl3.api.controller.ControllerBuilder import dev.isxander.yacl3.api.controller.DoubleSliderControllerBuilder import dev.isxander.yacl3.api.controller.EnumControllerBuilder @@ -17,16 +18,19 @@ import dev.isxander.yacl3.api.controller.StringControllerBuilder import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder import dev.isxander.yacl3.api.controller.ValueFormatter import dev.isxander.yacl3.gui.YACLScreen -import dev.isxander.yacl3.gui.tab.ListHolderWidget +import io.github.notenoughupdates.moulconfig.ChromaColour +import java.awt.Color import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import kotlin.time.DurationUnit -import net.minecraft.client.gui.Element -import net.minecraft.client.gui.screen.Screen -import net.minecraft.text.Text +import net.minecraft.client.gui.components.events.GuiEventListener +import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.gui.components.AbstractContainerWidget +import net.minecraft.network.chat.Component import moe.nea.firmament.gui.config.BooleanHandler import moe.nea.firmament.gui.config.ChoiceHandler import moe.nea.firmament.gui.config.ClickHandler +import moe.nea.firmament.gui.config.ColourHandler import moe.nea.firmament.gui.config.DurationHandler import moe.nea.firmament.gui.config.EnumRenderer import moe.nea.firmament.gui.config.FirmamentConfigScreenProvider @@ -34,11 +38,13 @@ import moe.nea.firmament.gui.config.HudMeta import moe.nea.firmament.gui.config.HudMetaHandler import moe.nea.firmament.gui.config.IntegerHandler import moe.nea.firmament.gui.config.KeyBindingHandler -import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.util.data.ManagedConfig import moe.nea.firmament.gui.config.ManagedOption import moe.nea.firmament.gui.config.StringHandler import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.util.FirmFormatters +import moe.nea.firmament.util.getRGBAWithoutAnimation +import moe.nea.firmament.util.toChromaWithoutAnimation @AutoService(FirmamentConfigScreenProvider::class) @@ -56,20 +62,22 @@ class YaclIntegration : FirmamentConfigScreenProvider { OptionGroup.createBuilder() .name(it.labelText) .options(buildOptions(it.sortedOptions)) - .build()) + .build() + ) } } .build() } fun buildOptions(options: List<ManagedOption<*>>): Collection<Option<*>> = - options.map { buildOption(it) } + options.flatMap { buildOption(it) } - private fun <T : Any> buildOption(managedOption: ManagedOption<T>): Option<*> { + private fun <T : Any> buildOption(managedOption: ManagedOption<T>): Collection<Option<*>> { val handler = managedOption.handler - val binding = Binding.generic(managedOption.default(), - managedOption::value, - { managedOption.value = it; managedOption.element.save() }) + val binding = Binding.generic( + managedOption.default(), + managedOption::value, + { managedOption.value = it; managedOption.element.markDirty() }) fun <T> createDefaultBinding(function: (Option<T>) -> ControllerBuilder<T>): Option.Builder<T> { return Option.createBuilder<T>() @@ -78,47 +86,89 @@ class YaclIntegration : FirmamentConfigScreenProvider { .binding(binding as Binding<T>) .controller { function(it) } } + + fun Option<out Any>.single() = listOf(this) + fun ButtonOption.Builder.single() = build().single() + fun Option.Builder<out Any>.single() = build().single() when (handler) { is ClickHandler -> return ButtonOption.createBuilder() .name(managedOption.labelText) .action { t, u -> handler.runnable() } - .build() + .single() is HudMetaHandler -> return ButtonOption.createBuilder() .name(managedOption.labelText) .action { t, u -> handler.openEditor(managedOption as ManagedOption<HudMeta>, t) } - .build() + .single() is ChoiceHandler<*> -> return createDefaultBinding { createChoiceBinding(handler as ChoiceHandler<*>, managedOption as ManagedOption<*>, it as Option<*>) - }.build() + }.single() + + is ColourHandler -> { + managedOption as ManagedOption<ChromaColour> + val colorBinding = + Binding.generic( + managedOption.default().getRGBAWithoutAnimation(), + { managedOption.value.getRGBAWithoutAnimation() }, + { + managedOption.value = + it.toChromaWithoutAnimation(managedOption.value.timeForFullRotationInMillis) + managedOption.element.markDirty() + }) + val speedBinding = + Binding.generic( + managedOption.default().timeForFullRotationInMillis, + { managedOption.value.timeForFullRotationInMillis }, + { + managedOption.value = managedOption.value.copy(timeForFullRotationInMillis = it) + managedOption.element.markDirty() + } + ) + + return listOf( + Option.createBuilder<Color>() + .name(managedOption.labelText) + .binding(colorBinding) + .controller { + ColorControllerBuilder.create(it) + .allowAlpha(true) + } + .build(), + Option.createBuilder<Int>() + .name(managedOption.labelText) + .binding(speedBinding) + .controller { IntegerSliderControllerBuilder.create(it).range(0, 60_000).step(10) } + .build(), + ) + } - is BooleanHandler -> return createDefaultBinding(TickBoxControllerBuilder::create).build() - is StringHandler -> return createDefaultBinding(StringControllerBuilder::create).build() + is BooleanHandler -> return createDefaultBinding(TickBoxControllerBuilder::create).single() + is StringHandler -> return createDefaultBinding(StringControllerBuilder::create).single() is IntegerHandler -> return createDefaultBinding { IntegerSliderControllerBuilder.create(it).range(handler.min, handler.max).step(1) - }.build() + }.single() is DurationHandler -> return Option.createBuilder<Double>() .name(managedOption.labelText) .binding((binding as Binding<Duration>).xmap({ it.toDouble(DurationUnit.SECONDS) }, { it.seconds })) .controller { DoubleSliderControllerBuilder.create(it) - .formatValue { Text.literal(FirmFormatters.formatTimespan(it.seconds)) } + .formatValue { Component.literal(FirmFormatters.formatTimespan(it.seconds)) } .step(0.1) .range(handler.min.toDouble(DurationUnit.SECONDS), handler.max.toDouble(DurationUnit.SECONDS)) } - .build() + .single() is KeyBindingHandler -> return createDefaultBinding { KeybindingBuilder(it, managedOption as ManagedOption<SavedKeyBinding>) - }.build() + }.single() - else -> return LabelOption.create(Text.literal("This option is currently unhandled for this config menu. Please report this as a bug.")) + else -> return listOf(LabelOption.create(Component.literal("This option is currently unhandled for this config menu. Please report this as a bug."))) } } @@ -146,7 +196,7 @@ class YaclIntegration : FirmamentConfigScreenProvider { fun buildConfig(): YetAnotherConfigLib { return YetAnotherConfigLib.createBuilder() - .title(Text.literal("Firmament")) + .title(Component.literal("Firmament")) .categories(buildCategories()) .build() } @@ -154,11 +204,11 @@ class YaclIntegration : FirmamentConfigScreenProvider { override val key: String get() = "yacl" - override fun open(parent: Screen?): Screen { + override fun open(search: String?, parent: Screen?): Screen { return object : YACLScreen(buildConfig(), parent) { - override fun setFocused(focused: Element?) { + override fun setFocused(focused: GuiEventListener?) { if (this.focused is KeybindingWidget && - focused is ListHolderWidget<*> + focused is AbstractContainerWidget ) { return } |
