diff options
Diffstat (limited to 'src/texturePacks/java')
15 files changed, 652 insertions, 64 deletions
diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt index 462b1e1..2d7a978 100644 --- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt +++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt @@ -2,6 +2,9 @@ package moe.nea.firmament.features.texturepack +import com.google.gson.JsonParseException +import com.google.gson.JsonParser +import com.mojang.serialization.JsonOps import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor import java.util.function.Function @@ -21,15 +24,21 @@ import kotlinx.serialization.serializer import kotlin.jvm.optionals.getOrNull import net.minecraft.block.Block import net.minecraft.block.BlockState +import net.minecraft.block.Blocks import net.minecraft.client.render.model.Baker import net.minecraft.client.render.model.BlockStateModel +import net.minecraft.client.render.model.BlockStatesLoader import net.minecraft.client.render.model.ReferencedModelsCollector import net.minecraft.client.render.model.SimpleBlockStateModel +import net.minecraft.client.render.model.json.BlockModelDefinition import net.minecraft.client.render.model.json.ModelVariant +import net.minecraft.registry.Registries import net.minecraft.registry.RegistryKey import net.minecraft.registry.RegistryKeys +import net.minecraft.resource.Resource import net.minecraft.resource.ResourceManager import net.minecraft.resource.SinglePreparationResourceReloader +import net.minecraft.state.StateManager import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.profiler.Profiler @@ -41,6 +50,7 @@ import moe.nea.firmament.events.FinalizeResourceManagerEvent import moe.nea.firmament.events.SkyblockServerUpdateEvent import moe.nea.firmament.features.texturepack.CustomBlockTextures.createBakedModels import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger +import moe.nea.firmament.util.ErrorUtil import moe.nea.firmament.util.IdentifierSerializer import moe.nea.firmament.util.MC import moe.nea.firmament.util.SBData @@ -62,12 +72,28 @@ object CustomBlockTextures { val block: Identifier, val sound: Identifier?, ) { + fun replace(block: BlockState): BlockStateModel? { + blockStateMap?.let { return it[block] } + return blockModel + } + + @Transient + lateinit var overridingBlock: Block @Transient val blockModelIdentifier get() = block.withPrefixedPath("block/") /** - * Guaranteed to be set after [BakedReplacements.modelBakingFuture] is complete. + * Guaranteed to be set after [BakedReplacements.modelBakingFuture] is complete, if [unbakedBlockStateMap] is set. + */ + @Transient + var blockStateMap: Map<BlockState, BlockStateModel>? = null + + @Transient + var unbakedBlockStateMap: Map<BlockState, BlockStateModel.UnbakedGrouped>? = null + + /** + * Guaranteed to be set after [BakedReplacements.modelBakingFuture] is complete. Prefer [blockStateMap] if present. */ @Transient lateinit var blockModel: BlockStateModel @@ -139,7 +165,15 @@ object CustomBlockTextures { data class LocationReplacements( val lookup: Map<Block, List<BlockReplacement>> - ) + ) { + init { + lookup.forEach { (block, replacements) -> + for (replacement in replacements) { + replacement.replacement.overridingBlock = block + } + } + } + } data class BlockReplacement( val checks: List<Area>?, @@ -213,7 +247,7 @@ object CustomBlockTextures { @JvmStatic fun getReplacementModel(block: BlockState, blockPos: BlockPos?): BlockStateModel? { - return getReplacement(block, blockPos)?.blockModel + return getReplacement(block, blockPos)?.replace(block) } @JvmStatic @@ -236,8 +270,12 @@ object CustomBlockTextures { } @Volatile - var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture(BakedReplacements( - mapOf())) + @get:JvmStatic + var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture( + BakedReplacements( + mapOf() + ) + ) val insideFallbackCall = ThreadLocal.withInitial { 0 } @@ -257,7 +295,8 @@ object CustomBlockTextures { fun onEarlyReload(event: EarlyResourceReloadEvent) { preparationFuture = CompletableFuture .supplyAsync( - { prepare(event.resourceManager) }, event.preparationExecutor) + { prepare(event.resourceManager) }, event.preparationExecutor + ) } private fun prepare(manager: ResourceManager): BakedReplacements { @@ -295,7 +334,7 @@ object CustomBlockTextures { @Subscribe fun onStart(event: FinalizeResourceManagerEvent) { event.resourceManager.registerReloader(object : - SinglePreparationResourceReloader<BakedReplacements>() { + SinglePreparationResourceReloader<BakedReplacements>() { override fun prepare(manager: ResourceManager, profiler: Profiler): BakedReplacements { return preparationFuture.join().also { it.modelBakingFuture.join() @@ -328,12 +367,28 @@ object CustomBlockTextures { @JvmStatic fun collectExtraModels(modelsCollector: ReferencedModelsCollector) { preparationFuture.join().collectAllReplacements() - .forEach { modelsCollector.resolve(simpleBlockModel(it.blockModelIdentifier)) } + .forEach { + modelsCollector.resolve(simpleBlockModel(it.blockModelIdentifier)) + it.unbakedBlockStateMap?.values?.forEach { + modelsCollector.resolve(it) + } + } } @JvmStatic fun createBakedModels(baker: Baker, executor: Executor): CompletableFuture<Void?> { return preparationFuture.thenComposeAsync(Function { replacements -> + val allBlockStates = CompletableFuture.allOf( + *replacements.collectAllReplacements().filter { it.unbakedBlockStateMap != null }.map { + CompletableFuture.supplyAsync({ + it.blockStateMap = it.unbakedBlockStateMap + ?.map { + it.key to it.value.bake(it.key, baker) + } + ?.toMap() + }, executor) + }.toList().toTypedArray() + ) val byModel = replacements.collectAllReplacements().groupBy { it.blockModelIdentifier } val modelBakingTask = AsyncHelper.mapValues(byModel, { blockId, replacements -> val unbakedModel = SimpleBlockStateModel.Unbaked( @@ -344,7 +399,55 @@ object CustomBlockTextures { it.blockModel = baked } }, executor) - modelBakingTask.thenAcceptAsync { replacements.modelBakingFuture.complete(Unit) } + modelBakingTask.thenComposeAsync { + allBlockStates + }.thenAcceptAsync { + replacements.modelBakingFuture.complete(Unit) + } }, executor) } + + @JvmStatic + fun collectExtraBlockStateMaps( + extra: BakedReplacements, + original: Map<Identifier, List<Resource>>, + stateManagers: Function<Identifier, StateManager<Block, BlockState>?> + ) { + extra.collectAllReplacements().forEach { + val blockId = Registries.BLOCK.getKey(it.overridingBlock).getOrNull()?.value ?: return@forEach + val allModels = mutableListOf<BlockStatesLoader.LoadedBlockStateDefinition>() + val stateManager = stateManagers.apply(blockId) ?: return@forEach + for (resource in original[BlockStatesLoader.FINDER.toResourcePath(it.block)] ?: return@forEach) { + try { + resource.reader.use { reader -> + val jsonElement = JsonParser.parseReader(reader) + val blockModelDefinition = + BlockModelDefinition.CODEC.parse(JsonOps.INSTANCE, jsonElement) + .getOrThrow { msg: String? -> JsonParseException(msg) } + allModels.add( + BlockStatesLoader.LoadedBlockStateDefinition( + resource.getPackId(), + blockModelDefinition + ) + ) + } + } catch (exception: Exception) { + ErrorUtil.softError( + "Failed to load custom blockstate definition ${it.block} from pack ${resource.packId}", + exception + ) + } + } + + try { + it.unbakedBlockStateMap = BlockStatesLoader.combine( + blockId, + stateManager, + allModels + ).models + } catch (exception: Exception) { + ErrorUtil.softError("Failed to combine custom blockstate definitions for ${it.block}", exception) + } + } + } } diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt index a9af059..8a2bde5 100644 --- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt +++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt @@ -118,7 +118,7 @@ object CustomGlobalArmorOverrides { val equipmentLayers = layers.map { EquipmentModel.Layer( it.identifier, if (it.tint) { - Optional.of(EquipmentModel.Dyeable(Optional.empty())) + Optional.of(EquipmentModel.Dyeable(Optional.of(0xFFA06540.toInt()))) } else { Optional.empty() }, diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomScreenLayouts.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomScreenLayouts.kt new file mode 100644 index 0000000..4785e90 --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomScreenLayouts.kt @@ -0,0 +1,224 @@ +package moe.nea.firmament.features.texturepack + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import net.minecraft.client.font.TextRenderer +import net.minecraft.client.gui.DrawContext +import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.screen.ingame.HandledScreen +import net.minecraft.client.render.RenderLayer +import net.minecraft.registry.Registries +import net.minecraft.resource.ResourceManager +import net.minecraft.resource.SinglePreparationResourceReloader +import net.minecraft.screen.slot.Slot +import net.minecraft.text.Text +import net.minecraft.util.Identifier +import net.minecraft.util.profiler.Profiler +import moe.nea.firmament.Firmament +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.FinalizeResourceManagerEvent +import moe.nea.firmament.events.ScreenChangeEvent +import moe.nea.firmament.features.texturepack.CustomScreenLayouts.Alignment.CENTER +import moe.nea.firmament.features.texturepack.CustomScreenLayouts.Alignment.LEFT +import moe.nea.firmament.features.texturepack.CustomScreenLayouts.Alignment.RIGHT +import moe.nea.firmament.mixins.accessor.AccessorHandledScreen +import moe.nea.firmament.util.ErrorUtil.intoCatch +import moe.nea.firmament.util.IdentifierSerializer + +object CustomScreenLayouts : SinglePreparationResourceReloader<List<CustomScreenLayouts.CustomScreenLayout>>() { + + @Serializable + data class CustomScreenLayout( + val predicates: Preds, + val background: BackgroundReplacer? = null, + val slots: List<SlotReplacer> = listOf(), + val playerTitle: TitleReplacer? = null, + val containerTitle: TitleReplacer? = null, + val repairCostTitle: TitleReplacer? = null, + val nameField: ComponentMover? = null, + ) + + @Serializable + data class ComponentMover( + val x: Int, + val y: Int, + val width: Int? = null, + val height: Int? = null, + ) + + @Serializable + data class Preds( + val label: StringMatcher, + @Serializable(with = IdentifierSerializer::class) + val screenType: Identifier? = null, + ) { + fun matches(screen: Screen): Boolean { + // TODO: does this deserve the restriction to handled screen + val s = screen as? HandledScreen<*>? ?: return false + val typeMatches = screenType == null || s.screenHandler.type.equals(Registries.SCREEN_HANDLER + .get(screenType)); + + return label.matches(s.title) && typeMatches + } + } + + @Serializable + data class BackgroundReplacer( + @Serializable(with = IdentifierSerializer::class) + val texture: Identifier, + // TODO: allow selectively still rendering some components (recipe button, trade backgrounds, furnace flame progress, arrows) + val x: Int, + val y: Int, + val width: Int, + val height: Int, + ) { + fun renderGeneric(context: DrawContext, screen: HandledScreen<*>) { + screen as AccessorHandledScreen + val originalX: Int = (screen.width - screen.backgroundWidth_Firmament) / 2 + val originalY: Int = (screen.height - screen.backgroundHeight_Firmament) / 2 + val modifiedX = originalX + this.x + val modifiedY = originalY + this.y + val textureWidth = this.width + val textureHeight = this.height + context.drawTexture( + RenderLayer::getGuiTextured, + this.texture, + modifiedX, + modifiedY, + 0.0f, + 0.0f, + textureWidth, + textureHeight, + textureWidth, + textureHeight + ) + + } + } + + @Serializable + data class SlotReplacer( + // TODO: override getRecipeBookButtonPos as well + // TODO: is this index or id (i always forget which one is duplicated per inventory) + val index: Int, + val x: Int, + val y: Int, + ) { + fun move(slots: List<Slot>) { + val slot = slots.getOrNull(index) ?: return + slot.x = x + slot.y = y + } + } + + @Serializable + enum class Alignment { + @SerialName("left") + LEFT, + + @SerialName("center") + CENTER, + + @SerialName("right") + RIGHT + } + + @Serializable + data class TitleReplacer( + val x: Int? = null, + val y: Int? = null, + val align: Alignment = Alignment.LEFT, + val replace: String? = null + ) { + @Transient + val replacedText: Text? = replace?.let(Text::literal) + + fun replaceText(text: Text): Text { + if (replacedText != null) return replacedText + return text + } + + fun replaceY(y: Int): Int { + return this.y ?: y + } + + fun replaceX(font: TextRenderer, text: Text, x: Int): Int { + val baseX = this.x ?: x + return baseX + when (this.align) { + LEFT -> 0 + CENTER -> -font.getWidth(text) / 2 + RIGHT -> -font.getWidth(text) + } + } + + /** + * Not technically part of the package, but it does allow for us to later on seamlessly integrate a color option into this class as well + */ + fun replaceColor(text: Text, color: Int): Int { + return CustomTextColors.mapTextColor(text, color) + } + } + + + @Subscribe + fun onStart(event: FinalizeResourceManagerEvent) { + event.resourceManager.registerReloader(CustomScreenLayouts) + } + + override fun prepare( + manager: ResourceManager, + profiler: Profiler + ): List<CustomScreenLayout> { + val allScreenLayouts = manager.findResources( + "overrides/screen_layout", + { it.path.endsWith(".json") && it.namespace == "firmskyblock" }) + val allParsedLayouts = allScreenLayouts.mapNotNull { (path, stream) -> + Firmament.tryDecodeJsonFromStream<CustomScreenLayout>(stream.inputStream) + .intoCatch("Could not read custom screen layout from $path").orNull() + } + return allParsedLayouts + } + + var customScreenLayouts = listOf<CustomScreenLayout>() + + override fun apply( + prepared: List<CustomScreenLayout>, + manager: ResourceManager?, + profiler: Profiler? + ) { + this.customScreenLayouts = prepared + } + + @get:JvmStatic + var activeScreenOverride = null as CustomScreenLayout? + + val DO_NOTHING_TEXT_REPLACER = TitleReplacer() + + @JvmStatic + fun <T>getMover(selector: (CustomScreenLayout)-> (T?)) = + activeScreenOverride?.let(selector) + + @JvmStatic + fun getTextMover(selector: (CustomScreenLayout) -> (TitleReplacer?)) = + getMover(selector) ?: DO_NOTHING_TEXT_REPLACER + + @Subscribe + fun onScreenOpen(event: ScreenChangeEvent) { + if (!CustomSkyBlockTextures.TConfig.allowLayoutChanges) { + activeScreenOverride = null + return + } + activeScreenOverride = event.new?.let { screen -> + customScreenLayouts.find { it.predicates.matches(screen) } + } + + val screen = event.new as? HandledScreen<*> ?: return + val handler = screen.screenHandler + activeScreenOverride?.let { override -> + override.slots.forEach { slotReplacer -> + slotReplacer.move(handler.slots) + } + } + } +} diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt index cf2a232..18949ff 100644 --- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt +++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt @@ -36,6 +36,7 @@ object CustomSkyBlockTextures : FirmamentFeature { val enableLegacyMinecraftCompat by toggle("legacy-minecraft-path-support") { true } val enableLegacyCIT by toggle("legacy-cit") { true } val allowRecoloringUiText by toggle("recolor-text") { true } + val allowLayoutChanges by toggle("screen-layouts") { true } } override val config: ManagedConfig diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomTextColors.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomTextColors.kt index 4ca1796..3ac895a 100644 --- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomTextColors.kt +++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomTextColors.kt @@ -2,6 +2,7 @@ package moe.nea.firmament.features.texturepack import java.util.Optional import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient import kotlin.jvm.optionals.getOrNull import net.minecraft.resource.ResourceManager import net.minecraft.resource.SinglePreparationResourceReloader @@ -18,12 +19,25 @@ object CustomTextColors : SinglePreparationResourceReloader<CustomTextColors.Tex data class TextOverrides( val defaultColor: Int, val overrides: List<TextOverride> = listOf() - ) + ) { + /** + * Stub custom text color to allow always returning a text override + */ + @Transient + val baseOverride = TextOverride( + StringMatcher.Equals("", false), + defaultColor, + 0, + 0 + ) + } @Serializable data class TextOverride( val predicate: StringMatcher, val override: Int, + val x: Int = 0, + val y: Int = 0, ) @Subscribe @@ -31,14 +45,14 @@ object CustomTextColors : SinglePreparationResourceReloader<CustomTextColors.Tex event.resourceManager.registerReloader(this) } - val cache = WeakCache.memoize<Text, Optional<Int>>("CustomTextColor") { text -> + val cache = WeakCache.memoize<Text, Optional<TextOverride>>("CustomTextColor") { text -> val override = textOverrides ?: return@memoize Optional.empty() - Optional.of(override.overrides.find { it.predicate.matches(text) }?.override ?: override.defaultColor) + Optional.ofNullable(override.overrides.find { it.predicate.matches(text) }) } fun mapTextColor(text: Text, oldColor: Int): Int { - if (textOverrides == null) return oldColor - return cache(text).getOrNull() ?: oldColor + val override = cache(text).orElse(null) + return override?.override ?: textOverrides?.defaultColor ?: oldColor } override fun prepare( diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/LoadExtraBlockStates.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/LoadExtraBlockStates.java new file mode 100644 index 0000000..c33fd04 --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/LoadExtraBlockStates.java @@ -0,0 +1,34 @@ +package moe.nea.firmament.mixins.custommodels; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import moe.nea.firmament.features.texturepack.CustomBlockTextures; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BlockStatesLoader; +import net.minecraft.resource.Resource; +import net.minecraft.state.StateManager; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Function; + +@Mixin(BlockStatesLoader.class) +public class LoadExtraBlockStates { + @ModifyExpressionValue(method = "load", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;supplyAsync(Ljava/util/function/Supplier;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;")) + private static CompletableFuture<Map<Identifier, List<Resource>>> loadExtraModels( + CompletableFuture<Map<Identifier, List<Resource>>> x, + @Local(argsOnly = true) Executor executor, + @Local Function<Identifier, StateManager<Block, BlockState>> stateManagers + ) { + return x.thenCombineAsync(CustomBlockTextures.getPreparationFuture(), (original, extra) -> { + CustomBlockTextures.collectExtraBlockStateMaps(extra, original, stateManagers); + return original; + }, executor); + } +} diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceBlockHitSoundPatch.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceBlockHitSoundPatch.java index f9a1d0d..95e7dce 100644 --- a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceBlockHitSoundPatch.java +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceBlockHitSoundPatch.java @@ -16,7 +16,8 @@ import org.spongepowered.asm.mixin.injection.At; @Mixin(ClientPlayerInteractionManager.class) public class ReplaceBlockHitSoundPatch { - @WrapOperation(method = "updateBlockBreakingProgress", at = @At(value = "NEW", target = "(Lnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FFLnet/minecraft/util/math/random/Random;Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/client/sound/PositionedSoundInstance;")) + @WrapOperation(method = "updateBlockBreakingProgress", + at = @At(value = "NEW", target = "(Lnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FFLnet/minecraft/util/math/random/Random;Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/client/sound/PositionedSoundInstance;")) private PositionedSoundInstance replaceSound( SoundEvent sound, SoundCategory category, float volume, float pitch, Random random, BlockPos pos, Operation<PositionedSoundInstance> original, diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceTextColorInHandledScreen.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceTextColorInHandledScreen.java deleted file mode 100644 index e4834e9..0000000 --- a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceTextColorInHandledScreen.java +++ /dev/null @@ -1,48 +0,0 @@ -package moe.nea.firmament.mixins.custommodels; - - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import moe.nea.firmament.features.texturepack.CustomTextColors; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ingame.AnvilScreen; -import net.minecraft.client.gui.screen.ingame.BeaconScreen; -import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.gui.screen.ingame.InventoryScreen; -import net.minecraft.client.gui.screen.ingame.MerchantScreen; -import net.minecraft.text.Text; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -@Mixin({HandledScreen.class, InventoryScreen.class, CreativeInventoryScreen.class, MerchantScreen.class, - AnvilScreen.class, BeaconScreen.class}) -public class ReplaceTextColorInHandledScreen { - - // To my future self: double check those mixins, but don't be too concerned about errors. Some of the wrapopertions - // only apply in some of the specified subclasses. - - @WrapOperation( - method = "drawForeground", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/gui/DrawContext;drawText(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;IIIZ)I"), - expect = 0, - require = 0) - private int replaceTextColorWithVariableShadow(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, boolean shadow, Operation<Integer> original) { - return original.call(instance, textRenderer, text, x, y, CustomTextColors.INSTANCE.mapTextColor(text, color), shadow); - } - - @WrapOperation( - method = "drawForeground", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;III)I"), - expect = 0, - require = 0) - private int replaceTextColorWithShadow(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, Operation<Integer> original) { - return original.call(instance, textRenderer, text, x, y, CustomTextColors.INSTANCE.mapTextColor(text, color)); - } - -} diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ExpandScreenBoundaries.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ExpandScreenBoundaries.java new file mode 100644 index 0000000..e2cae45 --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ExpandScreenBoundaries.java @@ -0,0 +1,21 @@ +package moe.nea.firmament.mixins.custommodels.screenlayouts; + +import moe.nea.firmament.features.texturepack.CustomScreenLayouts; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.ingame.RecipeBookScreen; +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.CallbackInfoReturnable; + +@Mixin({HandledScreen.class, RecipeBookScreen.class}) +public class ExpandScreenBoundaries { + @Inject(method = "isClickOutsideBounds", at = @At("HEAD"), cancellable = true) + private void onClickOutsideBounds(double mouseX, double mouseY, int left, int top, int button, CallbackInfoReturnable<Boolean> cir) { + var background = CustomScreenLayouts.getMover(CustomScreenLayouts.CustomScreenLayout::getBackground); + if (background == null) return; + var x = background.getX() + left; + var y = background.getY() + top; + cir.setReturnValue(mouseX < (double) x || mouseY < (double) y || mouseX >= (double) (x + background.getWidth()) || mouseY >= (double) (y + background.getHeight())); + } +} diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceAnvilScreen.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceAnvilScreen.java new file mode 100644 index 0000000..7c5dc45 --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceAnvilScreen.java @@ -0,0 +1,55 @@ +package moe.nea.firmament.mixins.custommodels.screenlayouts; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import moe.nea.firmament.features.texturepack.CustomScreenLayouts; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.AnvilScreen; +import net.minecraft.client.gui.screen.ingame.ForgingScreen; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.AnvilScreenHandler; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AnvilScreen.class) +public abstract class ReplaceAnvilScreen extends ForgingScreen<AnvilScreenHandler> { + @Shadow + private TextFieldWidget nameField; + + public ReplaceAnvilScreen(AnvilScreenHandler handler, PlayerInventory playerInventory, Text title, Identifier texture) { + super(handler, playerInventory, title, texture); + } + + @Inject(method = "setup", at = @At("TAIL")) + private void moveNameField(CallbackInfo ci) { + var override = CustomScreenLayouts.getMover(CustomScreenLayouts.CustomScreenLayout::getNameField); + if (override == null) return; + int baseX = (this.width - this.backgroundWidth) / 2; + int baseY = (this.height - this.backgroundHeight) / 2; + nameField.setX(baseX + override.getX()); + nameField.setY(baseY + override.getY()); + if (override.getWidth() != null) + nameField.setWidth(override.getWidth()); + if (override.getHeight() != null) + nameField.setHeight(override.getHeight()); + } + + @WrapOperation(method = "drawForeground", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;III)I"), + allow = 1) + private int onDrawRepairCost(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, Operation<Integer> original) { + var textOverride = CustomScreenLayouts.getTextMover(CustomScreenLayouts.CustomScreenLayout::getRepairCostTitle); + return original.call(instance, textRenderer, + textOverride.replaceText(text), + textOverride.replaceX(textRenderer, text, x), + textOverride.replaceY(y), + textOverride.replaceColor(text, color)); + } +} diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceForgingScreen.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceForgingScreen.java new file mode 100644 index 0000000..6e9023d --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceForgingScreen.java @@ -0,0 +1,9 @@ +package moe.nea.firmament.mixins.custommodels.screenlayouts; + +import net.minecraft.client.gui.screen.ingame.ForgingScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Inject; + +@Mixin(ForgingScreen.class) +public class ReplaceForgingScreen { +} diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceFurnaceBackgrounds.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceFurnaceBackgrounds.java new file mode 100644 index 0000000..6b076db --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceFurnaceBackgrounds.java @@ -0,0 +1,31 @@ +package moe.nea.firmament.mixins.custommodels.screenlayouts; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import moe.nea.firmament.features.texturepack.CustomScreenLayouts; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.AbstractFurnaceScreen; +import net.minecraft.client.gui.screen.ingame.RecipeBookScreen; +import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.AbstractFurnaceScreenHandler; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import java.util.function.Function; + +@Mixin(AbstractFurnaceScreen.class) +public abstract class ReplaceFurnaceBackgrounds<T extends AbstractFurnaceScreenHandler> extends RecipeBookScreen<T> { + public ReplaceFurnaceBackgrounds(T handler, RecipeBookWidget<?> recipeBook, PlayerInventory inventory, Text title) { + super(handler, recipeBook, inventory, title); + } + + @WrapWithCondition(method = "drawBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Ljava/util/function/Function;Lnet/minecraft/util/Identifier;IIFFIIII)V"), allow = 1) + private boolean onDrawBackground(DrawContext instance, Function<Identifier, RenderLayer> renderLayers, Identifier sprite, int x, int y, float u, float v, int width, int height, int textureWidth, int textureHeight) { + final var override = CustomScreenLayouts.getActiveScreenOverride(); + if (override == null || override.getBackground() == null) return true; + override.getBackground().renderGeneric(instance, this); + return false; + } +} diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceGenericBackgrounds.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceGenericBackgrounds.java new file mode 100644 index 0000000..bd12177 --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceGenericBackgrounds.java @@ -0,0 +1,28 @@ +package moe.nea.firmament.mixins.custommodels.screenlayouts; + +import moe.nea.firmament.features.texturepack.CustomScreenLayouts; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.*; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.text.Text; +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; + +@Mixin({CraftingScreen.class, CrafterScreen.class, Generic3x3ContainerScreen.class, GenericContainerScreen.class, HopperScreen.class, ShulkerBoxScreen.class,}) +public abstract class ReplaceGenericBackgrounds extends HandledScreen<ScreenHandler> { + // TODO: split out screens with special background components like flames, arrows, etc. (maybe arrows deserve generic handling tho) + public ReplaceGenericBackgrounds(ScreenHandler handler, PlayerInventory inventory, Text title) { + super(handler, inventory, title); + } + + @Inject(method = "drawBackground", at = @At("HEAD"), cancellable = true) + private void replaceDrawBackground(DrawContext context, float deltaTicks, int mouseX, int mouseY, CallbackInfo ci) { + final var override = CustomScreenLayouts.getActiveScreenOverride(); + if (override == null || override.getBackground() == null) return; + override.getBackground().renderGeneric(context, this); + ci.cancel(); + } +} diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplacePlayerBackgrounds.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplacePlayerBackgrounds.java new file mode 100644 index 0000000..e02a821 --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplacePlayerBackgrounds.java @@ -0,0 +1,50 @@ +package moe.nea.firmament.mixins.custommodels.screenlayouts; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import moe.nea.firmament.features.texturepack.CustomScreenLayouts; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; +import net.minecraft.client.gui.screen.ingame.RecipeBookScreen; +import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.PlayerScreenHandler; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.function.Function; + +@Mixin(InventoryScreen.class) +public abstract class ReplacePlayerBackgrounds extends RecipeBookScreen<PlayerScreenHandler> { + public ReplacePlayerBackgrounds(PlayerScreenHandler handler, RecipeBookWidget<?> recipeBook, PlayerInventory inventory, Text title) { + super(handler, recipeBook, inventory, title); + } + + + @WrapOperation(method = "drawForeground", + allow = 1, + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawText(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;IIIZ)I")) + private int onDrawForegroundText(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, boolean shadow, Operation<Integer> original) { + var textOverride = CustomScreenLayouts.getTextMover(CustomScreenLayouts.CustomScreenLayout::getContainerTitle); + return original.call(instance, textRenderer, + textOverride.replaceText(text), + textOverride.replaceX(textRenderer, text, x), + textOverride.replaceY(y), + textOverride.replaceColor(text, color), + shadow); + } + + @WrapWithCondition(method = "drawBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Ljava/util/function/Function;Lnet/minecraft/util/Identifier;IIFFIIII)V")) + private boolean onDrawBackground(DrawContext instance, Function<Identifier, RenderLayer> renderLayers, Identifier sprite, int x, int y, float u, float v, int width, int height, int textureWidth, int textureHeight) { + final var override = CustomScreenLayouts.getActiveScreenOverride(); + if (override == null || override.getBackground() == null) return true; + override.getBackground().renderGeneric(instance, this); + return false; + } + // TODO: allow moving the player +} diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceTextColorInHandledScreen.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceTextColorInHandledScreen.java new file mode 100644 index 0000000..4f0905a --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/ReplaceTextColorInHandledScreen.java @@ -0,0 +1,65 @@ +package moe.nea.firmament.mixins.custommodels.screenlayouts; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import moe.nea.firmament.features.texturepack.CustomScreenLayouts; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.AnvilScreen; +import net.minecraft.client.gui.screen.ingame.BeaconScreen; +import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; +import net.minecraft.client.gui.screen.ingame.MerchantScreen; +import net.minecraft.text.Text; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Slice; + +@Mixin(HandledScreen.class) +// TODO: MerchantScreen.class, BeaconScreen.class +public class ReplaceTextColorInHandledScreen { + + @WrapOperation( + method = "drawForeground", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/DrawContext;drawText(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;IIIZ)I"), + slice = @Slice( + from = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;title:Lnet/minecraft/text/Text;", opcode = Opcodes.GETFIELD), + to = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;playerInventoryTitle:Lnet/minecraft/text/Text;", opcode = Opcodes.GETFIELD) + ), + allow = 1, + require = 1) + private int replaceContainerTitle(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, boolean shadow, Operation<Integer> original) { + var textOverride = CustomScreenLayouts.getTextMover(CustomScreenLayouts.CustomScreenLayout::getContainerTitle); + return original.call(instance, textRenderer, + textOverride.replaceText(text), + textOverride.replaceX(textRenderer, text, x), + textOverride.replaceY(y), + textOverride.replaceColor(text, color), + shadow); + } + + @WrapOperation( + method = "drawForeground", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/DrawContext;drawText(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;IIIZ)I"), + slice = @Slice( + from = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;playerInventoryTitle:Lnet/minecraft/text/Text;", opcode = Opcodes.GETFIELD), + to = @At(value = "TAIL") + ), + allow = 1, + require = 1) + private int replacePlayerTitle(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, boolean shadow, Operation<Integer> original) { + var textOverride = CustomScreenLayouts.getTextMover(CustomScreenLayouts.CustomScreenLayout::getPlayerTitle); + return original.call(instance, textRenderer, + textOverride.replaceText(text), + textOverride.replaceX(textRenderer, text, x), + textOverride.replaceY(y), + textOverride.replaceColor(text, color), + shadow); + } +} |