diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 629f0ed..a8afb2c 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,7 +1,7 @@ -name: Bug Report +name: Report A Bug description: >- - Please use this template when you have encountered a bug in this mod. -title: '[Bug]: ' + Only Minecraft versions listed on the main branch are supported. Support may additionally vary based on the severity of an issue. +title: "[Bug]: " labels: ["bug"] assignees: - Fuzss @@ -14,35 +14,49 @@ body: id: loader attributes: label: Mod Loader (Required) - description: What mod loader are you using the mod on? + description: What mod loader are you using to play the mod? multiple: false options: - - Forge - - Fabric - - Quilt + - "⸺" + - "Fabric" + - "NeoForge" + - "Forge" + - "Quilt" validations: required: true - type: input id: minecraft attributes: - label: Minecraft Version (Required) - description: What is the Minecraft version you are playing with? - placeholder: ex. 1.19 + label: Minecraft Version(s) (Required) + description: What Minecraft version(s) are you using the play the mod? + placeholder: ex. 1.20.1 validations: required: true - type: input id: version attributes: - label: Mod Version (Required) - description: What version of the mod are you playing with? - placeholder: ex. v4.0.0 + label: Mod Version(s) (Required) + description: What mod version(s) are you using to play? + placeholder: ex. v8.0.0 + validations: + required: true + - type: dropdown + id: mods + attributes: + label: Minimal Setup (Required) + description: Can your issue be reproduced with a minimal set of mods (only this mod + dependencies)? + multiple: false + options: + - "⸺" + - "Yes" + - "No" validations: required: true - type: textarea id: notes attributes: label: Notes (Required) - description: Please explain what happens because of the bug (including all the steps required to cause the bug), and what behavior you would expect if the bug were fixed. + description: Please explain what happens because of the issue (including all the steps required to cause it), and what behavior you would expect if the issue were fixed. placeholder: >- ex. @@ -60,6 +74,8 @@ body: - type: input id: latest-log attributes: - label: latest.log (Optional) - description: Please paste the url to your shared `latest.log` file. [To share your `latest.log` here, please follow these steps.](https://gist.github.com/Fuzss/866b384d353912986e37b17eeef7a285) - placeholder: ex. https://gist.github.com// \ No newline at end of file + label: latest.log (Required) + description: Please paste the url to your shared `latest.log` file. Note that issue reports without this file are difficult to solve and unlikely to be processed. [To share your `latest.log` here, please follow these steps.](https://gist.github.com/Fuzss/866b384d353912986e37b17eeef7a285) + placeholder: ex. https://gist.github.com// + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 7f79bf6..cbe96b9 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - - name: Questions - url: https://discord.gg/8ZmhaPPbjE - about: "Please ask questions on the Luna Pixel Studios Discord (in #fuzs-projects) or contact me directly on Discord at Fuzs#0212." \ No newline at end of file + - name: Ask A Question + url: https://lunapixel.studio/discord + about: "Join the Luna Pixel Studios Discord Server in the #fuzs-projects channel for additional support." \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/crash.yml b/.github/ISSUE_TEMPLATE/crash.yml index 690bc86..c91c5ae 100644 --- a/.github/ISSUE_TEMPLATE/crash.yml +++ b/.github/ISSUE_TEMPLATE/crash.yml @@ -1,8 +1,8 @@ -name: Crash Report +name: Submit A Crash Report description: >- - Please use this template when this mod has caused your game to crash. -title: '[Crash]: ' -labels: ["bug"] + Only Minecraft versions listed on the main branch are supported. Support may additionally vary based on the severity of an issue. +title: "[Crash]: " +labels: ["bug", "high priority"] assignees: - Fuzss body: @@ -14,35 +14,49 @@ body: id: loader attributes: label: Mod Loader (Required) - description: What mod loader are you using the mod on? + description: What mod loader are you using to play the mod? multiple: false options: - - Forge - - Fabric - - Quilt + - "⸺" + - "Fabric" + - "NeoForge" + - "Forge" + - "Quilt" validations: required: true - type: input id: minecraft attributes: - label: Minecraft Version (Required) - description: What is the Minecraft version you are playing with? - placeholder: ex. 1.19 + label: Minecraft Version(s) (Required) + description: What Minecraft version(s) are you using the play the mod? + placeholder: ex. 1.20.1 validations: required: true - type: input id: version attributes: - label: Mod Version (Required) - description: What version of the mod are you playing with? - placeholder: ex. v4.0.0 + label: Mod Version(s) (Required) + description: What mod version(s) are you using to play? + placeholder: ex. v8.0.0 + validations: + required: true + - type: dropdown + id: mods + attributes: + label: Minimal Setup + description: Can your issue be reproduced with a minimal set of mods (only this mod + dependencies)? + multiple: false + options: + - "⸺" + - "Yes" + - "No" validations: required: true - type: textarea id: notes attributes: label: Notes (Required) - description: Please explain which steps we need to do to reproduce the crash. Please include anything else you'd like to say about the crash. + description: Please explain which steps we need to do to reproduce the issue. Please include anything else you'd like to say about it. placeholder: >- ex. @@ -58,7 +72,7 @@ body: attributes: label: Crash Report (Required) description: >- - Please paste the url to your shared crash report. [To share your crash report here, please follow these steps.](https://gist.github.com/Fuzss/9692f6ed5e8cca485a58004c28c9045b) + Please paste the url to your shared crash report. Note that issue reports without this file are difficult to solve and unlikely to be processed. [To share your crash report here, please follow these steps.](https://gist.github.com/Fuzss/9692f6ed5e8cca485a58004c28c9045b) placeholder: ex. https://gist.github.com// validations: required: true @@ -67,4 +81,4 @@ body: attributes: label: latest.log (Optional) description: Please paste the url to your shared `latest.log` file. [To share your `latest.log` here, please follow these steps.](https://gist.github.com/Fuzss/866b384d353912986e37b17eeef7a285) - placeholder: ex. https://gist.github.com// \ No newline at end of file + placeholder: ex. https://gist.github.com// diff --git a/.github/ISSUE_TEMPLATE/suggestion.yml b/.github/ISSUE_TEMPLATE/suggestion.yml index b4d3720..2a25400 100644 --- a/.github/ISSUE_TEMPLATE/suggestion.yml +++ b/.github/ISSUE_TEMPLATE/suggestion.yml @@ -1,11 +1,41 @@ -name: Suggestion +name: Leave A Suggestion description: >- - Please use this template when you want to suggest a feature. -title: '[Suggestion]: ' + Please do not ask for mod updates or ports, they will come when they are ready. +title: "[Suggestion]: " labels: ["enhancement"] assignees: - Fuzss body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this issue report! + - type: dropdown + id: loader + attributes: + label: Mod Loader (Optional) + description: What mod loader are you using to play the mod? + multiple: false + options: + - "⸺" + - "Fabric" + - "NeoForge" + - "Forge" + - "Quilt" + validations: + required: true + - type: input + id: minecraft + attributes: + label: Minecraft Version(s) (Optional) + description: What Minecraft version(s) are you using the play the mod? + placeholder: ex. 1.20.1 + - type: input + id: version + attributes: + label: Mod Version(s) (Optional) + description: What mod version(s) are you using to play? + placeholder: ex. v8.0.0 - type: textarea id: suggestion attributes: diff --git a/.gitignore b/.gitignore index 29fed19..0360158 100644 --- a/.gitignore +++ b/.gitignore @@ -32,8 +32,7 @@ classes *.ipr *.iws *.iml -.idea/* -!.idea/scopes +**/.idea/* ### NetBeans ### nbproject/private/ @@ -59,14 +58,9 @@ gradle-app.setting .gradletasknamecache ### Other ### +run .DS_Store *.txt -!run -run/* -!run/options.txt -!run/config -run/config/* -!run/config/modmenu.json # Log file *.log diff --git a/.idea/scopes/Fabric_sources.xml b/.idea/scopes/Fabric_sources.xml deleted file mode 100644 index 0448412..0000000 --- a/.idea/scopes/Fabric_sources.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/scopes/Forge_sources.xml b/.idea/scopes/Forge_sources.xml deleted file mode 100644 index 7b5f24d..0000000 --- a/.idea/scopes/Forge_sources.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/1.18.2/CHANGELOG.md b/1.18.2/CHANGELOG.md new file mode 100644 index 0000000..916e567 --- /dev/null +++ b/1.18.2/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog]. + +## [v3.0.0-1.18.2] - 2023-06-25 +- Ported to Minecraft 1.18.2 + +[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ diff --git a/1.18.2/Common/build.gradle b/1.18.2/Common/build.gradle new file mode 100644 index 0000000..949b426 --- /dev/null +++ b/1.18.2/Common/build.gradle @@ -0,0 +1,11 @@ +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/common.gradle' + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.common +} + +// @see https://github.com/jaredlll08/MultiLoader-Template/issues/17#issuecomment-1221598082 +tasks.withType(net.fabricmc.loom.task.AbstractRemapJarTask).each { + it.targetNamespace = "named" +} diff --git a/1.18.2/Common/src/main/java/fuzs/examplemod/ExampleMod.java b/1.18.2/Common/src/main/java/fuzs/examplemod/ExampleMod.java new file mode 100644 index 0000000..45113b2 --- /dev/null +++ b/1.18.2/Common/src/main/java/fuzs/examplemod/ExampleMod.java @@ -0,0 +1,16 @@ +package fuzs.examplemod; + +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.minecraft.resources.ResourceLocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ExampleMod implements ModConstructor { + public static final String MOD_ID = "examplemod"; + public static final String MOD_NAME = "Example Mod"; + public static final Logger LOGGER = LoggerFactory.getLogger(MOD_NAME); + + public static ResourceLocation id(String path) { + return new ResourceLocation(MOD_ID, path); + } +} diff --git a/1.18.2/Common/src/main/java/fuzs/examplemod/client/ExampleModClient.java b/1.18.2/Common/src/main/java/fuzs/examplemod/client/ExampleModClient.java new file mode 100644 index 0000000..e1afacc --- /dev/null +++ b/1.18.2/Common/src/main/java/fuzs/examplemod/client/ExampleModClient.java @@ -0,0 +1,7 @@ +package fuzs.examplemod.client; + +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; + +public class ExampleModClient implements ClientModConstructor { + +} diff --git a/1.18.2/Common/src/main/resources/examplemod.common.mixins.json b/1.18.2/Common/src/main/resources/examplemod.common.mixins.json new file mode 100644 index 0000000..ec8895e --- /dev/null +++ b/1.18.2/Common/src/main/resources/examplemod.common.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "fuzs.examplemod.mixin", + "refmap": "examplemod.refmap.json", + "plugin": "fuzs.examplemod.mixin.ModMixinConfigPlugin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.18.2/Common/src/main/resources/mod_banner.png b/1.18.2/Common/src/main/resources/mod_banner.png new file mode 100644 index 0000000..bda5a0f Binary files /dev/null and b/1.18.2/Common/src/main/resources/mod_banner.png differ diff --git a/1.18.2/Common/src/main/resources/mod_logo.png b/1.18.2/Common/src/main/resources/mod_logo.png new file mode 100644 index 0000000..98c247e Binary files /dev/null and b/1.18.2/Common/src/main/resources/mod_logo.png differ diff --git a/1.18.2/Common/src/main/resources/pack.mcmeta b/1.18.2/Common/src/main/resources/pack.mcmeta new file mode 100755 index 0000000..19bfab2 --- /dev/null +++ b/1.18.2/Common/src/main/resources/pack.mcmeta @@ -0,0 +1,8 @@ +{ + "pack": { + "description": "${modDescription}", + "pack_format": ${resourcePackFormat}, + "forge:resource_pack_format": ${resourcePackFormat}, + "forge:data_pack_format": ${dataPackFormat} + } +} diff --git a/1.18.2/Fabric/build.gradle b/1.18.2/Fabric/build.gradle new file mode 100644 index 0000000..cd8b641 --- /dev/null +++ b/1.18.2/Fabric/build.gradle @@ -0,0 +1,29 @@ +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/fabric.gradle' + +def versionCatalog = extensions.getByType(VersionCatalogsExtension).named("libs") + +dependencies { + // Fabric Api + modApi libs.fabricapi.fabric + + // Puzzles Lib + modApi libs.puzzleslib.fabric + + // Cardinal Components +// modApi(include(libs.cardinalcomponentsbase.fabric.get())) +// modApi(include(libs.cardinalcomponentsentity.fabric.get())) +// modApi(include(libs.cardinalcomponentsblock.fabric.get())) +// modApi(include(libs.cardinalcomponentschunk.fabric.get())) +// modApi(include(libs.cardinalcomponentsworld.fabric.get())) + + // Extensible Enums +// modApi(include(libs.extensibleenums.fabric.get())) + + // Quality of Life Mods + versionCatalog.findLibrary("modmenu.fabric").ifPresent { + modLocalRuntime(it) + } + versionCatalog.findLibrary("forgeconfigscreens.fabric").ifPresent { + modLocalRuntime(it) + } +} diff --git a/1.18.2/Fabric/src/main/java/fuzs/examplemod/ExampleModFabric.java b/1.18.2/Fabric/src/main/java/fuzs/examplemod/ExampleModFabric.java new file mode 100644 index 0000000..124b062 --- /dev/null +++ b/1.18.2/Fabric/src/main/java/fuzs/examplemod/ExampleModFabric.java @@ -0,0 +1,12 @@ +package fuzs.examplemod; + +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.fabricmc.api.ModInitializer; + +public class ExampleModFabric implements ModInitializer { + + @Override + public void onInitialize() { + ModConstructor.construct(ExampleMod.MOD_ID, ExampleMod::new); + } +} diff --git a/1.18.2/Fabric/src/main/java/fuzs/examplemod/client/ExampleModFabricClient.java b/1.18.2/Fabric/src/main/java/fuzs/examplemod/client/ExampleModFabricClient.java new file mode 100644 index 0000000..7b55517 --- /dev/null +++ b/1.18.2/Fabric/src/main/java/fuzs/examplemod/client/ExampleModFabricClient.java @@ -0,0 +1,13 @@ +package fuzs.examplemod.client; + +import fuzs.examplemod.ExampleMod; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import net.fabricmc.api.ClientModInitializer; + +public class ExampleModFabricClient implements ClientModInitializer { + + @Override + public void onInitializeClient() { + ClientModConstructor.construct(ExampleMod.MOD_ID, ExampleModClient::new); + } +} diff --git a/1.18.2/Fabric/src/main/java/fuzs/examplemod/mixin/ModMixinConfigPlugin.java b/1.18.2/Fabric/src/main/java/fuzs/examplemod/mixin/ModMixinConfigPlugin.java new file mode 100644 index 0000000..6d413e8 --- /dev/null +++ b/1.18.2/Fabric/src/main/java/fuzs/examplemod/mixin/ModMixinConfigPlugin.java @@ -0,0 +1,47 @@ +package fuzs.examplemod.mixin; + +import net.fabricmc.loader.api.FabricLoader; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class ModMixinConfigPlugin implements IMixinConfigPlugin { + + @Override + public void onLoad(String mixinPackage) { + + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return FabricLoader.getInstance().isModLoaded("puzzleslib"); + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } +} diff --git a/1.18.2/Fabric/src/main/resources/examplemod.fabric.mixins.json b/1.18.2/Fabric/src/main/resources/examplemod.fabric.mixins.json new file mode 100644 index 0000000..ec8895e --- /dev/null +++ b/1.18.2/Fabric/src/main/resources/examplemod.fabric.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "fuzs.examplemod.mixin", + "refmap": "examplemod.refmap.json", + "plugin": "fuzs.examplemod.mixin.ModMixinConfigPlugin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.18.2/Fabric/src/main/resources/fabric.mod.json b/1.18.2/Fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..07fc00c --- /dev/null +++ b/1.18.2/Fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,45 @@ +{ + "schemaVersion": 1, + "id": "${modId}", + "version": "${modVersion}", + + "name": "${modName}", + "description": "${modDescription}", + + "authors": [ + "${modAuthor}" + ], + + "contact": { + "homepage": "${modPageUrl}", + "issues": "${modIssueUrl}", + "sources": "${modPageUrl}" + }, + + "license": "${modLicense}", + "icon": "mod_logo.png", + + "environment": "${modFabricEnvironment}", + + "entrypoints": { + "main": [ + "${mainEntryPoint}" + ], + "client": [ + "${clientEntryPoint}" + ] + }, + + "mixins": [ + "${modId}.common.mixins.json", + "${modId}.fabric.mixins.json" + ], + + "depends": { + "fabricloader": ">=${minFabricVersion}", + "fabric-api": ">=${minFabricApiVersion}", + "puzzleslib": ">=${minPuzzlesVersion}", + "minecraft": "${minecraftVersion}", + "java": ">=17" + } +} diff --git a/1.18.2/Forge/build.gradle b/1.18.2/Forge/build.gradle new file mode 100644 index 0000000..dec494a --- /dev/null +++ b/1.18.2/Forge/build.gradle @@ -0,0 +1,28 @@ +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/forge.gradle' + +def versionCatalog = extensions.getByType(VersionCatalogsExtension).named("libs") + +dependencies { + // Puzzles Lib + api fg.deobf(libs.puzzleslib.forge.get()) + + // Quality of Life Mods + versionCatalog.findLibrary("bettermodsbutton.forge").ifPresent { + runtimeOnly fg.deobf(it.get()) + } + versionCatalog.findLibrary("forgeconfigscreens.forge").ifPresent { + runtimeOnly fg.deobf(it.get()) + } +} + +task signJar(type: net.minecraftforge.gradle.common.tasks.SignJar, dependsOn: tasks.reobfJarJar) { + onlyIf { project.hasProperty('keyStore') } + keyStore = project.findProperty('keyStore') + alias = project.findProperty('keyStoreAlias') + storePass = project.findProperty('keyStorePass') + keyPass = project.findProperty('keyStoreKeyPass') + inputFile = outputFile = tasks.jarJar.archivePath +} + +jar.finalizedBy 'signJar' +signJar.mustRunAfter 'reobfJar' diff --git a/1.18.2/Forge/src/main/java/fuzs/examplemod/ExampleModForge.java b/1.18.2/Forge/src/main/java/fuzs/examplemod/ExampleModForge.java new file mode 100644 index 0000000..18d3c6f --- /dev/null +++ b/1.18.2/Forge/src/main/java/fuzs/examplemod/ExampleModForge.java @@ -0,0 +1,25 @@ +package fuzs.examplemod; + +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.minecraft.data.DataGenerator; +import net.minecraftforge.common.data.ExistingFileHelper; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; +import net.minecraftforge.forge.event.lifecycle.GatherDataEvent; + +@Mod(ExampleMod.MOD_ID) +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public class ExampleModForge { + + @SubscribeEvent + public static void onConstructMod(final FMLConstructModEvent evt) { + ModConstructor.construct(ExampleMod.MOD_ID, ExampleMod::new); + } + + @SubscribeEvent + public static void onGatherData(final GatherDataEvent evt) { + final DataGenerator dataGenerator = evt.getGenerator(); + final ExistingFileHelper fileHelper = evt.getExistingFileHelper(); + } +} diff --git a/1.18.2/Forge/src/main/java/fuzs/examplemod/client/ExampleModForgeClient.java b/1.18.2/Forge/src/main/java/fuzs/examplemod/client/ExampleModForgeClient.java new file mode 100644 index 0000000..d6502ba --- /dev/null +++ b/1.18.2/Forge/src/main/java/fuzs/examplemod/client/ExampleModForgeClient.java @@ -0,0 +1,17 @@ +package fuzs.examplemod.client; + +import fuzs.examplemod.ExampleMod; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; + +@Mod.EventBusSubscriber(modid = ExampleMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) +public class ExampleModForgeClient { + + @SubscribeEvent + public static void onConstructMod(final FMLConstructModEvent evt) { + ClientModConstructor.construct(ExampleMod.MOD_ID, ExampleModClient::new); + } +} diff --git a/1.18.2/Forge/src/main/java/fuzs/examplemod/mixin/ModMixinConfigPlugin.java b/1.18.2/Forge/src/main/java/fuzs/examplemod/mixin/ModMixinConfigPlugin.java new file mode 100644 index 0000000..af281eb --- /dev/null +++ b/1.18.2/Forge/src/main/java/fuzs/examplemod/mixin/ModMixinConfigPlugin.java @@ -0,0 +1,47 @@ +package fuzs.examplemod.mixin; + +import net.minecraftforge.fml.loading.FMLLoader; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class ModMixinConfigPlugin implements IMixinConfigPlugin { + + @Override + public void onLoad(String mixinPackage) { + + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return FMLLoader.getLoadingModList().getModFileById("puzzleslib") != null; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } +} diff --git a/1.18.2/Forge/src/main/resources/META-INF/mods.toml b/1.18.2/Forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..e15b638 --- /dev/null +++ b/1.18.2/Forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,40 @@ +modLoader = "javafml" +loaderVersion = "[${minFMLVersion},)" +license = "${modLicense}" +issueTrackerURL = "${modIssueUrl}" + +[[mods]] +modId = "${modId}" +displayName = "${modName}" +description = "${modDescription}" +version = "${modVersion}" +authors = "${modAuthor}" +logoFile = "mod_banner.png" +logoBlur = false +displayURL = "${modPageUrl}" +updateJSONURL = "${modUpdateUrl}" +displayTest = "${modForgeDisplayTest}" + +[[dependencies.${ modId }]] +modId = "forge" +mandatory = true +versionRange = "[${minForgeVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "minecraft" +mandatory = true +versionRange = "[${minecraftVersion}]" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "puzzleslib" +mandatory = true +versionRange = "[${minPuzzlesVersion},)" +ordering = "NONE" +side = "BOTH" + +[modproperties.${ modId }] +catalogueImageIcon = "mod_logo.png" diff --git a/1.18.2/Forge/src/main/resources/examplemod.forge.mixins.json b/1.18.2/Forge/src/main/resources/examplemod.forge.mixins.json new file mode 100644 index 0000000..ec8895e --- /dev/null +++ b/1.18.2/Forge/src/main/resources/examplemod.forge.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "fuzs.examplemod.mixin", + "refmap": "examplemod.refmap.json", + "plugin": "fuzs.examplemod.mixin.ModMixinConfigPlugin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.18.2/build.gradle b/1.18.2/build.gradle new file mode 100644 index 0000000..fadade3 --- /dev/null +++ b/1.18.2/build.gradle @@ -0,0 +1,12 @@ +plugins { + alias libs.plugins.loom apply false + alias libs.plugins.quiltflower apply false + alias libs.plugins.forgegradle apply false + alias libs.plugins.mixin apply false + alias libs.plugins.librarian apply false + alias libs.plugins.cursegradle apply false + alias libs.plugins.minotaur apply false +} + +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/main.gradle' +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/tasks.gradle' diff --git a/1.18.2/gradle.properties b/1.18.2/gradle.properties new file mode 100755 index 0000000..02bba8d --- /dev/null +++ b/1.18.2/gradle.properties @@ -0,0 +1,37 @@ +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +# This is required to provide enough memory for the Minecraft decompilation process. +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false +org.gradle.parallel=true +copyBuildJar=true + +# Mod Attributes +modId=examplemod +modName=Example Mod +modVersion=3.0.0 +modAuthor=Fuzs +modDescription=Example description. +modLicense=MPL-2.0 +modSourceUrl=https://github.com/Fuzss/examplemod +modIssueUrl=https://github.com/Fuzss/examplemod/issues +modUpdateUrl=https://raw.githubusercontent.com/Fuzss/modresources/main/update/examplemod.json +modMavenGroup=fuzs.examplemod +# "MATCH_VERSION" for a mod required on both sides, "IGNORE_SERVER_VERSION" for a server only mod, "IGNORE_ALL_VERSION" for a client only mod +modForgeDisplayTest=MATCH_VERSION +# "*" for a mod loaded on both sides, "server" for a server only mod, "client" for a client only mod +modFabricEnvironment=* + +# Mod Publishing +projectReleaseType=release +projectCurseForgeId=0 +projectModrinthId=0 + +dependenciesVersionCatalog=1.18.2-v6 +#dependenciesPuzzlesLibVersion=3.0.0 +#dependenciesMinPuzzlesLibVersion=3.0.0 +dependenciesRequiredForgeCurseForge=puzzles-lib +dependenciesRequiredFabricCurseForge=fabric-api, forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredForgeModrinth=puzzles-lib +dependenciesRequiredFabricModrinth=fabric-api, forge-config-api-port, puzzles-lib +#dependenciesEmbeddedFabricCurseForge=cardinal-components +#dependenciesEmbeddedFabricModrinth=cardinal-components-api diff --git a/1.18.2/gradle/wrapper/gradle-wrapper.jar b/1.18.2/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..943f0cb Binary files /dev/null and b/1.18.2/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/1.18.2/gradle/wrapper/gradle-wrapper.properties similarity index 84% rename from gradle/wrapper/gradle-wrapper.properties rename to 1.18.2/gradle/wrapper/gradle-wrapper.properties index 41dfb87..f398c33 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/1.18.2/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/1.18.2/gradlew similarity index 93% rename from gradlew rename to 1.18.2/gradlew index 1b6c787..65dcd68 100755 --- a/gradlew +++ b/1.18.2/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -205,6 +209,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/1.18.2/gradlew.bat similarity index 89% rename from gradlew.bat rename to 1.18.2/gradlew.bat index 107acd3..93e3f59 100644 --- a/gradlew.bat +++ b/1.18.2/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/1.18.2/settings.gradle b/1.18.2/settings.gradle new file mode 100644 index 0000000..bf7e2e5 --- /dev/null +++ b/1.18.2/settings.gradle @@ -0,0 +1,23 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + maven { + name = 'Sponge' + url = 'https://repo.spongepowered.org/repository/maven-public/' + } + maven { + name = 'Quilt' + url = 'https://maven.quiltmc.org/repository/release' + } + maven { + name = 'Minecraft Forge' + url = 'https://maven.minecraftforge.net/' + } + } +} + +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/settings.gradle' diff --git a/1.19.2/CHANGELOG.md b/1.19.2/CHANGELOG.md new file mode 100644 index 0000000..1e7e251 --- /dev/null +++ b/1.19.2/CHANGELOG.md @@ -0,0 +1,63 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog]. + +## [v4.0.8-1.19.2] - 2023-07-29 +### Fixed +- Fixed an issue with always receiving an incorrect error message when trying to edit an armor stand on a server with the Vanilla Tweaks data pack installed + +## [v4.0.7-1.19.2] - 2023-07-28 +### Added +- Added a new tab that only shows when Armor Statues is being used in conjunction with the Vanilla Tweaks data pack offering Vanilla Tweaks exclusive toggles +### Changed +- Optimized some actions during editing to be performed much quicker when Armor Statues is being used in conjunction with the Vanilla Tweaks data pack + +## [v4.0.6-1.19.2] - 2023-07-26 +### Fixed +- Fixed an issue where new rotations set on the rotations screen wouldn't save if the sliders were moved using the arrow keys + +## [v4.0.5-1.19.2] - 2023-07-25 +### Added +- Added support for the Vanilla Tweaks Armor Statues data pack +- This means when Armor Statues as a mod is **NOT** installed on the server you are playing on, it is now enough to install said data pack on the server to be able to use almost all features in the statue configuration screen +- Some actions such as switching poses take a couple of seconds to apply due to the nature of how data packs work unfortunately +- Alternatively players with operator permissions on the server are still able to configure armor stands from the screen without any data pack requirement +- Added a button to the rotations screen for mirroring the current pose +- Added many new poses from the Vanilla Tweaks Armor Statues data pack +- Added the ability to switch between tabs by pressing hotbar keys +### Changed +- Opening the statue menu no longer requires a stick to be held, instead shift + right-click with an empty hand is the way to go, which the statue item tooltip reflects +### Fixed +- Fixed opening the armor stand configuration screen with a Fabric client on a server without the mod interacting with the armor stand server-side (e.g. removing equipment) +- Fixed alignments on the alignment screen not working correctly if the armor stand was rotated at certain angles +- Fixed left and right arm and leg parts being swapped on the rotations screen +- Fixed a rare network issue on servers when both Armor Statues and Straw Statues are installed + +## [v4.0.4-1.19.2] - 2023-01-17 +### Changed +- Opening the statue menu now requires a stick to be held in addition to sneaking, this was changed to improve compatibility with the Quark mod +- Removed the option to cycle through statue menu tabs using the tab key, it was conflicting with showing the vanilla server player list (at least on Fabric) +- Instead, you can now scroll through the tabs when your cursor is hovering them + +## [v4.0.3-1.19.2] - 2023-01-06 +### Fixed +- Fixed crash when trying to interact with entities using Fabric API 0.72.0+1.19.2 + +## [v4.0.2-1.19.2] - 2022-10-19 +### Changed +- Any item can now be placed into the head slot on the equipment screen (thanks to [Mephodio]) +- Tooltips on the rotations screen will no longer obstruct the armor stand model (thanks to [Mephodio]) +- Tooltips on the style screen are now split into multiple lines to prevent them from flowing off the screen + +## [v4.0.1-1.19.2] - 2022-10-15 +### Changed +- Updated to Forge 43.1.40+ which is also now required +### Fixed +- Armor stand interactions are now properly disabled when trying to open the menu when the mod is only installed client-side + +## [v4.0.0-1.19.2] - 2022-09-22 +- Initial release + +[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ +[Mephodio]: https://github.com/Mephodio diff --git a/1.19.2/Common/build.gradle b/1.19.2/Common/build.gradle new file mode 100644 index 0000000..949b426 --- /dev/null +++ b/1.19.2/Common/build.gradle @@ -0,0 +1,11 @@ +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/common.gradle' + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.common +} + +// @see https://github.com/jaredlll08/MultiLoader-Template/issues/17#issuecomment-1221598082 +tasks.withType(net.fabricmc.loom.task.AbstractRemapJarTask).each { + it.targetNamespace = "named" +} diff --git a/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java similarity index 79% rename from Common/src/main/java/fuzs/armorstatues/ArmorStatues.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java index a6e6f8a..ff91896 100644 --- a/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java @@ -1,10 +1,13 @@ package fuzs.armorstatues; +import fuzs.armorstatues.config.ClientConfig; import fuzs.armorstatues.init.ModRegistry; import fuzs.armorstatues.network.S2CPingMessage; import fuzs.armorstatues.proxy.ClientProxy; import fuzs.armorstatues.proxy.Proxy; import fuzs.armorstatues.proxy.ServerProxy; +import fuzs.puzzleslib.config.ConfigHolder; +import fuzs.puzzleslib.core.CommonFactories; import fuzs.puzzleslib.core.CoreServices; import fuzs.puzzleslib.core.DistTypeExecutor; import fuzs.puzzleslib.core.ModConstructor; @@ -20,10 +23,13 @@ public class ArmorStatues implements ModConstructor { public static final NetworkHandler NETWORK = CoreServices.FACTORIES.network(MOD_ID, true, true); @SuppressWarnings("Convert2MethodRef") + public static final ConfigHolder CONFIG = CommonFactories.INSTANCE.clientConfig(ClientConfig.class, () -> new ClientConfig()); + @SuppressWarnings("Convert2MethodRef") public static final Proxy PROXY = DistTypeExecutor.getForDistType(() -> () -> new ClientProxy(), () -> () -> new ServerProxy()); @Override public void onConstructMod() { + CONFIG.bakeConfigs(MOD_ID); ModRegistry.touch(); registerMessages(); } diff --git a/Common/src/main/java/fuzs/armorstatues/api/ArmorStatuesApi.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/StatuesApi.java similarity index 82% rename from Common/src/main/java/fuzs/armorstatues/api/ArmorStatuesApi.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/StatuesApi.java index 9ebfd20..79bd724 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/ArmorStatuesApi.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/StatuesApi.java @@ -13,9 +13,9 @@ import java.util.Locale; -public class ArmorStatuesApi implements ModConstructor { - public static final String MOD_ID = "armorstatues"; - public static final String MOD_NAME = "Armor Statues"; +public class StatuesApi implements ModConstructor { + public static final String MOD_ID = "statues"; + public static final String MOD_NAME = "Statues"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_NAME); public static final NetworkHandler NETWORK = CoreServices.FACTORIES.network(MOD_ID, true, true); @@ -37,7 +37,11 @@ private static void registerMessages() { public void onCommonSetup() { // do this here instead of in enum constructor to avoid potential issues with the enum class not having been loaded yet on server-side, therefore nothing being registered for (ArmorStandStyleOptions styleOption : ArmorStandStyleOptions.values()) { - ArmorStandStyleOption.register(new ResourceLocation(MOD_ID, styleOption.getTranslationId().toLowerCase(Locale.ROOT)), styleOption); + ArmorStandStyleOption.register(id(styleOption.getName().toLowerCase(Locale.ROOT)), styleOption); } } + + public static ResourceLocation id(String path) { + return new ResourceLocation(MOD_ID, path); + } } diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/ArmorStatuesApiClient.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/StatuesApiClient.java similarity index 89% rename from Common/src/main/java/fuzs/armorstatues/api/client/ArmorStatuesApiClient.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/StatuesApiClient.java index f67dfb5..216cb94 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/ArmorStatuesApiClient.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/StatuesApiClient.java @@ -8,7 +8,7 @@ import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.inventory.InventoryMenu; -public class ArmorStatuesApiClient implements ClientModConstructor { +public class StatuesApiClient implements ClientModConstructor { @Override public void onClientSetup() { @@ -17,7 +17,6 @@ public void onClientSetup() { ArmorStandScreenFactory.register(ArmorStandScreenType.STYLE, ArmorStandStyleScreen::new); ArmorStandScreenFactory.register(ArmorStandScreenType.POSES, ArmorStandPosesScreen::new); ArmorStandScreenFactory.register(ArmorStandScreenType.POSITION, ArmorStandPositionScreen::new); - ArmorStandScreenFactory.register(ArmorStandScreenType.ALIGNMENTS, ArmorStandAlignmentsScreen::new); ArmorStandRotationsScreen.registerPosePartMutatorFilter(PosePartMutator.LEFT_ARM, ArmorStand::isShowArms); ArmorStandRotationsScreen.registerPosePartMutatorFilter(PosePartMutator.RIGHT_ARM, ArmorStand::isShowArms); } diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/BoxedSliderButton.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/BoxedSliderButton.java similarity index 99% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/components/BoxedSliderButton.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/BoxedSliderButton.java index a9dbe18..df4bfc6 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/BoxedSliderButton.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/BoxedSliderButton.java @@ -64,7 +64,7 @@ public void updateNarration(NarrationElementOutput narrationElementOutput) { @Override public void renderButton(PoseStack poseStack, int mouseX, int mouseY, float partialTick) { RenderSystem.setShader(GameRenderer::getPositionTexShader); - RenderSystem.setShaderTexture(0, AbstractArmorStandScreen.ARMOR_STAND_WIDGETS_LOCATION); + RenderSystem.setShaderTexture(0, AbstractArmorStandScreen.getArmorStandWidgetsLocation()); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha); RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); @@ -81,7 +81,7 @@ public void renderButton(PoseStack poseStack, int mouseX, int mouseY, float part } else if (horizontalValueLocked) { this.blit(poseStack, this.x + sliderX, this.y, 54, 120, SLIDER_SIZE + 2, this.height); } else { - this.blit(poseStack, this.x, this.y + sliderY, 136, 45, this.width, SLIDER_SIZE + 2); + this.blit(poseStack, this.x, this.y + sliderY, 136, 49, this.width, SLIDER_SIZE + 2); } int i = this.getYImage(hoveredOrFocused); this.blit(poseStack, this.x + 1 + sliderX, this.y + 1 + sliderY, 151, i * SLIDER_SIZE, SLIDER_SIZE, SLIDER_SIZE); diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/LiveSliderButton.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/LiveSliderButton.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/components/LiveSliderButton.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/LiveSliderButton.java diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureButton.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureButton.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureButton.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureButton.java diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureSliderButton.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureSliderButton.java similarity index 98% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureSliderButton.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureSliderButton.java index 81446a7..ec56bd0 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureSliderButton.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureSliderButton.java @@ -20,7 +20,7 @@ public abstract class NewTextureSliderButton extends AbstractSliderButton implem private final int textureY; protected final ResourceLocation textureLocation; protected final OnTooltip onTooltip; - public double snapInterval; + public double snapInterval = -1.0; public NewTextureSliderButton(int x, int y, int width, int height, int textureX, int textureY, ResourceLocation textureLocation, Component component, double value) { this(x, y, width, height, textureX, textureY, textureLocation, component, value, (button, poseStack, mouseX, mouseY) -> { @@ -35,11 +35,6 @@ public NewTextureSliderButton(int x, int y, int width, int height, int textureX, this.onTooltip = onTooltip; } - @Override - public boolean isDirty() { - return false; - } - @Override public void renderButton(PoseStack poseStack, int mouseX, int mouseY, float partialTick) { Minecraft minecraft = Minecraft.getInstance(); diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureTickButton.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureTickButton.java similarity index 97% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureTickButton.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureTickButton.java index 2fe914e..c8c7b27 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureTickButton.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/NewTextureTickButton.java @@ -20,7 +20,7 @@ public NewTextureTickButton(int x, int y, int width, int height, int imageTextur } public NewTextureTickButton(int x, int y, int width, int height, int imageTextureX, int imageTextureY, ResourceLocation imageTextureLocation, OnPress onPress, OnTooltip onTooltip) { - super(x, y, width, height, 0, 184, AbstractArmorStandScreen.ARMOR_STAND_WIDGETS_LOCATION, CommonComponents.EMPTY, onPress, onTooltip); + super(x, y, width, height, 0, 184, AbstractArmorStandScreen.getArmorStandWidgetsLocation(), CommonComponents.EMPTY, onPress, onTooltip); this.imageTextureX = imageTextureX; this.imageTextureY = imageTextureY; this.imageTextureLocation = imageTextureLocation; diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickBoxButton.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickBoxButton.java similarity index 75% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickBoxButton.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickBoxButton.java index 7238924..0e780bc 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickBoxButton.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickBoxButton.java @@ -8,38 +8,25 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.components.Button; -import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; +import java.util.function.BooleanSupplier; + public class TickBoxButton extends Button { private final int textMargin; - private boolean selected; - - public TickBoxButton(int posX, int posY, boolean selected, OnPress onPress, OnTooltip onTooltip) { - this(posX, posY, 0, 0, CommonComponents.EMPTY, selected, onPress, onTooltip); - } + private final BooleanSupplier supplier; - public TickBoxButton(int posX, int posY, int textMargin, int textWidth, Component component, boolean selected, OnPress onPress, OnTooltip onTooltip) { + public TickBoxButton(int posX, int posY, int textMargin, int textWidth, Component component, BooleanSupplier supplier, OnPress onPress, OnTooltip onTooltip) { super(posX, posY, 20 + textMargin + textWidth, 20, component, onPress, onTooltip); this.textMargin = textMargin; - this.selected = selected; - } - - @Override - public void onPress() { - this.selected = !this.selected; - super.onPress(); - } - - public boolean isSelected() { - return this.selected; + this.supplier = supplier; } @Override public void renderButton(PoseStack poseStack, int mouseX, int mouseY, float partialTick) { Minecraft minecraft = Minecraft.getInstance(); - RenderSystem.setShaderTexture(0, AbstractArmorStandScreen.ARMOR_STAND_WIDGETS_LOCATION); + RenderSystem.setShaderTexture(0, AbstractArmorStandScreen.getArmorStandWidgetsLocation()); RenderSystem.enableDepthTest(); Font font = minecraft.font; RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha); @@ -47,7 +34,7 @@ public void renderButton(PoseStack poseStack, int mouseX, int mouseY, float part RenderSystem.defaultBlendFunc(); RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); blit(poseStack, this.x + 2, this.y + 2, 196.0F, this.isHoveredOrFocused() ? 16.0F : 0.0F, 16, 16, 256, 256); - if (this.selected) { + if (this.supplier.getAsBoolean()) { blit(poseStack, this.x + 2, this.y + 2, 196.0F, 32.0F + (this.isHoveredOrFocused() ? 16.0F : 0.0F), 16, 16, 256, 256); } final int textColor = this.active ? (this.isHoveredOrFocused() ? ChatFormatting.YELLOW.getColor() : 16777215) : 10526880; diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickButton.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickButton.java similarity index 95% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickButton.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickButton.java index 5a02786..6e51b54 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickButton.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickButton.java @@ -14,13 +14,13 @@ public class TickButton extends NewTextureButton implements TickingButton { protected int lastClickedTicksDelay = 30; public TickButton(int x, int y, int width, int height, Component title, Component clickedTitle, OnPress onPress) { - super(x, y, width, height, 0, 184, AbstractArmorStandScreen.ARMOR_STAND_WIDGETS_LOCATION, title, onPress); + super(x, y, width, height, 0, 184, AbstractArmorStandScreen.getArmorStandWidgetsLocation(), title, onPress); this.title = title; this.clickedTitle = clickedTitle; } public TickButton(int x, int y, int width, int height, Component title, Component clickedTitle, OnPress onPress, OnTooltip onTooltip) { - super(x, y, width, height, 0, 184, AbstractArmorStandScreen.ARMOR_STAND_WIDGETS_LOCATION, title, onPress, onTooltip); + super(x, y, width, height, 0, 184, AbstractArmorStandScreen.getArmorStandWidgetsLocation(), title, onPress, onTooltip); this.title = title; this.clickedTitle = clickedTitle; } diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickingButton.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickingButton.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickingButton.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/TickingButton.java diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/UnboundedSliderButton.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/UnboundedSliderButton.java new file mode 100644 index 0000000..e2f499b --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/UnboundedSliderButton.java @@ -0,0 +1,12 @@ +package fuzs.armorstatues.api.client.gui.components; + +public interface UnboundedSliderButton { + + default boolean isDirty() { + return false; + } + + default void clearDirty() { + + } +} diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/VerticalSliderButton.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/VerticalSliderButton.java similarity index 99% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/components/VerticalSliderButton.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/VerticalSliderButton.java index 1dd17ca..fcbf5b4 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/VerticalSliderButton.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/VerticalSliderButton.java @@ -57,7 +57,7 @@ public void updateNarration(NarrationElementOutput narrationElementOutput) { @Override public void renderButton(PoseStack poseStack, int mouseX, int mouseY, float partialTick) { RenderSystem.setShader(GameRenderer::getPositionTexShader); - RenderSystem.setShaderTexture(0, AbstractArmorStandScreen.ARMOR_STAND_WIDGETS_LOCATION); + RenderSystem.setShaderTexture(0, AbstractArmorStandScreen.getArmorStandWidgetsLocation()); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha); RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/AbstractArmorStandScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/AbstractArmorStandScreen.java similarity index 61% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/AbstractArmorStandScreen.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/AbstractArmorStandScreen.java index f8fcbae..aba9bad 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/AbstractArmorStandScreen.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/AbstractArmorStandScreen.java @@ -2,37 +2,49 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; -import fuzs.armorstatues.api.ArmorStatuesApi; +import fuzs.armorstatues.api.StatuesApi; import fuzs.armorstatues.api.client.gui.components.TickingButton; import fuzs.armorstatues.api.client.gui.components.UnboundedSliderButton; -import fuzs.armorstatues.api.client.init.ModClientRegistry; import fuzs.armorstatues.api.network.client.data.DataSyncHandler; import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; import fuzs.armorstatues.api.world.inventory.ArmorStandMenu; import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; import fuzs.puzzleslib.client.core.ClientCoreServices; +import fuzs.puzzleslib.client.gui.screens.CommonScreens; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiComponent; import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.ImageButton; import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.ConfirmLinkScreen; import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.inventory.InventoryScreen; import net.minecraft.client.gui.screens.inventory.MenuAccess; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.AbstractContainerMenu; import org.apache.commons.lang3.ArrayUtils; +import org.jetbrains.annotations.Nullable; import java.util.Optional; public abstract class AbstractArmorStandScreen extends Screen implements MenuAccess, ArmorStandScreen { - private static final ResourceLocation ARMOR_STAND_BACKGROUND_LOCATION = new ResourceLocation(ArmorStatuesApi.MOD_ID, "textures/gui/container/armor_stand/background.png"); - public static final ResourceLocation ARMOR_STAND_WIDGETS_LOCATION = new ResourceLocation(ArmorStatuesApi.MOD_ID, "textures/gui/container/armor_stand/widgets.png"); + public static final String VANILLA_TWEAKS_HOMEPAGE = "https://vanillatweaks.net/"; + public static final String CREDITS_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.credits"; + private static final ResourceLocation ARMOR_STAND_BACKGROUND_LOCATION = StatuesApi.id("textures/gui/container/statue/background.png"); + private static final ResourceLocation ARMOR_STAND_WIDGETS_LOCATION = StatuesApi.id("textures/gui/container/statue/widgets.png"); + private static final ResourceLocation ARMOR_STAND_EQUIPMENT_LOCATION = StatuesApi.id("textures/gui/container/statue/equipment.png"); + + @Nullable + static ArmorStandScreenType lastScreenType; + static ArmorStandInInventoryRenderer armorStandRenderer = ArmorStandInInventoryRenderer.SIMPLE; protected final int imageWidth = 210; protected final int imageHeight = 188; @@ -46,6 +58,7 @@ public abstract class AbstractArmorStandScreen extends Screen implements MenuAcc protected boolean smallInventoryEntity; protected int mouseX; protected int mouseY; + @Nullable private AbstractWidget closeButton; public AbstractArmorStandScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { @@ -55,11 +68,28 @@ public AbstractArmorStandScreen(ArmorStandHolder holder, Inventory inventory, Co this.dataSyncHandler = dataSyncHandler; } + public static ResourceLocation getArmorStandBackgroundLocation() { + return ARMOR_STAND_BACKGROUND_LOCATION; + } + + public static ResourceLocation getArmorStandWidgetsLocation() { + return ARMOR_STAND_WIDGETS_LOCATION; + } + + public static ResourceLocation getArmorStandEquipmentLocation() { + return ARMOR_STAND_EQUIPMENT_LOCATION; + } + @Override public ArmorStandHolder getHolder() { return this.holder; } + @Override + public DataSyncHandler getDataSyncHandler() { + return this.dataSyncHandler; + } + @Override public & ArmorStandScreen> T createScreenType(ArmorStandScreenType screenType) { T screen = ArmorStandScreenFactory.createScreenType(screenType, this.holder, this.inventory, this.title, this.dataSyncHandler); @@ -80,7 +110,7 @@ public void setMouseY(int mouseY) { @Override public void tick() { - super.tick(); + this.dataSyncHandler.tick(); for (GuiEventListener child : this.children()) { if (child instanceof TickingButton button) button.tick(); } @@ -96,7 +126,7 @@ protected void init() { } public static AbstractButton makeCloseButton(Screen screen, int leftPos, int imageWidth, int topPos) { - return new ImageButton(leftPos + imageWidth - 15 - 8, topPos + 8, 15, 15, 136, 0, ARMOR_STAND_WIDGETS_LOCATION, button -> { + return new ImageButton(leftPos + imageWidth - 15 - 8, topPos + 8, 15, 15, 136, 0, getArmorStandWidgetsLocation(), button -> { screen.onClose(); }); } @@ -114,13 +144,26 @@ protected boolean disableMenuRendering() { } protected void toggleMenuRendering(boolean disableMenuRendering) { - this.closeButton.visible = !disableMenuRendering; + if (this.closeButton != null) { + this.closeButton.visible = !disableMenuRendering; + } + } + + protected void addVanillaTweaksCreditsButton() { + this.addRenderableWidget(new ImageButton(this.leftPos + 6, this.topPos + 6, 20, 20, 136, 64, 20, getArmorStandWidgetsLocation(), 256, 256, button -> { + this.minecraft.setScreen(new ConfirmLinkScreen((bl) -> { + if (bl) Util.getPlatform().openUri(VANILLA_TWEAKS_HOMEPAGE); + this.minecraft.setScreen(this); + }, VANILLA_TWEAKS_HOMEPAGE, true)); + }, (button, poseStack, mouseX, mouseY) -> { + this.renderTooltip(poseStack, this.font.split(Component.translatable(CREDITS_TRANSLATION_KEY), 175), mouseX, mouseY); + }, CommonComponents.EMPTY)); } @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { if (button == 0) { - if (!this.disableMenuRendering() && handleTabClicked((int) mouseX, (int) mouseY, this.leftPos, this.topPos, this.imageHeight, this, this.dataSyncHandler.tabs())) { + if (!this.disableMenuRendering() && handleTabClicked((int) mouseX, (int) mouseY, this.leftPos, this.topPos, this.imageHeight, this, this.dataSyncHandler.getScreenTypes())) { return true; } } @@ -135,8 +178,8 @@ public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTic this.renderBg(poseStack, partialTick, mouseX, mouseY); super.render(poseStack, mouseX, mouseY, partialTick); if (!this.disableMenuRendering()) { - findHoveredTab(this.leftPos, this.topPos, this.imageHeight, mouseX, mouseY, this.dataSyncHandler.tabs()).ifPresent(hoveredTab -> { - this.renderTooltip(poseStack, hoveredTab.getComponent(), mouseX, mouseY); + findHoveredTab(this.leftPos, this.topPos, this.imageHeight, mouseX, mouseY, this.dataSyncHandler.getScreenTypes()).ifPresent(hoveredTab -> { + this.renderTooltip(poseStack, Component.translatable(hoveredTab.getTranslationKey()), mouseX, mouseY); }); } this.mouseX = mouseX; @@ -147,9 +190,9 @@ protected void renderBg(PoseStack poseStack, float partialTick, int mouseX, int if (!this.disableMenuRendering()) { RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - RenderSystem.setShaderTexture(0, ARMOR_STAND_BACKGROUND_LOCATION); + RenderSystem.setShaderTexture(0, getArmorStandBackgroundLocation()); this.blit(poseStack, this.leftPos, this.topPos, 0, 0, this.imageWidth, this.imageHeight); - drawTabs(poseStack, this.leftPos, this.topPos, this.imageHeight, this, this.dataSyncHandler.tabs()); + drawTabs(poseStack, this.leftPos, this.topPos, this.imageHeight, this, this.dataSyncHandler.getScreenTypes()); this.renderEntityInInventory(poseStack); } } @@ -158,13 +201,13 @@ private void renderEntityInInventory(PoseStack poseStack) { if (this.renderInventoryEntity()) { RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - RenderSystem.setShaderTexture(0, ARMOR_STAND_WIDGETS_LOCATION); + RenderSystem.setShaderTexture(0, getArmorStandWidgetsLocation()); if (this.smallInventoryEntity) { this.blit(poseStack, this.leftPos + this.inventoryEntityX, this.topPos + this.inventoryEntityY, 200, 184, 50, 72); - InventoryScreen.renderEntityInInventory(this.leftPos + this.inventoryEntityX + 24, this.topPos + this.inventoryEntityY + 65, 30, this.leftPos + this.inventoryEntityX + 24 - 10 - this.mouseX, this.topPos + this.inventoryEntityY + 65 - 44 - this.mouseY, this.holder.getArmorStand()); + this.renderArmorStandInInventory(this.leftPos + this.inventoryEntityX + 24, this.topPos + this.inventoryEntityY + 65, 30, this.leftPos + this.inventoryEntityX + 24 - 10 - this.mouseX, this.topPos + this.inventoryEntityY + 65 - 44 - this.mouseY); } else { this.blit(poseStack, this.leftPos + this.inventoryEntityX, this.topPos + this.inventoryEntityY, 0, 0, 76, 108); - InventoryScreen.renderEntityInInventory(this.leftPos + this.inventoryEntityX + 38, this.topPos + this.inventoryEntityY + 98, 45, (float) (this.leftPos + this.inventoryEntityX + 38 - 5) - this.mouseX, (float) (this.topPos + this.inventoryEntityY + 98 - 66) - this.mouseY, this.holder.getArmorStand()); + this.renderArmorStandInInventory(this.leftPos + this.inventoryEntityX + 38, this.topPos + this.inventoryEntityY + 98, 45, (float) (this.leftPos + this.inventoryEntityX + 38 - 5) - this.mouseX, (float) (this.topPos + this.inventoryEntityY + 98 - 66) - this.mouseY); } } } @@ -185,37 +228,63 @@ public boolean mouseReleased(double mouseX, double mouseY, int button) { @Override public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - // hook this in before super, as the default key tab is normally used for cycling focused widgets (which we don't really need...) - if (tryCycleTabs(this, keyCode, scanCode, modifiers)) { - return true; - } else if (super.keyPressed(keyCode, scanCode, modifiers)) { + if (super.keyPressed(keyCode, scanCode, modifiers)) { return true; } else if (this.minecraft.options.keyInventory.matches(keyCode, scanCode)) { this.onClose(); return true; + } else if (handleHotbarKeyPressed(keyCode, scanCode, this, this.dataSyncHandler.getScreenTypes())) { + return true; } return false; } - public static boolean handleTabClicked(int mouseX, int mouseY, int leftPos, int topPos, int imageHeight, T screen, ArmorStandScreenType[] tabs) { - Optional hoveredTab = findHoveredTab(leftPos, topPos, imageHeight, mouseX, mouseY, tabs); - return hoveredTab.filter(armorStandScreenType -> openTabScreen(screen, armorStandScreenType)).isPresent(); + public static boolean handleHotbarKeyPressed(int keyCode, int scanCode, T screen, ArmorStandScreenType[] tabs) { + Minecraft minecraft = CommonScreens.INSTANCE.getMinecraft(screen); + for (int i = 0; i < Math.min(tabs.length, 9); ++i) { + if (minecraft.options.keyHotbarSlots[i].matches(keyCode, scanCode)) { + if (openTabScreen(screen, tabs[i], true)) { + return true; + } + } + } + return false; } - private static boolean openTabScreen(T screen, ArmorStandScreenType screenType) { - if (screenType != screen.getScreenType()) { - ClientCoreServices.SCREENS.getMinecraft(screen).getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); - ClientCoreServices.SCREENS.getMinecraft(screen).setScreen(screen.createScreenType(screenType)); + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double delta) { + if (super.mouseScrolled(mouseX, mouseY, delta)) { return true; + } else { + return handleMouseScrolled((int) mouseX, (int) mouseY, delta, this.leftPos, this.topPos, this.imageHeight, this, this.dataSyncHandler.getScreenTypes()); + } + } + + public static boolean handleMouseScrolled(int mouseX, int mouseY, double delta, int leftPos, int topPos, int imageHeight, T screen, ArmorStandScreenType[] tabs) { + delta = Math.signum(delta); + if (delta != 0.0) { + Optional optional = findHoveredTab(leftPos, topPos, imageHeight, mouseX, mouseY, tabs); + if (optional.isPresent()) { + ArmorStandScreenType screenType = cycleTabs(screen.getScreenType(), tabs, delta > 0.0); + return openTabScreen(screen, screenType, false); + } } return false; } - public static boolean tryCycleTabs(T screen, int keyCode, int scanCode, int modifiers) { - // keep a way to let vanilla cycle widgets - if (!hasControlDown() && !hasAltDown() && ModClientRegistry.CYCLE_TABS_KEY_MAPPING.matches(keyCode, scanCode)) { - ArmorStandScreenType screenType = cycleTabs(screen.getScreenType(), screen.getHolder().getDataProvider().getScreenTypes(), hasShiftDown()); - openTabScreen(screen, screenType); + public static boolean handleTabClicked(int mouseX, int mouseY, int leftPos, int topPos, int imageHeight, T screen, ArmorStandScreenType[] tabs) { + Optional hoveredTab = findHoveredTab(leftPos, topPos, imageHeight, mouseX, mouseY, tabs); + return hoveredTab.filter(armorStandScreenType -> openTabScreen(screen, armorStandScreenType, true)).isPresent(); + } + + private static boolean openTabScreen(T screen, ArmorStandScreenType screenType, boolean clickSound) { + if (screenType != screen.getScreenType()) { + Minecraft minecraft = ClientCoreServices.SCREENS.getMinecraft(screen); + if (clickSound) { + SimpleSoundInstance sound = SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F); + minecraft.getSoundManager().play(sound); + } + minecraft.setScreen(screen.createScreenType(screenType)); return true; } return false; @@ -234,8 +303,8 @@ public static void drawTabs(PoseStack pose int tabY = topPos + tabsStartY + 27 * i; RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - RenderSystem.setShaderTexture(0, ARMOR_STAND_WIDGETS_LOCATION); - screen.blit(poseStack, tabX, tabY, 212, tabType == screen.getScreenType() ? 0 : 27, 35, 26); + RenderSystem.setShaderTexture(0, getArmorStandBackgroundLocation()); + GuiComponent.blit(poseStack, tabX, tabY, tabY <= topPos ? 36 : tabY >= topPos + imageHeight - 36 ? 72 : 0, 188 + (tabType == screen.getScreenType() ? 0 : 26), 36, 26, 256, 256); ItemRenderer itemRenderer = ClientCoreServices.SCREENS.getItemRenderer(screen); itemRenderer.blitOffset = 100.0F; itemRenderer.renderAndDecorateItem(tabType.getIcon(), tabX + 10, tabY + 5); @@ -270,6 +339,15 @@ public boolean isPauseScreen() { return false; } + @Override + public void removed() { + for (GuiEventListener child : this.children()) { + if (child instanceof UnboundedSliderButton sliderButton) { + sliderButton.clearDirty(); + } + } + } + @Override public void onClose() { if (this.holder instanceof AbstractContainerMenu) { diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandButtonsScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandButtonsScreen.java new file mode 100644 index 0000000..486e9cb --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandButtonsScreen.java @@ -0,0 +1,84 @@ +package fuzs.armorstatues.api.client.gui.screens.armorstand; + +import fuzs.armorstatues.api.client.gui.components.TickButton; +import fuzs.armorstatues.api.network.client.data.DataSyncHandler; +import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; +import net.minecraft.client.gui.components.Button; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; + +public abstract class ArmorStandButtonsScreen extends ArmorStandWidgetsScreen { + + public ArmorStandButtonsScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + protected class SingleButtonWidget extends AbstractArmorStandWidget { + private final Component title; + private final Component description; + private final Component clickedTitle; + private final Button.OnPress onPress; + + public SingleButtonWidget(Component title, Component description, Component clickedTitle, Button.OnPress onPress) { + this.title = title; + this.description = description; + this.clickedTitle = clickedTitle; + this.onPress = onPress; + } + + @Override + protected boolean shouldTick() { + return true; + } + + @Override + public void init(int posX, int posY) { + super.init(posX, posY); + this.children.add(ArmorStandButtonsScreen.this.addRenderableWidget(new TickButton(posX, posY + 1, 194, 20, this.title, this.clickedTitle, this.onPress, (button, poseStack, mouseX, mouseY) -> { + ArmorStandButtonsScreen.this.renderTooltip(poseStack, ArmorStandButtonsScreen.this.font.split(this.description, 175), mouseX, mouseY); + }))); + } + } + + protected class DoubleButtonWidget extends AbstractArmorStandWidget { + private final Component titleLeft; + private final Component titleRight; + private final Component descriptionLeft; + private final Component descriptionRight; + private final Component clickedTitleLeft; + private final Component clickedTitleRight; + private final Button.OnPress onPressLeft; + private final Button.OnPress onPressRight; + + public DoubleButtonWidget(Component titleLeft, Component titleRight, Component descriptionLeft, Component descriptionRight, Component clickedTitle, Button.OnPress onPressLeft, Button.OnPress onPressRight) { + this(titleLeft, titleRight, descriptionLeft, descriptionRight, clickedTitle, clickedTitle, onPressLeft, onPressRight); + } + + public DoubleButtonWidget(Component titleLeft, Component titleRight, Component descriptionLeft, Component descriptionRight, Component clickedTitleLeft, Component clickedTitleRight, Button.OnPress onPressLeft, Button.OnPress onPressRight) { + this.titleLeft = titleLeft; + this.titleRight = titleRight; + this.descriptionLeft = descriptionLeft; + this.descriptionRight = descriptionRight; + this.clickedTitleLeft = clickedTitleLeft; + this.clickedTitleRight = clickedTitleRight; + this.onPressLeft = onPressLeft; + this.onPressRight = onPressRight; + } + + @Override + protected boolean shouldTick() { + return true; + } + + @Override + public void init(int posX, int posY) { + super.init(posX, posY); + this.children.add(ArmorStandButtonsScreen.this.addRenderableWidget(new TickButton(posX, posY + 1, 94, 20, this.titleLeft, this.clickedTitleLeft, this.onPressLeft, (button, poseStack, mouseX, mouseY) -> { + ArmorStandButtonsScreen.this.renderTooltip(poseStack, ArmorStandButtonsScreen.this.font.split(this.descriptionLeft, 175), mouseX, mouseY); + }))); + this.children.add(ArmorStandButtonsScreen.this.addRenderableWidget(new TickButton(posX + 100, posY + 1, 94, 20, this.titleRight, this.clickedTitleRight, this.onPressRight, (button, poseStack, mouseX, mouseY) -> { + ArmorStandButtonsScreen.this.renderTooltip(poseStack, ArmorStandButtonsScreen.this.font.split(this.descriptionRight, 175), mouseX, mouseY); + }))); + } + } +} diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandEquipmentScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandEquipmentScreen.java similarity index 64% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandEquipmentScreen.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandEquipmentScreen.java index 7d88d86..a52d43b 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandEquipmentScreen.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandEquipmentScreen.java @@ -2,26 +2,21 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; -import fuzs.armorstatues.api.ArmorStatuesApi; import fuzs.armorstatues.api.network.client.data.DataSyncHandler; import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; import fuzs.armorstatues.api.world.inventory.ArmorStandMenu; import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; -import net.minecraft.client.gui.screens.inventory.InventoryScreen; import net.minecraft.client.gui.screens.inventory.MenuAccess; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.Slot; public class ArmorStandEquipmentScreen extends AbstractContainerScreen implements ArmorStandScreen { - private static final ResourceLocation ARMOR_STAND_EQUIPMENT_LOCATION = new ResourceLocation(ArmorStatuesApi.MOD_ID, "textures/gui/container/armor_stand/equipment.png"); - private final Inventory inventory; private final DataSyncHandler dataSyncHandler; private int mouseX; @@ -40,6 +35,11 @@ public ArmorStandHolder getHolder() { return this.menu; } + @Override + public DataSyncHandler getDataSyncHandler() { + return this.dataSyncHandler; + } + @Override public & ArmorStandScreen> T createScreenType(ArmorStandScreenType screenType) { T screen = ArmorStandScreenFactory.createScreenType(screenType, this.menu, this.inventory, this.title, this.dataSyncHandler); @@ -58,6 +58,11 @@ public void setMouseY(int mouseY) { this.mouseY = mouseY; } + @Override + protected void containerTick() { + this.dataSyncHandler.tick(); + } + @Override protected void init() { super.init(); @@ -67,7 +72,7 @@ protected void init() { @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { if (button == 0) { - if (AbstractArmorStandScreen.handleTabClicked((int) mouseX, (int) mouseY, this.leftPos, this.topPos, this.imageHeight, this, this.dataSyncHandler.tabs())) { + if (AbstractArmorStandScreen.handleTabClicked((int) mouseX, (int) mouseY, this.leftPos, this.topPos, this.imageHeight, this, this.dataSyncHandler.getScreenTypes())) { return true; } } @@ -75,23 +80,22 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { } @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - // hook this in before super, as the default key tab is normally used for cycling focused widgets (which we don't really need...) - if (AbstractArmorStandScreen.tryCycleTabs(this, keyCode, scanCode, modifiers)) { + public boolean mouseScrolled(double mouseX, double mouseY, double delta) { + if (super.mouseScrolled(mouseX, mouseY, delta)) { return true; } - return super.keyPressed(keyCode, scanCode, modifiers); + return AbstractArmorStandScreen.handleMouseScrolled((int) mouseX, (int) mouseY, delta, this.leftPos, this.topPos, this.imageHeight, this, this.dataSyncHandler.getScreenTypes()); } @Override - public void render(PoseStack poseStack, int mouseX, int mouseY, float pPartialTick) { + public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTick) { this.renderBackground(poseStack); - this.renderBg(poseStack, pPartialTick, mouseX, mouseY); - super.render(poseStack, mouseX, mouseY, pPartialTick); + this.renderBg(poseStack, partialTick, mouseX, mouseY); + super.render(poseStack, mouseX, mouseY, partialTick); this.renderTooltip(poseStack, mouseX, mouseY); if (this.menu.getCarried().isEmpty()) { - AbstractArmorStandScreen.findHoveredTab(this.leftPos, this.topPos, this.imageHeight, mouseX, mouseY, this.dataSyncHandler.tabs()).ifPresent(hoveredTab -> { - this.renderTooltip(poseStack, hoveredTab.getComponent(), mouseX, mouseY); + AbstractArmorStandScreen.findHoveredTab(this.leftPos, this.topPos, this.imageHeight, mouseX, mouseY, this.dataSyncHandler.getScreenTypes()).ifPresent(hoveredTab -> { + this.renderTooltip(poseStack, Component.translatable(hoveredTab.getTranslationKey()), mouseX, mouseY); }); } this.mouseX = mouseX; @@ -102,20 +106,19 @@ public void render(PoseStack poseStack, int mouseX, int mouseY, float pPartialTi protected void renderBg(PoseStack poseStack, float pPartialTick, int pX, int pY) { RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - RenderSystem.setShaderTexture(0, ARMOR_STAND_EQUIPMENT_LOCATION); + RenderSystem.setShaderTexture(0, AbstractArmorStandScreen.getArmorStandEquipmentLocation()); this.blit(poseStack, this.leftPos, this.topPos, 0, 0, this.imageWidth, this.imageHeight); for (int k = 0; k < ArmorStandMenu.SLOT_IDS.length; ++k) { Slot slot = this.menu.slots.get(k); - if (slot.isActive() && this.isSlotRestricted(ArmorStandMenu.SLOT_IDS[k])) { + if (slot.isActive() && isSlotRestricted(this.menu.getArmorStand(), ArmorStandMenu.SLOT_IDS[k])) { this.blit(poseStack, this.leftPos + slot.x - 1, this.topPos + slot.y - 1, 210, 0, 18, 18); } } - AbstractArmorStandScreen.drawTabs(poseStack, this.leftPos, this.topPos, this.imageHeight, this, this.dataSyncHandler.tabs()); - InventoryScreen.renderEntityInInventory(this.leftPos + 104, this.topPos + 84, 30, (float) (this.leftPos + 104 - 10) - this.mouseX, (float) (this.topPos + 84 - 44) - this.mouseY, this.menu.getArmorStand()); + AbstractArmorStandScreen.drawTabs(poseStack, this.leftPos, this.topPos, this.imageHeight, this, this.dataSyncHandler.getScreenTypes()); + this.renderArmorStandInInventory(this.leftPos + 104, this.topPos + 84, 30, (float) (this.leftPos + 104 - 10) - this.mouseX, (float) (this.topPos + 84 - 44) - this.mouseY); } - private boolean isSlotRestricted(EquipmentSlot equipmentSlot) { - ArmorStand armorStand = this.menu.getArmorStand(); + private static boolean isSlotRestricted(ArmorStand armorStand, EquipmentSlot equipmentSlot) { return ArmorStandMenu.isSlotDisabled(armorStand, equipmentSlot, 0) || ArmorStandMenu.isSlotDisabled(armorStand, equipmentSlot, ArmorStand.DISABLE_TAKING_OFFSET) || ArmorStandMenu.isSlotDisabled(armorStand, equipmentSlot, ArmorStand.DISABLE_PUTTING_OFFSET); } @@ -124,6 +127,32 @@ protected void renderLabels(PoseStack poseStack, int mouseX, int mouseY) { } + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + ArmorStandScreenType[] tabs = this.dataSyncHandler.getScreenTypes(); + if (this.menu.getCarried().isEmpty() && this.hoveredSlot == null) { + AbstractArmorStandScreen.handleHotbarKeyPressed(keyCode, scanCode, this, tabs); + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + private boolean shouldHandleHotbarSlotKeys(int keyCode, int scanCode, ArmorStandScreenType[] tabs) { + if (this.menu.getCarried().isEmpty() && this.hoveredSlot != null) { + if (this.hoveredSlot.hasItem()) { + return false; + } else { + for (int i = 0; i < Math.min(tabs.length, 9); ++i) { + if (this.minecraft.options.keyHotbarSlots[i].matches(keyCode, scanCode)) { + if (!this.minecraft.player.getInventory().getItem(i).isEmpty()) { + return false; + } + } + } + } + } + return true; + } + @Override public ArmorStandScreenType getScreenType() { return ArmorStandScreenType.EQUIPMENT; diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandInInventoryRenderer.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandInInventoryRenderer.java new file mode 100644 index 0000000..82d276c --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandInInventoryRenderer.java @@ -0,0 +1,14 @@ +package fuzs.armorstatues.api.client.gui.screens.armorstand; + +import net.minecraft.client.gui.screens.inventory.InventoryScreen; +import net.minecraft.world.entity.LivingEntity; + +public interface ArmorStandInInventoryRenderer { + ArmorStandInInventoryRenderer SIMPLE = InventoryScreen::renderEntityInInventory; + + void renderEntityInInventory(int posX, int posY, int scale, float mouseX, float mouseY, LivingEntity livingEntity); + + static void setArmorStandRenderer(ArmorStandInInventoryRenderer armorStandRenderer) { + AbstractArmorStandScreen.armorStandRenderer = armorStandRenderer; + } +} diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPosesScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPosesScreen.java new file mode 100644 index 0000000..124431e --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPosesScreen.java @@ -0,0 +1,126 @@ +package fuzs.armorstatues.api.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import com.mojang.blaze3d.vertex.PoseStack; +import fuzs.armorstatues.api.StatuesApi; +import fuzs.armorstatues.api.network.client.data.DataSyncHandler; +import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandPose; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.ImageButton; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.util.StringUtil; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; + +import java.util.List; +import java.util.Optional; + +public class ArmorStandPosesScreen extends AbstractArmorStandScreen { + public static final String POSE_SOURCE_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.pose.by"; + private static final int POSES_PER_PAGE = 4; + + private static int firstPoseIndex; + + private final AbstractWidget[] cycleButtons = new AbstractWidget[2]; + private final AbstractWidget[] poseButtons = new AbstractWidget[POSES_PER_PAGE]; + + public ArmorStandPosesScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + this.inventoryEntityX = 5; + this.inventoryEntityY = 40; + } + + @Override + protected void init() { + super.init(); + this.cycleButtons[0] = this.addRenderableWidget(new ImageButton(this.leftPos + 17, this.topPos + 153, 20, 20, 156, 64, getArmorStandWidgetsLocation(), button -> { + this.toggleCycleButtons(-POSES_PER_PAGE); + })); + this.cycleButtons[1] = this.addRenderableWidget(new ImageButton(this.leftPos + 49, this.topPos + 153, 20, 20, 176, 64, getArmorStandWidgetsLocation(), button -> { + this.toggleCycleButtons(POSES_PER_PAGE); + })); + for (int i = 0; i < this.poseButtons.length; i++) { + final int index = i; + this.poseButtons[i] = this.addRenderableWidget(new ImageButton(this.leftPos + 83 + i % 2 * 62, this.topPos + 9 + i / 2 * 88, 60, 82, 76, 0, 82, getArmorStandWidgetsLocation(), 256, 256, button -> { + getPoseAt(index).ifPresent(this.dataSyncHandler::sendPose); + }, (Button button, PoseStack poseStack, int mouseX, int mouseY) -> { + getPoseAt(index).ifPresent(pose -> { + String translationKey = pose.getTranslationKey(); + if (translationKey != null) { + Component component = Component.translatable(translationKey); + List lines = Lists.newArrayList(component); + String sourceType = pose.getSourceType().getDisplayName(); + if (!StringUtil.isNullOrEmpty(sourceType)) { + lines.add(Component.translatable(POSE_SOURCE_TRANSLATION_KEY, sourceType).withStyle(ChatFormatting.GRAY)); + } + this.renderTooltip(poseStack, lines, Optional.empty(), mouseX, mouseY); + } + }); + }, CommonComponents.EMPTY)); + } + this.toggleCycleButtons(0); + this.addVanillaTweaksCreditsButton(); + } + + private void toggleCycleButtons(int increment) { + int newFirstPoseIndex = firstPoseIndex + increment; + if (newFirstPoseIndex >= 0 && newFirstPoseIndex < ArmorStandPose.valuesLength()) { + firstPoseIndex = newFirstPoseIndex; + this.cycleButtons[0].active = newFirstPoseIndex - POSES_PER_PAGE >= 0; + this.cycleButtons[1].active = newFirstPoseIndex + POSES_PER_PAGE < ArmorStandPose.valuesLength(); + for (int i = 0; i < this.poseButtons.length; i++) { + this.poseButtons[i].visible = getPoseAt(i).isPresent(); + } + } + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double delta) { + if (super.mouseScrolled(mouseX, mouseY, delta)) { + return true; + } else if (mouseX >= this.leftPos && mouseX < this.leftPos + this.imageWidth && mouseY >= this.topPos && mouseY < this.topPos + this.imageHeight) { + delta = Math.signum(delta); + if (delta != 0.0) { + this.toggleCycleButtons((int) (-1.0 * delta * POSES_PER_PAGE)); + return true; + } + } + return false; + } + + @Override + protected void renderBg(PoseStack poseStack, float partialTick, int mouseX, int mouseY) { + super.renderBg(poseStack, partialTick, mouseX, mouseY); + ArmorStand armorStand = this.holder.getArmorStand(); + ArmorStandPose entityPose = ArmorStandPose.fromEntity(armorStand); + for (int i = 0; i < POSES_PER_PAGE; i++) { + Optional pose = getPoseAt(i); + if (pose.isPresent()) { + pose.get().applyToEntity(armorStand); + this.renderArmorStandInInventory(this.leftPos + 112 + i % 2 * 62, this.topPos + 79 + i / 2 * 88, 30, this.leftPos + 112 + i % 2 * 62 - 10 - this.mouseX, this.topPos + 79 + i / 2 * 88 - 44 - this.mouseY); + } + } + entityPose.applyToEntity(armorStand); + } + + @Override + protected boolean withCloseButton() { + return false; + } + + @Override + public ArmorStandScreenType getScreenType() { + return ArmorStandScreenType.POSES; + } + + private static Optional getPoseAt(int index) { + index += firstPoseIndex; + if (index >= ArmorStandPose.valuesLength()) return Optional.empty(); + return Optional.of(ArmorStandPose.values()[index]); + } +} diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPositionScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPositionScreen.java similarity index 59% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPositionScreen.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPositionScreen.java index 844a51c..ebefce1 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPositionScreen.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPositionScreen.java @@ -2,6 +2,7 @@ import com.google.common.collect.Lists; import com.mojang.blaze3d.vertex.PoseStack; +import fuzs.armorstatues.api.StatuesApi; import fuzs.armorstatues.api.client.gui.components.NewTextureButton; import fuzs.armorstatues.api.client.gui.components.NewTextureSliderButton; import fuzs.armorstatues.api.network.client.data.DataSyncHandler; @@ -29,10 +30,24 @@ import java.util.function.Consumer; import java.util.function.DoubleConsumer; import java.util.function.DoubleSupplier; -import java.util.function.Supplier; import java.util.stream.Collectors; -public class ArmorStandPositionScreen extends ArmorStandWidgetsScreen { +public class ArmorStandPositionScreen extends ArmorStandButtonsScreen { + public static final String ROTATION_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.position.rotation"; + public static final String POSITION_X_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.position.x"; + public static final String POSITION_Y_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.position.y"; + public static final String POSITION_Z_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.position.z"; + public static final String INCREMENT_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.position.increment"; + public static final String DECREMENT_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.position.decrement"; + public static final String PIXELS_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.position.pixels"; + public static final String BLOCKS_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.position.blocks"; + public static final String DEGREES_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.position.degrees"; + public static final String MOVE_BY_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.position.moveBy"; + public static final String CENTERED_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.centered"; + public static final String CENTERED_DESCRIPTION_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.centered.description"; + public static final String CORNERED_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.cornered"; + public static final String CORNERED_DESCRIPTION_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.cornered.description"; + public static final String ALIGNED_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.aligned"; private static final DecimalFormat BLOCK_INCREMENT_FORMAT = Util.make(new DecimalFormat("#.####"), (decimalFormat) -> { decimalFormat.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ROOT)); }); @@ -57,18 +72,18 @@ public void removed() { } @Override - protected List buildWidgets(ArmorStand armorStand) { + protected List buildWidgets(ArmorStand armorStand) { // only move server-side to prevent rubber banding return Lists.newArrayList( - new RotationWidget(armorStand::getYRot, this.dataSyncHandler::sendRotation), + new RotationWidget(Component.translatable(ROTATION_TRANSLATION_KEY), armorStand::getYRot, this.dataSyncHandler::sendRotation), new PositionIncrementWidget(), - new PositionComponentWidget("x", armorStand::getX, x -> { + new PositionComponentWidget(POSITION_X_TRANSLATION_KEY, armorStand::getX, x -> { this.dataSyncHandler.sendPosition(x, armorStand.getY(), armorStand.getZ()); }), - new PositionComponentWidget("y", armorStand::getY, y -> { + new PositionComponentWidget(POSITION_Y_TRANSLATION_KEY, armorStand::getY, y -> { this.dataSyncHandler.sendPosition(armorStand.getX(), y, armorStand.getZ()); }), - new PositionComponentWidget("z", armorStand::getZ, z -> { + new PositionComponentWidget(POSITION_Z_TRANSLATION_KEY, armorStand::getZ, z -> { this.dataSyncHandler.sendPosition(armorStand.getX(), armorStand.getY(), z); }) ); @@ -80,41 +95,70 @@ public ArmorStandScreenType getScreenType() { } private static Component getPixelIncrementComponent(double increment) { - return Component.translatable("armorstatues.screen.position.pixels", getBlockPixelIncrement(increment)); + return Component.translatable(PIXELS_TRANSLATION_KEY, getBlockPixelIncrement(increment)); } private static Component getBlockIncrementComponent(double increment) { - return Component.translatable("armorstatues.screen.position.blocks", BLOCK_INCREMENT_FORMAT.format(increment)); + return Component.translatable(BLOCKS_TRANSLATION_KEY, BLOCK_INCREMENT_FORMAT.format(increment)); } private static int getBlockPixelIncrement(double increment) { return (int) Math.round(increment * 16.0); } - public static double fromWrappedDegrees(double value) { - return (Mth.wrapDegrees(value) + 180.0) / 360.0; - } + protected class RotationWidget extends AbstractArmorStandWidget { + protected final DoubleSupplier currentValue; + protected final Consumer newValue; + private final double snapInterval; + @Nullable + private Runnable reset; - public static float toWrappedDegrees(double value) { - return (float) Mth.wrapDegrees(value * 360.0 - 180.0); - } + public RotationWidget(Component title, DoubleSupplier currentValue, Consumer newValue) { + this(title, currentValue, newValue, ArmorStandPose.DEGREES_SNAP_INTERVAL); + } + + public RotationWidget(Component title, DoubleSupplier currentValue, Consumer newValue, double snapInterval) { + super(title); + this.currentValue = currentValue; + this.newValue = newValue; + this.snapInterval = snapInterval; + } + + protected double getCurrentValue() { + return fromWrappedDegrees(this.currentValue.getAsDouble()); + } + + protected void setNewValue(double newValue) { + this.newValue.accept(toWrappedDegrees(newValue)); + } + + protected Component getTooltipComponent(double mouseValue) { + return Component.translatable(DEGREES_TRANSLATION_KEY, ArmorStandPose.ROTATION_FORMAT.format(toWrappedDegrees(mouseValue))); + } + + protected static double fromWrappedDegrees(double value) { + return (Mth.wrapDegrees(value) + 180.0) / 360.0; + } + + protected static float toWrappedDegrees(double value) { + return (float) Mth.wrapDegrees(value * 360.0 - 180.0); + } - private class RotationWidget extends AbstractPositionScreenWidget { - private final Supplier currentRotation; - private final Consumer newRotation; + protected void applyClientValue(double newValue) { - public RotationWidget(Supplier currentRotation, Consumer newRotation) { - super(Component.translatable("armorstatues.screen.position.rotation")); - this.currentRotation = currentRotation; - this.newRotation = newRotation; + } + + @Override + public void reset() { + if (this.reset != null) this.reset.run(); } @Override public void init(int posX, int posY) { super.init(posX, posY); - NewTextureSliderButton sliderButton = ArmorStandPositionScreen.this.addRenderableWidget(new NewTextureSliderButton(posX + 76, posY + 1, 90, 20, 0, 184, ARMOR_STAND_WIDGETS_LOCATION, CommonComponents.EMPTY, fromWrappedDegrees(this.currentRotation.get()), (button, poseStack, mouseX, mouseY) -> { - double mouseValue = ArmorStandPose.snapValue((mouseX - button.x) / (double) button.getWidth(), ArmorStandPose.DEGREES_SNAP_INTERVAL); - ArmorStandPositionScreen.this.renderTooltip(poseStack, Component.translatable("armorstatues.screen.position.degrees", ArmorStandPose.ROTATION_FORMAT.format(toWrappedDegrees(mouseValue))), mouseX, mouseY); + var sliderButton = ArmorStandPositionScreen.this.addRenderableWidget(new NewTextureSliderButton(posX + 76, posY + 1, 90, 20, 0, 184, getArmorStandWidgetsLocation(), CommonComponents.EMPTY, this.getCurrentValue(), (button, poseStack, mouseX, mouseY) -> { + double mouseValue = ArmorStandPose.snapValue((mouseX - button.x) / (double) button.getWidth(), this.snapInterval); + ArmorStandPositionScreen.this.renderTooltip(poseStack, this.getTooltipComponent(mouseValue), mouseX, mouseY); }) { private boolean dirty; @@ -123,38 +167,49 @@ protected void updateMessage() { } + public void reset() { + this.value = RotationWidget.this.getCurrentValue(); + } + @Override protected void applyValue() { this.dirty = true; + RotationWidget.this.applyClientValue(this.value); } @Override public void onRelease(double mouseX, double mouseY) { super.onRelease(mouseX, mouseY); - // we use #onRelease instead of directly applying in #applyValue as the armor stand will otherwise glitch out visually since the server constantly sends outdated values - if (this.isDirty()) { - this.dirty = false; - RotationWidget.this.newRotation.accept(toWrappedDegrees(this.value)); - } + this.clearDirty(); } @Override public boolean isDirty() { return this.dirty; } + + @Override + public void clearDirty() { + // we use #onRelease instead of directly applying in #applyValue as the armor stand will otherwise glitch out visually since the server constantly sends outdated values + if (this.isDirty()) { + this.dirty = false; + RotationWidget.this.setNewValue(this.value); + } + } }); - sliderButton.snapInterval = ArmorStandPose.DEGREES_SNAP_INTERVAL; + sliderButton.snapInterval = this.snapInterval; + this.reset = sliderButton::reset; this.children.add(sliderButton); - this.children.add(ArmorStandPositionScreen.this.addRenderableWidget(new ImageButton(posX + 174, posY + 1, 20, 20, 236, 64, ARMOR_STAND_WIDGETS_LOCATION, button -> { + this.children.add(ArmorStandPositionScreen.this.addRenderableWidget(new ImageButton(posX + 174, posY + 1, 20, 20, 236, 64, getArmorStandWidgetsLocation(), button -> { ArmorStandPositionScreen.this.setActiveWidget(this); }))); } } - private class PositionIncrementWidget extends AbstractPositionScreenWidget { + private class PositionIncrementWidget extends AbstractArmorStandWidget { public PositionIncrementWidget() { - super(Component.translatable("armorstatues.screen.position.moveBy")); + super(Component.translatable(MOVE_BY_TRANSLATION_KEY)); } @Override @@ -162,7 +217,7 @@ public void init(int posX, int posY) { super.init(posX, posY); for (int i = 0; i < INCREMENTS.length; i++) { double increment = INCREMENTS[i]; - AbstractWidget widget = ArmorStandPositionScreen.this.addRenderableWidget(new NewTextureButton(posX + 76 + i * 24 + (i > 1 ? 1 : 0), posY + 1, 20, 20, 0, 184, ARMOR_STAND_WIDGETS_LOCATION, Component.literal(String.valueOf(getBlockPixelIncrement(increment))), button -> { + AbstractWidget widget = ArmorStandPositionScreen.this.addRenderableWidget(new NewTextureButton(posX + 76 + i * 24 + (i > 1 ? 1 : 0), posY + 1, 20, 20, 0, 184, getArmorStandWidgetsLocation(), Component.literal(String.valueOf(getBlockPixelIncrement(increment))), button -> { this.setActiveIncrement(button, increment); }, (Button button, PoseStack poseStack, int mouseX, int mouseY) -> { List lines = Lists.newArrayList(getPixelIncrementComponent(increment), getBlockIncrementComponent(increment)); @@ -173,7 +228,7 @@ public void init(int posX, int posY) { widget.active = false; } } - this.children.add(ArmorStandPositionScreen.this.addRenderableWidget(new ImageButton(posX + 174, posY + 1, 20, 20, 236, 64, ARMOR_STAND_WIDGETS_LOCATION, button -> { + this.children.add(ArmorStandPositionScreen.this.addRenderableWidget(new ImageButton(posX + 174, posY + 1, 20, 20, 236, 64, getArmorStandWidgetsLocation(), button -> { ArmorStandPositionScreen.this.setActiveWidget(this); }))); } @@ -186,20 +241,20 @@ private void setActiveIncrement(AbstractWidget source, double increment) { } @Override - public boolean alwaysVisible(@Nullable PositionScreenWidget activeWidget) { + public boolean alwaysVisible(@Nullable ArmorStandWidgetsScreen.ArmorStandWidget activeWidget) { return !(activeWidget instanceof RotationWidget); } } - private class PositionComponentWidget extends AbstractPositionScreenWidget { + private class PositionComponentWidget extends AbstractArmorStandWidget { private final DoubleSupplier currentValue; private final DoubleConsumer newValue; private EditBox editBox; private int ticks; - public PositionComponentWidget(String translationId, DoubleSupplier currentValue, DoubleConsumer newValue) { - super(Component.translatable("armorstatues.screen.position." + translationId)); + public PositionComponentWidget(String translationKey, DoubleSupplier currentValue, DoubleConsumer newValue) { + super(Component.translatable(translationKey)); this.currentValue = currentValue; this.newValue = newValue; } @@ -224,17 +279,17 @@ public void init(int posX, int posY) { this.editBox.setTextColorUneditable(14737632); this.editBox.setValue(BLOCK_INCREMENT_FORMAT.format(this.getPositionValue())); this.children.add(this.editBox); - this.children.add(ArmorStandPositionScreen.this.addRenderableWidget(new ImageButton(posX + 149, posY + 1, 20, 10, 196, 64, 20, ARMOR_STAND_WIDGETS_LOCATION, 256, 256, button -> { + this.children.add(ArmorStandPositionScreen.this.addRenderableWidget(new ImageButton(posX + 149, posY + 1, 20, 10, 196, 64, 20, getArmorStandWidgetsLocation(), 256, 256, button -> { this.setPositionValue(this.getPositionValue() + currentIncrement); }, (Button button, PoseStack poseStack, int mouseX, int mouseY) -> { - ArmorStandPositionScreen.this.renderTooltip(poseStack, Component.translatable("armorstatues.screen.position.increment", getPixelIncrementComponent(currentIncrement)), mouseX, mouseY); + ArmorStandPositionScreen.this.renderTooltip(poseStack, Component.translatable(INCREMENT_TRANSLATION_KEY, getPixelIncrementComponent(currentIncrement)), mouseX, mouseY); }, CommonComponents.EMPTY))); - this.children.add(ArmorStandPositionScreen.this.addRenderableWidget(new ImageButton(posX + 149, posY + 11, 20, 10, 216, 74, 20, ARMOR_STAND_WIDGETS_LOCATION, 256, 256, button -> { + this.children.add(ArmorStandPositionScreen.this.addRenderableWidget(new ImageButton(posX + 149, posY + 11, 20, 10, 216, 74, 20, getArmorStandWidgetsLocation(), 256, 256, button -> { this.setPositionValue(this.getPositionValue() - currentIncrement); }, (Button button, PoseStack poseStack, int mouseX, int mouseY) -> { - ArmorStandPositionScreen.this.renderTooltip(poseStack, Component.translatable("armorstatues.screen.position.decrement", getPixelIncrementComponent(currentIncrement)), mouseX, mouseY); + ArmorStandPositionScreen.this.renderTooltip(poseStack, Component.translatable(DECREMENT_TRANSLATION_KEY, getPixelIncrementComponent(currentIncrement)), mouseX, mouseY); }, CommonComponents.EMPTY))); - this.children.add(ArmorStandPositionScreen.this.addRenderableWidget(new ImageButton(posX + 174, posY + 1, 20, 20, 236, 64, ARMOR_STAND_WIDGETS_LOCATION, button -> { + this.children.add(ArmorStandPositionScreen.this.addRenderableWidget(new ImageButton(posX + 174, posY + 1, 20, 20, 236, 64, getArmorStandWidgetsLocation(), button -> { ArmorStandPositionScreen.this.setActiveWidget(this); }))); } @@ -259,7 +314,7 @@ public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTic } @Override - public boolean alwaysVisible(@Nullable PositionScreenWidget activeWidget) { + public boolean alwaysVisible(@Nullable ArmorStandWidgetsScreen.ArmorStandWidget activeWidget) { return activeWidget instanceof PositionIncrementWidget || super.alwaysVisible(activeWidget); } } diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandRotationsScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandRotationsScreen.java similarity index 69% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandRotationsScreen.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandRotationsScreen.java index ec01ee8..09de06e 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandRotationsScreen.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandRotationsScreen.java @@ -2,11 +2,11 @@ import com.google.common.collect.Maps; import com.mojang.blaze3d.vertex.PoseStack; +import fuzs.armorstatues.api.StatuesApi; import fuzs.armorstatues.api.client.gui.components.BoxedSliderButton; import fuzs.armorstatues.api.client.gui.components.LiveSliderButton; import fuzs.armorstatues.api.client.gui.components.NewTextureTickButton; import fuzs.armorstatues.api.client.gui.components.VerticalSliderButton; -import fuzs.armorstatues.api.client.init.ModClientRegistry; import fuzs.armorstatues.api.network.client.data.DataSyncHandler; import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; import fuzs.armorstatues.api.world.inventory.data.ArmorStandPose; @@ -16,6 +16,7 @@ import net.minecraft.client.gui.components.ImageButton; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.sounds.SoundManager; +import net.minecraft.locale.Language; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.decoration.ArmorStand; @@ -23,15 +24,22 @@ import org.apache.commons.compress.utils.Lists; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.function.Predicate; import java.util.stream.Collectors; public class ArmorStandRotationsScreen extends AbstractArmorStandScreen { + public static final String TIP_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.tip"; + public static final String UNLIMITED_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.unlimited"; + public static final String LIMITED_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.limited"; + public static final String RESET_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.reset"; + public static final String RANDOMIZE_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.randomize"; + public static final String PASTE_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.paste"; + public static final String COPY_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.copy"; + public static final String MIRROR_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.mirror"; private static final Map> POSE_PART_MUTATOR_FILTERS = Maps.newHashMap(); - private static final Random RANDOM = new Random(); private static boolean clampRotations = true; @Nullable @@ -52,53 +60,59 @@ public ArmorStandRotationsScreen(ArmorStandHolder holder, Inventory inventory, C protected void init() { super.init(); this.minecraft.keyboardHandler.setSendRepeatsToGui(true); - this.lockButtons[0] = this.addRenderableWidget(new ImageButton(this.leftPos + 83, this.topPos + 10, 20, 20, 156, 124, 20, ARMOR_STAND_WIDGETS_LOCATION, 256, 256, button -> { + this.lockButtons[0] = this.addRenderableWidget(new ImageButton(this.leftPos + 83, this.topPos + 10, 20, 20, 156, 124, 20, getArmorStandWidgetsLocation(), 256, 256, button -> { clampRotations = true; this.toggleLockButtons(); this.refreshLiveButtons(); }, (button, poseStack, mouseX, mouseY) -> { - this.renderTooltip(poseStack, Component.translatable("armorstatues.screen.rotations.unlimited"), mouseX, mouseY); - }, Component.translatable("armorstatues.screen.rotations.unlimited"))); - this.lockButtons[1] = this.addRenderableWidget(new ImageButton(this.leftPos + 83, this.topPos + 10, 20, 20, 136, 124, 20, ARMOR_STAND_WIDGETS_LOCATION, 256, 256, button -> { + this.renderTooltip(poseStack, Component.translatable(UNLIMITED_TRANSLATION_KEY), mouseX, mouseY); + }, CommonComponents.EMPTY)); + this.lockButtons[1] = this.addRenderableWidget(new ImageButton(this.leftPos + 83, this.topPos + 10, 20, 20, 136, 124, 20, getArmorStandWidgetsLocation(), 256, 256, button -> { clampRotations = false; this.toggleLockButtons(); this.refreshLiveButtons(); }, (button, poseStack, mouseX, mouseY) -> { - this.renderTooltip(poseStack, Component.translatable("armorstatues.screen.rotations.limited"), mouseX, mouseY); - }, Component.translatable("armorstatues.screen.rotations.limited"))); + this.renderTooltip(poseStack, Component.translatable(LIMITED_TRANSLATION_KEY), mouseX, mouseY); + }, CommonComponents.EMPTY)); Component tipComponent = this.getTipComponent(); - this.addRenderableWidget(new ImageButton(this.leftPos + 107, this.topPos + 10, 20, 20, 136, 64, 20, ARMOR_STAND_WIDGETS_LOCATION, 256, 256, button -> {}, (button, poseStack, mouseX, mouseY) -> { - this.renderTooltip(poseStack, tipComponent, mouseX, mouseY); - }, CommonComponents.EMPTY) { + if (tipComponent != null) { + this.addRenderableWidget(new ImageButton(this.leftPos + 107, this.topPos + 10, 20, 20, 136, 64, 20, getArmorStandWidgetsLocation(), 256, 256, button -> {}, (button, poseStack, mouseX, mouseY) -> { + this.renderTooltip(poseStack, this.font.split(tipComponent, 175), mouseX, mouseY); + }, CommonComponents.EMPTY) { - @Override - public void playDownSound(SoundManager handler) { + @Override + public void playDownSound(SoundManager handler) { - } - }); - this.addRenderableWidget(new NewTextureTickButton(this.leftPos + 83, this.topPos + 34, 20, 20, 240, 124, ARMOR_STAND_WIDGETS_LOCATION, button -> { - this.setCurrentPose(ArmorStandPose.empty()); + } + }); + } + this.addRenderableWidget(new NewTextureTickButton(this.leftPos + 107, this.topPos + 34, 20, 20, 240, 124, getArmorStandWidgetsLocation(), button -> { + this.setCurrentPose(ArmorStandPose.EMPTY); }, (button, poseStack, mouseX, mouseY) -> { - this.renderTooltip(poseStack, Component.translatable("armorstatues.screen.rotations.reset"), mouseX, mouseY); + this.renderTooltip(poseStack, Component.translatable(RESET_TRANSLATION_KEY), mouseX, mouseY); })); - this.addRenderableWidget(new NewTextureTickButton(this.leftPos + 107, this.topPos + 34, 20, 20, 192, 124, ARMOR_STAND_WIDGETS_LOCATION, button -> { + this.addRenderableWidget(new NewTextureTickButton(this.leftPos + 83, this.topPos + 34, 20, 20, 192, 124, getArmorStandWidgetsLocation(), button -> { this.setCurrentPose(this.holder.getDataProvider().getRandomPose(true)); }, (button, poseStack, mouseX, mouseY) -> { - this.renderTooltip(poseStack, Component.translatable("armorstatues.screen.rotations.randomize"), mouseX, mouseY); + this.renderTooltip(poseStack, Component.translatable(RANDOMIZE_TRANSLATION_KEY), mouseX, mouseY); })); - AbstractWidget pasteButton = this.addRenderableWidget(new NewTextureTickButton(this.leftPos + 83, this.topPos + 158, 44, 20, 224, 124, ARMOR_STAND_WIDGETS_LOCATION, button -> { + AbstractWidget pasteButton = this.addRenderableWidget(new NewTextureTickButton(this.leftPos + 107, this.topPos + 158, 20, 20, 224, 124, getArmorStandWidgetsLocation(), button -> { if (clipboard != null) this.setCurrentPose(clipboard); }, (button, poseStack, mouseX, mouseY) -> { - this.renderTooltip(poseStack, Component.translatable("armorstatues.screen.rotations.paste"), mouseX, mouseY); + this.renderTooltip(poseStack, Component.translatable(PASTE_TRANSLATION_KEY), mouseX, mouseY); })); pasteButton.active = clipboard != null; - this.addRenderableWidget(new NewTextureTickButton(this.leftPos + 83, this.topPos + 134, 44, 20, 208, 124, ARMOR_STAND_WIDGETS_LOCATION, button -> { + this.addRenderableWidget(new NewTextureTickButton(this.leftPos + 83, this.topPos + 158, 20, 20, 208, 124, getArmorStandWidgetsLocation(), button -> { clipboard = this.currentPose; pasteButton.active = true; }, (button, poseStack, mouseX, mouseY) -> { - this.renderTooltip(poseStack, Component.translatable("armorstatues.screen.rotations.copy"), mouseX, mouseY); + this.renderTooltip(poseStack, Component.translatable(COPY_TRANSLATION_KEY), mouseX, mouseY); + })); + this.addRenderableWidget(new NewTextureTickButton(this.leftPos + 83, this.topPos + 134, 44, 20, 179, 0, getArmorStandWidgetsLocation(), button -> { + this.setCurrentPose(this.currentPose.mirror()); + }, (button, poseStack, mouseX, mouseY) -> { + this.renderTooltip(poseStack, Component.translatable(MIRROR_TRANSLATION_KEY), mouseX, mouseY); })); - ArmorStand armorStand = this.holder.getArmorStand(); PosePartMutator[] values = this.holder.getDataProvider().getPosePartMutators(); ArmorStandPose.checkMutatorsSize(values); for (int i = 0; i < values.length; i++) { @@ -106,7 +120,7 @@ public void playDownSound(SoundManager handler) { boolean isLeft = i % 2 == 0; this.addRenderableWidget(new BoxedSliderButton(this.leftPos + 23 + i % 2 * 110, this.topPos + 7 + i / 2 * 60, () -> mutator.getNormalizedRotationsAtAxis(1, this.currentPose, clampRotations), () -> mutator.getNormalizedRotationsAtAxis(0, this.currentPose, clampRotations), (button, poseStack, mouseX, mouseY) -> { List lines = Lists.newArrayList(); - lines.add(mutator.getComponent()); + lines.add(Component.translatable(mutator.getTranslationKey())); lines.add(mutator.getAxisComponent(this.currentPose, 0)); lines.add(mutator.getAxisComponent(this.currentPose, 1)); int offset = isLeft ? 24 + lines.stream().mapToInt(minecraft.font::width).max().orElse(0) : 0; @@ -124,20 +138,25 @@ protected void applyValue() { @Override public void onRelease(double mouseX, double mouseY) { super.onRelease(mouseX, mouseY); - if (this.isDirty()) { - this.dirty = false; - ArmorStandRotationsScreen.this.dataSyncHandler.sendPose(ArmorStandRotationsScreen.this.currentPose); - } + this.clearDirty(); } @Override public boolean isDirty() { return this.dirty; } - }).active = isPosePartMutatorActive(mutator, armorStand); + + @Override + public void clearDirty() { + if (this.isDirty()) { + this.dirty = false; + ArmorStandRotationsScreen.this.setCurrentPose(ArmorStandRotationsScreen.this.currentPose); + } + } + }).active = isPosePartMutatorActive(mutator, this.holder.getArmorStand()); this.addRenderableWidget(new VerticalSliderButton(this.leftPos + 6 + i % 2 * 183, this.topPos + 7 + i / 2 * 60, () -> mutator.getNormalizedRotationsAtAxis(2, this.currentPose, clampRotations), (button, poseStack, mouseX, mouseY) -> { List lines = Lists.newArrayList(); - lines.add(mutator.getComponent()); + lines.add(Component.translatable(mutator.getTranslationKey())); lines.add(mutator.getAxisComponent(this.currentPose, 2)); int offset = isLeft ? 24 + lines.stream().mapToInt(minecraft.font::width).max().orElse(0) : 0; this.renderTooltip(poseStack, lines.stream().map(Component::getVisualOrderText).collect(Collectors.toList()), mouseX - offset, mouseY); @@ -153,28 +172,34 @@ protected void applyValue() { @Override public void onRelease(double mouseX, double mouseY) { super.onRelease(mouseX, mouseY); - if (this.isDirty()) { - this.dirty = false; - ArmorStandRotationsScreen.this.dataSyncHandler.sendPose(ArmorStandRotationsScreen.this.currentPose); - } + this.clearDirty(); } @Override public boolean isDirty() { return this.dirty; } - }).active = isPosePartMutatorActive(mutator, armorStand); + + @Override + public void clearDirty() { + if (this.isDirty()) { + this.dirty = false; + ArmorStandRotationsScreen.this.setCurrentPose(ArmorStandRotationsScreen.this.currentPose); + } + } + }).active = isPosePartMutatorActive(mutator, this.holder.getArmorStand()); this.toggleLockButtons(); } } + @Nullable private Component getTipComponent() { - Component[] components = { - Component.translatable("armorstatues.screen.rotations.tip1"), - Component.translatable("armorstatues.screen.rotations.tip2"), - Component.translatable("armorstatues.screen.rotations.tip3", ModClientRegistry.CYCLE_TABS_KEY_MAPPING.getTranslatedKeyMessage(), ModClientRegistry.CYCLE_TABS_KEY_MAPPING.getTranslatedKeyMessage()) - }; - return components[RANDOM.nextInt(components.length)]; + List components = Lists.newArrayList(); + for (int i = 1; Language.getInstance().has(TIP_TRANSLATION_KEY + i); i++) { + components.add(Component.translatable(TIP_TRANSLATION_KEY + i)); + } + Collections.shuffle(components); + return components.stream().findAny().orElse(null); } private void toggleLockButtons() { @@ -207,9 +232,10 @@ public ArmorStandScreenType getScreenType() { return ArmorStandScreenType.ROTATIONS; } - private void setCurrentPose(ArmorStandPose currentPose) { - this.currentPose = currentPose; - this.dataSyncHandler.sendPose(this.currentPose); + private void setCurrentPose(ArmorStandPose pose) { + this.dataSyncHandler.sendPose(pose); + ArmorStandPose lastSyncedPose = this.dataSyncHandler.getLastSyncedPose(); + this.currentPose = lastSyncedPose != null ? lastSyncedPose : pose; this.refreshLiveButtons(); } diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreen.java similarity index 64% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreen.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreen.java index db7c29d..3f0fc9c 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreen.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreen.java @@ -1,5 +1,6 @@ package fuzs.armorstatues.api.client.gui.screens.armorstand; +import fuzs.armorstatues.api.network.client.data.DataSyncHandler; import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; import fuzs.armorstatues.api.world.inventory.ArmorStandMenu; import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; @@ -17,4 +18,10 @@ public interface ArmorStandScreen { void setMouseX(int mouseX); void setMouseY(int mouseY); + + DataSyncHandler getDataSyncHandler(); + + default void renderArmorStandInInventory(int posX, int posY, int scale, float mouseX, float mouseY) { + AbstractArmorStandScreen.armorStandRenderer.renderEntityInInventory(posX, posY, scale, mouseX, mouseY, this.getHolder().getArmorStand()); + } } diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreenFactory.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreenFactory.java similarity index 80% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreenFactory.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreenFactory.java index bfc0647..24c60c7 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreenFactory.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandScreenFactory.java @@ -1,6 +1,7 @@ package fuzs.armorstatues.api.client.gui.screens.armorstand; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import fuzs.armorstatues.api.network.client.data.DataSyncHandler; import fuzs.armorstatues.api.network.client.data.NetworkDataSyncHandler; import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; @@ -12,6 +13,8 @@ import net.minecraft.world.entity.player.Inventory; import java.util.Map; +import java.util.Optional; +import java.util.Set; @FunctionalInterface public interface ArmorStandScreenFactory & ArmorStandScreen> { @@ -20,7 +23,7 @@ public interface ArmorStandScreenFactory & ArmorStandScreen> T createScreenType(ArmorStandScreenType screenType, ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { - dataSyncHandler.setLastType(screenType); + AbstractArmorStandScreen.lastScreenType = screenType; ArmorStandScreenFactory factory = FACTORIES.get(screenType); if (factory == null) throw new IllegalStateException("No screen factory registered for armor stand screen type %s".formatted(screenType)); T screen = (T) factory.create(holder, inventory, component, dataSyncHandler); @@ -29,11 +32,13 @@ static & ArmorStandScreen> T crea } static & ArmorStandScreen> T createLastScreenType(ArmorStandMenu menu, Inventory inventory, Component component) { - return createLastScreenType(menu, inventory, component, new NetworkDataSyncHandler(menu.getArmorStand())); + return createLastScreenType(menu, inventory, component, new NetworkDataSyncHandler(menu)); } static & ArmorStandScreen> T createLastScreenType(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { - ArmorStandScreenType screenType = dataSyncHandler.getLastType().orElse(dataSyncHandler.getDataProvider().getDefaultScreenType()); + Set screenTypes = Sets.newHashSet(dataSyncHandler.getScreenTypes()); + Optional lastScreenType = Optional.ofNullable(AbstractArmorStandScreen.lastScreenType).filter(screenTypes::contains).filter(dataSyncHandler::supportsScreenType); + ArmorStandScreenType screenType = lastScreenType.orElse(dataSyncHandler.getArmorStandHolder().getDataProvider().getDefaultScreenType()); return createScreenType(screenType, holder, inventory, component, dataSyncHandler); } diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandStyleScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandStyleScreen.java similarity index 64% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandStyleScreen.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandStyleScreen.java index 4338733..9faabcc 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandStyleScreen.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandStyleScreen.java @@ -1,6 +1,7 @@ package fuzs.armorstatues.api.client.gui.screens.armorstand; import com.mojang.blaze3d.vertex.PoseStack; +import fuzs.armorstatues.api.StatuesApi; import fuzs.armorstatues.api.client.gui.components.TickBoxButton; import fuzs.armorstatues.api.network.client.data.DataSyncHandler; import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; @@ -16,6 +17,7 @@ import java.util.stream.Stream; public class ArmorStandStyleScreen extends ArmorStandTickBoxScreen { + public static final String TEXT_BOX_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.style.name"; public ArmorStandStyleScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { super(holder, inventory, component, dataSyncHandler); @@ -30,13 +32,33 @@ protected ArmorStandStyleOption[] getAllTickBoxValues() { @Override protected AbstractWidget makeTickBoxWidget(ArmorStand armorStand, int buttonStartY, int index, ArmorStandStyleOption option) { - return new TickBoxButton(this.leftPos + 96, this.topPos + buttonStartY + index * 22, 6, 76, option.getComponent(), option.getOption(armorStand), (Button button) -> { - this.dataSyncHandler.sendStyleOption(option, ((TickBoxButton) button).isSelected()); + return new TickBoxButton(this.leftPos + 96, this.topPos + buttonStartY + index * 22, 6, 76, Component.translatable(option.getTranslationKey()), () -> option.getOption(armorStand), (Button button) -> { + this.dataSyncHandler.sendStyleOption(option, !option.getOption(armorStand)); }, (Button button, PoseStack poseStack, int mouseX, int mouseY) -> { - this.renderTooltip(poseStack, option.getDescriptionComponent(), mouseX, mouseY); + this.renderTooltip(poseStack, this.minecraft.font.split(Component.translatable(option.getDescriptionKey()), 175), mouseX, mouseY); }); } + @Override + protected void syncNameChange(String input) { + this.dataSyncHandler.sendName(input); + } + + @Override + protected int getNameMaxLength() { + return 50; + } + + @Override + protected String getNameDefaultValue() { + return this.holder.getArmorStand().getName().getString(); + } + + @Override + protected Component getNameComponent() { + return Component.translatable(TEXT_BOX_TRANSLATION_KEY); + } + @Override public ArmorStandScreenType getScreenType() { return ArmorStandScreenType.STYLE; diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandTickBoxScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandTickBoxScreen.java similarity index 81% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandTickBoxScreen.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandTickBoxScreen.java index a030369..234343b 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandTickBoxScreen.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandTickBoxScreen.java @@ -35,18 +35,15 @@ public void tick() { private void testNameInputChanged(boolean testEquality) { if (this.inputUpdateTicks == 0 || !testEquality && this.inputUpdateTicks != -1) { - this.onNameChanged(this.name.getValue()); + String name = this.name.getValue().trim(); + if (!name.equals(this.getNameDefaultValue())) { + this.syncNameChange(name); + } this.inputUpdateTicks = -1; } } - private void onNameChanged(String input) { - input = input.trim(); - ArmorStand armorStand = this.holder.getArmorStand(); - if (!input.equals(armorStand.getName().getString())) { - this.dataSyncHandler.sendName(input); - } - } + protected abstract void syncNameChange(String input); @Override protected void init() { @@ -56,8 +53,8 @@ protected void init() { this.name = new EditBox(this.font, this.leftPos + 16, this.topPos + 32, 66, 9, EntityType.ARMOR_STAND.getDescription()); this.name.setTextColor(16777215); this.name.setBordered(false); - this.name.setMaxLength(50); - this.name.setValue(armorStand.getName().getString()); + this.name.setMaxLength(this.getNameMaxLength()); + this.name.setValue(this.getNameDefaultValue()); this.name.setResponder(input -> this.inputUpdateTicks = 20); this.addWidget(this.name); this.inputUpdateTicks = -1; @@ -68,10 +65,24 @@ protected void init() { } } + protected abstract int getNameMaxLength(); + + protected abstract String getNameDefaultValue(); + protected abstract T[] getAllTickBoxValues(); protected abstract AbstractWidget makeTickBoxWidget(ArmorStand armorStand, int buttonStartY, int index, T option); + @Override + public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTick) { + super.render(poseStack, mouseX, mouseY, partialTick); + if (this.name.isMouseOver(mouseX, mouseY)) { + this.renderTooltip(poseStack, this.font.split(this.getNameComponent(), 175), mouseX, mouseY); + } + } + + protected abstract Component getNameComponent(); + @Override public void resize(Minecraft pMinecraft, int pWidth, int pHeight) { this.testNameInputChanged(false); @@ -107,7 +118,7 @@ protected void renderBg(PoseStack poseStack, float partialTick, int mouseX, int super.renderBg(poseStack, partialTick, mouseX, mouseY); RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - RenderSystem.setShaderTexture(0, ARMOR_STAND_WIDGETS_LOCATION); + RenderSystem.setShaderTexture(0, getArmorStandWidgetsLocation()); // name edit box background this.blit(poseStack, this.leftPos + 14, this.topPos + 30, 0, 108, 76, 12); this.name.render(poseStack, mouseX, mouseY, partialTick); diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandWidgetsScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandWidgetsScreen.java similarity index 70% rename from Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandWidgetsScreen.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandWidgetsScreen.java index b0a6e99..6ec56ab 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandWidgetsScreen.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandWidgetsScreen.java @@ -4,9 +4,11 @@ import com.google.common.collect.Lists; import com.mojang.blaze3d.vertex.PoseStack; import fuzs.armorstatues.api.client.gui.components.NewTextureButton; +import fuzs.armorstatues.api.client.gui.components.TickingButton; import fuzs.armorstatues.api.network.client.data.DataSyncHandler; import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.entity.player.Inventory; @@ -16,21 +18,21 @@ import java.util.List; public abstract class ArmorStandWidgetsScreen extends AbstractArmorStandScreen { - private final List widgets; + protected final List widgets; @Nullable - private PositionScreenWidget activeWidget; + private ArmorStandWidgetsScreen.ArmorStandWidget activeWidget; public ArmorStandWidgetsScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { super(holder, inventory, component, dataSyncHandler); this.widgets = ImmutableList.copyOf(this.buildWidgets(holder.getArmorStand())); } - protected abstract List buildWidgets(ArmorStand armorStand); + protected abstract List buildWidgets(ArmorStand armorStand); - private Collection getActivePositionComponentWidgets() { + private Collection getActivePositionComponentWidgets() { if (this.activeWidget != null) { - List activeWidgets = Lists.newArrayList(this.activeWidget); - for (PositionScreenWidget widget : this.widgets) { + List activeWidgets = Lists.newArrayList(this.activeWidget); + for (ArmorStandWidget widget : this.widgets) { if (widget.alwaysVisible(this.activeWidget)) activeWidgets.add(widget); } return activeWidgets; @@ -38,7 +40,7 @@ private Collection getActivePositionComponentWidgets() { return this.widgets; } - protected void setActiveWidget(PositionScreenWidget widget) { + protected void setActiveWidget(ArmorStandWidget widget) { if (this.activeWidget == widget) { this.toggleMenuRendering(false); this.activeWidget = null; @@ -61,7 +63,7 @@ protected boolean disableMenuRendering() { @Override protected void toggleMenuRendering(boolean disableMenuRendering) { super.toggleMenuRendering(disableMenuRendering); - for (PositionScreenWidget widget : this.widgets) { + for (ArmorStandWidget widget : this.widgets) { widget.setVisible(!disableMenuRendering || widget.alwaysVisible(this.activeWidget)); } } @@ -69,7 +71,7 @@ protected void toggleMenuRendering(boolean disableMenuRendering) { @Override public void tick() { super.tick(); - this.getActivePositionComponentWidgets().forEach(PositionScreenWidget::tick); + this.getActivePositionComponentWidgets().forEach(ArmorStandWidget::tick); } @Override @@ -88,36 +90,55 @@ protected int getWidgetRenderOffset() { @Override protected void renderBg(PoseStack poseStack, float partialTick, int mouseX, int mouseY) { super.renderBg(poseStack, partialTick, mouseX, mouseY); - for (PositionScreenWidget widget : this.getActivePositionComponentWidgets()) { + for (ArmorStandWidget widget : this.getActivePositionComponentWidgets()) { widget.render(poseStack, mouseX, mouseY, partialTick); } } - protected interface PositionScreenWidget { + protected interface ArmorStandWidget { void tick(); + void reset(); + void init(int posX, int posY); void setVisible(boolean visible); void render(PoseStack poseStack, int mouseX, int mouseY, float partialTick); - boolean alwaysVisible(@Nullable PositionScreenWidget activeWidget); + boolean alwaysVisible(@Nullable ArmorStandWidgetsScreen.ArmorStandWidget activeWidget); } - protected abstract class AbstractPositionScreenWidget implements PositionScreenWidget { + protected abstract class AbstractArmorStandWidget implements ArmorStandWidget { protected final Component title; protected int posX; protected int posY; protected List children; - protected AbstractPositionScreenWidget(Component title) { + protected AbstractArmorStandWidget() { + this(CommonComponents.EMPTY); + } + + protected AbstractArmorStandWidget(Component title) { this.title = title; } @Override public void tick() { + if (this.shouldTick()) { + for (AbstractWidget widget : this.children) { + if (widget instanceof TickingButton tickButton) tickButton.tick(); + } + } + } + + protected boolean shouldTick() { + return false; + } + + @Override + public void reset() { } @@ -138,14 +159,14 @@ public final void setVisible(boolean visible) { @Override public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTick) { if (ArmorStandWidgetsScreen.this.disableMenuRendering()) { - NewTextureButton.drawCenteredString(poseStack, ArmorStandWidgetsScreen.this.font, this.title, this.posX + 36, this.posY + 6, -1, true); + NewTextureButton.drawCenteredString(poseStack, ArmorStandWidgetsScreen.this.font, this.title, this.posX + 36, this.posY + 6, 0xFFFFFFFF, true); } else { - NewTextureButton.drawCenteredString(poseStack, ArmorStandWidgetsScreen.this.font, this.title, this.posX + 36, this.posY + 6, 4210752, false); + NewTextureButton.drawCenteredString(poseStack, ArmorStandWidgetsScreen.this.font, this.title, this.posX + 36, this.posY + 6, 0x404040, false); } } @Override - public boolean alwaysVisible(@Nullable PositionScreenWidget activeWidget) { + public boolean alwaysVisible(@Nullable ArmorStandWidgetsScreen.ArmorStandWidget activeWidget) { return activeWidget == this; } } diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/helper/ArmorStandInteractHelper.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/helper/ArmorStandInteractHelper.java new file mode 100644 index 0000000..6e056d1 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/helper/ArmorStandInteractHelper.java @@ -0,0 +1,41 @@ +package fuzs.armorstatues.api.helper; + +import fuzs.armorstatues.api.world.entity.decoration.ArmorStandDataProvider; +import fuzs.armorstatues.api.world.inventory.ArmorStandMenu; +import fuzs.puzzleslib.core.CommonAbstractions; +import fuzs.armorstatues.mixin.accessor.ArmorStandAccessor; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ArmorStandInteractHelper { + + public static Optional tryOpenArmorStatueMenu(Player player, Level level, InteractionHand interactionHand, ArmorStand entity, MenuType menuType, @Nullable ArmorStandDataProvider dataProvider) { + ItemStack itemInHand = player.getItemInHand(interactionHand); + if (player.isShiftKeyDown() && itemInHand.isEmpty() && (!entity.isInvulnerable() || player.getAbilities().instabuild)) { + openArmorStatueMenu(player, entity, menuType, dataProvider); + return Optional.of(InteractionResult.sidedSuccess(level.isClientSide)); + } + return Optional.empty(); + } + + public static void openArmorStatueMenu(Player player, ArmorStand entity, MenuType menuType, @Nullable ArmorStandDataProvider dataProvider) { + if (!(player instanceof ServerPlayer serverPlayer)) return; + CommonAbstractions.INSTANCE.openMenu(serverPlayer, new SimpleMenuProvider((containerId, inventory, player1) -> { + return ArmorStandMenu.create(menuType, containerId, inventory, entity, dataProvider); + }, entity.getDisplayName()), (serverPlayer1, friendlyByteBuf) -> { + friendlyByteBuf.writeInt(entity.getId()); + friendlyByteBuf.writeBoolean(entity.isInvulnerable()); + friendlyByteBuf.writeInt(((ArmorStandAccessor) entity).getDisabledSlots()); + }); + } +} diff --git a/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandNameMessage.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandNameMessage.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandNameMessage.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandNameMessage.java diff --git a/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandPoseMessage.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandPoseMessage.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandPoseMessage.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandPoseMessage.java diff --git a/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandPositionMessage.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandPositionMessage.java similarity index 92% rename from Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandPositionMessage.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandPositionMessage.java index 2f8d679..7e34fdb 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandPositionMessage.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandPositionMessage.java @@ -1,6 +1,6 @@ package fuzs.armorstatues.api.network.client; -import fuzs.armorstatues.api.ArmorStatuesApi; +import fuzs.armorstatues.api.StatuesApi; import fuzs.armorstatues.api.world.inventory.ArmorStandMenu; import fuzs.puzzleslib.network.Message; import net.minecraft.network.FriendlyByteBuf; @@ -44,7 +44,7 @@ public MessageHandler makeHandler() { public void handle(C2SArmorStandPositionMessage message, Player player, Object gameInstance) { if (player.containerMenu instanceof ArmorStandMenu menu && menu.stillValid(player)) { if (!tryMoveArmorStandTo(menu.getArmorStand(), message.posX, message.posY, message.posZ)) { - ArmorStatuesApi.LOGGER.warn("Player {} attempted to move armor stand further than allowed", player); + StatuesApi.LOGGER.warn("Player {} attempted to move armor stand further than allowed", player); } } } diff --git a/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandRotationMessage.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandRotationMessage.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandRotationMessage.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandRotationMessage.java diff --git a/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandStyleMessage.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandStyleMessage.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandStyleMessage.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/C2SArmorStandStyleMessage.java diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/data/DataSyncHandler.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/data/DataSyncHandler.java new file mode 100644 index 0000000..f8cf925 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/data/DataSyncHandler.java @@ -0,0 +1,119 @@ +package fuzs.armorstatues.api.network.client.data; + +import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; +import fuzs.armorstatues.api.world.inventory.data.*; +import net.minecraft.SharedConstants; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; + +import java.util.stream.Stream; + +public interface DataSyncHandler { + + ArmorStandHolder getArmorStandHolder(); + + default ArmorStand getArmorStand() { + return this.getArmorStandHolder().getArmorStand(); + } + + void sendName(String name); + + void sendPose(ArmorStandPose pose); + + default void sendPose(ArmorStandPose pose, boolean finalize) { + this.sendPose(pose); + } + + @Nullable + default ArmorStandPose getLastSyncedPose() { + return null; + } + + void sendPosition(double posX, double posY, double posZ); + + default void sendPosition(double posX, double posY, double posZ, boolean finalize) { + this.sendPosition(posX, posY, posZ); + } + + void sendRotation(float rotation); + + default void sendRotation(float rotation, boolean finalize) { + this.sendRotation(rotation); + } + + void sendStyleOption(ArmorStandStyleOption styleOption, boolean value); + + default void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + this.sendStyleOption(styleOption, value); + } + + default void sendAlignment(ArmorStandAlignment alignment) { + if (!this.getArmorStand().isInvisible()) { + this.sendStyleOption(ArmorStandStyleOptions.INVISIBLE, true, false); + } + if (!this.getArmorStand().isNoGravity()) { + this.sendStyleOption(ArmorStandStyleOptions.NO_GRAVITY, true, false); + } + this.sendPose(alignment.getPose(), false); + Vec3 alignmentOffset = alignment.getAlignmentOffset(this.getArmorStand().isSmall()); + Vec3 newPosition = getLocalPosition(this.getArmorStand(), alignmentOffset); + this.sendPosition(newPosition.x(), newPosition.y(), newPosition.z(), false); + this.finalizeCurrentOperation(); + } + + /** + * Copied from {@link net.minecraft.commands.arguments.coordinates.LocalCoordinates#getPosition(CommandSourceStack)}. + */ + private static Vec3 getLocalPosition(Entity entity, Vec3 offset) { + Vec2 vec2 = entity.getRotationVector(); + Vec3 vec3 = entity.position(); + float f = Mth.cos((vec2.y + 90.0F) * 0.017453292F); + float g = Mth.sin((vec2.y + 90.0F) * 0.017453292F); + float h = Mth.cos(-vec2.x * 0.017453292F); + float i = Mth.sin(-vec2.x * 0.017453292F); + float j = Mth.cos((-vec2.x + 90.0F) * 0.017453292F); + float k = Mth.sin((-vec2.x + 90.0F) * 0.017453292F); + Vec3 vec32 = new Vec3(f * h, i, g * h); + Vec3 vec33 = new Vec3(f * j, k, g * j); + Vec3 vec34 = vec32.cross(vec33).scale(-1.0); + double d = vec32.x * offset.z() + vec33.x * offset.y() + vec34.x * offset.x(); + double e = vec32.y * offset.z() + vec33.y * offset.y() + vec34.y * offset.x(); + double l = vec32.z * offset.z() + vec33.z * offset.y() + vec34.z * offset.x(); + return new Vec3(vec3.x + d, vec3.y + e, vec3.z + l); + } + + default ArmorStandScreenType[] getScreenTypes() { + return Stream.of(this.getArmorStandHolder().getDataProvider().getScreenTypes()).filter(this::supportsScreenType).toArray(ArmorStandScreenType[]::new); + } + + default boolean supportsScreenType(ArmorStandScreenType screenType) { + return true; + } + + default void tick() { + + } + + default boolean shouldContinueTicking() { + return false; + } + + default void finalizeCurrentOperation() { + + } + + static void setCustomArmorStandName(ArmorStand armorStand, String name) { + name = SharedConstants.filterText(name); + if (name.length() <= 50) { + boolean remove = name.isBlank() || name.equals(EntityType.ARMOR_STAND.getDescription().getString()); + armorStand.setCustomName(remove ? null : Component.literal(name)); + } + } +} diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/data/NetworkDataSyncHandler.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/data/NetworkDataSyncHandler.java new file mode 100644 index 0000000..2c8c9d0 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/network/client/data/NetworkDataSyncHandler.java @@ -0,0 +1,51 @@ +package fuzs.armorstatues.api.network.client.data; + +import fuzs.armorstatues.api.StatuesApi; +import fuzs.armorstatues.api.network.client.*; +import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandPose; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandStyleOption; +import net.minecraft.nbt.CompoundTag; + +public class NetworkDataSyncHandler implements DataSyncHandler { + private final ArmorStandHolder holder; + + public NetworkDataSyncHandler(ArmorStandHolder holder) { + this.holder = holder; + } + + @Override + public ArmorStandHolder getArmorStandHolder() { + return this.holder; + } + + @Override + public void sendName(String name) { + DataSyncHandler.setCustomArmorStandName(this.getArmorStand(), name); + StatuesApi.NETWORK.sendToServer(new C2SArmorStandNameMessage(name)); + } + + @Override + public void sendPose(ArmorStandPose pose) { + pose.applyToEntity(this.getArmorStand()); + CompoundTag tag = new CompoundTag(); + pose.serializeAllPoses(tag); + StatuesApi.NETWORK.sendToServer(new C2SArmorStandPoseMessage(tag)); + } + + @Override + public void sendPosition(double posX, double posY, double posZ) { + StatuesApi.NETWORK.sendToServer(new C2SArmorStandPositionMessage(posX, posY, posZ)); + } + + @Override + public void sendRotation(float rotation) { + StatuesApi.NETWORK.sendToServer(new C2SArmorStandRotationMessage(rotation)); + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value) { + styleOption.setOption(this.getArmorStand(), value); + StatuesApi.NETWORK.sendToServer(new C2SArmorStandStyleMessage(styleOption, value)); + } +} diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/proxy/ClientProxy.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/proxy/ClientProxy.java new file mode 100644 index 0000000..ff48505 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/proxy/ClientProxy.java @@ -0,0 +1,18 @@ +package fuzs.armorstatues.api.proxy; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Items; + +public class ClientProxy extends ServerProxy { + public static final String OPEN_SCREEN_TRANSLATION_KEY = Items.ARMOR_STAND.getDescriptionId() + ".description"; + + @Override + public Component getStatueHoverText() { + Minecraft minecraft = Minecraft.getInstance(); + Component shiftComponent = Component.empty().append(minecraft.options.keyShift.getTranslatedKeyMessage()).withStyle(ChatFormatting.LIGHT_PURPLE); + Component useComponent = Component.empty().append(minecraft.options.keyUse.getTranslatedKeyMessage()).withStyle(ChatFormatting.LIGHT_PURPLE); + return Component.translatable(OPEN_SCREEN_TRANSLATION_KEY, shiftComponent, useComponent).withStyle(ChatFormatting.GRAY); + } +} diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/proxy/Proxy.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/proxy/Proxy.java new file mode 100644 index 0000000..c7c4d24 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/proxy/Proxy.java @@ -0,0 +1,11 @@ +package fuzs.armorstatues.api.proxy; + +import fuzs.puzzleslib.core.DistTypeExecutor; +import net.minecraft.network.chat.Component; + +public interface Proxy { + @SuppressWarnings("Convert2MethodRef") + Proxy INSTANCE = DistTypeExecutor.getForDistType(() -> () -> new ClientProxy(), () -> () -> new ServerProxy()); + + Component getStatueHoverText(); +} diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/proxy/ServerProxy.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/proxy/ServerProxy.java new file mode 100644 index 0000000..a70146c --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/proxy/ServerProxy.java @@ -0,0 +1,11 @@ +package fuzs.armorstatues.api.proxy; + +import net.minecraft.network.chat.Component; + +public class ServerProxy implements Proxy { + + @Override + public Component getStatueHoverText() { + return Component.empty(); + } +} diff --git a/Common/src/main/java/fuzs/armorstatues/api/world/entity/decoration/ArmorStandDataProvider.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/entity/decoration/ArmorStandDataProvider.java similarity index 82% rename from Common/src/main/java/fuzs/armorstatues/api/world/entity/decoration/ArmorStandDataProvider.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/entity/decoration/ArmorStandDataProvider.java index df86a7f..9efaec3 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/world/entity/decoration/ArmorStandDataProvider.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/entity/decoration/ArmorStandDataProvider.java @@ -9,7 +9,7 @@ public interface ArmorStandDataProvider { ArmorStandDataProvider INSTANCE = new ArmorStandDataProvider() {}; default ArmorStandScreenType[] getScreenTypes() { - return new ArmorStandScreenType[]{ArmorStandScreenType.ROTATIONS, ArmorStandScreenType.POSES, ArmorStandScreenType.STYLE, ArmorStandScreenType.POSITION, ArmorStandScreenType.ALIGNMENTS, ArmorStandScreenType.EQUIPMENT}; + return new ArmorStandScreenType[]{ArmorStandScreenType.ROTATIONS, ArmorStandScreenType.POSES, ArmorStandScreenType.STYLE, ArmorStandScreenType.POSITION, ArmorStandScreenType.EQUIPMENT}; } default ArmorStandScreenType getDefaultScreenType() { @@ -17,7 +17,7 @@ default ArmorStandScreenType getDefaultScreenType() { } default PosePartMutator[] getPosePartMutators() { - return new PosePartMutator[]{PosePartMutator.HEAD, PosePartMutator.BODY, PosePartMutator.LEFT_ARM, PosePartMutator.RIGHT_ARM, PosePartMutator.LEFT_LEG, PosePartMutator.RIGHT_LEG}; + return new PosePartMutator[]{PosePartMutator.HEAD, PosePartMutator.BODY, PosePartMutator.RIGHT_ARM, PosePartMutator.LEFT_ARM, PosePartMutator.RIGHT_LEG, PosePartMutator.LEFT_LEG}; } default ArmorStandPose getRandomPose(boolean clampRotations) { diff --git a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandHolder.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandHolder.java similarity index 81% rename from Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandHolder.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandHolder.java index d586756..5ca0fc1 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandHolder.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandHolder.java @@ -10,8 +10,4 @@ public interface ArmorStandHolder { default ArmorStandDataProvider getDataProvider() { return this.getArmorStand() instanceof ArmorStandDataProvider dataProvider ? dataProvider : ArmorStandDataProvider.INSTANCE; } - - static ArmorStandHolder simple(ArmorStand armorStand) { - return () -> armorStand; - } } diff --git a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandMenu.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandMenu.java similarity index 87% rename from Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandMenu.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandMenu.java index c6e9128..dd24750 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandMenu.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/ArmorStandMenu.java @@ -1,7 +1,8 @@ package fuzs.armorstatues.api.world.inventory; import com.mojang.datafixers.util.Pair; -import fuzs.armorstatues.api.ArmorStatuesApi; +import fuzs.armorstatues.api.StatuesApi; +import fuzs.armorstatues.api.world.entity.decoration.ArmorStandDataProvider; import fuzs.armorstatues.api.world.inventory.data.ArmorStandStyleOption; import fuzs.armorstatues.core.ModServices; import fuzs.armorstatues.mixin.accessor.ArmorStandAccessor; @@ -22,29 +23,35 @@ import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; public class ArmorStandMenu extends AbstractContainerMenu implements ArmorStandHolder { - public static final ResourceLocation EMPTY_ARMOR_SLOT_SWORD = new ResourceLocation(ArmorStatuesApi.MOD_ID, "item/empty_armor_slot_sword"); + public static final ResourceLocation EMPTY_ARMOR_SLOT_SWORD = StatuesApi.id("item/empty_armor_slot_sword"); static final ResourceLocation[] TEXTURE_EMPTY_SLOTS = new ResourceLocation[]{InventoryMenu.EMPTY_ARMOR_SLOT_BOOTS, InventoryMenu.EMPTY_ARMOR_SLOT_LEGGINGS, InventoryMenu.EMPTY_ARMOR_SLOT_CHESTPLATE, InventoryMenu.EMPTY_ARMOR_SLOT_HELMET, InventoryMenu.EMPTY_ARMOR_SLOT_SHIELD, EMPTY_ARMOR_SLOT_SWORD}; public static final EquipmentSlot[] SLOT_IDS = new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET, EquipmentSlot.MAINHAND, EquipmentSlot.OFFHAND}; - private final Container armorStandInventory; + private final Container container; private final ArmorStand armorStand; + @Nullable + private final ArmorStandDataProvider dataProvider; - public static ArmorStandMenu create(MenuType menuType, int containerId, Inventory inventory, FriendlyByteBuf buf) { - // not sure how likely it is for this to fail, in that case the menu should just close immediately - ArmorStand entity = (ArmorStand) inventory.player.level.getEntity(buf.readInt()); + public static ArmorStandMenu create(MenuType menuType, int containerId, Inventory inventory, FriendlyByteBuf buf, @Nullable ArmorStandDataProvider dataProvider) { + int entityId = buf.readInt(); + ArmorStand entity = (ArmorStand) inventory.player.level.getEntity(entityId); if (entity != null) { // vanilla doesn't sync these automatically, we need them for the menu entity.setInvulnerable(buf.readBoolean()); ((ArmorStandAccessor) entity).setDisabledSlots(buf.readInt()); // also create the armor stand container client side, so that visual update instantly instead of having to wait for the server to resync data - return create(menuType, containerId, inventory, entity); + return create(menuType, containerId, inventory, entity, dataProvider); } - return new ArmorStandMenu(menuType, containerId, inventory, new SimpleContainer(6), null); + // exception is caught, so nothing will crash, only the screen will not open + // not sure how this is even possible, but there was a report about it + // report was concerning just placed statues, so maybe entity data arrived at remote after menu was opened + throw new IllegalStateException("Entity for id %s missing on client".formatted(entityId)); } - public static ArmorStandMenu create(MenuType menuType, int containerId, Inventory inventory, ArmorStand armorStand) { + public static ArmorStandMenu create(MenuType menuType, int containerId, Inventory inventory, ArmorStand armorStand, @Nullable ArmorStandDataProvider dataProvider) { // we could also copy all items from the armor stand to a SimpleContainer, then update the armor stand using a listener using LivingEntity::setItemSlot // problem is that way we miss out on anything changing with the armor stand entity itself, therefore this approach NonNullList armorItems = ((ArmorStandAccessor) armorStand).getArmorItems(); @@ -56,7 +63,7 @@ public static ArmorStandMenu create(MenuType menuType, int containerId, Inven } }); CompoundContainer container = new CompoundContainer(simpleContainer(armorItems), handItemsContainer); - return new ArmorStandMenu(menuType, containerId, inventory, container, armorStand); + return new ArmorStandMenu(menuType, containerId, inventory, container, armorStand, dataProvider); } private static SimpleContainer simpleContainer(NonNullList items) { @@ -65,13 +72,14 @@ private static SimpleContainer simpleContainer(NonNullList items) { return container; } - private ArmorStandMenu(MenuType menuType, int containerId, Inventory inventory, Container container, ArmorStand armorStand) { + private ArmorStandMenu(MenuType menuType, int containerId, Inventory inventory, Container container, ArmorStand armorStand, @Nullable ArmorStandDataProvider dataProvider) { super(menuType, containerId); - this.armorStandInventory = container; + this.container = container; this.armorStand = armorStand; + this.dataProvider = dataProvider; for (int k = 0; k < 4; ++k) { final EquipmentSlot equipmentslot = SLOT_IDS[k]; - this.addSlot(new Slot(this.armorStandInventory, 3 - k, 58, 20 + k * 18) { + this.addSlot(new Slot(this.container, 3 - k, 58, 20 + k * 18) { @Override public void set(ItemStack stack) { @@ -121,7 +129,7 @@ public Pair getNoItemIcon() { for (int i = 0; i < 2; i++) { final EquipmentSlot equipmentslot = SLOT_IDS[4 + i]; final ResourceLocation slotTexture = TEXTURE_EMPTY_SLOTS[5 - i]; - this.addSlot(new Slot(this.armorStandInventory, 4 + i, 136, 56 + i * 18) { + this.addSlot(new Slot(this.container, 4 + i, 136, 56 + i * 18) { @Override public boolean mayPlace(ItemStack stack) { @@ -227,4 +235,9 @@ public boolean stillValid(Player player) { public ArmorStand getArmorStand() { return this.armorStand; } + + @Override + public ArmorStandDataProvider getDataProvider() { + return this.dataProvider != null ? this.dataProvider : ArmorStandHolder.super.getDataProvider(); + } } diff --git a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandAlignment.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandAlignment.java similarity index 59% rename from Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandAlignment.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandAlignment.java index a7910b7..25b59a7 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandAlignment.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandAlignment.java @@ -1,11 +1,13 @@ package fuzs.armorstatues.api.world.inventory.data; +import fuzs.armorstatues.api.StatuesApi; import net.minecraft.core.Rotations; -import net.minecraft.network.chat.Component; import net.minecraft.world.phys.Vec3; +import java.util.Locale; + /** - * values copied from Vanilla Tweaks data pack + * Values copied from Vanilla Tweaks data pack. */ public enum ArmorStandAlignment { BLOCK("block", new Rotations(-15.0f, 135.0f, 0.0f), new Vec3(0.5725, -0.655, 0.352), new Vec3(0.28625, -0.3275, 0.176)), @@ -15,30 +17,34 @@ public enum ArmorStandAlignment { private final String name; private final ArmorStandPose pose; - private final Vec3 position; - private final Vec3 smallPosition; + private final Vec3 offset; + private final Vec3 offsetIfSmall; - ArmorStandAlignment(String name, Rotations rightArmRotations, Vec3 position, Vec3 smallPosition) { + ArmorStandAlignment(String name, Rotations rightArmRotations, Vec3 offset, Vec3 offsetIfSmall) { this.name = name; this.pose = ArmorStandPose.empty().withRightArmPose(rightArmRotations); - this.position = position; - this.smallPosition = smallPosition; + this.offset = offset; + this.offsetIfSmall = offsetIfSmall; } @Override public String toString() { - return this.name; + return this.name.toUpperCase(Locale.ROOT); + } + + public String getTranslationKey() { + return StatuesApi.MOD_ID + ".screen.alignments." + this.name; } - public Component getComponent() { - return Component.translatable("armorstatues.screen.alignments." + this.name); + public String getDescriptionsKey() { + return this.getTranslationKey() + ".description"; } public ArmorStandPose getPose() { return this.pose; } - public Vec3 getPosition(boolean small) { - return small ? this.smallPosition : this.position; + public Vec3 getAlignmentOffset(boolean small) { + return small ? this.offsetIfSmall : this.offset; } } diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandPose.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandPose.java new file mode 100644 index 0000000..83aa1e2 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandPose.java @@ -0,0 +1,309 @@ +package fuzs.armorstatues.api.world.inventory.data; + +import fuzs.armorstatues.api.StatuesApi; +import fuzs.armorstatues.mixin.accessor.ArmorStandAccessor; +import net.minecraft.Util; +import net.minecraft.core.Rotations; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +public class ArmorStandPose { + private static final Rotations ZERO_ROTATIONS = new Rotations(0.0F, 0.0F, 0.0F); + public static final double DEGREES_SNAP_INTERVAL = 0.125; + public static final DecimalFormat ROTATION_FORMAT = Util.make(new DecimalFormat("#.##"), (decimalFormat) -> { + decimalFormat.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ROOT)); + }); + public static final ArmorStandPose EMPTY = new ArmorStandPose(null).withSourceType(SourceType.EMPTY); + public static final ArmorStandPose ATHENA = new ArmorStandPose("athena").withBodyPose(new Rotations(0.0F, 0.0F, 2.0F)).withHeadPose(new Rotations(-5.0F, 0.0F, 0.0F)).withLeftArmPose(new Rotations(10.0F, 0.0F, -5.0F)).withLeftLegPose(new Rotations(-3.0F, -3.0F, -3.0F)).withRightArmPose(new Rotations(-60.0F, 20.0F, -10.0F)).withRightLegPose(new Rotations(3.0F, 3.0F, 3.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose BRANDISH = new ArmorStandPose("brandish").withBodyPose(new Rotations(0.0F, 0.0F, -2.0F)).withHeadPose(new Rotations(-15.0F, 0.0F, 0.0F)).withLeftArmPose(new Rotations(20.0F, 0.0F, -10.0F)).withLeftLegPose(new Rotations(5.0F, -3.0F, -3.0F)).withRightArmPose(new Rotations(-110.0F, 50.0F, 0.0F)).withRightLegPose(new Rotations(-5.0F, 3.0F, 3.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose CANCAN = new ArmorStandPose("cancan").withBodyPose(new Rotations(0.0F, 22.0F, 0.0F)).withHeadPose(new Rotations(-5.0F, 18.0F, 0.0F)).withLeftArmPose(new Rotations(8.0F, 0.0F, -114.0F)).withLeftLegPose(new Rotations(-111.0F, 55.0F, 0.0F)).withRightArmPose(new Rotations(0.0F, 84.0F, 111.0F)).withRightLegPose(new Rotations(0.0F, 23.0F, -13.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose DEFAULT = new ArmorStandPose("default").withLeftArmPose(new Rotations(-10.0F, 0.0F, -10.0F)).withLeftLegPose(new Rotations(-1.0F, 0.0F, -1.0F)).withRightArmPose(new Rotations(-15.0F, 0.0F, 10.0F)).withRightLegPose(new Rotations(1.0F, 0.0F, 1.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose ENTERTAIN = new ArmorStandPose("entertain").withHeadPose(new Rotations(-15.0F, 0.0F, 0.0F)).withLeftArmPose(new Rotations(-110.0F, -35.0F, 0.0F)).withLeftLegPose(new Rotations(5.0F, -3.0F, -3.0F)).withRightArmPose(new Rotations(-110.0F, 35.0F, 0.0F)).withRightLegPose(new Rotations(-5.0F, 3.0F, 3.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose HERO = new ArmorStandPose("hero").withBodyPose(new Rotations(0.0F, 8.0F, 0.0F)).withHeadPose(new Rotations(-4.0F, 67.0F, 0.0F)).withLeftArmPose(new Rotations(16.0F, 32.0F, -8.0F)).withLeftLegPose(new Rotations(0.0F, -75.0F, -8.0F)).withRightArmPose(new Rotations(-99.0F, 63.0F, 0.0F)).withRightLegPose(new Rotations(4.0F, 63.0F, 8.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose HONOR = new ArmorStandPose("honor").withHeadPose(new Rotations(-15.0F, 0.0F, 0.0F)).withLeftArmPose(new Rotations(-110.0F, 35.0F, 0.0F)).withLeftLegPose(new Rotations(5.0F, -3.0F, -3.0F)).withRightArmPose(new Rotations(-110.0F, -35.0F, 0.0F)).withRightLegPose(new Rotations(-5.0F, 3.0F, 3.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose RIPOSTE = new ArmorStandPose("riposte").withHeadPose(new Rotations(16.0F, 20.0F, 0.0F)).withLeftArmPose(new Rotations(4.0F, 8.0F, 237.0F)).withLeftLegPose(new Rotations(-14.0F, -18.0F, -16.0F)).withRightArmPose(new Rotations(246.0F, 0.0F, 89.0F)).withRightLegPose(new Rotations(8.0F, 20.0F, 4.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose SALUTE = new ArmorStandPose("salute").withLeftArmPose(new Rotations(10.0F, 0.0F, -5.0F)).withLeftLegPose(new Rotations(-1.0F, 0.0F, -1.0F)).withRightArmPose(new Rotations(-70.0F, -40.0F, 0.0F)).withRightLegPose(new Rotations(1.0F, 0.0F, 1.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose SOLEMN = new ArmorStandPose("solemn").withBodyPose(new Rotations(0.0F, 0.0F, 2.0F)).withHeadPose(new Rotations(15.0F, 0.0F, 0.0F)).withLeftArmPose(new Rotations(-30.0F, 15.0F, 15.0F)).withLeftLegPose(new Rotations(-1.0F, 0.0F, -1.0F)).withRightArmPose(new Rotations(-60.0F, -20.0F, -10.0F)).withRightLegPose(new Rotations(1.0F, 0.0F, 1.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose ZOMBIE = new ArmorStandPose("zombie").withHeadPose(new Rotations(-10.0F, 0.0F, -5.0F)).withLeftArmPose(new Rotations(-105.0F, 0.0F, 0.0F)).withLeftLegPose(new Rotations(7.0F, 0.0F, 0.0F)).withRightArmPose(new Rotations(-100.0F, 0.0F, 0.0F)).withRightLegPose(new Rotations(-46.0F, 0.0F, 0.0F)).withSourceType(SourceType.MINECRAFT); + public static final ArmorStandPose WALKING = new ArmorStandPose("walking").withRightArmPose(new Rotations(20.0F,0.0F,10.0F)).withLeftArmPose(new Rotations(-20.0F,0.0F,-10.0F)).withRightLegPose(new Rotations(-20.0F,0.0F,0.0F)).withLeftLegPose(new Rotations(20.0F,0.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose RUNNING = new ArmorStandPose("running").withRightArmPose(new Rotations(-40.0F,0.0F,10.0F)).withLeftArmPose(new Rotations(40.0F,0.0F,-10.0F)).withRightLegPose(new Rotations(40.0F,0.0F,0.0F)).withLeftLegPose(new Rotations(-40.0F,0.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose POINTING = new ArmorStandPose("pointing").withHeadPose(new Rotations(0.0F,20.0F,0.0F)).withRightArmPose(new Rotations(-90.0F,18.0F,0.0F)).withLeftArmPose(new Rotations(0.0F,0.0F,-10.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose BLOCKING = new ArmorStandPose("blocking").withRightArmPose(new Rotations(-20.0F,-20.0F,0.0F)).withLeftArmPose(new Rotations(-50.0F,50.0F,0.0F)).withRightLegPose(new Rotations(-20.0F,0.0F,0.0F)).withLeftLegPose(new Rotations(20.0F,0.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose LUNGEING = new ArmorStandPose("lungeing").withBodyPose(new Rotations(15.0F,0.0F,0.0F)).withRightArmPose(new Rotations(-60.0F,-10.0F,0.0F)).withLeftArmPose(new Rotations(10.0F,0.0F,-10.0F)).withRightLegPose(new Rotations(-15.0F,0.0F,0.0F)).withLeftLegPose(new Rotations(30.0F,0.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose WINNING = new ArmorStandPose("winning").withHeadPose(new Rotations(-15.0F,0.0F,0.0F)).withRightArmPose(new Rotations(-120.0F,-10.0F,0.0F)).withLeftArmPose(new Rotations(10.0F,0.0F,-10.0F)).withLeftLegPose(new Rotations(15.0F,0.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose SITTING = new ArmorStandPose("sitting").withRightArmPose(new Rotations(-80.0F,20.0F,0.0F)).withLeftArmPose(new Rotations(-80.0F,-20.0F,0.0F)).withRightLegPose(new Rotations(-90.0F,10.0F,0.0F)).withLeftLegPose(new Rotations(-90.0F,-10.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose ARABESQUE = new ArmorStandPose("arabesque").withHeadPose(new Rotations(-15.0F,0.0F,0.0F)).withBodyPose(new Rotations(10.0F,0.0F,0.0F)).withRightArmPose(new Rotations(-140.0F,-10.0F,0.0F)).withLeftArmPose(new Rotations(70.0F,0.0F,-10.0F)).withLeftLegPose(new Rotations(75.0F,0.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose CUPID = new ArmorStandPose("cupid").withBodyPose(new Rotations(10.0F,0.0F,0.0F)).withRightArmPose(new Rotations(-90.0F,-10.0F,0.0F)).withLeftArmPose(new Rotations(-75.0F,0.0F,10.0F)).withLeftLegPose(new Rotations(75.0F,0.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose CONFIDENT = new ArmorStandPose("confident").withHeadPose(new Rotations(-10.0F,20.0F,0.0F)).withBodyPose(new Rotations(-2.0F,0.0F,0.0F)).withRightArmPose(new Rotations(5.0F,0.0F,0.0F)).withLeftArmPose(new Rotations(5.0F,0.0F,0.0F)).withRightLegPose(new Rotations(16.0F,2.0F,10.0F)).withLeftLegPose(new Rotations(0.0F,-10.0F,-4.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose DEATH = new ArmorStandPose("death").withHeadPose(new Rotations(-85.0F,0.0F,0.0F)).withBodyPose(new Rotations(-90.0F,0.0F,0.0F)).withRightArmPose(new Rotations(-90.0F,10.0F,0.0F)).withLeftArmPose(new Rotations(-90.0F,-10.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose FACEPALM = new ArmorStandPose("facepalm").withHeadPose(new Rotations(45.0F,-4.0F,1.0F)).withBodyPose(new Rotations(10.0F,0.0F,0.0F)).withRightArmPose(new Rotations(18.0F,-14.0F,0.0F)).withLeftArmPose(new Rotations(-72.0F,24.0F,47.0F)).withRightLegPose(new Rotations(25.0F,-2.0F,0.0F)).withLeftLegPose(new Rotations(-4.0F,-6.0F,-2.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose LAZING = new ArmorStandPose("lazing").withHeadPose(new Rotations(14.0F,-12.0F,6.0F)).withBodyPose(new Rotations(5.0F,0.0F,0.0F)).withRightArmPose(new Rotations(-40.0F,20.0F,0.0F)).withLeftArmPose(new Rotations(-4.0F,-20.0F,-10.0F)).withRightLegPose(new Rotations(-88.0F,71.0F,0.0F)).withLeftLegPose(new Rotations(-88.0F,46.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose CONFUSED = new ArmorStandPose("confused").withHeadPose(new Rotations(0.0F,30.0F,0f)).withBodyPose(new Rotations(0.0F,13.0F,0.0F)).withRightArmPose(new Rotations(-22.0F,31.0F,10.0F)).withLeftArmPose(new Rotations(145.0F,22.0F,-49.0F)).withRightLegPose(new Rotations(6.0F,-20.0F,0.0F)).withLeftLegPose(new Rotations(-6.0F,0.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose FORMAL = new ArmorStandPose("formal").withHeadPose(new Rotations(4.0F,0.0F,0.0F)).withBodyPose(new Rotations(4.0F,0.0F,0.0F)).withRightArmPose(new Rotations(30.0F,22.0F,-20.0F)).withLeftArmPose(new Rotations(30.0F,-20.0F,21.0F)).withRightLegPose(new Rotations(0.0F,0.0F,5.0F)).withLeftLegPose(new Rotations(0.0F,0.0F,-5.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose SAD = new ArmorStandPose("sad").withHeadPose(new Rotations(63.0F,0.0F,0.0F)).withBodyPose(new Rotations(10.0F,0.0F,0.0F)).withRightArmPose(new Rotations(-5.0F,0.0F,5.0F)).withLeftArmPose(new Rotations(-5.0F,0.0F,-5.0F)).withRightLegPose(new Rotations(-5.0F,-10.0F,5.0F)).withLeftLegPose(new Rotations(-5.0F,16.0F,-5.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose JOYOUS = new ArmorStandPose("joyous").withHeadPose(new Rotations(-11.0F,0.0F,0.0F)).withBodyPose(new Rotations(-4.0F,0.0F,0.0F)).withRightArmPose(new Rotations(0.0F,0.0F,100.0F)).withLeftArmPose(new Rotations(0.0F,0.0F,-100.0F)).withRightLegPose(new Rotations(-8.0F,0.0F,60.0F)).withLeftLegPose(new Rotations(-8.0F,0.0F,-60.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + public static final ArmorStandPose STARGAZING = new ArmorStandPose("stargazing").withHeadPose(new Rotations(-22.0F,25.0F,0.0F)).withBodyPose(new Rotations(-4.0F,10.0F,0.0F)).withRightArmPose(new Rotations(-153.0F,34.0F,-3.0F)).withLeftArmPose(new Rotations(4.0F,18.0F,0.0F)).withRightLegPose(new Rotations(-4.0F,17.0F,2.0F)).withLeftLegPose(new Rotations(6.0F,24.0F,0.0F)).withSourceType(SourceType.VANILLA_TWEAKS); + private static final ArmorStandPose[] VALUES = {DEFAULT, SOLEMN, ATHENA, BRANDISH, HONOR, ENTERTAIN, SALUTE, HERO, RIPOSTE, ZOMBIE, CANCAN, WALKING, RUNNING, POINTING, BLOCKING, LUNGEING, WINNING, SITTING, ARABESQUE, CUPID, CONFIDENT, DEATH, FACEPALM, LAZING, CONFUSED, FORMAL, SAD, JOYOUS, STARGAZING}; + + @Nullable + private final String name; + @Nullable + private final SourceType sourceType; + @Nullable + private final Rotations headPose; + @Nullable + private final Rotations bodyPose; + @Nullable + private final Rotations leftArmPose; + @Nullable + private final Rotations rightArmPose; + @Nullable + private final Rotations leftLegPose; + @Nullable + private final Rotations rightLegPose; + + private ArmorStandPose(@Nullable String name) { + this(name, null, ZERO_ROTATIONS, ZERO_ROTATIONS, ZERO_ROTATIONS, ZERO_ROTATIONS, ZERO_ROTATIONS, ZERO_ROTATIONS); + } + + private ArmorStandPose(@Nullable String name, @Nullable SourceType sourceType, @Nullable Rotations headPose, @Nullable Rotations bodyPose, @Nullable Rotations leftArmPose, @Nullable Rotations rightArmPose, @Nullable Rotations leftLegPose, @Nullable Rotations rightLegPose) { + this.name = name; + this.sourceType = sourceType; + this.headPose = headPose; + this.bodyPose = bodyPose; + this.leftArmPose = leftArmPose; + this.rightArmPose = rightArmPose; + this.leftLegPose = leftLegPose; + this.rightLegPose = rightLegPose; + } + + public static ArmorStandPose empty() { + return new ArmorStandPose(null, null, null, null, null, null, null, null); + } + + @Override + public String toString() { + return this.name != null ? this.name.toUpperCase(Locale.ROOT) : "POSE"; + } + + public String getTranslationKey() { + return this.name != null ? StatuesApi.MOD_ID + ".screen.pose." + this.name : null; + } + + public SourceType getSourceType() { + return this.sourceType != null ? this.sourceType : SourceType.TRANSIENT; + } + + public Rotations getHeadPose() { + return this.headPose != null ? this.headPose : ZERO_ROTATIONS; + } + + public Rotations getBodyPose() { + return this.bodyPose != null ? this.bodyPose : ZERO_ROTATIONS; + } + + public Rotations getLeftArmPose() { + return this.leftArmPose != null ? this.leftArmPose : ZERO_ROTATIONS; + } + + public Rotations getRightArmPose() { + return this.rightArmPose != null ? this.rightArmPose : ZERO_ROTATIONS; + } + + public Rotations getLeftLegPose() { + return this.leftLegPose != null ? this.leftLegPose : ZERO_ROTATIONS; + } + + public Rotations getRightLegPose() { + return this.rightLegPose != null ? this.rightLegPose : ZERO_ROTATIONS; + } + + @Nullable + public Rotations getNullableHeadPose() { + return this.headPose; + } + + @Nullable + public Rotations getNullableBodyPose() { + return this.bodyPose; + } + + @Nullable + public Rotations getNullableLeftArmPose() { + return this.leftArmPose; + } + + @Nullable + public Rotations getNullableRightArmPose() { + return this.rightArmPose; + } + + @Nullable + public Rotations getNullableLeftLegPose() { + return this.leftLegPose; + } + + @Nullable + public Rotations getNullableRightLegPose() { + return this.rightLegPose; + } + + public ArmorStandPose withHeadPose(Rotations rotation) { + return new ArmorStandPose(this.name, null, rotation, this.bodyPose, this.leftArmPose, this.rightArmPose, this.leftLegPose, this.rightLegPose); + } + + public ArmorStandPose withBodyPose(Rotations rotation) { + return new ArmorStandPose(this.name, null, this.headPose, rotation, this.leftArmPose, this.rightArmPose, this.leftLegPose, this.rightLegPose); + } + + public ArmorStandPose withLeftArmPose(Rotations rotation) { + return new ArmorStandPose(this.name, null, this.headPose, this.bodyPose, rotation, this.rightArmPose, this.leftLegPose, this.rightLegPose); + } + + public ArmorStandPose withRightArmPose(Rotations rotation) { + return new ArmorStandPose(this.name, null, this.headPose, this.bodyPose, this.leftArmPose, rotation, this.leftLegPose, this.rightLegPose); + } + + public ArmorStandPose withLeftLegPose(Rotations rotation) { + return new ArmorStandPose(this.name, null, this.headPose, this.bodyPose, this.leftArmPose, this.rightArmPose, rotation, this.rightLegPose); + } + + public ArmorStandPose withRightLegPose(Rotations rotation) { + return new ArmorStandPose(this.name, null, this.headPose, this.bodyPose, this.leftArmPose, this.rightArmPose, this.leftLegPose, rotation); + } + + public ArmorStandPose withSourceType(SourceType sourceType) { + return new ArmorStandPose(this.name, sourceType, this.headPose, this.bodyPose, this.leftArmPose, this.rightArmPose, this.leftLegPose, this.rightLegPose); + } + + public ArmorStandPose mirror() { + return new ArmorStandPose(this.name, SourceType.MIRRORED, mirrorRotations(this.headPose), mirrorRotations(this.bodyPose), mirrorRotations(this.rightArmPose), mirrorRotations(this.leftArmPose), mirrorRotations(this.rightLegPose), mirrorRotations(this.leftLegPose)); + } + + @Nullable + private static Rotations mirrorRotations(@Nullable Rotations rotations) { + return rotations != null ? new Rotations(rotations.getX(), -rotations.getY(), -rotations.getZ()) : null; + } + + public ArmorStandPose copyAndFillFrom(ArmorStandPose fillFrom) { + return new ArmorStandPose(this.name, this.sourceType, this.headPose != null ? this.headPose : fillFrom.headPose, this.bodyPose != null ? this.bodyPose : fillFrom.bodyPose, this.leftArmPose != null ? this.leftArmPose : fillFrom.leftArmPose, this.rightArmPose != null ? this.rightArmPose : fillFrom.rightArmPose, this.leftLegPose != null ? this.leftLegPose : fillFrom.leftLegPose, this.rightLegPose != null ? this.rightLegPose : fillFrom.rightLegPose); + } + + public void applyToEntity(ArmorStand armorStand) { + armorStand.setHeadPose(this.getHeadPose()); + armorStand.setBodyPose(this.getBodyPose()); + armorStand.setLeftArmPose(this.getLeftArmPose()); + armorStand.setRightArmPose(this.getRightArmPose()); + armorStand.setLeftLegPose(this.getLeftLegPose()); + armorStand.setRightLegPose(this.getRightLegPose()); + } + + public void serializeAllPoses(CompoundTag tag) { + this.serializeBodyPoses(tag, null); + this.serializeArmPoses(tag, null); + this.serializeLegPoses(tag, null); + } + + public boolean serializeBodyPoses(CompoundTag tag, @Nullable ArmorStandPose lastSentPose) { + boolean hasChanged = false; + if (this.headPose != null && (lastSentPose == null || !this.headPose.equals(lastSentPose.headPose))) { + tag.put("Head", this.headPose.save()); + hasChanged = true; + } + if (this.bodyPose != null && (lastSentPose == null || !this.bodyPose.equals(lastSentPose.bodyPose))) { + tag.put("Body", this.bodyPose.save()); + hasChanged = true; + } + return hasChanged; + } + + public boolean serializeArmPoses(CompoundTag tag, @Nullable ArmorStandPose lastSentPose) { + boolean hasChanged = false; + if (this.leftArmPose != null && (lastSentPose == null || !this.leftArmPose.equals(lastSentPose.leftArmPose))) { + tag.put("LeftArm", this.leftArmPose.save()); + hasChanged = true; + } + if (this.rightArmPose != null && (lastSentPose == null || !this.rightArmPose.equals(lastSentPose.rightArmPose))) { + tag.put("RightArm", this.rightArmPose.save()); + hasChanged = true; + } + return hasChanged; + } + + public boolean serializeLegPoses(CompoundTag tag, @Nullable ArmorStandPose lastSentPose) { + boolean hasChanged = false; + if (this.leftLegPose != null && (lastSentPose == null || !this.leftLegPose.equals(lastSentPose.leftLegPose))) { + tag.put("LeftLeg", this.leftLegPose.save()); + hasChanged = true; + } + if (this.rightLegPose != null && (lastSentPose == null || !this.rightLegPose.equals(lastSentPose.rightLegPose))) { + tag.put("RightLeg", this.rightLegPose.save()); + hasChanged = true; + } + return hasChanged; + } + + public static ArmorStandPose fromEntity(ArmorStand armorStand) { + return new ArmorStandPose(null, null, armorStand.getHeadPose(), armorStand.getBodyPose(), armorStand.getLeftArmPose(), armorStand.getRightArmPose(), armorStand.getLeftLegPose(), armorStand.getRightLegPose()); + } + + public static void applyTagToEntity(ArmorStand armorStand, CompoundTag tag) { + ((ArmorStandAccessor) armorStand).callReadPose(tag); + } + + public static ArmorStandPose random(PosePartMutator[] mutators, boolean clampRotations) { + checkMutatorsSize(mutators); + return new ArmorStandPose(null, null, mutators[0].randomRotations(clampRotations), mutators[1].randomRotations(clampRotations), mutators[2].randomRotations(clampRotations), mutators[3].randomRotations(clampRotations), mutators[4].randomRotations(clampRotations), mutators[5].randomRotations(clampRotations)); + } + + public static ArmorStandPose[] values() { + return VALUES.clone(); + } + + public static int valuesLength() { + return VALUES.length; + } + + public static ArmorStandPose randomValue() { + List poses = Arrays.asList(VALUES); + Collections.shuffle(poses); + return poses.stream().findAny().orElseThrow(); + } + + public static void checkMutatorsSize(PosePartMutator[] mutators) { + if (mutators.length != 6) throw new IllegalArgumentException("Invalid mutators size: Expected 6, was %s".formatted(mutators.length)); + } + + public static double snapValue(double value, double snapInterval) { + if (snapInterval > 0.0 && snapInterval < 1.0) { + double currentSnap = 0.0; + while (currentSnap < 1.0) { + double snapRegion = snapInterval * 0.1; + if (value >= currentSnap - snapRegion && value < currentSnap + snapRegion) { + return currentSnap; + } + currentSnap += snapInterval; + } + } + return value; + } + + public enum SourceType { + MINECRAFT, VANILLA_TWEAKS, TRANSIENT, EMPTY, MIRRORED; + + @Nullable + public String getDisplayName() { + if (this == MINECRAFT) return "Minecraft"; + if (this == VANILLA_TWEAKS) return "Vanilla Tweaks"; + return null; + } + } +} diff --git a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandScreenType.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandScreenType.java similarity index 64% rename from Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandScreenType.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandScreenType.java index b177433..4cb1e91 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandScreenType.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandScreenType.java @@ -1,6 +1,6 @@ package fuzs.armorstatues.api.world.inventory.data; -import net.minecraft.network.chat.Component; +import fuzs.armorstatues.api.StatuesApi; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -12,29 +12,28 @@ public class ArmorStandScreenType { public static final ArmorStandScreenType STYLE = new ArmorStandScreenType("style", new ItemStack(Items.PAINTING)); public static final ArmorStandScreenType POSES = new ArmorStandScreenType("poses", new ItemStack(Items.SPYGLASS)); public static final ArmorStandScreenType POSITION = new ArmorStandScreenType("position", new ItemStack(Items.GRASS_BLOCK)); - public static final ArmorStandScreenType ALIGNMENTS = new ArmorStandScreenType("alignments", new ItemStack(Items.DIAMOND_PICKAXE)); - private final String translationId; + private final String name; private final ItemStack icon; private final boolean requiresServer; - public ArmorStandScreenType(String translationId, ItemStack icon) { - this(translationId, icon, false); + public ArmorStandScreenType(String name, ItemStack icon) { + this(name, icon, false); } - public ArmorStandScreenType(String translationId, ItemStack icon, boolean requiresServer) { - this.translationId = translationId; + public ArmorStandScreenType(String name, ItemStack icon, boolean requiresServer) { + this.name = name; this.icon = icon; this.requiresServer = requiresServer; } @Override public String toString() { - return this.translationId.toUpperCase(Locale.ROOT); + return this.name.toUpperCase(Locale.ROOT); } - public Component getComponent() { - return Component.translatable("armorstatues.screen.type." + this.translationId); + public String getTranslationKey() { + return StatuesApi.MOD_ID + ".screen.type." + this.name; } public ItemStack getIcon() { diff --git a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOption.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOption.java similarity index 79% rename from Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOption.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOption.java index ad8d47d..3014552 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOption.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOption.java @@ -2,8 +2,8 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import fuzs.armorstatues.api.StatuesApi; import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.entity.player.Player; @@ -11,17 +11,16 @@ import java.util.Objects; public interface ArmorStandStyleOption { - int ARMOR_STAND_ALL_SLOTS_DISABLED = 4144959; BiMap OPTIONS_REGISTRY = HashBiMap.create(); - String getTranslationId(); + String getName(); - default Component getComponent() { - return Component.translatable("armorstatues.screen.style." + this.getTranslationId()); + default String getTranslationKey() { + return StatuesApi.MOD_ID + ".screen.style." + this.getName(); } - default Component getDescriptionComponent() { - return Component.translatable("armorstatues.screen.style." + this.getTranslationId() + ".description"); + default String getDescriptionKey() { + return this.getTranslationKey() + ".description"; } void setOption(ArmorStand armorStand, boolean setting); @@ -35,7 +34,7 @@ default boolean allowChanges(Player player) { } default ResourceLocation getId() { - return Objects.requireNonNull(OPTIONS_REGISTRY.inverse().get(this), "Armor stand style option %s has not been registered".formatted(this.getTranslationId())); + return Objects.requireNonNull(OPTIONS_REGISTRY.inverse().get(this), "Armor stand style option %s has not been registered".formatted(this.getName())); } static void register(ResourceLocation id, ArmorStandStyleOption styleOption) { @@ -54,7 +53,7 @@ static void setArmorStandData(ArmorStand armorStand, boolean setting, int offset armorStand.getEntityData().set(ArmorStand.DATA_CLIENT_FLAGS, setBit(armorStand.getEntityData().get(ArmorStand.DATA_CLIENT_FLAGS), offset, setting)); } - private static byte setBit(byte oldBit, int offset, boolean value) { + static byte setBit(byte oldBit, int offset, boolean value) { if (value) { oldBit = (byte) (oldBit | offset); } else { diff --git a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOptions.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOptions.java similarity index 85% rename from Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOptions.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOptions.java index 5040407..7c30070 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOptions.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandStyleOptions.java @@ -18,22 +18,24 @@ public enum ArmorStandStyleOptions implements ArmorStandStyleOption { NO_GRAVITY("noGravity", Entity::setNoGravity, Entity::isNoGravity), SEALED("sealed", (armorStand, setting) -> { armorStand.setInvulnerable(setting); - ((ArmorStandAccessor) armorStand).setDisabledSlots(setting ? ArmorStandStyleOption.ARMOR_STAND_ALL_SLOTS_DISABLED : 0); + ((ArmorStandAccessor) armorStand).setDisabledSlots(setting ? ArmorStandStyleOptions.ARMOR_STAND_ALL_SLOTS_DISABLED : 0); }, Entity::isInvulnerable); - private final String translationId; + public static final int ARMOR_STAND_ALL_SLOTS_DISABLED = 4144959; + + private final String name; private final BiConsumer newValue; private final Function currentValue; - ArmorStandStyleOptions(String translationId, BiConsumer newValue, Function currentValue) { - this.translationId = translationId; + ArmorStandStyleOptions(String name, BiConsumer newValue, Function currentValue) { + this.name = name; this.newValue = newValue; this.currentValue = currentValue; } @Override - public String getTranslationId() { - return this.translationId; + public String getName() { + return this.name; } @Override @@ -59,7 +61,7 @@ public void toTag(CompoundTag tag, boolean currentValue) { }; tag.putBoolean(dataKey, currentValue); if (this == ArmorStandStyleOptions.SEALED) { - tag.putInt("DisabledSlots", currentValue ? ArmorStandStyleOption.ARMOR_STAND_ALL_SLOTS_DISABLED : 0); + tag.putInt("DisabledSlots", currentValue ? ARMOR_STAND_ALL_SLOTS_DISABLED : 0); } } diff --git a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/PosePartMutator.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/PosePartMutator.java similarity index 74% rename from Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/PosePartMutator.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/PosePartMutator.java index f9099ad..62af0a1 100644 --- a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/PosePartMutator.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/PosePartMutator.java @@ -1,5 +1,6 @@ package fuzs.armorstatues.api.world.inventory.data; +import fuzs.armorstatues.api.StatuesApi; import net.minecraft.core.Direction; import net.minecraft.core.Rotations; import net.minecraft.network.chat.Component; @@ -13,24 +14,27 @@ public final class PosePartMutator { public static final PosePartMutator HEAD = new PosePartMutator("head", ArmorStandPose::getHeadPose, ArmorStandPose::withHeadPose, PosePartAxisRange.range(-60.0F, 60.0F), PosePartAxisRange.range(-60.0F, 60.0F), PosePartAxisRange.range(-120.0, 120.0)); public static final PosePartMutator BODY = new PosePartMutator("body", ArmorStandPose::getBodyPose, ArmorStandPose::withBodyPose, PosePartAxisRange.range(-30.0F, 30.0F), PosePartAxisRange.range(-30.0F, 30.0F), PosePartAxisRange.range(-120.0, 120.0)); - public static final PosePartMutator LEFT_ARM = new PosePartMutator("leftArm", ArmorStandPose::getRightArmPose, ArmorStandPose::withRightArmPose, PosePartAxisRange.range(-180.0, 0.0), PosePartAxisRange.range(-90.0, 45.0), PosePartAxisRange.range(-120.0, 120.0)); - public static final PosePartMutator RIGHT_ARM = new PosePartMutator("rightArm", ArmorStandPose::getLeftArmPose, ArmorStandPose::withLeftArmPose, PosePartAxisRange.range(-180.0, 0.0), PosePartAxisRange.range(-45.0, 90.0), PosePartAxisRange.range(-120.0, 120.0)); - public static final PosePartMutator LEFT_LEG = new PosePartMutator("leftLeg", ArmorStandPose::getRightLegPose, ArmorStandPose::withRightLegPose, PosePartAxisRange.range(-120.0, 120.0), PosePartAxisRange.range(-90.0, 0.0), PosePartAxisRange.range(-120.0, 120.0)); - public static final PosePartMutator RIGHT_LEG = new PosePartMutator("rightLeg", ArmorStandPose::getLeftLegPose, ArmorStandPose::withLeftLegPose, PosePartAxisRange.range(-120.0, 120.0), PosePartAxisRange.range(0.0, 90.0), PosePartAxisRange.range(-120.0, 120.0)); - - private final String translationId; + public static final PosePartMutator RIGHT_ARM = new PosePartMutator("rightArm", ArmorStandPose::getRightArmPose, ArmorStandPose::withRightArmPose, PosePartAxisRange.range(-180.0, 0.0), PosePartAxisRange.range(-90.0, 45.0), PosePartAxisRange.range(-120.0, 120.0)); + public static final PosePartMutator LEFT_ARM = new PosePartMutator("leftArm", ArmorStandPose::getLeftArmPose, ArmorStandPose::withLeftArmPose, PosePartAxisRange.range(-180.0, 0.0), PosePartAxisRange.range(-45.0, 90.0), PosePartAxisRange.range(-120.0, 120.0)); + public static final PosePartMutator RIGHT_LEG = new PosePartMutator("rightLeg", ArmorStandPose::getRightLegPose, ArmorStandPose::withRightLegPose, PosePartAxisRange.range(-120.0, 120.0), PosePartAxisRange.range(-90.0, 0.0), PosePartAxisRange.range(-120.0, 120.0)); + public static final PosePartMutator LEFT_LEG = new PosePartMutator("leftLeg", ArmorStandPose::getLeftLegPose, ArmorStandPose::withLeftLegPose, PosePartAxisRange.range(-120.0, 120.0), PosePartAxisRange.range(0.0, 90.0), PosePartAxisRange.range(-120.0, 120.0)); + public static final String AXIS_X_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.x"; + public static final String AXIS_Y_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.y"; + public static final String AXIS_Z_TRANSLATION_KEY = StatuesApi.MOD_ID + ".screen.rotations.z"; + + private final String name; private final Function getRotations; private final BiFunction setRotations; private final PosePartAxisRange[] axisRanges; private final Direction.Axis[] axisOrder; private final byte invertedIndices; - public PosePartMutator(String translationId, Function getRotations, BiFunction setRotations, PosePartAxisRange rangeX, PosePartAxisRange rangeY, PosePartAxisRange rangeZ) { - this(translationId, getRotations, setRotations, rangeX, rangeY, rangeZ, new Direction.Axis[]{Direction.Axis.X, Direction.Axis.Y, Direction.Axis.Z}, Direction.Axis.Y); + public PosePartMutator(String name, Function getRotations, BiFunction setRotations, PosePartAxisRange rangeX, PosePartAxisRange rangeY, PosePartAxisRange rangeZ) { + this(name, getRotations, setRotations, rangeX, rangeY, rangeZ, new Direction.Axis[]{Direction.Axis.X, Direction.Axis.Y, Direction.Axis.Z}, Direction.Axis.Y); } - public PosePartMutator(String translationId, Function getRotations, BiFunction setRotations, PosePartAxisRange rangeX, PosePartAxisRange rangeY, PosePartAxisRange rangeZ, Direction.Axis[] axisOrder, Direction.Axis... invertedAxes) { - this.translationId = translationId; + public PosePartMutator(String name, Function getRotations, BiFunction setRotations, PosePartAxisRange rangeX, PosePartAxisRange rangeY, PosePartAxisRange rangeZ, Direction.Axis[] axisOrder, Direction.Axis... invertedAxes) { + this.name = name; this.getRotations = getRotations; this.setRotations = setRotations; this.axisRanges = new PosePartAxisRange[]{rangeX, rangeY, rangeZ}; @@ -48,16 +52,24 @@ private static byte computeInvertedIndices(Direction.Axis[] invertedAxes) { @Override public String toString() { - return this.translationId.toUpperCase(Locale.ROOT); + return this.name.toUpperCase(Locale.ROOT); } - public Component getComponent() { - return Component.translatable("armorstatues.screen.rotations.pose." + this.translationId); + public String getTranslationKey() { + return StatuesApi.MOD_ID + ".screen.rotations.pose." + this.name; } public Component getAxisComponent(ArmorStandPose pose, int index) { double value = ArmorStandPose.snapValue(this.getRotationsAtAxis(index, pose), ArmorStandPose.DEGREES_SNAP_INTERVAL); - return Component.translatable("armorstatues.screen.rotations." + this.getAxisAt(index), ArmorStandPose.ROTATION_FORMAT.format(value)); + return Component.translatable(this.getAxisTranslationKey(this.getAxisAt(index)), ArmorStandPose.ROTATION_FORMAT.format(value)); + } + + private String getAxisTranslationKey(Direction.Axis axis) { + return switch (axis) { + case X -> AXIS_X_TRANSLATION_KEY; + case Y -> AXIS_Y_TRANSLATION_KEY; + case Z -> AXIS_Z_TRANSLATION_KEY; + }; } public double getRotationsAtAxis(int index, ArmorStandPose pose) { diff --git a/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java similarity index 66% rename from Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java index c7f1cfc..9025709 100644 --- a/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java @@ -1,8 +1,9 @@ package fuzs.armorstatues.client; import fuzs.armorstatues.api.client.gui.screens.armorstand.ArmorStandScreenFactory; -import fuzs.armorstatues.api.client.init.ModClientRegistry; import fuzs.armorstatues.api.world.inventory.ArmorStandMenu; +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandAlignmentsScreen; +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; import fuzs.armorstatues.init.ModRegistry; import fuzs.puzzleslib.client.core.ClientModConstructor; import net.minecraft.network.chat.Component; @@ -10,6 +11,12 @@ public class ArmorStatuesClient implements ClientModConstructor { + @Override + public void onClientSetup() { + ArmorStandScreenFactory.register(ModRegistry.ALIGNMENTS_SCREEN_TYPE, ArmorStandAlignmentsScreen::new); + ArmorStandScreenFactory.register(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE, ArmorStandVanillaTweaksScreen::new); + } + @Override public void onRegisterMenuScreens(MenuScreensContext context) { // compiler doesn't like method reference :( @@ -17,9 +24,4 @@ public void onRegisterMenuScreens(MenuScreensContext context) { return ArmorStandScreenFactory.createLastScreenType(menu, inventory, component); }); } - - @Override - public void onRegisterKeyMappings(KeyMappingsContext context) { - context.registerKeyMappings(ModClientRegistry.CYCLE_TABS_KEY_MAPPING); - } } diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java new file mode 100644 index 0000000..da3832b --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java @@ -0,0 +1,54 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.api.client.gui.screens.armorstand.ArmorStandButtonsScreen; +import fuzs.armorstatues.api.client.gui.screens.armorstand.ArmorStandPositionScreen; +import fuzs.armorstatues.api.network.client.data.DataSyncHandler; +import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandAlignment; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; +import fuzs.armorstatues.init.ModRegistry; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.phys.Vec3; + +import java.util.EnumSet; +import java.util.List; + +public class ArmorStandAlignmentsScreen extends ArmorStandButtonsScreen { + + public ArmorStandAlignmentsScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new DoubleButtonWidget(Component.translatable(ArmorStandPositionScreen.CENTERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CENTERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)).add(0.5, 0.0, 0.5); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + }, button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + })); + for (ArmorStandAlignment alignment : ArmorStandAlignment.values()) { + widgets.add(new SingleButtonWidget(Component.translatable(alignment.getTranslationKey()), Component.translatable(alignment.getDescriptionsKey()), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + ArmorStandAlignmentsScreen.this.dataSyncHandler.sendAlignment(alignment); + })); + } + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.ALIGNMENTS_SCREEN_TYPE; + } +} diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java new file mode 100644 index 0000000..88dff11 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java @@ -0,0 +1,75 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.api.client.gui.screens.armorstand.ArmorStandButtonsScreen; +import fuzs.armorstatues.api.network.client.data.DataSyncHandler; +import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; + +import java.util.List; + +public class ArmorStandVanillaTweaksScreen extends ArmorStandButtonsScreen { + public static final String TRIGGER_SENT_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.triggerSent"; + public static final String CHECK_TARGET_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget"; + public static final String CHECK_TARGET_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget.description"; + public static final String LOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock"; + public static final String LOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock.description"; + public static final String UNLOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock"; + public static final String UNLOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock.description"; + public static final String TOOL_RACK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack"; + public static final String TOOL_RACK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack.description"; + public static final String SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand"; + public static final String SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand.description"; + public static final String SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead"; + public static final String SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead.description"; + + public ArmorStandVanillaTweaksScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + public VanillaTweaksDataSyncHandler getDataSyncHandler() { + return (VanillaTweaksDataSyncHandler) super.getDataSyncHandler(); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new SingleButtonWidget(Component.translatable(CHECK_TARGET_TRANSLATION_KEY), Component.translatable(CHECK_TARGET_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.CHECK_TARGET); + this.onClose(); + })); + widgets.add(new DoubleButtonWidget(Component.translatable(LOCK_TRANSLATION_KEY), Component.translatable(UNLOCK_TRANSLATION_KEY), Component.translatable(LOCK_DESCRIPTION_KEY), Component.translatable(UNLOCK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_LOCK); + }, button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_UNLOCK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(TOOL_RACK_TRANSLATION_KEY), Component.translatable(TOOL_RACK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.AUTO_ALIGNMENT_TOOL_RACK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_OFFHAND); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_HEAD); + })); + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE; + } +} diff --git a/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java similarity index 70% rename from Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java index 873aa31..c6c26fc 100644 --- a/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java @@ -1,6 +1,6 @@ package fuzs.armorstatues.client.handler; -import net.minecraft.ChatFormatting; +import fuzs.armorstatues.api.proxy.Proxy; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -12,9 +12,9 @@ public class ArmorStandTooltipHandler { public static void onItemTooltip(ItemStack stack, TooltipFlag context, List lines) { if (stack.is(Items.ARMOR_STAND)) { - Component component = Component.translatable("armorstatues.item.armor_stand.description").withStyle(ChatFormatting.GRAY); + Component component = Proxy.INSTANCE.getStatueHoverText(); if (context.isAdvanced()) { - lines.add(lines.size() - 1, component); + lines.add(lines.size() - (stack.hasTag() ? 2 : 1), component); } else { lines.add(component); } diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java new file mode 100644 index 0000000..e534fa6 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java @@ -0,0 +1,28 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.armorstatues.api.client.gui.screens.armorstand.ArmorStandScreen; +import fuzs.armorstatues.api.network.client.data.DataSyncHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import org.jetbrains.annotations.Nullable; + +public class DataSyncTickHandler { + @Nullable + private static DataSyncHandler dataSyncHandler; + + public static void onScreenClose(Screen screen) { + if (screen instanceof ArmorStandScreen armorStandScreen && armorStandScreen.getDataSyncHandler().shouldContinueTicking()) { + dataSyncHandler = armorStandScreen.getDataSyncHandler(); + } + } + + public static void onClientTickEnd(Minecraft minecraft) { + if (minecraft.player != null && !(minecraft.screen instanceof ArmorStandScreen) && dataSyncHandler != null) { + if (dataSyncHandler.shouldContinueTicking()) { + dataSyncHandler.tick(); + } else { + dataSyncHandler = null; + } + } + } +} diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java new file mode 100644 index 0000000..32e7897 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java @@ -0,0 +1,13 @@ +package fuzs.armorstatues.config; + +import fuzs.armorstatues.api.client.gui.screens.armorstand.AbstractArmorStandScreen; +import fuzs.puzzleslib.config.ConfigCore; +import fuzs.puzzleslib.config.annotation.Config; + +public class ClientConfig implements ConfigCore { + @Config(description = {"Allows for using this mod on a server without it (like a vanilla server) when the Vanilla Tweaks Armor Statues data pack is installed without the need for being a server operator.", "Download the Vanilla Tweaks Armor Statues data pack from here: " + AbstractArmorStandScreen.VANILLA_TWEAKS_HOMEPAGE}) + public boolean useVanillaTweaksTriggers = false; + @Config(description = "The delay in ticks for sending queued client commands for editing armor stands to the server. Increase this values if commands are sent too quickly and the server fails to process all of them.") + @Config.IntRange(min = 20) + public int clientCommandDelay = 20; +} diff --git a/Common/src/main/java/fuzs/armorstatues/core/CommonAbstractions.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/core/CommonAbstractions.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/core/CommonAbstractions.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/core/CommonAbstractions.java diff --git a/Common/src/main/java/fuzs/armorstatues/core/ModServices.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/core/ModServices.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/core/ModServices.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/core/ModServices.java diff --git a/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java similarity index 65% rename from Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java index 1a5f049..1170c20 100644 --- a/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java @@ -22,17 +22,15 @@ public class ArmorStandInteractHandler { private static boolean presentServerside; - public static Optional onEntityInteract(Player player, Level level, InteractionHand interactionHand, Entity target, Vec3 hitVector) { + public static Optional onUseEntityAt(Player player, Level level, InteractionHand interactionHand, Entity target, Vec3 hitVector) { if (!player.isSpectator() && target.getType() == EntityType.ARMOR_STAND) { - Optional result = ArmorStandInteractHelper.tryOpenArmorStatueMenu(player, level, (ArmorStand) target, ModRegistry.ARMOR_STAND_MENU_TYPE.get()); - if (result.isPresent() && level.isClientSide && !presentServerside) { - // better to check for client once more, we don't want to accidentally run on the server thread when showing the screen + Optional result = ArmorStandInteractHelper.tryOpenArmorStatueMenu(player, level, interactionHand, (ArmorStand) target, ModRegistry.ARMOR_STAND_MENU_TYPE.get(), ModRegistry.ARMOR_STAND_DATA_PROVIDER); + if (result.isPresent() && level.isClientSide && !presentServerside) { ArmorStatues.PROXY.openArmorStandScreen((ArmorStand) target, player); // required so no packet is sent to server when only installed client-side, so the server doesn't change any equipment when we only want to open the screen - // we must return InteractionResult.CONSUME so the interaction is a success, but is not sent to the server (as it will try to remove the armor stand's equipment) - // this will miss out on the player arm swing animation, which we manually play here - player.swing(interactionHand); - return Optional.of(InteractionResult.CONSUME); + // returning InteractionResult.FAIL will miss out on the player arm swing animation, which we manually play here + player.swing(interactionHand); + return Optional.of(InteractionResult.FAIL); } return result; } diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java new file mode 100644 index 0000000..47ff5c5 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java @@ -0,0 +1,32 @@ +package fuzs.armorstatues.init; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.api.world.entity.decoration.ArmorStandDataProvider; +import fuzs.armorstatues.api.world.inventory.ArmorStandMenu; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; +import fuzs.puzzleslib.core.CoreServices; +import fuzs.puzzleslib.init.RegistryManager; +import fuzs.puzzleslib.init.RegistryReference; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class ModRegistry { + static final RegistryManager REGISTRY = CoreServices.FACTORIES.registration(ArmorStatues.MOD_ID); + public static final RegistryReference> ARMOR_STAND_MENU_TYPE = REGISTRY.registerExtendedMenuTypeSupplier("armor_stand", () -> (containerId, inventory, data) -> { + return ArmorStandMenu.create(ModRegistry.ARMOR_STAND_MENU_TYPE.get(), containerId, inventory, data, ModRegistry.ARMOR_STAND_DATA_PROVIDER); + }); + public static final ArmorStandDataProvider ARMOR_STAND_DATA_PROVIDER = new ArmorStandDataProvider() { + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return new ArmorStandScreenType[]{ArmorStandScreenType.ROTATIONS, ArmorStandScreenType.POSES, ArmorStandScreenType.STYLE, ArmorStandScreenType.POSITION, ModRegistry.ALIGNMENTS_SCREEN_TYPE, ArmorStandScreenType.EQUIPMENT}; + } + }; + public static final ArmorStandScreenType ALIGNMENTS_SCREEN_TYPE = new ArmorStandScreenType("alignments", new ItemStack(Items.DIAMOND_PICKAXE)); + public static final ArmorStandScreenType VANILLA_TWEAKS_SCREEN_TYPE = new ArmorStandScreenType("vanillaTweaks", new ItemStack(Items.WRITTEN_BOOK)); + + public static void touch() { + + } +} diff --git a/Common/src/main/java/fuzs/armorstatues/mixin/accessor/ArmorStandAccessor.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/mixin/accessor/ArmorStandAccessor.java similarity index 77% rename from Common/src/main/java/fuzs/armorstatues/mixin/accessor/ArmorStandAccessor.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/mixin/accessor/ArmorStandAccessor.java index 0e68716..45e2b2e 100644 --- a/Common/src/main/java/fuzs/armorstatues/mixin/accessor/ArmorStandAccessor.java +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/mixin/accessor/ArmorStandAccessor.java @@ -2,7 +2,6 @@ import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; @@ -26,10 +25,4 @@ public interface ArmorStandAccessor { @Invoker void callReadPose(CompoundTag compound); - - @Invoker - void callBrokenByAnything(DamageSource damageSource); - - @Invoker - void callCauseDamage(DamageSource damageSource, float amount); } diff --git a/Common/src/main/java/fuzs/armorstatues/mixin/accessor/SimpleContainerAccessor.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/mixin/accessor/SimpleContainerAccessor.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/mixin/accessor/SimpleContainerAccessor.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/mixin/accessor/SimpleContainerAccessor.java diff --git a/Common/src/main/java/fuzs/armorstatues/network/S2CPingMessage.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/network/S2CPingMessage.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/network/S2CPingMessage.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/network/S2CPingMessage.java diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java new file mode 100644 index 0000000..bfb8821 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java @@ -0,0 +1,225 @@ +package fuzs.armorstatues.network.client.data; + +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.api.network.client.data.DataSyncHandler; +import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandAlignment; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandPose; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandStyleOption; +import net.minecraft.ChatFormatting; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.FloatTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.function.BiPredicate; + +public class CommandDataSyncHandler implements DataSyncHandler { + public static final String NO_PERMISSION_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noPermission"; + public static final String NO_ARMOR_STAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noArmorStand"; + public static final String OUT_OF_RANGE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.outOfRange"; + public static final String NOT_FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.notFinished"; + public static final String FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.finished"; + public static final String FAILURE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure"; + private static final Queue CLIENT_COMMAND_QUEUE = new ArrayDeque<>(); + + @Nullable + private static ArmorStand queueArmorStand; + private static int itemDequeuedTicks; + + private final ArmorStandHolder holder; + protected final LocalPlayer player; + protected ArmorStandPose lastSyncedPose; + + public CommandDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + this.holder = holder; + this.lastSyncedPose = ArmorStandPose.fromEntity(this.holder.getArmorStand()); + this.player = player; + } + + @Override + public ArmorStandHolder getArmorStandHolder() { + return this.holder; + } + + @Override + public void sendName(String name) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.setCustomArmorStandName(this.getArmorStand(), name); + CompoundTag tag = new CompoundTag(); + tag.putString("CustomName", Component.Serializer.toJson(Component.literal(name))); + this.enqueueEntityData(tag); + this.finalizeCurrentOperation(); + } + + @Override + public final void sendPose(ArmorStandPose pose) { + this.sendPose(pose, true); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + // split this into multiple chat messages as the client chat field has a very low character limit + this.sendPosePart(pose::serializeBodyPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeArmPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeLegPoses, this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + if (finalize) this.finalizeCurrentOperation(); + } + + private void sendPosePart(BiPredicate dataWriter, ArmorStandPose lastSyncedPose) { + CompoundTag tag = new CompoundTag(); + if (dataWriter.test(tag, lastSyncedPose)) { + CompoundTag tagToSend = new CompoundTag(); + tagToSend.put("Pose", tag); + this.enqueueEntityData(tagToSend); + } + } + + @Override + public @Nullable ArmorStandPose getLastSyncedPose() { + return this.lastSyncedPose; + } + + @Override + public final void sendPosition(double posX, double posY, double posZ) { + this.sendPosition(posX, posY, posZ, true); + + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(DoubleTag.valueOf(posX)); + listTag.add(DoubleTag.valueOf(posY)); + listTag.add(DoubleTag.valueOf(posZ)); + CompoundTag tag = new CompoundTag(); + tag.put("Pos", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public final void sendRotation(float rotation) { + this.sendRotation(rotation, true); + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(FloatTag.valueOf(rotation)); + CompoundTag tag = new CompoundTag(); + tag.put("Rotation", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public final void sendStyleOption(ArmorStandStyleOption styleOption, boolean value) { + this.sendStyleOption(styleOption, value, true); + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + CompoundTag tag = new CompoundTag(); + styleOption.toTag(tag, value); + this.enqueueEntityData(tag); + styleOption.setOption(this.getArmorStand(), value); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.super.sendAlignment(alignment); + } + + @Override + public boolean supportsScreenType(ArmorStandScreenType screenType) { + return !screenType.requiresServer(); + } + + @Override + public void tick() { + if (itemDequeuedTicks > 0) itemDequeuedTicks--; + if (itemDequeuedTicks == 0 && queueArmorStand != null && !CLIENT_COMMAND_QUEUE.isEmpty()) { + if (this.testArmorStand(queueArmorStand).right().isPresent()) { + this.player.commandSigned(CLIENT_COMMAND_QUEUE.poll(), null); + } else { + CLIENT_COMMAND_QUEUE.clear(); + } + itemDequeuedTicks = this.getDequeueDelayTicks(); + } else if (itemDequeuedTicks == 1 && CLIENT_COMMAND_QUEUE.isEmpty()) { + this.sendDisplayMessage(Component.translatable(FINISHED_TRANSLATION_KEY), false); + } + } + + protected int getDequeueDelayTicks() { + return 5; + } + + @Override + public boolean shouldContinueTicking() { + return !CLIENT_COMMAND_QUEUE.isEmpty() || itemDequeuedTicks != 0; + } + + protected boolean isEditingAllowed() { + return this.isEditingAllowed(true); + } + + protected final boolean isEditingAllowed(boolean testPermissionLevel) { + if (testPermissionLevel && !this.player.hasPermissions(2)) { + this.sendFailureMessage(Component.translatable(NO_PERMISSION_TRANSLATION_KEY)); + return false; + } + return this.testArmorStand(this.getArmorStand()).ifLeft(this::sendFailureMessage).right().isPresent(); + } + + protected Either testArmorStand(ArmorStand armorStand) { + return !armorStand.isAlive() ? Either.left(Component.translatable(NO_ARMOR_STAND_TRANSLATION_KEY)) : Either.right(Unit.INSTANCE); + } + + protected boolean enqueueClientCommand(String clientCommand) { + if (CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = null; + } else if (queueArmorStand != null) { + this.sendFailureMessage(Component.translatable(NOT_FINISHED_TRANSLATION_KEY)); + return false; + } + CLIENT_COMMAND_QUEUE.offer(clientCommand); + return true; + } + + @Override + public void finalizeCurrentOperation() { + if (!CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = this.getArmorStand(); + } + } + + protected void sendFailureMessage(Component component) { + this.sendDisplayMessage(Component.translatable(FAILURE_TRANSLATION_KEY, component), true); + } + + protected void sendDisplayMessage(Component component, boolean failure) { + this.player.displayClientMessage(Component.empty().append(component).withStyle(failure ? ChatFormatting.RED : ChatFormatting.GREEN), false); + } + + private void enqueueEntityData(CompoundTag tag) { + this.enqueueClientCommand("data merge entity %s %s".formatted(this.getArmorStand().getStringUUID(), tag.getAsString())); + } +} diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java new file mode 100644 index 0000000..592d7d6 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java @@ -0,0 +1,359 @@ +package fuzs.armorstatues.network.client.data; + +import com.google.common.collect.ImmutableSortedMap; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; +import fuzs.armorstatues.api.world.inventory.data.*; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.Rotations; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.NavigableMap; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +public class VanillaTweaksDataSyncHandler extends CommandDataSyncHandler { + private static final int MAX_INCREMENTAL_OPERATIONS = 12; + public static final int CHECK_TARGET = 999; + public static final int SWAP_SLOTS_MAINHAND_AND_OFFHAND = 161; + public static final int SWAP_SLOTS_MAINHAND_AND_HEAD = 162; + public static final int MIRROR_ARMS_LEFT_TO_RIGHT = 131; + public static final int MIRROR_ARMS_RIGHT_TO_LEFT = 132; + public static final int MIRROR_LEGS_LEFT_TO_RIGHT = 133; + public static final int MIRROR_LEGS_RIGHT_TO_LEFT = 134; + public static final int UTILITIES_LOCK = 1000; + public static final int UTILITIES_UNLOCK = 1001; + public static final int MIRROR_AND_FLIP_FLIP = 135; + public static final int SHOW_BASE_PLATE_YES = 1; + public static final int SHOW_BASE_PLATE_NO = 2; + public static final int SHOW_ARMS_YES = 3; + public static final int SHOW_ARMS_NO = 4; + public static final int SMALL_STAND_YES = 5; + public static final int SMALL_STAND_NO = 6; + public static final int APPLY_GRAVITY_YES = 7; + public static final int APPLY_GRAVITY_NO = 8; + public static final int STAND_VISIBLE_YES = 9; + public static final int STAND_VISIBLE_NO = 10; + public static final int DISPLAY_NAME_YES = 11; + public static final int DISPLAY_NAME_NO = 12; + public static final int NUDGE_POSITION_X8_NEGATIVE = 40; + public static final int NUDGE_POSITION_X3_NEGATIVE = 101; + public static final int NUDGE_POSITION_X1_NEGATIVE = 102; + public static final int NUDGE_POSITION_X1_POSITIVE = 103; + public static final int NUDGE_POSITION_X3_POSITIVE = 104; + public static final int NUDGE_POSITION_X8_POSITIVE = 43; + public static final int NUDGE_POSITION_Y8_NEGATIVE = 44; + public static final int NUDGE_POSITION_Y3_NEGATIVE = 105; + public static final int NUDGE_POSITION_Y1_NEGATIVE = 106; + public static final int NUDGE_POSITION_Y1_POSITIVE = 107; + public static final int NUDGE_POSITION_Y3_POSITIVE = 108; + public static final int NUDGE_POSITION_Y8_POSITIVE = 47; + public static final int NUDGE_POSITION_Z8_NEGATIVE = 48; + public static final int NUDGE_POSITION_Z3_NEGATIVE = 109; + public static final int NUDGE_POSITION_Z1_NEGATIVE = 110; + public static final int NUDGE_POSITION_Z1_POSITIVE = 111; + public static final int NUDGE_POSITION_Z3_POSITIVE = 112; + public static final int NUDGE_POSITION_Z8_POSITIVE = 51; + public static final int ADJUST_ROTATION_ANGLE_STEP_45 = 120; + public static final int ADJUST_ROTATION_ANGLE_STEP_15 = 121; + public static final int ADJUST_ROTATION_ANGLE_STEP_5 = 122; + public static final int ADJUST_ROTATION_ANGLE_STEP_1 = 123; + public static final int ADJUST_ROTATION_ROTATE_RIGHT = 56; + public static final int ADJUST_ROTATION_ROTATE_LEFT = 57; + public static final int POSE_PRESETS_ATTENTION = 20; + public static final int POSE_PRESETS_WALKING = 21; + public static final int POSE_PRESETS_RUNNING = 22; + public static final int POSE_PRESETS_POINTING = 23; + public static final int POSE_PRESETS_BLOCKING = 24; + public static final int POSE_PRESETS_LUNGEING = 25; + public static final int POSE_PRESETS_WINNING = 26; + public static final int POSE_PRESETS_SITTING = 27; + public static final int POSE_PRESETS_ARABESQUE = 28; + public static final int POSE_PRESETS_CUPID = 29; + public static final int POSE_PRESETS_CONFIDENT = 30; + public static final int POSE_PRESETS_SALUTE = 31; + public static final int POSE_PRESETS_DEATH = 32; + public static final int POSE_PRESETS_FACEPALM = 33; + public static final int POSE_PRESETS_LAZING = 34; + public static final int POSE_PRESETS_CONFUSED = 35; + public static final int POSE_PRESETS_FORMAL = 36; + public static final int POSE_PRESETS_SAD = 37; + public static final int POSE_PRESETS_JOYOUS = 38; + public static final int POSE_PRESETS_STARGAZING = 39; + public static final int AUTO_ALIGNMENT_BLOCK_ON_SURFACE = 151; + public static final int AUTO_ALIGNMENT_ITEM_ON_SURFACE = 152; + public static final int AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE = 153; + public static final int AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE = 154; + public static final int AUTO_ALIGNMENT_TOOL_RACK = 155; + public static final int POSE_ADJUSTMENT_HEAD_X_NEGATIVE = 60; + public static final int POSE_ADJUSTMENT_HEAD_X_POSITIVE = 61; + public static final int POSE_ADJUSTMENT_HEAD_Y_NEGATIVE = 62; + public static final int POSE_ADJUSTMENT_HEAD_Y_POSITIVE = 63; + public static final int POSE_ADJUSTMENT_HEAD_Z_NEGATIVE = 64; + public static final int POSE_ADJUSTMENT_HEAD_Z_POSITIVE = 65; + public static final int POSE_ADJUSTMENT_BODY_X_NEGATIVE = 67; + public static final int POSE_ADJUSTMENT_BODY_X_POSITIVE = 66; + public static final int POSE_ADJUSTMENT_BODY_Y_NEGATIVE = 68; + public static final int POSE_ADJUSTMENT_BODY_Y_POSITIVE = 69; + public static final int POSE_ADJUSTMENT_BODY_Z_NEGATIVE = 70; + public static final int POSE_ADJUSTMENT_BODY_Z_POSITIVE = 71; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE = 72; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE = 73; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE = 74; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE = 75; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE = 77; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE = 76; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE = 78; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE = 79; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE = 81; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE = 80; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE = 82; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE = 83; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE = 84; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE = 85; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE = 87; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE = 86; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE = 89; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE = 88; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE = 90; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE = 91; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE = 92; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE = 93; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE = 94; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE = 95; + private static final int[] POSE_ADJUSTMENT_HEAD = new int[]{POSE_ADJUSTMENT_HEAD_X_NEGATIVE, POSE_ADJUSTMENT_HEAD_X_POSITIVE, POSE_ADJUSTMENT_HEAD_Y_NEGATIVE, POSE_ADJUSTMENT_HEAD_Y_POSITIVE, POSE_ADJUSTMENT_HEAD_Z_NEGATIVE, POSE_ADJUSTMENT_HEAD_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_BODY = new int[]{POSE_ADJUSTMENT_BODY_X_POSITIVE, POSE_ADJUSTMENT_BODY_X_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_POSITIVE, POSE_ADJUSTMENT_BODY_Z_NEGATIVE, POSE_ADJUSTMENT_BODY_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_ARM = new int[]{POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_ARM = new int[]{POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_LEG = new int[]{POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_LEG = new int[]{POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE}; + private static final NavigableMap NUDGE_POSITIONS_X_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_X3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_X8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_X_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_X3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_X8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_POSITIVE); + private static final NavigableMap ADJUST_ROTATION_ANGLE_STEPS = ImmutableSortedMap.of(1.0F, ADJUST_ROTATION_ANGLE_STEP_1, 5.0F, ADJUST_ROTATION_ANGLE_STEP_5, 15.0F, ADJUST_ROTATION_ANGLE_STEP_15, 45.0F, ADJUST_ROTATION_ANGLE_STEP_45); + + public VanillaTweaksDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + super(holder, player); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue = this.getTriggerValueFromPose(pose); + if (triggerValue != -1) { + if (this.enqueueTriggerValue(triggerValue)) { + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + } + } else { + this.tryApplyAllPoseParts(pose); + } + if (finalize) this.finalizeCurrentOperation(); + } + + private int getTriggerValueFromPose(ArmorStandPose pose) { + if (pose.getSourceType() == ArmorStandPose.SourceType.EMPTY) return POSE_PRESETS_ATTENTION; + if (pose.getSourceType() == ArmorStandPose.SourceType.MIRRORED) return MIRROR_AND_FLIP_FLIP; + if (pose.getSourceType() != ArmorStandPose.SourceType.VANILLA_TWEAKS) return -1; + if (pose == ArmorStandPose.WALKING) return POSE_PRESETS_WALKING; + if (pose == ArmorStandPose.RUNNING) return POSE_PRESETS_RUNNING; + if (pose == ArmorStandPose.POINTING) return POSE_PRESETS_POINTING; + if (pose == ArmorStandPose.BLOCKING) return POSE_PRESETS_BLOCKING; + if (pose == ArmorStandPose.LUNGEING) return POSE_PRESETS_LUNGEING; + if (pose == ArmorStandPose.WINNING) return POSE_PRESETS_WINNING; + if (pose == ArmorStandPose.SITTING) return POSE_PRESETS_SITTING; + if (pose == ArmorStandPose.ARABESQUE) return POSE_PRESETS_ARABESQUE; + if (pose == ArmorStandPose.CUPID) return POSE_PRESETS_CUPID; + if (pose == ArmorStandPose.CONFIDENT) return POSE_PRESETS_CONFIDENT; + if (pose == ArmorStandPose.SALUTE) return POSE_PRESETS_SALUTE; + if (pose == ArmorStandPose.DEATH) return POSE_PRESETS_DEATH; + if (pose == ArmorStandPose.FACEPALM) return POSE_PRESETS_FACEPALM; + if (pose == ArmorStandPose.LAZING) return POSE_PRESETS_LAZING; + if (pose == ArmorStandPose.CONFUSED) return POSE_PRESETS_CONFUSED; + if (pose == ArmorStandPose.FORMAL) return POSE_PRESETS_FORMAL; + if (pose == ArmorStandPose.SAD) return POSE_PRESETS_SAD; + if (pose == ArmorStandPose.JOYOUS) return POSE_PRESETS_JOYOUS; + if (pose == ArmorStandPose.STARGAZING) return POSE_PRESETS_STARGAZING; + return -1; + } + + private void tryApplyAllPoseParts(ArmorStandPose pose) { + if (!this.tryApplyPosePart(this.lastSyncedPose.getHeadPose(), pose.getNullableHeadPose(), POSE_ADJUSTMENT_HEAD, this.lastSyncedPose::withHeadPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getBodyPose(), pose.getNullableBodyPose(), POSE_ADJUSTMENT_BODY, this.lastSyncedPose::withBodyPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightArmPose(), pose.getNullableRightArmPose(), POSE_ADJUSTMENT_RIGHT_ARM, this.lastSyncedPose::withRightArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftArmPose(), pose.getNullableLeftArmPose(), POSE_ADJUSTMENT_LEFT_ARM, this.lastSyncedPose::withLeftArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightLegPose(), pose.getNullableRightLegPose(), POSE_ADJUSTMENT_RIGHT_LEG, this.lastSyncedPose::withRightLegPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftLegPose(), pose.getNullableLeftLegPose(), POSE_ADJUSTMENT_LEFT_LEG, this.lastSyncedPose::withLeftLegPose)) + return; + } + + private boolean tryApplyPosePart(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment, Function function) { + if (this.tryApplyPoseAdjustment(oldPose, newPose, poseAdjustment)) { + this.lastSyncedPose = function.apply(newPose != null ? newPose : oldPose); + return true; + } else { + return false; + } + } + + private boolean tryApplyPoseAdjustment(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment) { + if (newPose == null || oldPose.equals(newPose)) return true; + if (!this.applyIncrementsFromSteps(oldPose.getX(), newPose.getX(), poseAdjustment[0], poseAdjustment[1])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getY(), newPose.getY(), poseAdjustment[2], poseAdjustment[3])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getZ(), newPose.getZ(), poseAdjustment[4], poseAdjustment[5])) return false; + return true; + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyPositionIncrements(this.getArmorStand().getX(), posX, NUDGE_POSITIONS_X_POSITIVE, NUDGE_POSITIONS_X_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getY(), posY, NUDGE_POSITIONS_Y_POSITIVE, NUDGE_POSITIONS_Y_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getZ(), posZ, NUDGE_POSITIONS_Z_POSITIVE, NUDGE_POSITIONS_Z_NEGATIVE); + if (finalize) this.finalizeCurrentOperation(); + } + + private void applyPositionIncrements(double oldValue, double newValue, NavigableMap positiveNudgePositions, NavigableMap negativeNudgePositions) { + double value = newValue - oldValue; + double signum = Math.signum(value); + value = Math.abs(value); + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = (signum == -1.0F ? negativeNudgePositions : positiveNudgePositions).floorEntry(value); + if (entry != null) { + value -= entry.getKey(); + if (!this.enqueueTriggerValue(entry.getValue())) { + return; + } + } else { + break; + } + } + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyIncrementsFromSteps(this.getArmorStand().getYRot(), rotation, ADJUST_ROTATION_ROTATE_RIGHT, ADJUST_ROTATION_ROTATE_LEFT); + if (finalize) this.finalizeCurrentOperation(); + } + + private boolean applyIncrementsFromSteps(float oldValue, float newValue, int triggerValueNegative, int triggerValuePositive) { + float value = newValue - oldValue; + float signum = Math.signum(value); + value = Math.abs(value); + float lastIncrement = 0.0F; + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = ADJUST_ROTATION_ANGLE_STEPS.floorEntry(value); + if (entry != null) { + float currentIncrement = entry.getKey(); + value -= currentIncrement; + if (currentIncrement != lastIncrement) { + lastIncrement = currentIncrement; + if (!this.enqueueTriggerValue(entry.getValue())) { + return false; + } + } + if (!this.enqueueTriggerValue(signum == -1.0F ? triggerValuePositive : triggerValueNegative)) { + return false; + } + } else { + break; + } + } + return true; + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue; + if (styleOption == ArmorStandStyleOptions.SHOW_NAME) { + triggerValue = value ? DISPLAY_NAME_YES : DISPLAY_NAME_NO; + } else if (styleOption == ArmorStandStyleOptions.SHOW_ARMS) { + triggerValue = value ? SHOW_ARMS_YES : SHOW_ARMS_NO; + } else if (styleOption == ArmorStandStyleOptions.SMALL) { + triggerValue = value ? SMALL_STAND_YES : SMALL_STAND_NO; + } else if (styleOption == ArmorStandStyleOptions.INVISIBLE) { + triggerValue = value ? STAND_VISIBLE_NO : STAND_VISIBLE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_BASE_PLATE) { + triggerValue = value ? SHOW_BASE_PLATE_NO : SHOW_BASE_PLATE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_GRAVITY) { + triggerValue = value ? APPLY_GRAVITY_NO : APPLY_GRAVITY_YES; + } else { + super.sendStyleOption(styleOption, value, finalize); + return; + } + if (this.sendSingleTriggerValue(triggerValue, finalize)) { + styleOption.setOption(this.getArmorStand(), value); + } + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + int triggerValue = switch (alignment) { + case BLOCK -> AUTO_ALIGNMENT_BLOCK_ON_SURFACE; + case FLOATING_ITEM -> AUTO_ALIGNMENT_ITEM_ON_SURFACE; + case FLAT_ITEM -> AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE; + case TOOL -> AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE; + }; + this.sendSingleTriggerValue(triggerValue, true); + } + + public void sendSingleTriggerValue(int triggerValue) { + if (!this.isEditingAllowed()) return; + this.sendSingleTriggerValue(triggerValue, true); + } + + private boolean sendSingleTriggerValue(int triggerValue, boolean finalize) { + boolean result = this.enqueueTriggerValue(triggerValue); + if (finalize) this.finalizeCurrentOperation(); + return result; + } + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return Stream.concat(Stream.of(super.getScreenTypes()), Stream.of(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE)).toArray(ArmorStandScreenType[]::new); + } + + @Override + protected boolean isEditingAllowed() { + return this.isEditingAllowed(false); + } + + @Override + protected Either testArmorStand(ArmorStand armorStand) { + return super.testArmorStand(armorStand).>map(Optional::of, $ -> { + if (this.player.distanceToSqr(armorStand) < 9.0) { + return Optional.empty(); + } else { + return Optional.of(Component.translatable(OUT_OF_RANGE_TRANSLATION_KEY)); + } + }).>map(Either::left).orElse(Either.right(Unit.INSTANCE)); + } + + @Override + protected int getDequeueDelayTicks() { + return ArmorStatues.CONFIG.get(ClientConfig.class).clientCommandDelay; + } + + private boolean enqueueTriggerValue(int triggerValue) { + return this.enqueueClientCommand("trigger as_trigger set %s".formatted(triggerValue)); + } +} diff --git a/1.19.2/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java new file mode 100644 index 0000000..9ea63a2 --- /dev/null +++ b/1.19.2/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java @@ -0,0 +1,47 @@ +package fuzs.armorstatues.proxy; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.api.client.gui.screens.armorstand.ArmorStandScreenFactory; +import fuzs.armorstatues.api.world.entity.decoration.ArmorStandDataProvider; +import fuzs.armorstatues.api.world.inventory.ArmorStandMenu; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.armorstatues.api.network.client.data.DataSyncHandler; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; +import fuzs.armorstatues.config.ClientConfig; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ClientProxy extends ServerProxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + ArmorStandHolder holder = new ArmorStandHolder() { + + @Override + public ArmorStand getArmorStand() { + return armorStand; + } + + @Override + public ArmorStandDataProvider getDataProvider() { + return ModRegistry.ARMOR_STAND_DATA_PROVIDER; + } + }; + Screen screen = ArmorStandScreenFactory.createLastScreenType(holder, player.getInventory(), armorStand.getDisplayName(), createDataSyncHandler(holder, (LocalPlayer) player)); + Minecraft minecraft = Minecraft.getInstance(); + minecraft.setScreen(screen); + } + + private static DataSyncHandler createDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + if (!player.hasPermissions(2) && ArmorStatues.CONFIG.get(ClientConfig.class).useVanillaTweaksTriggers) { + return new VanillaTweaksDataSyncHandler(holder, player); + } else { + return new CommandDataSyncHandler(holder, player); + } + } +} diff --git a/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java diff --git a/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java b/1.19.2/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java similarity index 100% rename from Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java rename to 1.19.2/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java diff --git a/Common/src/main/resources/armorstatues.common.mixins.json b/1.19.2/Common/src/main/resources/armorstatues.common.mixins.json similarity index 84% rename from Common/src/main/resources/armorstatues.common.mixins.json rename to 1.19.2/Common/src/main/resources/armorstatues.common.mixins.json index c543bb7..6306a76 100644 --- a/Common/src/main/resources/armorstatues.common.mixins.json +++ b/1.19.2/Common/src/main/resources/armorstatues.common.mixins.json @@ -4,6 +4,7 @@ "compatibilityLevel": "JAVA_17", "package": "fuzs.armorstatues.mixin", "refmap": "armorstatues.refmap.json", + "plugin": "fuzs.armorstatues.mixin.ModMixinConfigPlugin", "mixins": [ "accessor.ArmorStandAccessor", "accessor.SimpleContainerAccessor" diff --git a/Common/src/main/resources/mod_banner.png b/1.19.2/Common/src/main/resources/mod_banner.png similarity index 100% rename from Common/src/main/resources/mod_banner.png rename to 1.19.2/Common/src/main/resources/mod_banner.png diff --git a/Common/src/main/resources/mod_logo.png b/1.19.2/Common/src/main/resources/mod_logo.png similarity index 100% rename from Common/src/main/resources/mod_logo.png rename to 1.19.2/Common/src/main/resources/mod_logo.png diff --git a/1.19.2/Common/src/main/resources/pack.mcmeta b/1.19.2/Common/src/main/resources/pack.mcmeta new file mode 100755 index 0000000..19bfab2 --- /dev/null +++ b/1.19.2/Common/src/main/resources/pack.mcmeta @@ -0,0 +1,8 @@ +{ + "pack": { + "description": "${modDescription}", + "pack_format": ${resourcePackFormat}, + "forge:resource_pack_format": ${resourcePackFormat}, + "forge:data_pack_format": ${dataPackFormat} + } +} diff --git a/1.19.2/Fabric/build.gradle b/1.19.2/Fabric/build.gradle new file mode 100644 index 0000000..cd8b641 --- /dev/null +++ b/1.19.2/Fabric/build.gradle @@ -0,0 +1,29 @@ +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/fabric.gradle' + +def versionCatalog = extensions.getByType(VersionCatalogsExtension).named("libs") + +dependencies { + // Fabric Api + modApi libs.fabricapi.fabric + + // Puzzles Lib + modApi libs.puzzleslib.fabric + + // Cardinal Components +// modApi(include(libs.cardinalcomponentsbase.fabric.get())) +// modApi(include(libs.cardinalcomponentsentity.fabric.get())) +// modApi(include(libs.cardinalcomponentsblock.fabric.get())) +// modApi(include(libs.cardinalcomponentschunk.fabric.get())) +// modApi(include(libs.cardinalcomponentsworld.fabric.get())) + + // Extensible Enums +// modApi(include(libs.extensibleenums.fabric.get())) + + // Quality of Life Mods + versionCatalog.findLibrary("modmenu.fabric").ifPresent { + modLocalRuntime(it) + } + versionCatalog.findLibrary("forgeconfigscreens.fabric").ifPresent { + modLocalRuntime(it) + } +} diff --git a/Fabric/src/main/java/fuzs/armorstatues/ArmorStatuesFabric.java b/1.19.2/Fabric/src/main/java/fuzs/armorstatues/ArmorStatuesFabric.java similarity index 77% rename from Fabric/src/main/java/fuzs/armorstatues/ArmorStatuesFabric.java rename to 1.19.2/Fabric/src/main/java/fuzs/armorstatues/ArmorStatuesFabric.java index 87fd287..4a639cd 100644 --- a/Fabric/src/main/java/fuzs/armorstatues/ArmorStatuesFabric.java +++ b/1.19.2/Fabric/src/main/java/fuzs/armorstatues/ArmorStatuesFabric.java @@ -1,6 +1,6 @@ package fuzs.armorstatues; -import fuzs.armorstatues.api.ArmorStatuesApi; +import fuzs.armorstatues.api.StatuesApi; import fuzs.armorstatues.handler.ArmorStandInteractHandler; import fuzs.puzzleslib.core.CoreServices; import net.fabricmc.api.ModInitializer; @@ -13,22 +13,22 @@ import net.minecraft.world.level.Level; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.Vec3; -import org.jetbrains.annotations.Nullable; public class ArmorStatuesFabric implements ModInitializer { @Override public void onInitialize() { - CoreServices.FACTORIES.modConstructor(ArmorStatues.MOD_ID).accept(new ArmorStatuesApi()); + CoreServices.FACTORIES.modConstructor(ArmorStatues.MOD_ID).accept(new StatuesApi()); CoreServices.FACTORIES.modConstructor(ArmorStatues.MOD_ID).accept(new ArmorStatues()); registerHandlers(); } private static void registerHandlers() { UseEntityCallback.EVENT.register((Player player, Level level, InteractionHand interactionHand, Entity target, EntityHitResult entityHitResult) -> { - // entityHitResult is marked as nullable in Fabric Api, but can actually not be null + // this callback runs in two places, one runs only for armor stands and is hit location aware, that's the one we need + if (entityHitResult == null) return InteractionResult.PASS; Vec3 vec3 = entityHitResult.getLocation().subtract(target.getX(), target.getY(), target.getZ()); - return ArmorStandInteractHandler.onEntityInteract(player, level, interactionHand, target, vec3).orElse(InteractionResult.PASS); + return ArmorStandInteractHandler.onUseEntityAt(player, level, interactionHand, target, vec3).orElse(InteractionResult.PASS); }); ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { ArmorStandInteractHandler.onPlayerLoggedIn(handler.getPlayer()); diff --git a/Fabric/src/main/java/fuzs/armorstatues/client/ArmorStatuesFabricClient.java b/1.19.2/Fabric/src/main/java/fuzs/armorstatues/client/ArmorStatuesFabricClient.java similarity index 59% rename from Fabric/src/main/java/fuzs/armorstatues/client/ArmorStatuesFabricClient.java rename to 1.19.2/Fabric/src/main/java/fuzs/armorstatues/client/ArmorStatuesFabricClient.java index 2d2fc96..6005d3e 100644 --- a/Fabric/src/main/java/fuzs/armorstatues/client/ArmorStatuesFabricClient.java +++ b/1.19.2/Fabric/src/main/java/fuzs/armorstatues/client/ArmorStatuesFabricClient.java @@ -1,19 +1,23 @@ package fuzs.armorstatues.client; import fuzs.armorstatues.ArmorStatues; -import fuzs.armorstatues.api.client.ArmorStatuesApiClient; +import fuzs.armorstatues.api.client.StatuesApiClient; +import fuzs.armorstatues.api.client.gui.screens.armorstand.ArmorStandScreen; import fuzs.armorstatues.client.handler.ArmorStandTooltipHandler; import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.armorstatues.client.handler.DataSyncTickHandler; import fuzs.puzzleslib.client.core.ClientCoreServices; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; public class ArmorStatuesFabricClient implements ClientModInitializer { @Override public void onInitializeClient() { - ClientCoreServices.FACTORIES.clientModConstructor(ArmorStatues.MOD_ID).accept(new ArmorStatuesApiClient()); + ClientCoreServices.FACTORIES.clientModConstructor(ArmorStatues.MOD_ID).accept(new StatuesApiClient()); ClientCoreServices.FACTORIES.clientModConstructor(ArmorStatues.MOD_ID).accept(new ArmorStatuesClient()); registerHandlers(); } @@ -23,5 +27,11 @@ private static void registerHandlers() { ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> { ArmorStandInteractHandler.clearPresentServerside(); }); + ClientTickEvents.END_CLIENT_TICK.register(DataSyncTickHandler::onClientTickEnd); + ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + if (screen instanceof ArmorStandScreen) { + ScreenEvents.remove(screen).register(DataSyncTickHandler::onScreenClose); + } + }); } } diff --git a/Fabric/src/main/java/fuzs/armorstatues/core/FabricAbstractions.java b/1.19.2/Fabric/src/main/java/fuzs/armorstatues/core/FabricAbstractions.java similarity index 100% rename from Fabric/src/main/java/fuzs/armorstatues/core/FabricAbstractions.java rename to 1.19.2/Fabric/src/main/java/fuzs/armorstatues/core/FabricAbstractions.java diff --git a/1.19.2/Fabric/src/main/java/fuzs/armorstatues/mixin/ModMixinConfigPlugin.java b/1.19.2/Fabric/src/main/java/fuzs/armorstatues/mixin/ModMixinConfigPlugin.java new file mode 100644 index 0000000..33c80a2 --- /dev/null +++ b/1.19.2/Fabric/src/main/java/fuzs/armorstatues/mixin/ModMixinConfigPlugin.java @@ -0,0 +1,47 @@ +package fuzs.armorstatues.mixin; + +import net.fabricmc.loader.api.FabricLoader; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class ModMixinConfigPlugin implements IMixinConfigPlugin { + + @Override + public void onLoad(String mixinPackage) { + + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return FabricLoader.getInstance().isModLoaded("puzzleslib"); + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } +} diff --git a/Fabric/src/main/resources/META-INF/services/fuzs.armorstatues.core.CommonAbstractions b/1.19.2/Fabric/src/main/resources/META-INF/services/fuzs.armorstatues.core.CommonAbstractions similarity index 100% rename from Fabric/src/main/resources/META-INF/services/fuzs.armorstatues.core.CommonAbstractions rename to 1.19.2/Fabric/src/main/resources/META-INF/services/fuzs.armorstatues.core.CommonAbstractions diff --git a/Fabric/src/main/resources/fabric.mod.json b/1.19.2/Fabric/src/main/resources/fabric.mod.json similarity index 88% rename from Fabric/src/main/resources/fabric.mod.json rename to 1.19.2/Fabric/src/main/resources/fabric.mod.json index e370de7..c0c6cfa 100644 --- a/Fabric/src/main/resources/fabric.mod.json +++ b/1.19.2/Fabric/src/main/resources/fabric.mod.json @@ -5,19 +5,22 @@ "name": "${modName}", "description": "${modDescription}", + "authors": [ "${modAuthor}" ], + "contact": { "homepage": "${modPageUrl}", "issues": "${modIssueUrl}", "sources": "${modPageUrl}" }, - "license": "MPL-2", + "license": "${modLicense}", "icon": "mod_logo.png", "environment": "${modFabricEnvironment}", + "entrypoints": { "main": [ "${mainEntryPoint}" @@ -26,6 +29,7 @@ "${clientEntryPoint}" ] }, + "mixins": [ "${modId}.common.mixins.json" ], @@ -34,7 +38,7 @@ "fabricloader": ">=${minFabricVersion}", "fabric-api": ">=${minFabricApiVersion}", "puzzleslib": ">=${minPuzzlesVersion}", - "minecraft": ">=${minMinecraftVersion} <${nextMinecraftVersion}", + "minecraft": "${minecraftVersion}", "java": ">=17" } } diff --git a/1.19.2/Forge/build.gradle b/1.19.2/Forge/build.gradle new file mode 100644 index 0000000..fc4b4de --- /dev/null +++ b/1.19.2/Forge/build.gradle @@ -0,0 +1,29 @@ +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/forge.gradle' + +def versionCatalog = extensions.getByType(VersionCatalogsExtension).named("libs") + +dependencies { + // Puzzles Lib + api fg.deobf(libs.puzzleslib.forge.get()) + + // Quality of Life Mods + versionCatalog.findLibrary("bettermodsbutton.forge").ifPresent { + runtimeOnly fg.deobf(it.get()) + } +// runtimeOnly fg.deobf("fuzs.bettermodsbutton:bettermodsbutton-forge:${libs.versions.bettermodsbutton.get()}") + versionCatalog.findLibrary("forgeconfigscreens.forge").ifPresent { + runtimeOnly fg.deobf(it.get()) + } +} + +task signJar(type: net.minecraftforge.gradle.common.tasks.SignJar, dependsOn: tasks.reobfJarJar) { + onlyIf { project.hasProperty('keyStore') } + keyStore = project.findProperty('keyStore') + alias = project.findProperty('keyStoreAlias') + storePass = project.findProperty('keyStorePass') + keyPass = project.findProperty('keyStoreKeyPass') + inputFile = outputFile = tasks.jarJar.archivePath +} + +jar.finalizedBy 'signJar' +signJar.mustRunAfter 'reobfJar' diff --git a/1.19.2/Forge/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 b/1.19.2/Forge/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 new file mode 100644 index 0000000..525b2ea --- /dev/null +++ b/1.19.2/Forge/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 @@ -0,0 +1,2 @@ +// 1.19.2 2023-07-29T15:17:53.05358 Languages: en_us +4471aca48f8c14711835624b702c98ab8f90b317 assets/statues/lang/en_us.json diff --git a/1.19.2/Forge/src/generated/resources/assets/statues/lang/en_us.json b/1.19.2/Forge/src/generated/resources/assets/statues/lang/en_us.json new file mode 100644 index 0000000..f3b0b60 --- /dev/null +++ b/1.19.2/Forge/src/generated/resources/assets/statues/lang/en_us.json @@ -0,0 +1,116 @@ +{ + "armorstatues.dataSync.failure": "Unable to modify armor stand data: %s", + "armorstatues.dataSync.failure.noArmorStand": "No Valid Armor Stand", + "armorstatues.dataSync.failure.noPermission": "No Permission", + "armorstatues.dataSync.failure.notFinished": "Queue Not Empty", + "armorstatues.dataSync.failure.outOfRange": "Out Of Range", + "armorstatues.dataSync.finished": "Finished sending queued armor stand data", + "armorstatues.screen.vanillaTweaks.checkTarget": "Check Armor Stand Target", + "armorstatues.screen.vanillaTweaks.checkTarget.description": "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, this isn't necessarily the armor stand which opened this menu.", + "armorstatues.screen.vanillaTweaks.lock": "Lock", + "armorstatues.screen.vanillaTweaks.lock.description": "Locking an armor stand prevents it from being changed using this menu and disables interaction with the equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead": "Swap Mainhand & Helmet", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead.description": "Swaps items between the main hand and helmet equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand": "Swap Mainhand & Offhand", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand.description": "Swaps items between the main hand and off hand equipment slots.", + "armorstatues.screen.vanillaTweaks.toolRack": "Align Tool As Tool Rack", + "armorstatues.screen.vanillaTweaks.toolRack.description": "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand.", + "armorstatues.screen.vanillaTweaks.triggerSent": "Sent!", + "armorstatues.screen.vanillaTweaks.unlock": "Unlock", + "armorstatues.screen.vanillaTweaks.unlock.description": "Unlocking an armor stand reverts any adjustments made via a previous lock action.", + "item.minecraft.armor_stand.description": "Use [%s] + [%s] with an empty hand to open configuration screen.", + "statues.screen.aligned": "Aligned!", + "statues.screen.alignments.block": "Align Block On Surface", + "statues.screen.alignments.block.description": "Align an armor stand placed on a surface so that a block held by it appears on the surface.", + "statues.screen.alignments.itemFlat": "Align Item Flat On Surface", + "statues.screen.alignments.itemFlat.description": "Align an armor stand placed on a surface so that a non-tool item held by it appears flat on the surface.", + "statues.screen.alignments.itemFloating": "Align Item On Surface", + "statues.screen.alignments.itemFloating.description": "Align an armor stand placed on a surface so that an item held by it appears upright on the surface.", + "statues.screen.alignments.tool": "Align Tool Flat On Surface", + "statues.screen.alignments.tool.description": "Align an armor stand placed on a surface so that a tool held by it appears flat on the surface.", + "statues.screen.centered": "Align Centered", + "statues.screen.centered.description": "Align an armor stand in the center of the block position it is placed on.", + "statues.screen.cornered": "Align Cornered", + "statues.screen.cornered.description": "Align an armor stand at the corner of the block position it is placed on.", + "statues.screen.credits": "Some content on this page originates from the Vanilla Tweaks \"Armor Statues\" data pack. Click this button to go to their website!", + "statues.screen.pose.arabesque": "Arabesque", + "statues.screen.pose.athena": "Athena", + "statues.screen.pose.blocking": "Blocking", + "statues.screen.pose.brandish": "Brandish", + "statues.screen.pose.by": "By %s", + "statues.screen.pose.cancan": "Cancan", + "statues.screen.pose.confident": "Confident", + "statues.screen.pose.confused": "Confused", + "statues.screen.pose.cupid": "Cupid", + "statues.screen.pose.death": "Death", + "statues.screen.pose.default": "Default", + "statues.screen.pose.entertain": "Entertain", + "statues.screen.pose.facepalm": "Facepalm", + "statues.screen.pose.formal": "Formal", + "statues.screen.pose.hero": "Hero", + "statues.screen.pose.honor": "Honor", + "statues.screen.pose.joyous": "Joyous", + "statues.screen.pose.lazing": "Lazing", + "statues.screen.pose.lungeing": "Lungeing", + "statues.screen.pose.pointing": "Pointing", + "statues.screen.pose.riposte": "Riposte", + "statues.screen.pose.running": "Running", + "statues.screen.pose.sad": "Sad", + "statues.screen.pose.salute": "Salute", + "statues.screen.pose.sitting": "Sitting", + "statues.screen.pose.solemn": "Solemn", + "statues.screen.pose.stargazing": "Stargazing", + "statues.screen.pose.walking": "Walking", + "statues.screen.pose.winning": "Winning", + "statues.screen.pose.zombie": "Zombie", + "statues.screen.position.blocks": "%s Block(s)", + "statues.screen.position.decrement": "Decrement by %s", + "statues.screen.position.degrees": "%s°", + "statues.screen.position.increment": "Increment by %s", + "statues.screen.position.moveBy": "Move By:", + "statues.screen.position.pixels": "%s Pixel(s)", + "statues.screen.position.rotation": "Rotation:", + "statues.screen.position.x": "X-Position:", + "statues.screen.position.y": "Y-Position:", + "statues.screen.position.z": "Z-Position:", + "statues.screen.rotations.copy": "Copy", + "statues.screen.rotations.limited": "Limited Rotations", + "statues.screen.rotations.mirror": "Mirror", + "statues.screen.rotations.paste": "Paste", + "statues.screen.rotations.pose.body": "Body", + "statues.screen.rotations.pose.head": "Head", + "statues.screen.rotations.pose.leftArm": "Left Arm", + "statues.screen.rotations.pose.leftLeg": "Left Leg", + "statues.screen.rotations.pose.rightArm": "Right Arm", + "statues.screen.rotations.pose.rightLeg": "Right Leg", + "statues.screen.rotations.randomize": "Randomize", + "statues.screen.rotations.reset": "Reset", + "statues.screen.rotations.tip1": "Hold any [§dShift§r] or [§dAlt§r] key to lock two-dimensional sliders to a single axis while dragging!", + "statues.screen.rotations.tip2": "Use arrow keys to move sliders with greater precision than when dragging! Focus a slider first by clicking.", + "statues.screen.rotations.unlimited": "Unlimited Rotations", + "statues.screen.rotations.x": "X: %s", + "statues.screen.rotations.y": "Y: %s", + "statues.screen.rotations.z": "Z: %s", + "statues.screen.style.invisible": "Invisible", + "statues.screen.style.invisible.description": "Makes the statue itself invisible, but still shows all equipped items.", + "statues.screen.style.name": "Set a name to display above the entity if enabled.", + "statues.screen.style.noBasePlate": "No Base Plate", + "statues.screen.style.noBasePlate.description": "Hide the stone base plate at the statue's feet.", + "statues.screen.style.noGravity": "No Gravity", + "statues.screen.style.noGravity.description": "Prevents the statue from falling down, so it may float freely.", + "statues.screen.style.sealed": "Sealed", + "statues.screen.style.sealed.description": "The statue can no longer be broken, equipment cannot be changed. Disallows opening this menu in survival mode.", + "statues.screen.style.showArms": "Show Arms", + "statues.screen.style.showArms.description": "Shows the statue's arms, so it may hold items in both hands.", + "statues.screen.style.showName": "Show Name", + "statues.screen.style.showName.description": "Render the statue's name tag above it's head.", + "statues.screen.style.small": "Small", + "statues.screen.style.small.description": "Makes the statue half it's size like a baby mob.", + "statues.screen.type.alignments": "Alignments", + "statues.screen.type.equipment": "Equipment", + "statues.screen.type.poses": "Poses", + "statues.screen.type.position": "Position", + "statues.screen.type.rotations": "Rotations", + "statues.screen.type.style": "Style", + "statues.screen.type.vanillaTweaks": "Vanilla Tweaks" +} \ No newline at end of file diff --git a/Forge/src/main/java/fuzs/armorstatues/ArmorStatuesForge.java b/1.19.2/Forge/src/main/java/fuzs/armorstatues/ArmorStatuesForge.java similarity index 78% rename from Forge/src/main/java/fuzs/armorstatues/ArmorStatuesForge.java rename to 1.19.2/Forge/src/main/java/fuzs/armorstatues/ArmorStatuesForge.java index 1f3f9c4..e95fa8d 100644 --- a/Forge/src/main/java/fuzs/armorstatues/ArmorStatuesForge.java +++ b/1.19.2/Forge/src/main/java/fuzs/armorstatues/ArmorStatuesForge.java @@ -1,6 +1,6 @@ package fuzs.armorstatues; -import fuzs.armorstatues.api.ArmorStatuesApi; +import fuzs.armorstatues.api.StatuesApi; import fuzs.armorstatues.data.ModLanguageProvider; import fuzs.armorstatues.handler.ArmorStandInteractHandler; import fuzs.puzzleslib.core.CoreServices; @@ -11,6 +11,7 @@ import net.minecraftforge.data.event.GatherDataEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; @@ -21,16 +22,15 @@ public class ArmorStatuesForge { @SubscribeEvent public static void onConstructMod(final FMLConstructModEvent evt) { - CoreServices.FACTORIES.modConstructor(ArmorStatues.MOD_ID).accept(new ArmorStatuesApi()); + CoreServices.FACTORIES.modConstructor(ArmorStatues.MOD_ID).accept(new StatuesApi()); CoreServices.FACTORIES.modConstructor(ArmorStatues.MOD_ID).accept(new ArmorStatues()); registerHandlers(); } private static void registerHandlers() { - MinecraftForge.EVENT_BUS.addListener((final PlayerInteractEvent.EntityInteractSpecific evt) -> { - ArmorStandInteractHandler.onEntityInteract(evt.getEntity(), evt.getLevel(), evt.getHand(), evt.getTarget(), evt.getLocalPos()).ifPresent(result -> { - // we use our custom event client-side, as it allows for cancelling the packet being sent to the server - if (!evt.getSide().isServer()) return; + // high priority so we run before the Quark mod + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGH, (final PlayerInteractEvent.EntityInteractSpecific evt) -> { + ArmorStandInteractHandler.onUseEntityAt(evt.getEntity(), evt.getLevel(), evt.getHand(), evt.getTarget(), evt.getLocalPos()).ifPresent(result -> { evt.setCancellationResult(result); evt.setCanceled(true); }); @@ -44,6 +44,6 @@ private static void registerHandlers() { public static void onGatherData(final GatherDataEvent evt) { DataGenerator generator = evt.getGenerator(); final ExistingFileHelper existingFileHelper = evt.getExistingFileHelper(); - generator.addProvider(true, new ModLanguageProvider(generator, ArmorStatuesApi.MOD_ID)); + generator.addProvider(true, new ModLanguageProvider(generator, StatuesApi.MOD_ID)); } } diff --git a/1.19.2/Forge/src/main/java/fuzs/armorstatues/client/ArmorStatuesForgeClient.java b/1.19.2/Forge/src/main/java/fuzs/armorstatues/client/ArmorStatuesForgeClient.java new file mode 100644 index 0000000..c537aee --- /dev/null +++ b/1.19.2/Forge/src/main/java/fuzs/armorstatues/client/ArmorStatuesForgeClient.java @@ -0,0 +1,73 @@ +package fuzs.armorstatues.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.api.client.StatuesApiClient; +import fuzs.armorstatues.client.handler.ArmorStandTooltipHandler; +import fuzs.armorstatues.client.handler.DataSyncTickHandler; +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.puzzleslib.client.core.ClientCoreServices; +import net.minecraft.client.Minecraft; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.ClientPlayerNetworkEvent; +import net.minecraftforge.client.event.InputEvent; +import net.minecraftforge.client.event.ScreenEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; + +import java.util.Optional; + +@Mod.EventBusSubscriber(modid = ArmorStatues.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) +public class ArmorStatuesForgeClient { + + @SubscribeEvent + public static void onConstructMod(final FMLConstructModEvent evt) { + ClientCoreServices.FACTORIES.clientModConstructor(ArmorStatues.MOD_ID).accept(new StatuesApiClient()); + ClientCoreServices.FACTORIES.clientModConstructor(ArmorStatues.MOD_ID).accept(new ArmorStatuesClient()); + registerHandlers(); + } + + private static void registerHandlers() { + MinecraftForge.EVENT_BUS.addListener((final InputEvent.InteractionKeyMappingTriggered evt) -> { + + Minecraft minecraft = Minecraft.getInstance(); + if (evt.isUseItem() && minecraft.hitResult != null && minecraft.hitResult.getType() == HitResult.Type.ENTITY) { + + EntityHitResult hitResult = (EntityHitResult) minecraft.hitResult; + Entity entity = hitResult.getEntity(); + if (minecraft.level.getWorldBorder().isWithinBounds(entity.blockPosition()) && minecraft.player.canInteractWith(entity, 0)) { + + Vec3 hitVector = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ()); + Optional result = ArmorStandInteractHandler.onUseEntityAt(minecraft.player, minecraft.level, evt.getHand(), entity, hitVector); + // if InteractionResult.FAIL is returned the mod is missing server-side, and we open the menu client-side without sending a packet to the server, so the server does not try to interact + // only Fabric sending the packet is simple prevented by returning InteractionResult.FAIL from ArmorStandInteractHandler::onUseEntityAt, on Forge this approach seems to work + if (result.filter(t -> t == InteractionResult.FAIL).isPresent()) { + evt.setSwingHand(false); + evt.setCanceled(true); + } + } + } + }); + MinecraftForge.EVENT_BUS.addListener((final ItemTooltipEvent evt) -> { + ArmorStandTooltipHandler.onItemTooltip(evt.getItemStack(), evt.getFlags(), evt.getToolTip()); + }); + MinecraftForge.EVENT_BUS.addListener((final ClientPlayerNetworkEvent.LoggingIn evt) -> { + ArmorStandInteractHandler.clearPresentServerside(); + }); + MinecraftForge.EVENT_BUS.addListener((final TickEvent.ClientTickEvent evt) -> { + if (evt.phase != TickEvent.Phase.END) return; + DataSyncTickHandler.onClientTickEnd(Minecraft.getInstance()); + }); + MinecraftForge.EVENT_BUS.addListener((final ScreenEvent.Closing evt) -> { + DataSyncTickHandler.onScreenClose(evt.getScreen()); + }); + } +} diff --git a/Forge/src/main/java/fuzs/armorstatues/core/ForgeAbstractions.java b/1.19.2/Forge/src/main/java/fuzs/armorstatues/core/ForgeAbstractions.java similarity index 100% rename from Forge/src/main/java/fuzs/armorstatues/core/ForgeAbstractions.java rename to 1.19.2/Forge/src/main/java/fuzs/armorstatues/core/ForgeAbstractions.java diff --git a/1.19.2/Forge/src/main/java/fuzs/armorstatues/data/ModLanguageProvider.java b/1.19.2/Forge/src/main/java/fuzs/armorstatues/data/ModLanguageProvider.java new file mode 100644 index 0000000..91e0ee1 --- /dev/null +++ b/1.19.2/Forge/src/main/java/fuzs/armorstatues/data/ModLanguageProvider.java @@ -0,0 +1,138 @@ +package fuzs.armorstatues.data; + +import fuzs.armorstatues.api.client.gui.screens.armorstand.*; +import fuzs.armorstatues.api.proxy.ClientProxy; +import fuzs.armorstatues.api.world.inventory.data.*; +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.armorstatues.api.world.inventory.data.ArmorStandAlignment; +import net.minecraft.data.DataGenerator; +import net.minecraftforge.common.data.LanguageProvider; + +public class ModLanguageProvider extends LanguageProvider { + + public ModLanguageProvider(DataGenerator gen, String modId) { + super(gen, modId, "en_us"); + } + + @Override + protected void addTranslations() { + // Armor Statues + this.add(CommandDataSyncHandler.FAILURE_TRANSLATION_KEY, "Unable to modify armor stand data: %s"); + this.add(CommandDataSyncHandler.NO_PERMISSION_TRANSLATION_KEY, "No Permission"); + this.add(CommandDataSyncHandler.NO_ARMOR_STAND_TRANSLATION_KEY, "No Valid Armor Stand"); + this.add(CommandDataSyncHandler.OUT_OF_RANGE_TRANSLATION_KEY, "Out Of Range"); + this.add(CommandDataSyncHandler.NOT_FINISHED_TRANSLATION_KEY, "Queue Not Empty"); + this.add(CommandDataSyncHandler.FINISHED_TRANSLATION_KEY, "Finished sending queued armor stand data"); + this.add(ModRegistry.ALIGNMENTS_SCREEN_TYPE.getTranslationKey(), "Alignments"); + this.add(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE.getTranslationKey(), "Vanilla Tweaks"); + this.add(ArmorStandVanillaTweaksScreen.TRIGGER_SENT_TRANSLATION_KEY, "Sent!"); + this.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_TRANSLATION_KEY, "Check Armor Stand Target"); + this.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_DESCRIPTION_KEY, "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, this isn't necessarily the armor stand which opened this menu."); + this.add(ArmorStandVanillaTweaksScreen.LOCK_TRANSLATION_KEY, "Lock"); + this.add(ArmorStandVanillaTweaksScreen.LOCK_DESCRIPTION_KEY, "Locking an armor stand prevents it from being changed using this menu and disables interaction with the equipment slots."); + this.add(ArmorStandVanillaTweaksScreen.UNLOCK_TRANSLATION_KEY, "Unlock"); + this.add(ArmorStandVanillaTweaksScreen.UNLOCK_DESCRIPTION_KEY, "Unlocking an armor stand reverts any adjustments made via a previous lock action."); + this.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_TRANSLATION_KEY, "Align Tool As Tool Rack"); + this.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_DESCRIPTION_KEY, "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand."); + this.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY, "Swap Mainhand & Offhand"); + this.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY, "Swaps items between the main hand and off hand equipment slots."); + this.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY, "Swap Mainhand & Helmet"); + this.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY, "Swaps items between the main hand and helmet equipment slots."); + // Statues Api + this.add(ClientProxy.OPEN_SCREEN_TRANSLATION_KEY, "Use [%s] + [%s] with an empty hand to open configuration screen."); + this.add(ArmorStandPosesScreen.POSE_SOURCE_TRANSLATION_KEY, "By %s"); + this.add(ArmorStandPose.ATHENA.getTranslationKey(), "Athena"); + this.add(ArmorStandPose.BRANDISH.getTranslationKey(), "Brandish"); + this.add(ArmorStandPose.CANCAN.getTranslationKey(), "Cancan"); + this.add(ArmorStandPose.DEFAULT.getTranslationKey(), "Default"); + this.add(ArmorStandPose.ENTERTAIN.getTranslationKey(), "Entertain"); + this.add(ArmorStandPose.HERO.getTranslationKey(), "Hero"); + this.add(ArmorStandPose.HONOR.getTranslationKey(), "Honor"); + this.add(ArmorStandPose.RIPOSTE.getTranslationKey(), "Riposte"); + this.add(ArmorStandPose.SALUTE.getTranslationKey(), "Salute"); + this.add(ArmorStandPose.SOLEMN.getTranslationKey(), "Solemn"); + this.add(ArmorStandPose.ZOMBIE.getTranslationKey(), "Zombie"); + this.add(ArmorStandPose.WALKING.getTranslationKey(), "Walking"); + this.add(ArmorStandPose.RUNNING.getTranslationKey(), "Running"); + this.add(ArmorStandPose.POINTING.getTranslationKey(), "Pointing"); + this.add(ArmorStandPose.BLOCKING.getTranslationKey(), "Blocking"); + this.add(ArmorStandPose.LUNGEING.getTranslationKey(), "Lungeing"); + this.add(ArmorStandPose.WINNING.getTranslationKey(), "Winning"); + this.add(ArmorStandPose.SITTING.getTranslationKey(), "Sitting"); + this.add(ArmorStandPose.ARABESQUE.getTranslationKey(), "Arabesque"); + this.add(ArmorStandPose.CUPID.getTranslationKey(), "Cupid"); + this.add(ArmorStandPose.CONFIDENT.getTranslationKey(), "Confident"); + this.add(ArmorStandPose.DEATH.getTranslationKey(), "Death"); + this.add(ArmorStandPose.FACEPALM.getTranslationKey(), "Facepalm"); + this.add(ArmorStandPose.LAZING.getTranslationKey(), "Lazing"); + this.add(ArmorStandPose.CONFUSED.getTranslationKey(), "Confused"); + this.add(ArmorStandPose.FORMAL.getTranslationKey(), "Formal"); + this.add(ArmorStandPose.SAD.getTranslationKey(), "Sad"); + this.add(ArmorStandPose.JOYOUS.getTranslationKey(), "Joyous"); + this.add(ArmorStandPose.STARGAZING.getTranslationKey(), "Stargazing"); + this.add(ArmorStandScreenType.EQUIPMENT.getTranslationKey(), "Equipment"); + this.add(ArmorStandScreenType.ROTATIONS.getTranslationKey(), "Rotations"); + this.add(ArmorStandScreenType.STYLE.getTranslationKey(), "Style"); + this.add(ArmorStandScreenType.POSES.getTranslationKey(), "Poses"); + this.add(ArmorStandScreenType.POSITION.getTranslationKey(), "Position"); + this.add(ArmorStandStyleScreen.TEXT_BOX_TRANSLATION_KEY, "Set a name to display above the entity if enabled."); + this.add(ArmorStandStyleOptions.SHOW_ARMS.getTranslationKey(), "Show Arms"); + this.add(ArmorStandStyleOptions.SHOW_ARMS.getDescriptionKey(), "Shows the statue's arms, so it may hold items in both hands."); + this.add(ArmorStandStyleOptions.SMALL.getTranslationKey(), "Small"); + this.add(ArmorStandStyleOptions.SMALL.getDescriptionKey(), "Makes the statue half it's size like a baby mob."); + this.add(ArmorStandStyleOptions.INVISIBLE.getTranslationKey(), "Invisible"); + this.add(ArmorStandStyleOptions.INVISIBLE.getDescriptionKey(), "Makes the statue itself invisible, but still shows all equipped items."); + this.add(ArmorStandStyleOptions.NO_BASE_PLATE.getTranslationKey(), "No Base Plate"); + this.add(ArmorStandStyleOptions.NO_BASE_PLATE.getDescriptionKey(), "Hide the stone base plate at the statue's feet."); + this.add(ArmorStandStyleOptions.SHOW_NAME.getTranslationKey(), "Show Name"); + this.add(ArmorStandStyleOptions.SHOW_NAME.getDescriptionKey(), "Render the statue's name tag above it's head."); + this.add(ArmorStandStyleOptions.NO_GRAVITY.getTranslationKey(), "No Gravity"); + this.add(ArmorStandStyleOptions.NO_GRAVITY.getDescriptionKey(), "Prevents the statue from falling down, so it may float freely."); + this.add(ArmorStandStyleOptions.SEALED.getTranslationKey(), "Sealed"); + this.add(ArmorStandStyleOptions.SEALED.getDescriptionKey(), "The statue can no longer be broken, equipment cannot be changed. Disallows opening this menu in survival mode."); + this.add(ArmorStandPositionScreen.ROTATION_TRANSLATION_KEY, "Rotation:"); + this.add(ArmorStandPositionScreen.POSITION_X_TRANSLATION_KEY, "X-Position:"); + this.add(ArmorStandPositionScreen.POSITION_Y_TRANSLATION_KEY, "Y-Position:"); + this.add(ArmorStandPositionScreen.POSITION_Z_TRANSLATION_KEY, "Z-Position:"); + this.add(ArmorStandPositionScreen.INCREMENT_TRANSLATION_KEY, "Increment by %s"); + this.add(ArmorStandPositionScreen.DECREMENT_TRANSLATION_KEY, "Decrement by %s"); + this.add(ArmorStandPositionScreen.DEGREES_TRANSLATION_KEY, "%s\u00B0"); + this.add(ArmorStandPositionScreen.MOVE_BY_TRANSLATION_KEY, "Move By:"); + this.add(ArmorStandPositionScreen.PIXELS_TRANSLATION_KEY, "%s Pixel(s)"); + this.add(ArmorStandPositionScreen.BLOCKS_TRANSLATION_KEY, "%s Block(s)"); + this.add(ArmorStandPositionScreen.CENTERED_TRANSLATION_KEY, "Align Centered"); + this.add(ArmorStandPositionScreen.CENTERED_DESCRIPTION_TRANSLATION_KEY, "Align an armor stand in the center of the block position it is placed on."); + this.add(ArmorStandPositionScreen.CORNERED_TRANSLATION_KEY, "Align Cornered"); + this.add(ArmorStandPositionScreen.CORNERED_DESCRIPTION_TRANSLATION_KEY, "Align an armor stand at the corner of the block position it is placed on."); + this.add(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY, "Aligned!"); + this.add(PosePartMutator.HEAD.getTranslationKey(), "Head"); + this.add(PosePartMutator.BODY.getTranslationKey(), "Body"); + this.add(PosePartMutator.LEFT_ARM.getTranslationKey(), "Left Arm"); + this.add(PosePartMutator.RIGHT_ARM.getTranslationKey(), "Right Arm"); + this.add(PosePartMutator.LEFT_LEG.getTranslationKey(), "Left Leg"); + this.add(PosePartMutator.RIGHT_LEG.getTranslationKey(), "Right Leg"); + this.add(PosePartMutator.AXIS_X_TRANSLATION_KEY, "X: %s"); + this.add(PosePartMutator.AXIS_Y_TRANSLATION_KEY, "Y: %s"); + this.add(PosePartMutator.AXIS_Z_TRANSLATION_KEY, "Z: %s"); + this.add(ArmorStandRotationsScreen.TIP_TRANSLATION_KEY + 1, "Hold any [§dShift§r] or [§dAlt§r] key to lock two-dimensional sliders to a single axis while dragging!"); + this.add(ArmorStandRotationsScreen.TIP_TRANSLATION_KEY + 2, "Use arrow keys to move sliders with greater precision than when dragging! Focus a slider first by clicking."); + this.add(ArmorStandRotationsScreen.RESET_TRANSLATION_KEY, "Reset"); + this.add(ArmorStandRotationsScreen.RANDOMIZE_TRANSLATION_KEY, "Randomize"); + this.add(ArmorStandRotationsScreen.LIMITED_TRANSLATION_KEY, "Limited Rotations"); + this.add(ArmorStandRotationsScreen.UNLIMITED_TRANSLATION_KEY, "Unlimited Rotations"); + this.add(ArmorStandRotationsScreen.COPY_TRANSLATION_KEY, "Copy"); + this.add(ArmorStandRotationsScreen.PASTE_TRANSLATION_KEY, "Paste"); + this.add(ArmorStandRotationsScreen.MIRROR_TRANSLATION_KEY, "Mirror"); + this.add(ArmorStandAlignment.BLOCK.getTranslationKey(), "Align Block On Surface"); + this.add(ArmorStandAlignment.BLOCK.getDescriptionsKey(), "Align an armor stand placed on a surface so that a block held by it appears on the surface."); + this.add(ArmorStandAlignment.FLOATING_ITEM.getTranslationKey(), "Align Item On Surface"); + this.add(ArmorStandAlignment.FLOATING_ITEM.getDescriptionsKey(), "Align an armor stand placed on a surface so that an item held by it appears upright on the surface."); + this.add(ArmorStandAlignment.FLAT_ITEM.getTranslationKey(), "Align Item Flat On Surface"); + this.add(ArmorStandAlignment.FLAT_ITEM.getDescriptionsKey(), "Align an armor stand placed on a surface so that a non-tool item held by it appears flat on the surface."); + this.add(ArmorStandAlignment.TOOL.getTranslationKey(), "Align Tool Flat On Surface"); + this.add(ArmorStandAlignment.TOOL.getDescriptionsKey(), "Align an armor stand placed on a surface so that a tool held by it appears flat on the surface."); + this.add(AbstractArmorStandScreen.CREDITS_TRANSLATION_KEY, "Some content on this page originates from the Vanilla Tweaks \"Armor Statues\" data pack. Click this button to go to their website!"); + } +} diff --git a/1.19.2/Forge/src/main/java/fuzs/armorstatues/mixin/ModMixinConfigPlugin.java b/1.19.2/Forge/src/main/java/fuzs/armorstatues/mixin/ModMixinConfigPlugin.java new file mode 100644 index 0000000..b9a58bc --- /dev/null +++ b/1.19.2/Forge/src/main/java/fuzs/armorstatues/mixin/ModMixinConfigPlugin.java @@ -0,0 +1,47 @@ +package fuzs.armorstatues.mixin; + +import net.minecraftforge.fml.loading.FMLLoader; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class ModMixinConfigPlugin implements IMixinConfigPlugin { + + @Override + public void onLoad(String mixinPackage) { + + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return FMLLoader.getLoadingModList().getModFileById("puzzleslib") != null; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } +} diff --git a/1.19.2/Forge/src/main/resources/META-INF/mods.toml b/1.19.2/Forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..e15b638 --- /dev/null +++ b/1.19.2/Forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,40 @@ +modLoader = "javafml" +loaderVersion = "[${minFMLVersion},)" +license = "${modLicense}" +issueTrackerURL = "${modIssueUrl}" + +[[mods]] +modId = "${modId}" +displayName = "${modName}" +description = "${modDescription}" +version = "${modVersion}" +authors = "${modAuthor}" +logoFile = "mod_banner.png" +logoBlur = false +displayURL = "${modPageUrl}" +updateJSONURL = "${modUpdateUrl}" +displayTest = "${modForgeDisplayTest}" + +[[dependencies.${ modId }]] +modId = "forge" +mandatory = true +versionRange = "[${minForgeVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "minecraft" +mandatory = true +versionRange = "[${minecraftVersion}]" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "puzzleslib" +mandatory = true +versionRange = "[${minPuzzlesVersion},)" +ordering = "NONE" +side = "BOTH" + +[modproperties.${ modId }] +catalogueImageIcon = "mod_logo.png" diff --git a/Forge/src/main/resources/META-INF/services/fuzs.armorstatues.core.CommonAbstractions b/1.19.2/Forge/src/main/resources/META-INF/services/fuzs.armorstatues.core.CommonAbstractions similarity index 100% rename from Forge/src/main/resources/META-INF/services/fuzs.armorstatues.core.CommonAbstractions rename to 1.19.2/Forge/src/main/resources/META-INF/services/fuzs.armorstatues.core.CommonAbstractions diff --git a/1.19.2/Forge/src/main/resources/assets/statues/textures/gui/container/statue/background.png b/1.19.2/Forge/src/main/resources/assets/statues/textures/gui/container/statue/background.png new file mode 100644 index 0000000..46a5a9a Binary files /dev/null and b/1.19.2/Forge/src/main/resources/assets/statues/textures/gui/container/statue/background.png differ diff --git a/Forge/src/main/resources/assets/armorstatues/textures/gui/container/armor_stand/equipment.png b/1.19.2/Forge/src/main/resources/assets/statues/textures/gui/container/statue/equipment.png similarity index 100% rename from Forge/src/main/resources/assets/armorstatues/textures/gui/container/armor_stand/equipment.png rename to 1.19.2/Forge/src/main/resources/assets/statues/textures/gui/container/statue/equipment.png diff --git a/1.19.2/Forge/src/main/resources/assets/statues/textures/gui/container/statue/widgets.png b/1.19.2/Forge/src/main/resources/assets/statues/textures/gui/container/statue/widgets.png new file mode 100644 index 0000000..4af651b Binary files /dev/null and b/1.19.2/Forge/src/main/resources/assets/statues/textures/gui/container/statue/widgets.png differ diff --git a/Forge/src/main/resources/assets/armorstatues/textures/item/empty_armor_slot_sword.png b/1.19.2/Forge/src/main/resources/assets/statues/textures/item/empty_armor_slot_sword.png similarity index 100% rename from Forge/src/main/resources/assets/armorstatues/textures/item/empty_armor_slot_sword.png rename to 1.19.2/Forge/src/main/resources/assets/statues/textures/item/empty_armor_slot_sword.png diff --git a/1.19.2/build.gradle b/1.19.2/build.gradle new file mode 100644 index 0000000..fadade3 --- /dev/null +++ b/1.19.2/build.gradle @@ -0,0 +1,12 @@ +plugins { + alias libs.plugins.loom apply false + alias libs.plugins.quiltflower apply false + alias libs.plugins.forgegradle apply false + alias libs.plugins.mixin apply false + alias libs.plugins.librarian apply false + alias libs.plugins.cursegradle apply false + alias libs.plugins.minotaur apply false +} + +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/main.gradle' +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/tasks.gradle' diff --git a/gradle.properties b/1.19.2/gradle.properties similarity index 65% rename from gradle.properties rename to 1.19.2/gradle.properties index 7f4cfed..c0873ca 100755 --- a/gradle.properties +++ b/1.19.2/gradle.properties @@ -3,32 +3,15 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false org.gradle.parallel=true - -# Common Project -minecraftVersion=1.19.2 -minMinecraftVersion=1.19.2 -parchmentMappingsVersion=2022.08.14 -puzzlesVersion=4.3.11 -minPuzzlesVersion=4.3.7 -packFormat=9 copyBuildJar=true -# Forge Project -forgeVersion=1.19.2-43.1.43 -minForgeVersion=43.1.40 - -# Fabric Project -fabricVersion=0.14.9 -minFabricVersion=0.14.9 -fabricApiVersion=0.60.0+1.19.2 -minFabricApiVersion=0.60.0 - # Mod Attributes modId=armorstatues modName=Armor Statues -modVersion=4.0.1 +modVersion=4.0.8 modAuthor=Fuzs modDescription=Unlock the full potential of armor stands! Works on vanilla servers, too. +modLicense=MPL-2.0 modSourceUrl=https://github.com/Fuzss/armorstatues modIssueUrl=https://github.com/Fuzss/armorstatues/issues modUpdateUrl=https://raw.githubusercontent.com/Fuzss/modresources/main/update/armorstatues.json @@ -40,6 +23,15 @@ modFabricEnvironment=* # Mod Publishing projectReleaseType=release -projectGameVersions=1.19.2 -projectCurseId=682566 +projectCurseForgeId=682566 projectModrinthId=bbGCtEvb + +dependenciesVersionCatalog=1.19.2-v11 +#dependenciesPuzzlesLibVersion=4.0.0 +#dependenciesMinPuzzlesLibVersion=4.0.0 +dependenciesRequiredForgeCurseForge=puzzles-lib +dependenciesRequiredFabricCurseForge=fabric-api, forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredForgeModrinth=puzzles-lib +dependenciesRequiredFabricModrinth=fabric-api, forge-config-api-port, puzzles-lib +#dependenciesEmbeddedFabricCurseForge=cardinal-components +#dependenciesEmbeddedFabricModrinth=cardinal-components-api diff --git a/1.19.2/gradle/wrapper/gradle-wrapper.jar b/1.19.2/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..943f0cb Binary files /dev/null and b/1.19.2/gradle/wrapper/gradle-wrapper.jar differ diff --git a/1.19.2/gradle/wrapper/gradle-wrapper.properties b/1.19.2/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..f398c33 --- /dev/null +++ b/1.19.2/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/1.19.2/gradlew b/1.19.2/gradlew new file mode 100755 index 0000000..65dcd68 --- /dev/null +++ b/1.19.2/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/1.19.2/gradlew.bat b/1.19.2/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/1.19.2/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/1.19.2/settings.gradle b/1.19.2/settings.gradle new file mode 100644 index 0000000..bf7e2e5 --- /dev/null +++ b/1.19.2/settings.gradle @@ -0,0 +1,23 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + maven { + name = 'Sponge' + url = 'https://repo.spongepowered.org/repository/maven-public/' + } + maven { + name = 'Quilt' + url = 'https://maven.quiltmc.org/repository/release' + } + maven { + name = 'Minecraft Forge' + url = 'https://maven.minecraftforge.net/' + } + } +} + +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/settings.gradle' diff --git a/1.20.1/CHANGELOG.md b/1.20.1/CHANGELOG.md new file mode 100644 index 0000000..1e48d5c --- /dev/null +++ b/1.20.1/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog]. + +## [v8.0.6-1.20.1] - 2024-04-07 +### Changed +- Allow `override_client_permissions_check` config option to also force enabling of the Vanilla Tweaks triggers command handler + +## [v8.0.5-1.20.1] - 2023-12-03 +### Changed +- Client side permissions check can now be disabled in the config +### Fixed +- Armor stand menu can no longer open in adventure mode + +## [v8.0.4-1.20.1] - 2023-12-03 +### Changed +- Updated to Puzzles Api v8.1.4 + +## [v8.0.3-1.20.1] - 2023-08-16 +### Fixed +- Fixed a crash trying to interact with an armor stand when playing on a Forge server that does not have Armor Statues installed (Forge only) + +## [v8.0.2-1.20.1] - 2023-07-29 +### Fixed +- Fixed an issue with always receiving an incorrect error message when trying to edit an armor stand on a server with the Vanilla Tweaks data pack installed + +## [v8.0.1-1.20.1] - 2023-07-28 +### Added +- Added a new tab that only shows when Armor Statues is being used in conjunction with the Vanilla Tweaks data pack offering Vanilla Tweaks exclusive toggles +### Changed +- Optimized some actions during editing to be performed much quicker when Armor Statues is being used in conjunction with the Vanilla Tweaks data pack + +## [v8.0.0-1.20.1] - 2023-07-27 +- Ported to Minecraft 1.20.1 + +[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ diff --git a/1.20.1/Common/build.gradle b/1.20.1/Common/build.gradle new file mode 100644 index 0000000..2434c15 --- /dev/null +++ b/1.20.1/Common/build.gradle @@ -0,0 +1,16 @@ +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/common.gradle' + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.common + + // Puzzles Api + api(libs.puzzlesapi.common) { + exclude group: "fuzs.puzzleslib" + } +} + +// @see https://github.com/jaredlll08/MultiLoader-Template/issues/17#issuecomment-1221598082 +tasks.withType(net.fabricmc.loom.task.AbstractRemapJarTask).each { + it.targetNamespace = "named" +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java new file mode 100644 index 0000000..041fd20 --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java @@ -0,0 +1,41 @@ +package fuzs.armorstatues; + +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.S2CPingMessage; +import fuzs.puzzleslib.api.config.v3.ConfigHolder; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import fuzs.puzzleslib.api.event.v1.core.EventPhase; +import fuzs.puzzleslib.api.event.v1.entity.player.PlayerEvents; +import fuzs.puzzleslib.api.event.v1.entity.player.PlayerInteractEvents; +import fuzs.puzzleslib.api.network.v2.MessageDirection; +import fuzs.puzzleslib.api.network.v2.NetworkHandlerV2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ArmorStatues implements ModConstructor { + public static final String MOD_ID = "armorstatues"; + public static final String MOD_NAME = "Armor Statues"; + public static final Logger LOGGER = LoggerFactory.getLogger(MOD_NAME); + + public static final NetworkHandlerV2 NETWORK = NetworkHandlerV2.build(MOD_ID, true, true); + public static final ConfigHolder CONFIG = ConfigHolder.builder(MOD_ID).client(ClientConfig.class); + + @Override + public void onConstructMod() { + ModRegistry.touch(); + registerMessages(); + registerHandlers(); + } + + private static void registerMessages() { + NETWORK.register(S2CPingMessage.class, S2CPingMessage::new, MessageDirection.TO_CLIENT); + } + + private static void registerHandlers() { + // high priority so we run before the Quark mod + PlayerInteractEvents.USE_ENTITY_AT.register(EventPhase.BEFORE, ArmorStandInteractHandler::onUseEntityAt); + PlayerEvents.LOGGED_IN.register(ArmorStandInteractHandler::onLoggedIn); + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java new file mode 100644 index 0000000..42de006 --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java @@ -0,0 +1,45 @@ +package fuzs.armorstatues.client; + +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandAlignmentsScreen; +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.client.handler.ArmorStandTooltipHandler; +import fuzs.armorstatues.client.handler.DataSyncTickHandler; +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzlesapi.api.client.statues.v1.gui.screens.armorstand.ArmorStandScreenFactory; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.ArmorStandMenu; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import fuzs.puzzleslib.api.client.event.v1.ClientPlayerEvents; +import fuzs.puzzleslib.api.client.event.v1.ClientTickEvents; +import fuzs.puzzleslib.api.client.event.v1.ItemTooltipCallback; +import fuzs.puzzleslib.api.client.event.v1.ScreenEvents; +import net.minecraft.client.gui.screens.MenuScreens; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; + +public class ArmorStatuesClient implements ClientModConstructor { + + @Override + public void onConstructMod() { + registerHandlers(); + } + + private static void registerHandlers() { + ItemTooltipCallback.EVENT.register(ArmorStandTooltipHandler::onItemTooltip); + ClientTickEvents.END.register(DataSyncTickHandler::onEndClientTick); + ScreenEvents.remove(Screen.class).register(DataSyncTickHandler::onRemove); + ClientPlayerEvents.LOGGED_IN.register(ArmorStandInteractHandler::onLoggedIn); + } + + @SuppressWarnings("Convert2MethodRef") + @Override + public void onClientSetup() { + // compiler doesn't like method reference :( + MenuScreens.register(ModRegistry.ARMOR_STAND_MENU_TYPE.get(), (ArmorStandMenu menu, Inventory inventory, Component component) -> { + return ArmorStandScreenFactory.createLastScreenType(menu, inventory, component); + }); + ArmorStandScreenFactory.register(ModRegistry.ALIGNMENTS_SCREEN_TYPE, ArmorStandAlignmentsScreen::new); + ArmorStandScreenFactory.register(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE, ArmorStandVanillaTweaksScreen::new); + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java new file mode 100644 index 0000000..da972b8 --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java @@ -0,0 +1,54 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzlesapi.api.client.statues.v1.gui.screens.armorstand.ArmorStandButtonsScreen; +import fuzs.puzzlesapi.api.client.statues.v1.gui.screens.armorstand.ArmorStandPositionScreen; +import fuzs.puzzlesapi.api.statues.v1.network.client.data.DataSyncHandler; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.ArmorStandHolder; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.data.ArmorStandAlignment; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.phys.Vec3; + +import java.util.EnumSet; +import java.util.List; + +public class ArmorStandAlignmentsScreen extends ArmorStandButtonsScreen { + + public ArmorStandAlignmentsScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new DoubleButtonWidget(Component.translatable(ArmorStandPositionScreen.CENTERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CENTERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)).add(0.5, 0.0, 0.5); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + }, button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + })); + for (ArmorStandAlignment alignment : ArmorStandAlignment.values()) { + widgets.add(new SingleButtonWidget(Component.translatable(alignment.getTranslationKey()), Component.translatable(alignment.getDescriptionsKey()), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + ArmorStandAlignmentsScreen.this.dataSyncHandler.sendAlignment(alignment); + })); + } + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.ALIGNMENTS_SCREEN_TYPE; + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java new file mode 100644 index 0000000..ed09c7d --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java @@ -0,0 +1,75 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.puzzlesapi.api.client.statues.v1.gui.screens.armorstand.ArmorStandButtonsScreen; +import fuzs.puzzlesapi.api.statues.v1.network.client.data.DataSyncHandler; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.ArmorStandHolder; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; + +import java.util.List; + +public class ArmorStandVanillaTweaksScreen extends ArmorStandButtonsScreen { + public static final String TRIGGER_SENT_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.triggerSent"; + public static final String CHECK_TARGET_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget"; + public static final String CHECK_TARGET_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget.description"; + public static final String LOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock"; + public static final String LOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock.description"; + public static final String UNLOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock"; + public static final String UNLOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock.description"; + public static final String TOOL_RACK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack"; + public static final String TOOL_RACK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack.description"; + public static final String SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand"; + public static final String SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand.description"; + public static final String SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead"; + public static final String SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead.description"; + + public ArmorStandVanillaTweaksScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + public VanillaTweaksDataSyncHandler getDataSyncHandler() { + return (VanillaTweaksDataSyncHandler) super.getDataSyncHandler(); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new SingleButtonWidget(Component.translatable(CHECK_TARGET_TRANSLATION_KEY), Component.translatable(CHECK_TARGET_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.CHECK_TARGET); + this.onClose(); + })); + widgets.add(new DoubleButtonWidget(Component.translatable(LOCK_TRANSLATION_KEY), Component.translatable(UNLOCK_TRANSLATION_KEY), Component.translatable(LOCK_DESCRIPTION_KEY), Component.translatable(UNLOCK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_LOCK); + }, button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_UNLOCK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(TOOL_RACK_TRANSLATION_KEY), Component.translatable(TOOL_RACK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.AUTO_ALIGNMENT_TOOL_RACK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_OFFHAND); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_HEAD); + })); + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE; + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java new file mode 100644 index 0000000..929250d --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java @@ -0,0 +1,25 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.puzzlesapi.api.statues.v1.helper.ArmorStandInteractHelper; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.TooltipFlag; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ArmorStandTooltipHandler { + + public static void onItemTooltip(ItemStack stack, @Nullable Player player, List lines, TooltipFlag context) { + if (stack.is(Items.ARMOR_STAND)) { + Component component = ArmorStandInteractHelper.getArmorStandHoverText(); + if (context.isAdvanced()) { + lines.add(lines.size() - (stack.hasTag() ? 2 : 1), component); + } else { + lines.add(component); + } + } + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java new file mode 100644 index 0000000..91ccdad --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java @@ -0,0 +1,28 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.puzzlesapi.api.client.statues.v1.gui.screens.armorstand.ArmorStandScreen; +import fuzs.puzzlesapi.api.statues.v1.network.client.data.DataSyncHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import org.jetbrains.annotations.Nullable; + +public class DataSyncTickHandler { + @Nullable + private static DataSyncHandler dataSyncHandler; + + public static void onRemove(Screen screen) { + if (screen instanceof ArmorStandScreen armorStandScreen && armorStandScreen.getDataSyncHandler().shouldContinueTicking()) { + dataSyncHandler = armorStandScreen.getDataSyncHandler(); + } + } + + public static void onEndClientTick(Minecraft minecraft) { + if (minecraft.player != null && !(minecraft.screen instanceof ArmorStandScreen) && dataSyncHandler != null) { + if (dataSyncHandler.shouldContinueTicking()) { + dataSyncHandler.tick(); + } else { + dataSyncHandler = null; + } + } + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java new file mode 100644 index 0000000..6e2a684 --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java @@ -0,0 +1,15 @@ +package fuzs.armorstatues.config; + +import fuzs.puzzlesapi.api.client.statues.v1.gui.screens.armorstand.AbstractArmorStandScreen; +import fuzs.puzzleslib.api.config.v3.Config; +import fuzs.puzzleslib.api.config.v3.ConfigCore; + +public class ClientConfig implements ConfigCore { + @Config(description = {"Allows for using this mod on a server without it (like a vanilla server) when the Vanilla Tweaks Armor Statues data pack is installed without the need for being a server operator.", "Download the Vanilla Tweaks Armor Statues data pack from here: " + AbstractArmorStandScreen.VANILLA_TWEAKS_HOMEPAGE}) + public boolean useVanillaTweaksTriggers = false; + @Config(description = "Do not check if the client has the necessary permission level for executing the '/data' command when trying to edit an armor stand. Useful when the current server is using custom permissions handling.") + public boolean overrideClientPermissionsCheck = false; + @Config(description = "The delay in ticks for sending queued client commands for editing armor stands to the server. Increase this values if commands are sent too quickly and the server fails to process all of them.") + @Config.IntRange(min = 20) + public int clientCommandDelay = 20; +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java new file mode 100644 index 0000000..7e4f965 --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java @@ -0,0 +1,55 @@ +package fuzs.armorstatues.handler; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.S2CPingMessage; +import fuzs.armorstatues.proxy.Proxy; +import fuzs.puzzlesapi.api.statues.v1.helper.ArmorStandInteractHelper; +import fuzs.puzzleslib.api.event.v1.core.EventResultHolder; +import net.minecraft.client.multiplayer.MultiPlayerGameMode; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.network.Connection; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; + +public class ArmorStandInteractHandler { + private static boolean presentServerside; + + public static EventResultHolder onUseEntityAt(Player player, Level level, InteractionHand interactionHand, Entity target, Vec3 hitVector) { + if (player.getAbilities().mayBuild && target.getType() == EntityType.ARMOR_STAND) { + boolean clientsideOnly = level.isClientSide && !presentServerside; + // the menu won't exist in the registry if the mod is missing serverside since Forge syncs registries to clients + MenuType menuType = clientsideOnly ? null : ModRegistry.ARMOR_STAND_MENU_TYPE.get(); + EventResultHolder result = ArmorStandInteractHelper.tryOpenArmorStatueMenu(player, level, interactionHand, (ArmorStand) target, menuType, ModRegistry.ARMOR_STAND_DATA_PROVIDER); + if (result.isInterrupt() && clientsideOnly) { + Proxy.INSTANCE.openArmorStandScreen((ArmorStand) target, player); + // required so no packet is sent to server when only installed client-side, so the server doesn't change any equipment when we only want to open the screen + // returning InteractionResult.FAIL will miss out on the player arm swing animation, which we manually play here + player.swing(interactionHand); + return EventResultHolder.interrupt(InteractionResult.FAIL); + } + return result; + } + return EventResultHolder.pass(); + } + + public static void onLoggedIn(ServerPlayer player) { + ArmorStatues.NETWORK.sendTo(new S2CPingMessage(), player); + } + + public static void onLoggedIn(LocalPlayer player, MultiPlayerGameMode multiPlayerGameMode, Connection connection) { + presentServerside = false; + } + + public static void setPresentServerside() { + presentServerside = true; + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java new file mode 100644 index 0000000..cd5e925 --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java @@ -0,0 +1,32 @@ +package fuzs.armorstatues.init; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzlesapi.api.statues.v1.world.entity.decoration.ArmorStandDataProvider; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.ArmorStandMenu; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.data.ArmorStandScreenType; +import fuzs.puzzleslib.api.init.v2.RegistryManager; +import fuzs.puzzleslib.api.init.v2.RegistryReference; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class ModRegistry { + static final RegistryManager REGISTRY = RegistryManager.instant(ArmorStatues.MOD_ID); + public static final RegistryReference> ARMOR_STAND_MENU_TYPE = REGISTRY.registerExtendedMenuType("armor_stand", () -> (containerId, inventory, data) -> { + return ArmorStandMenu.create(ModRegistry.ARMOR_STAND_MENU_TYPE.get(), containerId, inventory, data, ModRegistry.ARMOR_STAND_DATA_PROVIDER); + }); + + public static final ArmorStandScreenType ALIGNMENTS_SCREEN_TYPE = new ArmorStandScreenType("alignments", new ItemStack(Items.DIAMOND_PICKAXE)); + public static final ArmorStandScreenType VANILLA_TWEAKS_SCREEN_TYPE = new ArmorStandScreenType("vanillaTweaks", new ItemStack(Items.WRITTEN_BOOK)); + public static final ArmorStandDataProvider ARMOR_STAND_DATA_PROVIDER = new ArmorStandDataProvider() { + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return new ArmorStandScreenType[]{ArmorStandScreenType.ROTATIONS, ArmorStandScreenType.POSES, ArmorStandScreenType.STYLE, ArmorStandScreenType.POSITION, ModRegistry.ALIGNMENTS_SCREEN_TYPE, ArmorStandScreenType.EQUIPMENT}; + } + }; + + public static void touch() { + + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/network/S2CPingMessage.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/network/S2CPingMessage.java new file mode 100644 index 0000000..f2d0bbe --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/network/S2CPingMessage.java @@ -0,0 +1,30 @@ +package fuzs.armorstatues.network; + +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.puzzleslib.api.network.v2.MessageV2; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.player.Player; + +public class S2CPingMessage implements MessageV2 { + + @Override + public void write(FriendlyByteBuf buf) { + + } + + @Override + public void read(FriendlyByteBuf buf) { + + } + + @Override + public MessageHandler makeHandler() { + return new MessageHandler<>() { + + @Override + public void handle(S2CPingMessage message, Player player, Object gameInstance) { + ArmorStandInteractHandler.setPresentServerside(); + } + }; + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java new file mode 100644 index 0000000..145ec94 --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java @@ -0,0 +1,226 @@ +package fuzs.armorstatues.network.client.data; + +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.puzzlesapi.api.statues.v1.network.client.data.DataSyncHandler; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.ArmorStandHolder; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.data.ArmorStandAlignment; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.data.ArmorStandPose; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.data.ArmorStandScreenType; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.data.ArmorStandStyleOption; +import net.minecraft.ChatFormatting; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.FloatTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.function.BiPredicate; + +public class CommandDataSyncHandler implements DataSyncHandler { + public static final String NO_PERMISSION_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noPermission"; + public static final String NO_ARMOR_STAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noArmorStand"; + public static final String OUT_OF_RANGE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.outOfRange"; + public static final String NOT_FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.notFinished"; + public static final String FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.finished"; + public static final String FAILURE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure"; + private static final Queue CLIENT_COMMAND_QUEUE = new ArrayDeque<>(); + + @Nullable + private static ArmorStand queueArmorStand; + private static int itemDequeuedTicks; + + private final ArmorStandHolder holder; + protected final LocalPlayer player; + protected ArmorStandPose lastSyncedPose; + + public CommandDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + this.holder = holder; + this.lastSyncedPose = ArmorStandPose.fromEntity(this.holder.getArmorStand()); + this.player = player; + } + + @Override + public ArmorStandHolder getArmorStandHolder() { + return this.holder; + } + + @Override + public void sendName(String name) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.setCustomArmorStandName(this.getArmorStand(), name); + CompoundTag tag = new CompoundTag(); + tag.putString("CustomName", Component.Serializer.toJson(Component.literal(name))); + this.enqueueEntityData(tag); + this.finalizeCurrentOperation(); + } + + @Override + public final void sendPose(ArmorStandPose pose) { + this.sendPose(pose, true); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + // split this into multiple chat messages as the client chat field has a very low character limit + this.sendPosePart(pose::serializeBodyPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeArmPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeLegPoses, this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + if (finalize) this.finalizeCurrentOperation(); + } + + private void sendPosePart(BiPredicate dataWriter, ArmorStandPose lastSyncedPose) { + CompoundTag tag = new CompoundTag(); + if (dataWriter.test(tag, lastSyncedPose)) { + CompoundTag tagToSend = new CompoundTag(); + tagToSend.put("Pose", tag); + this.enqueueEntityData(tagToSend); + } + } + + @Override + public @Nullable ArmorStandPose getLastSyncedPose() { + return this.lastSyncedPose; + } + + @Override + public final void sendPosition(double posX, double posY, double posZ) { + this.sendPosition(posX, posY, posZ, true); + + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(DoubleTag.valueOf(posX)); + listTag.add(DoubleTag.valueOf(posY)); + listTag.add(DoubleTag.valueOf(posZ)); + CompoundTag tag = new CompoundTag(); + tag.put("Pos", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public final void sendRotation(float rotation) { + this.sendRotation(rotation, true); + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(FloatTag.valueOf(rotation)); + CompoundTag tag = new CompoundTag(); + tag.put("Rotation", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public final void sendStyleOption(ArmorStandStyleOption styleOption, boolean value) { + this.sendStyleOption(styleOption, value, true); + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + CompoundTag tag = new CompoundTag(); + styleOption.toTag(tag, value); + this.enqueueEntityData(tag); + styleOption.setOption(this.getArmorStand(), value); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.super.sendAlignment(alignment); + } + + @Override + public boolean supportsScreenType(ArmorStandScreenType screenType) { + return !screenType.requiresServer(); + } + + @Override + public void tick() { + if (itemDequeuedTicks > 0) itemDequeuedTicks--; + if (itemDequeuedTicks == 0 && queueArmorStand != null && !CLIENT_COMMAND_QUEUE.isEmpty()) { + if (this.testArmorStand(queueArmorStand).right().isPresent()) { + this.player.connection.sendCommand(CLIENT_COMMAND_QUEUE.poll()); + } else { + CLIENT_COMMAND_QUEUE.clear(); + } + itemDequeuedTicks = this.getDequeueDelayTicks(); + } else if (itemDequeuedTicks == 1 && CLIENT_COMMAND_QUEUE.isEmpty()) { + this.sendDisplayMessage(Component.translatable(FINISHED_TRANSLATION_KEY), false); + } + } + + protected int getDequeueDelayTicks() { + return 5; + } + + @Override + public boolean shouldContinueTicking() { + return !CLIENT_COMMAND_QUEUE.isEmpty() || itemDequeuedTicks != 0; + } + + protected boolean isEditingAllowed() { + return this.isEditingAllowed(!ArmorStatues.CONFIG.get(ClientConfig.class).overrideClientPermissionsCheck); + } + + protected final boolean isEditingAllowed(boolean testPermissionLevel) { + if (testPermissionLevel && !this.player.hasPermissions(2)) { + this.sendFailureMessage(Component.translatable(NO_PERMISSION_TRANSLATION_KEY)); + return false; + } + return this.player.getAbilities().mayBuild && this.testArmorStand(this.getArmorStand()).ifLeft(this::sendFailureMessage).right().isPresent(); + } + + protected Either testArmorStand(ArmorStand armorStand) { + return !armorStand.isAlive() ? Either.left(Component.translatable(NO_ARMOR_STAND_TRANSLATION_KEY)) : Either.right(Unit.INSTANCE); + } + + protected boolean enqueueClientCommand(String clientCommand) { + if (CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = null; + } else if (queueArmorStand != null) { + this.sendFailureMessage(Component.translatable(NOT_FINISHED_TRANSLATION_KEY)); + return false; + } + CLIENT_COMMAND_QUEUE.offer(clientCommand); + return true; + } + + @Override + public void finalizeCurrentOperation() { + if (!CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = this.getArmorStand(); + } + } + + protected void sendFailureMessage(Component component) { + this.sendDisplayMessage(Component.translatable(FAILURE_TRANSLATION_KEY, component), true); + } + + protected void sendDisplayMessage(Component component, boolean failure) { + this.player.displayClientMessage(Component.empty().append(component).withStyle(failure ? ChatFormatting.RED : ChatFormatting.GREEN), false); + } + + private void enqueueEntityData(CompoundTag tag) { + this.enqueueClientCommand("data merge entity %s %s".formatted(this.getArmorStand().getStringUUID(), tag.getAsString())); + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java new file mode 100644 index 0000000..e573f08 --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java @@ -0,0 +1,359 @@ +package fuzs.armorstatues.network.client.data; + +import com.google.common.collect.ImmutableSortedMap; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.ArmorStandHolder; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.data.*; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.Rotations; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.NavigableMap; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +public class VanillaTweaksDataSyncHandler extends CommandDataSyncHandler { + private static final int MAX_INCREMENTAL_OPERATIONS = 12; + public static final int CHECK_TARGET = 999; + public static final int SWAP_SLOTS_MAINHAND_AND_OFFHAND = 161; + public static final int SWAP_SLOTS_MAINHAND_AND_HEAD = 162; + public static final int MIRROR_ARMS_LEFT_TO_RIGHT = 131; + public static final int MIRROR_ARMS_RIGHT_TO_LEFT = 132; + public static final int MIRROR_LEGS_LEFT_TO_RIGHT = 133; + public static final int MIRROR_LEGS_RIGHT_TO_LEFT = 134; + public static final int UTILITIES_LOCK = 1000; + public static final int UTILITIES_UNLOCK = 1001; + public static final int MIRROR_AND_FLIP_FLIP = 135; + public static final int SHOW_BASE_PLATE_YES = 1; + public static final int SHOW_BASE_PLATE_NO = 2; + public static final int SHOW_ARMS_YES = 3; + public static final int SHOW_ARMS_NO = 4; + public static final int SMALL_STAND_YES = 5; + public static final int SMALL_STAND_NO = 6; + public static final int APPLY_GRAVITY_YES = 7; + public static final int APPLY_GRAVITY_NO = 8; + public static final int STAND_VISIBLE_YES = 9; + public static final int STAND_VISIBLE_NO = 10; + public static final int DISPLAY_NAME_YES = 11; + public static final int DISPLAY_NAME_NO = 12; + public static final int NUDGE_POSITION_X8_NEGATIVE = 40; + public static final int NUDGE_POSITION_X3_NEGATIVE = 101; + public static final int NUDGE_POSITION_X1_NEGATIVE = 102; + public static final int NUDGE_POSITION_X1_POSITIVE = 103; + public static final int NUDGE_POSITION_X3_POSITIVE = 104; + public static final int NUDGE_POSITION_X8_POSITIVE = 43; + public static final int NUDGE_POSITION_Y8_NEGATIVE = 44; + public static final int NUDGE_POSITION_Y3_NEGATIVE = 105; + public static final int NUDGE_POSITION_Y1_NEGATIVE = 106; + public static final int NUDGE_POSITION_Y1_POSITIVE = 107; + public static final int NUDGE_POSITION_Y3_POSITIVE = 108; + public static final int NUDGE_POSITION_Y8_POSITIVE = 47; + public static final int NUDGE_POSITION_Z8_NEGATIVE = 48; + public static final int NUDGE_POSITION_Z3_NEGATIVE = 109; + public static final int NUDGE_POSITION_Z1_NEGATIVE = 110; + public static final int NUDGE_POSITION_Z1_POSITIVE = 111; + public static final int NUDGE_POSITION_Z3_POSITIVE = 112; + public static final int NUDGE_POSITION_Z8_POSITIVE = 51; + public static final int ADJUST_ROTATION_ANGLE_STEP_45 = 120; + public static final int ADJUST_ROTATION_ANGLE_STEP_15 = 121; + public static final int ADJUST_ROTATION_ANGLE_STEP_5 = 122; + public static final int ADJUST_ROTATION_ANGLE_STEP_1 = 123; + public static final int ADJUST_ROTATION_ROTATE_RIGHT = 56; + public static final int ADJUST_ROTATION_ROTATE_LEFT = 57; + public static final int POSE_PRESETS_ATTENTION = 20; + public static final int POSE_PRESETS_WALKING = 21; + public static final int POSE_PRESETS_RUNNING = 22; + public static final int POSE_PRESETS_POINTING = 23; + public static final int POSE_PRESETS_BLOCKING = 24; + public static final int POSE_PRESETS_LUNGEING = 25; + public static final int POSE_PRESETS_WINNING = 26; + public static final int POSE_PRESETS_SITTING = 27; + public static final int POSE_PRESETS_ARABESQUE = 28; + public static final int POSE_PRESETS_CUPID = 29; + public static final int POSE_PRESETS_CONFIDENT = 30; + public static final int POSE_PRESETS_SALUTE = 31; + public static final int POSE_PRESETS_DEATH = 32; + public static final int POSE_PRESETS_FACEPALM = 33; + public static final int POSE_PRESETS_LAZING = 34; + public static final int POSE_PRESETS_CONFUSED = 35; + public static final int POSE_PRESETS_FORMAL = 36; + public static final int POSE_PRESETS_SAD = 37; + public static final int POSE_PRESETS_JOYOUS = 38; + public static final int POSE_PRESETS_STARGAZING = 39; + public static final int AUTO_ALIGNMENT_BLOCK_ON_SURFACE = 151; + public static final int AUTO_ALIGNMENT_ITEM_ON_SURFACE = 152; + public static final int AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE = 153; + public static final int AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE = 154; + public static final int AUTO_ALIGNMENT_TOOL_RACK = 155; + public static final int POSE_ADJUSTMENT_HEAD_X_NEGATIVE = 60; + public static final int POSE_ADJUSTMENT_HEAD_X_POSITIVE = 61; + public static final int POSE_ADJUSTMENT_HEAD_Y_NEGATIVE = 62; + public static final int POSE_ADJUSTMENT_HEAD_Y_POSITIVE = 63; + public static final int POSE_ADJUSTMENT_HEAD_Z_NEGATIVE = 64; + public static final int POSE_ADJUSTMENT_HEAD_Z_POSITIVE = 65; + public static final int POSE_ADJUSTMENT_BODY_X_NEGATIVE = 67; + public static final int POSE_ADJUSTMENT_BODY_X_POSITIVE = 66; + public static final int POSE_ADJUSTMENT_BODY_Y_NEGATIVE = 68; + public static final int POSE_ADJUSTMENT_BODY_Y_POSITIVE = 69; + public static final int POSE_ADJUSTMENT_BODY_Z_NEGATIVE = 70; + public static final int POSE_ADJUSTMENT_BODY_Z_POSITIVE = 71; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE = 72; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE = 73; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE = 74; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE = 75; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE = 77; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE = 76; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE = 78; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE = 79; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE = 81; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE = 80; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE = 82; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE = 83; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE = 84; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE = 85; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE = 87; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE = 86; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE = 89; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE = 88; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE = 90; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE = 91; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE = 92; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE = 93; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE = 94; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE = 95; + private static final int[] POSE_ADJUSTMENT_HEAD = new int[]{POSE_ADJUSTMENT_HEAD_X_NEGATIVE, POSE_ADJUSTMENT_HEAD_X_POSITIVE, POSE_ADJUSTMENT_HEAD_Y_NEGATIVE, POSE_ADJUSTMENT_HEAD_Y_POSITIVE, POSE_ADJUSTMENT_HEAD_Z_NEGATIVE, POSE_ADJUSTMENT_HEAD_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_BODY = new int[]{POSE_ADJUSTMENT_BODY_X_POSITIVE, POSE_ADJUSTMENT_BODY_X_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_POSITIVE, POSE_ADJUSTMENT_BODY_Z_NEGATIVE, POSE_ADJUSTMENT_BODY_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_ARM = new int[]{POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_ARM = new int[]{POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_LEG = new int[]{POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_LEG = new int[]{POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE}; + private static final NavigableMap NUDGE_POSITIONS_X_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_X3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_X8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_X_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_X3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_X8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_POSITIVE); + private static final NavigableMap ADJUST_ROTATION_ANGLE_STEPS = ImmutableSortedMap.of(1.0F, ADJUST_ROTATION_ANGLE_STEP_1, 5.0F, ADJUST_ROTATION_ANGLE_STEP_5, 15.0F, ADJUST_ROTATION_ANGLE_STEP_15, 45.0F, ADJUST_ROTATION_ANGLE_STEP_45); + + public VanillaTweaksDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + super(holder, player); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue = this.getTriggerValueFromPose(pose); + if (triggerValue != -1) { + if (this.enqueueTriggerValue(triggerValue)) { + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + } + } else { + this.tryApplyAllPoseParts(pose); + } + if (finalize) this.finalizeCurrentOperation(); + } + + private int getTriggerValueFromPose(ArmorStandPose pose) { + if (pose.getSourceType() == ArmorStandPose.SourceType.EMPTY) return POSE_PRESETS_ATTENTION; + if (pose.getSourceType() == ArmorStandPose.SourceType.MIRRORED) return MIRROR_AND_FLIP_FLIP; + if (pose.getSourceType() != ArmorStandPose.SourceType.VANILLA_TWEAKS) return -1; + if (pose == ArmorStandPose.WALKING) return POSE_PRESETS_WALKING; + if (pose == ArmorStandPose.RUNNING) return POSE_PRESETS_RUNNING; + if (pose == ArmorStandPose.POINTING) return POSE_PRESETS_POINTING; + if (pose == ArmorStandPose.BLOCKING) return POSE_PRESETS_BLOCKING; + if (pose == ArmorStandPose.LUNGEING) return POSE_PRESETS_LUNGEING; + if (pose == ArmorStandPose.WINNING) return POSE_PRESETS_WINNING; + if (pose == ArmorStandPose.SITTING) return POSE_PRESETS_SITTING; + if (pose == ArmorStandPose.ARABESQUE) return POSE_PRESETS_ARABESQUE; + if (pose == ArmorStandPose.CUPID) return POSE_PRESETS_CUPID; + if (pose == ArmorStandPose.CONFIDENT) return POSE_PRESETS_CONFIDENT; + if (pose == ArmorStandPose.SALUTE) return POSE_PRESETS_SALUTE; + if (pose == ArmorStandPose.DEATH) return POSE_PRESETS_DEATH; + if (pose == ArmorStandPose.FACEPALM) return POSE_PRESETS_FACEPALM; + if (pose == ArmorStandPose.LAZING) return POSE_PRESETS_LAZING; + if (pose == ArmorStandPose.CONFUSED) return POSE_PRESETS_CONFUSED; + if (pose == ArmorStandPose.FORMAL) return POSE_PRESETS_FORMAL; + if (pose == ArmorStandPose.SAD) return POSE_PRESETS_SAD; + if (pose == ArmorStandPose.JOYOUS) return POSE_PRESETS_JOYOUS; + if (pose == ArmorStandPose.STARGAZING) return POSE_PRESETS_STARGAZING; + return -1; + } + + private void tryApplyAllPoseParts(ArmorStandPose pose) { + if (!this.tryApplyPosePart(this.lastSyncedPose.getHeadPose(), pose.getNullableHeadPose(), POSE_ADJUSTMENT_HEAD, this.lastSyncedPose::withHeadPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getBodyPose(), pose.getNullableBodyPose(), POSE_ADJUSTMENT_BODY, this.lastSyncedPose::withBodyPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightArmPose(), pose.getNullableRightArmPose(), POSE_ADJUSTMENT_RIGHT_ARM, this.lastSyncedPose::withRightArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftArmPose(), pose.getNullableLeftArmPose(), POSE_ADJUSTMENT_LEFT_ARM, this.lastSyncedPose::withLeftArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightLegPose(), pose.getNullableRightLegPose(), POSE_ADJUSTMENT_RIGHT_LEG, this.lastSyncedPose::withRightLegPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftLegPose(), pose.getNullableLeftLegPose(), POSE_ADJUSTMENT_LEFT_LEG, this.lastSyncedPose::withLeftLegPose)) + return; + } + + private boolean tryApplyPosePart(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment, Function function) { + if (this.tryApplyPoseAdjustment(oldPose, newPose, poseAdjustment)) { + this.lastSyncedPose = function.apply(newPose != null ? newPose : oldPose); + return true; + } else { + return false; + } + } + + private boolean tryApplyPoseAdjustment(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment) { + if (newPose == null || oldPose.equals(newPose)) return true; + if (!this.applyIncrementsFromSteps(oldPose.getX(), newPose.getX(), poseAdjustment[0], poseAdjustment[1])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getY(), newPose.getY(), poseAdjustment[2], poseAdjustment[3])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getZ(), newPose.getZ(), poseAdjustment[4], poseAdjustment[5])) return false; + return true; + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyPositionIncrements(this.getArmorStand().getX(), posX, NUDGE_POSITIONS_X_POSITIVE, NUDGE_POSITIONS_X_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getY(), posY, NUDGE_POSITIONS_Y_POSITIVE, NUDGE_POSITIONS_Y_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getZ(), posZ, NUDGE_POSITIONS_Z_POSITIVE, NUDGE_POSITIONS_Z_NEGATIVE); + if (finalize) this.finalizeCurrentOperation(); + } + + private void applyPositionIncrements(double oldValue, double newValue, NavigableMap positiveNudgePositions, NavigableMap negativeNudgePositions) { + double value = newValue - oldValue; + double signum = Math.signum(value); + value = Math.abs(value); + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = (signum == -1.0F ? negativeNudgePositions : positiveNudgePositions).floorEntry(value); + if (entry != null) { + value -= entry.getKey(); + if (!this.enqueueTriggerValue(entry.getValue())) { + return; + } + } else { + break; + } + } + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyIncrementsFromSteps(this.getArmorStand().getYRot(), rotation, ADJUST_ROTATION_ROTATE_RIGHT, ADJUST_ROTATION_ROTATE_LEFT); + if (finalize) this.finalizeCurrentOperation(); + } + + private boolean applyIncrementsFromSteps(float oldValue, float newValue, int triggerValueNegative, int triggerValuePositive) { + float value = newValue - oldValue; + float signum = Math.signum(value); + value = Math.abs(value); + float lastIncrement = 0.0F; + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = ADJUST_ROTATION_ANGLE_STEPS.floorEntry(value); + if (entry != null) { + float currentIncrement = entry.getKey(); + value -= currentIncrement; + if (currentIncrement != lastIncrement) { + lastIncrement = currentIncrement; + if (!this.enqueueTriggerValue(entry.getValue())) { + return false; + } + } + if (!this.enqueueTriggerValue(signum == -1.0F ? triggerValuePositive : triggerValueNegative)) { + return false; + } + } else { + break; + } + } + return true; + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue; + if (styleOption == ArmorStandStyleOptions.SHOW_NAME) { + triggerValue = value ? DISPLAY_NAME_YES : DISPLAY_NAME_NO; + } else if (styleOption == ArmorStandStyleOptions.SHOW_ARMS) { + triggerValue = value ? SHOW_ARMS_YES : SHOW_ARMS_NO; + } else if (styleOption == ArmorStandStyleOptions.SMALL) { + triggerValue = value ? SMALL_STAND_YES : SMALL_STAND_NO; + } else if (styleOption == ArmorStandStyleOptions.INVISIBLE) { + triggerValue = value ? STAND_VISIBLE_NO : STAND_VISIBLE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_BASE_PLATE) { + triggerValue = value ? SHOW_BASE_PLATE_NO : SHOW_BASE_PLATE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_GRAVITY) { + triggerValue = value ? APPLY_GRAVITY_NO : APPLY_GRAVITY_YES; + } else { + super.sendStyleOption(styleOption, value, finalize); + return; + } + if (this.sendSingleTriggerValue(triggerValue, finalize)) { + styleOption.setOption(this.getArmorStand(), value); + } + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + int triggerValue = switch (alignment) { + case BLOCK -> AUTO_ALIGNMENT_BLOCK_ON_SURFACE; + case FLOATING_ITEM -> AUTO_ALIGNMENT_ITEM_ON_SURFACE; + case FLAT_ITEM -> AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE; + case TOOL -> AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE; + }; + this.sendSingleTriggerValue(triggerValue, true); + } + + public void sendSingleTriggerValue(int triggerValue) { + if (!this.isEditingAllowed()) return; + this.sendSingleTriggerValue(triggerValue, true); + } + + private boolean sendSingleTriggerValue(int triggerValue, boolean finalize) { + boolean result = this.enqueueTriggerValue(triggerValue); + if (finalize) this.finalizeCurrentOperation(); + return result; + } + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return Stream.concat(Stream.of(super.getScreenTypes()), Stream.of(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE)).toArray(ArmorStandScreenType[]::new); + } + + @Override + protected boolean isEditingAllowed() { + return this.isEditingAllowed(false); + } + + @Override + protected Either testArmorStand(ArmorStand armorStand) { + return super.testArmorStand(armorStand).>map(Optional::of, $ -> { + if (this.player.distanceToSqr(armorStand) < 9.0) { + return Optional.empty(); + } else { + return Optional.of(Component.translatable(OUT_OF_RANGE_TRANSLATION_KEY)); + } + }).>map(Either::left).orElse(Either.right(Unit.INSTANCE)); + } + + @Override + protected int getDequeueDelayTicks() { + return ArmorStatues.CONFIG.get(ClientConfig.class).clientCommandDelay; + } + + private boolean enqueueTriggerValue(int triggerValue) { + return this.enqueueClientCommand("trigger as_trigger set %s".formatted(triggerValue)); + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java new file mode 100644 index 0000000..3cf5225 --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java @@ -0,0 +1,51 @@ +package fuzs.armorstatues.proxy; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.puzzlesapi.api.client.statues.v1.gui.screens.armorstand.ArmorStandScreenFactory; +import fuzs.puzzlesapi.api.statues.v1.network.client.data.DataSyncHandler; +import fuzs.puzzlesapi.api.statues.v1.world.entity.decoration.ArmorStandDataProvider; +import fuzs.puzzlesapi.api.statues.v1.world.inventory.ArmorStandHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ClientProxy extends ServerProxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + ArmorStandHolder holder = new ArmorStandHolder() { + + @Override + public ArmorStand getArmorStand() { + return armorStand; + } + + @Override + public ArmorStandDataProvider getDataProvider() { + return ModRegistry.ARMOR_STAND_DATA_PROVIDER; + } + }; + Screen screen = ArmorStandScreenFactory.createLastScreenType(holder, + player.getInventory(), + armorStand.getDisplayName(), + createDataSyncHandler(holder, (LocalPlayer) player) + ); + Minecraft minecraft = Minecraft.getInstance(); + minecraft.setScreen(screen); + } + + private static DataSyncHandler createDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + if ((!player.hasPermissions(2) || ArmorStatues.CONFIG.get(ClientConfig.class).overrideClientPermissionsCheck) && + ArmorStatues.CONFIG.get(ClientConfig.class).useVanillaTweaksTriggers) { + return new VanillaTweaksDataSyncHandler(holder, player); + } else { + return new CommandDataSyncHandler(holder, player); + } + } +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java new file mode 100644 index 0000000..1f5ae83 --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java @@ -0,0 +1,11 @@ +package fuzs.armorstatues.proxy; + +import fuzs.puzzleslib.api.core.v1.ModLoaderEnvironment; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public interface Proxy { + Proxy INSTANCE = ModLoaderEnvironment.INSTANCE.isClient() ? new ClientProxy() : new ServerProxy(); + + void openArmorStandScreen(ArmorStand armorStand, Player player); +} diff --git a/1.20.1/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java b/1.20.1/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java new file mode 100644 index 0000000..81856ec --- /dev/null +++ b/1.20.1/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java @@ -0,0 +1,12 @@ +package fuzs.armorstatues.proxy; + +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ServerProxy implements Proxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + + } +} diff --git a/1.20.1/Common/src/main/resources/mod_banner.png b/1.20.1/Common/src/main/resources/mod_banner.png new file mode 100644 index 0000000..62a4610 Binary files /dev/null and b/1.20.1/Common/src/main/resources/mod_banner.png differ diff --git a/1.20.1/Common/src/main/resources/mod_logo.png b/1.20.1/Common/src/main/resources/mod_logo.png new file mode 100644 index 0000000..869766b Binary files /dev/null and b/1.20.1/Common/src/main/resources/mod_logo.png differ diff --git a/1.20.1/Common/src/main/resources/pack.mcmeta b/1.20.1/Common/src/main/resources/pack.mcmeta new file mode 100755 index 0000000..19bfab2 --- /dev/null +++ b/1.20.1/Common/src/main/resources/pack.mcmeta @@ -0,0 +1,8 @@ +{ + "pack": { + "description": "${modDescription}", + "pack_format": ${resourcePackFormat}, + "forge:resource_pack_format": ${resourcePackFormat}, + "forge:data_pack_format": ${dataPackFormat} + } +} diff --git a/1.20.1/Fabric/build.gradle b/1.20.1/Fabric/build.gradle new file mode 100644 index 0000000..56fc5da --- /dev/null +++ b/1.20.1/Fabric/build.gradle @@ -0,0 +1,34 @@ +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/fabric.gradle' + +def versionCatalog = extensions.getByType(VersionCatalogsExtension).named("libs") + +dependencies { + // Fabric Api + modApi libs.fabricapi.fabric + + // Puzzles Lib + modApi libs.puzzleslib.fabric + + // Puzzles Api + modApi(include(libs.puzzlesapi.fabric.get())) { + exclude group: "fuzs.puzzleslib" + } + + // Cardinal Components +// modApi(include(libs.cardinalcomponentsbase.fabric.get())) +// modApi(include(libs.cardinalcomponentsentity.fabric.get())) +// modApi(include(libs.cardinalcomponentsblock.fabric.get())) +// modApi(include(libs.cardinalcomponentschunk.fabric.get())) +// modApi(include(libs.cardinalcomponentsworld.fabric.get())) + + // Extensible Enums +// modApi(include(libs.extensibleenums.fabric.get())) + + // Quality of Life Mods + versionCatalog.findLibrary("modmenu.fabric").ifPresent { + modLocalRuntime(it) + } + versionCatalog.findLibrary("forgeconfigscreens.fabric").ifPresent { + modLocalRuntime(it) + } +} diff --git a/1.20.1/Fabric/src/main/java/fuzs/armorstatues/ArmorStatuesFabric.java b/1.20.1/Fabric/src/main/java/fuzs/armorstatues/ArmorStatuesFabric.java new file mode 100644 index 0000000..65493b3 --- /dev/null +++ b/1.20.1/Fabric/src/main/java/fuzs/armorstatues/ArmorStatuesFabric.java @@ -0,0 +1,12 @@ +package fuzs.armorstatues; + +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.fabricmc.api.ModInitializer; + +public class ArmorStatuesFabric implements ModInitializer { + + @Override + public void onInitialize() { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } +} diff --git a/1.20.1/Fabric/src/main/java/fuzs/armorstatues/client/ArmorStatuesFabricClient.java b/1.20.1/Fabric/src/main/java/fuzs/armorstatues/client/ArmorStatuesFabricClient.java new file mode 100644 index 0000000..ee3baac --- /dev/null +++ b/1.20.1/Fabric/src/main/java/fuzs/armorstatues/client/ArmorStatuesFabricClient.java @@ -0,0 +1,13 @@ +package fuzs.armorstatues.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import net.fabricmc.api.ClientModInitializer; + +public class ArmorStatuesFabricClient implements ClientModInitializer { + + @Override + public void onInitializeClient() { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + } +} diff --git a/1.20.1/Fabric/src/main/resources/fabric.mod.json b/1.20.1/Fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..d1caf67 --- /dev/null +++ b/1.20.1/Fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,44 @@ +{ + "schemaVersion": 1, + "id": "${modId}", + "version": "${modVersion}", + + "name": "${modName}", + "description": "${modDescription}", + + "authors": [ + "${modAuthor}" + ], + + "contact": { + "homepage": "${modPageUrl}", + "issues": "${modIssueUrl}", + "sources": "${modPageUrl}" + }, + + "license": "${modLicense}", + "icon": "mod_logo.png", + + "environment": "${modFabricEnvironment}", + + "entrypoints": { + "main": [ + "${mainEntryPoint}" + ], + "client": [ + "${clientEntryPoint}" + ] + }, + + "mixins": [ + ], + + "depends": { + "fabricloader": ">=${minFabricVersion}", + "fabric-api": ">=${minFabricApiVersion}", + "puzzleslib": ">=${minPuzzlesVersion}", + "puzzlesapi": "*", + "minecraft": "${minecraftVersion}", + "java": ">=17" + } +} diff --git a/1.20.1/Forge/build.gradle b/1.20.1/Forge/build.gradle new file mode 100644 index 0000000..29049d3 --- /dev/null +++ b/1.20.1/Forge/build.gradle @@ -0,0 +1,38 @@ +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/forge.gradle' + +def versionCatalog = extensions.getByType(VersionCatalogsExtension).named("libs") + +dependencies { + // Puzzles Lib + api fg.deobf(libs.puzzleslib.forge.get()) + + // Puzzles Api + api(fg.deobf(libs.puzzlesapi.forge.get())) { + transitive = false + } + jarJar(fg.deobf(libs.puzzlesapi.forge.get()) { + jarJar.ranged(it, "[${libs.versions.puzzlesapi.get()},)") + transitive = false + }) + + // Quality of Life Mods + versionCatalog.findLibrary("bettermodsbutton.forge").ifPresent { + runtimeOnly fg.deobf(it.get()) + } +// runtimeOnly fg.deobf("fuzs.bettermodsbutton:bettermodsbutton-forge:${libs.versions.bettermodsbutton.get()}") + versionCatalog.findLibrary("forgeconfigscreens.forge").ifPresent { + runtimeOnly fg.deobf(it.get()) + } +} + +task signJar(type: net.minecraftforge.gradle.common.tasks.SignJar, dependsOn: tasks.reobfJarJar) { + onlyIf { project.hasProperty('keyStore') } + keyStore = project.findProperty('keyStore') + alias = project.findProperty('keyStoreAlias') + storePass = project.findProperty('keyStorePass') + keyPass = project.findProperty('keyStoreKeyPass') + inputFile = outputFile = tasks.jarJar.archivePath +} + +jar.finalizedBy 'signJar' +signJar.mustRunAfter 'reobfJar' diff --git a/1.20.1/Forge/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 b/1.20.1/Forge/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 new file mode 100644 index 0000000..8e2c24f --- /dev/null +++ b/1.20.1/Forge/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 @@ -0,0 +1,2 @@ +// 1.20.1 2023-07-29T15:02:23.443069 Languages: en_us +9ecbeccea1af534ecf344346c3f5cecde17fd977 assets/armorstatues/lang/en_us.json diff --git a/1.20.1/Forge/src/generated/resources/assets/armorstatues/lang/en_us.json b/1.20.1/Forge/src/generated/resources/assets/armorstatues/lang/en_us.json new file mode 100644 index 0000000..e392806 --- /dev/null +++ b/1.20.1/Forge/src/generated/resources/assets/armorstatues/lang/en_us.json @@ -0,0 +1,23 @@ +{ + "armorstatues.dataSync.failure": "Unable to modify armor stand data: %s", + "armorstatues.dataSync.failure.noArmorStand": "No Valid Armor Stand", + "armorstatues.dataSync.failure.noPermission": "No Permission", + "armorstatues.dataSync.failure.notFinished": "Queue Not Empty", + "armorstatues.dataSync.failure.outOfRange": "Out Of Range", + "armorstatues.dataSync.finished": "Finished sending queued armor stand data", + "armorstatues.screen.vanillaTweaks.checkTarget": "Check Armor Stand Target", + "armorstatues.screen.vanillaTweaks.checkTarget.description": "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, this isn't necessarily the armor stand which opened this menu.", + "armorstatues.screen.vanillaTweaks.lock": "Lock", + "armorstatues.screen.vanillaTweaks.lock.description": "Locking an armor stand prevents it from being changed using this menu and disables interaction with the equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead": "Swap Mainhand & Helmet", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead.description": "Swaps items between the main hand and helmet equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand": "Swap Mainhand & Offhand", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand.description": "Swaps items between the main hand and off hand equipment slots.", + "armorstatues.screen.vanillaTweaks.toolRack": "Align Tool As Tool Rack", + "armorstatues.screen.vanillaTweaks.toolRack.description": "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand.", + "armorstatues.screen.vanillaTweaks.triggerSent": "Sent!", + "armorstatues.screen.vanillaTweaks.unlock": "Unlock", + "armorstatues.screen.vanillaTweaks.unlock.description": "Unlocking an armor stand reverts any adjustments made via a previous lock action.", + "puzzlesapi.screen.type.alignments": "Alignments", + "puzzlesapi.screen.type.vanillaTweaks": "Vanilla Tweaks" +} \ No newline at end of file diff --git a/1.20.1/Forge/src/generated/resources/assets/armorstatues/lang/es_mx.json b/1.20.1/Forge/src/generated/resources/assets/armorstatues/lang/es_mx.json new file mode 100644 index 0000000..6c483e6 --- /dev/null +++ b/1.20.1/Forge/src/generated/resources/assets/armorstatues/lang/es_mx.json @@ -0,0 +1,23 @@ +{ + "armorstatues.dataSync.failure": "No se puede modificar los datos del soporte de armadura: %s", + "armorstatues.dataSync.failure.noArmorStand": "No hay soporte de armadura válido", + "armorstatues.dataSync.failure.noPermission": "Sin permiso", + "armorstatues.dataSync.failure.notFinished": "Cola no vacía", + "armorstatues.dataSync.failure.outOfRange": "Fuera de rango", + "armorstatues.dataSync.finished": "Terminó de enviar los datos en cola del soporte de armadura", + "armorstatues.screen.vanillaTweaks.checkTarget": "Verificar objetivo del soporte de armadura", + "armorstatues.screen.vanillaTweaks.checkTarget.description": "Resalta el soporte de armadura más cercano dentro de tres bloques del jugador que será ajustado. Debido a cómo funcionan los paquetes de datos, este no es necesariamente el soporte de armadura que abrió este menú.", + "armorstatues.screen.vanillaTweaks.lock": "Bloquear", + "armorstatues.screen.vanillaTweaks.lock.description": "Bloquear un soporte de armadura evita que se cambie usando este menú y deshabilita la interacción con las ranuras de equipo.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead": "Intercambiar mano principal y casco", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead.description": "Intercambia objetos entre las ranuras de equipo de la mano principal y el casco.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand": "Intercambiar mano principal y mano secundaria", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand.description": "Intercambia objetos entre las ranuras de equipo de la mano principal y la mano secundaria.", + "armorstatues.screen.vanillaTweaks.toolRack": "Alinear herramienta como estante de herramientas", + "armorstatues.screen.vanillaTweaks.toolRack.description": "Alinea un soporte de armadura con un gancho de alambre en la pared encima de él para que una herramienta sostenida por él parezca estar colgada. También bloquea el soporte de armadura y deshabilita todas las ranuras excepto la de la mano principal.", + "armorstatues.screen.vanillaTweaks.triggerSent": "¡Enviado!", + "armorstatues.screen.vanillaTweaks.unlock": "Desbloquear", + "armorstatues.screen.vanillaTweaks.unlock.description": "Desbloquear un soporte de armadura revierte cualquier ajuste realizado mediante una acción de bloqueo anterior.", + "puzzlesapi.screen.type.alignments": "Alineaciones", + "puzzlesapi.screen.type.vanillaTweaks": "Ajustes de Vanilla" +} \ No newline at end of file diff --git a/1.20.1/Forge/src/main/java/fuzs/armorstatues/ArmorStatuesForge.java b/1.20.1/Forge/src/main/java/fuzs/armorstatues/ArmorStatuesForge.java new file mode 100644 index 0000000..346a4a5 --- /dev/null +++ b/1.20.1/Forge/src/main/java/fuzs/armorstatues/ArmorStatuesForge.java @@ -0,0 +1,23 @@ +package fuzs.armorstatues; + +import fuzs.armorstatues.data.ModLanguageProvider; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.minecraftforge.data.event.GatherDataEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; + +@Mod(ArmorStatues.MOD_ID) +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public class ArmorStatuesForge { + + @SubscribeEvent + public static void onConstructMod(final FMLConstructModEvent evt) { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } + + @SubscribeEvent + public static void onGatherData(final GatherDataEvent evt) { + evt.getGenerator().addProvider(true, new ModLanguageProvider(evt, ArmorStatues.MOD_ID)); + } +} diff --git a/1.20.1/Forge/src/main/java/fuzs/armorstatues/client/ArmorStatuesForgeClient.java b/1.20.1/Forge/src/main/java/fuzs/armorstatues/client/ArmorStatuesForgeClient.java new file mode 100644 index 0000000..c50ecce --- /dev/null +++ b/1.20.1/Forge/src/main/java/fuzs/armorstatues/client/ArmorStatuesForgeClient.java @@ -0,0 +1,51 @@ +package fuzs.armorstatues.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import fuzs.puzzleslib.api.event.v1.core.EventResultHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.InputEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; + +@Mod.EventBusSubscriber(modid = ArmorStatues.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) +public class ArmorStatuesForgeClient { + + @SubscribeEvent + public static void onConstructMod(final FMLConstructModEvent evt) { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + registerHandlers(); + } + + private static void registerHandlers() { + MinecraftForge.EVENT_BUS.addListener((final InputEvent.InteractionKeyMappingTriggered evt) -> { + + Minecraft minecraft = Minecraft.getInstance(); + if (evt.isUseItem() && minecraft.hitResult != null && minecraft.hitResult.getType() == HitResult.Type.ENTITY) { + + EntityHitResult hitResult = (EntityHitResult) minecraft.hitResult; + Entity entity = hitResult.getEntity(); + if (minecraft.level.getWorldBorder().isWithinBounds(entity.blockPosition())) { + + Vec3 hitVector = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ()); + EventResultHolder result = ArmorStandInteractHandler.onUseEntityAt(minecraft.player, minecraft.level, evt.getHand(), entity, hitVector); + // if InteractionResult.FAIL is returned the mod is missing server-side, and we open the menu client-side without sending a packet to the server, so the server does not try to interact + // only Fabric sending the packet is simple prevented by returning InteractionResult.FAIL from ArmorStandInteractHandler::onUseEntityAt, on Forge this approach seems to work + if (result.filter(t -> t == InteractionResult.FAIL).isInterrupt()) { + evt.setSwingHand(false); + evt.setCanceled(true); + } + } + } + }); + } +} diff --git a/1.20.1/Forge/src/main/java/fuzs/armorstatues/data/ModLanguageProvider.java b/1.20.1/Forge/src/main/java/fuzs/armorstatues/data/ModLanguageProvider.java new file mode 100644 index 0000000..53b29c2 --- /dev/null +++ b/1.20.1/Forge/src/main/java/fuzs/armorstatues/data/ModLanguageProvider.java @@ -0,0 +1,40 @@ +package fuzs.armorstatues.data; + +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.puzzleslib.api.data.v1.AbstractLanguageProvider; +import net.minecraftforge.data.event.GatherDataEvent; + +public class ModLanguageProvider extends AbstractLanguageProvider { + + public ModLanguageProvider(GatherDataEvent evt, String modId) { + super(evt, modId); + } + + @Override + protected void addTranslations() { + // Armor Statues + this.add(CommandDataSyncHandler.FAILURE_TRANSLATION_KEY, "Unable to modify armor stand data: %s"); + this.add(CommandDataSyncHandler.NO_PERMISSION_TRANSLATION_KEY, "No Permission"); + this.add(CommandDataSyncHandler.NO_ARMOR_STAND_TRANSLATION_KEY, "No Valid Armor Stand"); + this.add(CommandDataSyncHandler.OUT_OF_RANGE_TRANSLATION_KEY, "Out Of Range"); + this.add(CommandDataSyncHandler.NOT_FINISHED_TRANSLATION_KEY, "Queue Not Empty"); + this.add(CommandDataSyncHandler.FINISHED_TRANSLATION_KEY, "Finished sending queued armor stand data"); + this.add(ModRegistry.ALIGNMENTS_SCREEN_TYPE.getTranslationKey(), "Alignments"); + this.add(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE.getTranslationKey(), "Vanilla Tweaks"); + this.add(ArmorStandVanillaTweaksScreen.TRIGGER_SENT_TRANSLATION_KEY, "Sent!"); + this.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_TRANSLATION_KEY, "Check Armor Stand Target"); + this.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_DESCRIPTION_KEY, "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, this isn't necessarily the armor stand which opened this menu."); + this.add(ArmorStandVanillaTweaksScreen.LOCK_TRANSLATION_KEY, "Lock"); + this.add(ArmorStandVanillaTweaksScreen.LOCK_DESCRIPTION_KEY, "Locking an armor stand prevents it from being changed using this menu and disables interaction with the equipment slots."); + this.add(ArmorStandVanillaTweaksScreen.UNLOCK_TRANSLATION_KEY, "Unlock"); + this.add(ArmorStandVanillaTweaksScreen.UNLOCK_DESCRIPTION_KEY, "Unlocking an armor stand reverts any adjustments made via a previous lock action."); + this.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_TRANSLATION_KEY, "Align Tool As Tool Rack"); + this.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_DESCRIPTION_KEY, "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand."); + this.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY, "Swap Mainhand & Offhand"); + this.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY, "Swaps items between the main hand and off hand equipment slots."); + this.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY, "Swap Mainhand & Helmet"); + this.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY, "Swaps items between the main hand and helmet equipment slots."); + } +} diff --git a/1.20.1/Forge/src/main/resources/META-INF/mods.toml b/1.20.1/Forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..fac0264 --- /dev/null +++ b/1.20.1/Forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,47 @@ +modLoader = "javafml" +loaderVersion = "[${minFMLVersion},)" +license = "${modLicense}" +issueTrackerURL = "${modIssueUrl}" + +[[mods]] +modId = "${modId}" +displayName = "${modName}" +description = "${modDescription}" +version = "${modVersion}" +authors = "${modAuthor}" +logoFile = "mod_banner.png" +logoBlur = false +displayURL = "${modPageUrl}" +updateJSONURL = "${modUpdateUrl}" +displayTest = "${modForgeDisplayTest}" + +[[dependencies.${ modId }]] +modId = "forge" +mandatory = true +versionRange = "[${minForgeVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "minecraft" +mandatory = true +versionRange = "[${minecraftVersion}]" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "puzzleslib" +mandatory = true +versionRange = "[${minPuzzlesVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "puzzlesapi" +mandatory = true +versionRange = "*" +ordering = "NONE" +side = "BOTH" + +[modproperties.${ modId }] +catalogueImageIcon = "mod_logo.png" diff --git a/1.20.1/build.gradle b/1.20.1/build.gradle new file mode 100644 index 0000000..fadade3 --- /dev/null +++ b/1.20.1/build.gradle @@ -0,0 +1,12 @@ +plugins { + alias libs.plugins.loom apply false + alias libs.plugins.quiltflower apply false + alias libs.plugins.forgegradle apply false + alias libs.plugins.mixin apply false + alias libs.plugins.librarian apply false + alias libs.plugins.cursegradle apply false + alias libs.plugins.minotaur apply false +} + +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/main.gradle' +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/tasks.gradle' diff --git a/1.20.1/gradle.properties b/1.20.1/gradle.properties new file mode 100755 index 0000000..38d6730 --- /dev/null +++ b/1.20.1/gradle.properties @@ -0,0 +1,38 @@ +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +# This is required to provide enough memory for the Minecraft decompilation process. +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false +org.gradle.parallel=true +copyBuildJar=true + +# Mod Attributes +modId=armorstatues +modName=Armor Statues +modVersion=8.0.6 +modAuthor=Fuzs +modDescription=Unlock the full potential of armor stands! Works on vanilla servers, too. +modLicense=MPL-2.0 +modSourceUrl=https://github.com/Fuzss/armorstatues +modIssueUrl=https://github.com/Fuzss/armorstatues/issues +modUpdateUrl=https://raw.githubusercontent.com/Fuzss/modresources/main/update/armorstatues.json +modMavenGroup=fuzs.armorstatues +# "MATCH_VERSION" for a mod required on both sides, "IGNORE_SERVER_VERSION" for a server only mod, "IGNORE_ALL_VERSION" for a client only mod +modForgeDisplayTest=IGNORE_ALL_VERSION +# "*" for a mod loaded on both sides, "server" for a server only mod, "client" for a client only mod +modFabricEnvironment=* + +# Mod Publishing +projectReleaseType=release +projectCurseForgeId=682566 +projectModrinthId=bbGCtEvb + +dependenciesVersionCatalog=1.20.1-v25 +dependenciesPuzzlesLibVersion=8.1.11 +dependenciesMinPuzzlesLibVersion=8.1.11 +dependenciesPuzzlesApi=8.1.4 +dependenciesRequiredForgeCurseForge=puzzles-lib +dependenciesRequiredFabricCurseForge=fabric-api, forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredForgeModrinth=puzzles-lib +dependenciesRequiredFabricModrinth=fabric-api, forge-config-api-port, puzzles-lib +#dependenciesEmbeddedFabricCurseForge=cardinal-components +#dependenciesEmbeddedFabricModrinth=cardinal-components-api diff --git a/1.20.1/gradle/wrapper/gradle-wrapper.jar b/1.20.1/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..943f0cb Binary files /dev/null and b/1.20.1/gradle/wrapper/gradle-wrapper.jar differ diff --git a/1.20.1/gradle/wrapper/gradle-wrapper.properties b/1.20.1/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..37aef8d --- /dev/null +++ b/1.20.1/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/1.20.1/gradlew b/1.20.1/gradlew new file mode 100755 index 0000000..65dcd68 --- /dev/null +++ b/1.20.1/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/1.20.1/gradlew.bat b/1.20.1/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/1.20.1/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/1.20.1/settings.gradle b/1.20.1/settings.gradle new file mode 100644 index 0000000..bf7e2e5 --- /dev/null +++ b/1.20.1/settings.gradle @@ -0,0 +1,23 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + maven { + name = 'Sponge' + url = 'https://repo.spongepowered.org/repository/maven-public/' + } + maven { + name = 'Quilt' + url = 'https://maven.quiltmc.org/repository/release' + } + maven { + name = 'Minecraft Forge' + url = 'https://maven.minecraftforge.net/' + } + } +} + +apply from: 'https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/settings.gradle' diff --git a/1.20.4/CHANGELOG.md b/1.20.4/CHANGELOG.md new file mode 100644 index 0000000..f07319e --- /dev/null +++ b/1.20.4/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [v20.4.2-1.20.4] - 2024-04-07 +### Changed +- Allow `override_client_permissions_check` config option to also force enabling of the Vanilla Tweaks triggers command handler + +## [v20.4.1-1.20.4] - 2024-03-16 +### Changed +- Bump bundled Statue Menus mod to v20.4.2 +### Fixed +- Fix equipment changing when opening the armor stand menu on dedicated servers without the mod on Fabric + +## [v20.4.0-1.20.4] - 2024-02-10 +- Port to Minecraft 1.20.4 +- Port to NeoForge diff --git a/1.20.4/Common/build.gradle b/1.20.4/Common/build.gradle new file mode 100644 index 0000000..4971f9a --- /dev/null +++ b/1.20.4/Common/build.gradle @@ -0,0 +1,13 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/v2/common.gradle" + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.common + + // Statue Menus + modApi libs.statuemenus.common +} + +tasks.withType(net.fabricmc.loom.task.AbstractRemapJarTask).configureEach { + targetNamespace = "named" +} diff --git a/1.20.4/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 b/1.20.4/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 new file mode 100644 index 0000000..95dc085 --- /dev/null +++ b/1.20.4/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 @@ -0,0 +1,2 @@ +// 1.20.4 2024-02-10T01:11:18.844688 Language (en_us) +3b837616894d5f092cf8c1f8e4658d723dbc0320 assets/armorstatues/lang/en_us.json diff --git a/1.20.4/Common/src/generated/resources/assets/armorstatues/lang/en_us.json b/1.20.4/Common/src/generated/resources/assets/armorstatues/lang/en_us.json new file mode 100644 index 0000000..1235c24 --- /dev/null +++ b/1.20.4/Common/src/generated/resources/assets/armorstatues/lang/en_us.json @@ -0,0 +1,23 @@ +{ + "armorstatues.dataSync.failure": "Unable to modify armor stand data: %s", + "armorstatues.dataSync.failure.noArmorStand": "No Valid Armor Stand", + "armorstatues.dataSync.failure.noPermission": "No Permission", + "armorstatues.dataSync.failure.notFinished": "Queue Not Empty", + "armorstatues.dataSync.failure.outOfRange": "Out Of Range", + "armorstatues.dataSync.finished": "Finished sending queued armor stand data", + "armorstatues.screen.vanillaTweaks.checkTarget": "Check Armor Stand Target", + "armorstatues.screen.vanillaTweaks.checkTarget.description": "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, builder isn't necessarily the armor stand which opened builder menu.", + "armorstatues.screen.vanillaTweaks.lock": "Lock", + "armorstatues.screen.vanillaTweaks.lock.description": "Locking an armor stand prevents it from being changed using builder menu and disables interaction with the equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead": "Swap Mainhand & Helmet", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead.description": "Swaps items between the main hand and helmet equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand": "Swap Mainhand & Offhand", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand.description": "Swaps items between the main hand and off hand equipment slots.", + "armorstatues.screen.vanillaTweaks.toolRack": "Align Tool As Tool Rack", + "armorstatues.screen.vanillaTweaks.toolRack.description": "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand.", + "armorstatues.screen.vanillaTweaks.triggerSent": "Sent!", + "armorstatues.screen.vanillaTweaks.unlock": "Unlock", + "armorstatues.screen.vanillaTweaks.unlock.description": "Unlocking an armor stand reverts any adjustments made via a previous lock action.", + "statuemenus.screen.type.alignments": "Alignments", + "statuemenus.screen.type.vanillaTweaks": "Vanilla Tweaks" +} \ No newline at end of file diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java new file mode 100644 index 0000000..8ac4dac --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java @@ -0,0 +1,31 @@ +package fuzs.armorstatues; + +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzleslib.api.config.v3.ConfigHolder; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import fuzs.puzzleslib.api.event.v1.core.EventPhase; +import fuzs.puzzleslib.api.event.v1.entity.player.PlayerInteractEvents; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ArmorStatues implements ModConstructor { + public static final String MOD_ID = "armorstatues"; + public static final String MOD_NAME = "Armor Statues"; + public static final Logger LOGGER = LoggerFactory.getLogger(MOD_NAME); + + public static final ConfigHolder CONFIG = ConfigHolder.builder(MOD_ID).client(ClientConfig.class); + + @Override + public void onConstructMod() { + ModRegistry.touch(); + registerEventHandlers(); + } + + private static void registerEventHandlers() { + // high priority, so we run before other mods that add armor stand interactions + // we require empty hand + shift, so those other mods can still run their behaviors when those conditions are not met + PlayerInteractEvents.USE_ENTITY_AT.register(EventPhase.BEFORE, ArmorStandInteractHandler::onUseEntityAt); + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java new file mode 100644 index 0000000..2b4043c --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java @@ -0,0 +1,51 @@ +package fuzs.armorstatues.client; + +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandAlignmentsScreen; +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.client.handler.ArmorStandTooltipHandler; +import fuzs.armorstatues.client.handler.ClientInteractHandler; +import fuzs.armorstatues.client.handler.DataSyncTickHandler; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import fuzs.puzzleslib.api.client.core.v1.context.MenuScreensContext; +import fuzs.puzzleslib.api.client.event.v1.ClientTickEvents; +import fuzs.puzzleslib.api.client.event.v1.entity.player.InteractionInputEvents; +import fuzs.puzzleslib.api.client.event.v1.gui.ItemTooltipCallback; +import fuzs.puzzleslib.api.client.event.v1.gui.ScreenEvents; +import fuzs.puzzleslib.api.event.v1.core.EventPhase; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreenFactory; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandMenu; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; + +public class ArmorStatuesClient implements ClientModConstructor { + + @Override + public void onConstructMod() { + registerEventHandlers(); + } + + private static void registerEventHandlers() { + ItemTooltipCallback.EVENT.register(ArmorStandTooltipHandler::onItemTooltip); + ClientTickEvents.END.register(DataSyncTickHandler::onEndClientTick); + ScreenEvents.remove(Screen.class).register(DataSyncTickHandler::onRemove); + // event phase must match PlayerInteractEvents#USE_ENTITY_AT as both are implemented using the same event on Fabric + InteractionInputEvents.USE.register(EventPhase.BEFORE, ClientInteractHandler::onUseInteraction); + } + + @Override + public void onClientSetup() { + ArmorStandScreenFactory.register(ModRegistry.ALIGNMENTS_SCREEN_TYPE, ArmorStandAlignmentsScreen::new); + ArmorStandScreenFactory.register(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE, ArmorStandVanillaTweaksScreen::new); + } + + @SuppressWarnings("Convert2MethodRef") + @Override + public void onRegisterMenuScreens(MenuScreensContext context) { + // compiler doesn't like method reference :( + context.registerMenuScreen(ModRegistry.ARMOR_STAND_MENU_TYPE.value(), (ArmorStandMenu menu, Inventory inventory, Component component) -> { + return ArmorStandScreenFactory.createLastScreenType(menu, inventory, component); + }); + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java new file mode 100644 index 0000000..df01829 --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java @@ -0,0 +1,54 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandButtonsScreen; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandPositionScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandAlignment; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.phys.Vec3; + +import java.util.EnumSet; +import java.util.List; + +public class ArmorStandAlignmentsScreen extends ArmorStandButtonsScreen { + + public ArmorStandAlignmentsScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new DoubleButtonWidget(Component.translatable(ArmorStandPositionScreen.CENTERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CENTERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)).add(0.5, 0.0, 0.5); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + }, button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + })); + for (ArmorStandAlignment alignment : ArmorStandAlignment.values()) { + widgets.add(new SingleButtonWidget(Component.translatable(alignment.getTranslationKey()), Component.translatable(alignment.getDescriptionsKey()), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + ArmorStandAlignmentsScreen.this.dataSyncHandler.sendAlignment(alignment); + })); + } + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.ALIGNMENTS_SCREEN_TYPE; + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java new file mode 100644 index 0000000..f52054d --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java @@ -0,0 +1,75 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandButtonsScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; + +import java.util.List; + +public class ArmorStandVanillaTweaksScreen extends ArmorStandButtonsScreen { + public static final String TRIGGER_SENT_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.triggerSent"; + public static final String CHECK_TARGET_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget"; + public static final String CHECK_TARGET_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget.description"; + public static final String LOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock"; + public static final String LOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock.description"; + public static final String UNLOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock"; + public static final String UNLOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock.description"; + public static final String TOOL_RACK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack"; + public static final String TOOL_RACK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack.description"; + public static final String SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand"; + public static final String SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand.description"; + public static final String SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead"; + public static final String SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead.description"; + + public ArmorStandVanillaTweaksScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + public VanillaTweaksDataSyncHandler getDataSyncHandler() { + return (VanillaTweaksDataSyncHandler) super.getDataSyncHandler(); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new SingleButtonWidget(Component.translatable(CHECK_TARGET_TRANSLATION_KEY), Component.translatable(CHECK_TARGET_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.CHECK_TARGET); + this.onClose(); + })); + widgets.add(new DoubleButtonWidget(Component.translatable(LOCK_TRANSLATION_KEY), Component.translatable(UNLOCK_TRANSLATION_KEY), Component.translatable(LOCK_DESCRIPTION_KEY), Component.translatable(UNLOCK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_LOCK); + }, button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_UNLOCK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(TOOL_RACK_TRANSLATION_KEY), Component.translatable(TOOL_RACK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.AUTO_ALIGNMENT_TOOL_RACK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_OFFHAND); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_HEAD); + })); + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE; + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java new file mode 100644 index 0000000..29864c3 --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java @@ -0,0 +1,26 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.puzzleslib.api.core.v1.Proxy; +import fuzs.statuemenus.api.v1.helper.ArmorStandInteractHelper; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.TooltipFlag; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ArmorStandTooltipHandler { + + public static void onItemTooltip(ItemStack stack, @Nullable Player player, List lines, TooltipFlag context) { + if (stack.is(Items.ARMOR_STAND)) { + List components = Proxy.INSTANCE.splitTooltipLines(ArmorStandInteractHelper.getArmorStandHoverText()); + if (context.isAdvanced()) { + lines.addAll(lines.size() - (stack.hasTag() ? 2 : 1), components); + } else { + lines.addAll(components); + } + } + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java new file mode 100644 index 0000000..22b26d2 --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java @@ -0,0 +1,39 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.puzzleslib.api.event.v1.core.EventResult; +import fuzs.puzzleslib.api.event.v1.core.EventResultHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; + +public class ClientInteractHandler { + + public static EventResult onUseInteraction(Minecraft minecraft, LocalPlayer player, InteractionHand interactionHand, HitResult hitResult) { + + if (hitResult.getType() == HitResult.Type.ENTITY) { + + Entity entity = ((EntityHitResult) hitResult).getEntity(); + Vec3 hitVector = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ()); + EventResultHolder result = ArmorStandInteractHandler.onUseEntityAt(minecraft.player, + minecraft.level, + interactionHand, + entity, + hitVector + ); + + // if InteractionResult.FAIL is returned the mod is missing server-side, and we open the menu client-side without sending a packet to the server, so the server does not try to interact + if (result.filter(interactionResult -> interactionResult == InteractionResult.FAIL).isInterrupt()) { + + return EventResult.INTERRUPT; + } + } + + return EventResult.PASS; + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java new file mode 100644 index 0000000..822390a --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java @@ -0,0 +1,28 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import org.jetbrains.annotations.Nullable; + +public class DataSyncTickHandler { + @Nullable + private static DataSyncHandler dataSyncHandler; + + public static void onRemove(Screen screen) { + if (screen instanceof ArmorStandScreen armorStandScreen && armorStandScreen.getDataSyncHandler().shouldContinueTicking()) { + dataSyncHandler = armorStandScreen.getDataSyncHandler(); + } + } + + public static void onEndClientTick(Minecraft minecraft) { + if (minecraft.player != null && !(minecraft.screen instanceof ArmorStandScreen) && dataSyncHandler != null) { + if (dataSyncHandler.shouldContinueTicking()) { + dataSyncHandler.tick(); + } else { + dataSyncHandler = null; + } + } + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java new file mode 100644 index 0000000..137797d --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java @@ -0,0 +1,15 @@ +package fuzs.armorstatues.config; + +import fuzs.puzzleslib.api.config.v3.Config; +import fuzs.puzzleslib.api.config.v3.ConfigCore; +import fuzs.statuemenus.api.v1.client.gui.screens.AbstractArmorStandScreen; + +public class ClientConfig implements ConfigCore { + @Config(description = {"Allows for using this mod on a server without it (like a vanilla server) when the Vanilla Tweaks Armor Statues data pack is installed without the need for being a server operator.", "Download the Vanilla Tweaks Armor Statues data pack from here: " + AbstractArmorStandScreen.VANILLA_TWEAKS_HOMEPAGE}) + public boolean useVanillaTweaksTriggers = false; + @Config(description = "Do not check if the client has the necessary permission level for executing the '/data' command when trying to edit an armor stand. Useful when the current server is using custom permissions handling.") + public boolean overrideClientPermissionsCheck = false; + @Config(description = "The delay in ticks for sending queued client commands for editing armor stands to the server. Increase this values if commands are sent too quickly and the server fails to process all of them.") + @Config.IntRange(min = 20) + public int clientCommandDelay = 20; +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java new file mode 100644 index 0000000..44c0b50 --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java @@ -0,0 +1,39 @@ +package fuzs.armorstatues.data.client; + +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.puzzleslib.api.client.data.v2.AbstractLanguageProvider; +import fuzs.puzzleslib.api.data.v2.core.DataProviderContext; + +public class ModLanguageProvider extends AbstractLanguageProvider { + + public ModLanguageProvider(DataProviderContext context) { + super(context); + } + + @Override + public void addTranslations(TranslationBuilder builder) { + builder.add(CommandDataSyncHandler.FAILURE_TRANSLATION_KEY, "Unable to modify armor stand data: %s"); + builder.add(CommandDataSyncHandler.NO_PERMISSION_TRANSLATION_KEY, "No Permission"); + builder.add(CommandDataSyncHandler.NO_ARMOR_STAND_TRANSLATION_KEY, "No Valid Armor Stand"); + builder.add(CommandDataSyncHandler.OUT_OF_RANGE_TRANSLATION_KEY, "Out Of Range"); + builder.add(CommandDataSyncHandler.NOT_FINISHED_TRANSLATION_KEY, "Queue Not Empty"); + builder.add(CommandDataSyncHandler.FINISHED_TRANSLATION_KEY, "Finished sending queued armor stand data"); + builder.add(ModRegistry.ALIGNMENTS_SCREEN_TYPE.getTranslationKey(), "Alignments"); + builder.add(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE.getTranslationKey(), "Vanilla Tweaks"); + builder.add(ArmorStandVanillaTweaksScreen.TRIGGER_SENT_TRANSLATION_KEY, "Sent!"); + builder.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_TRANSLATION_KEY, "Check Armor Stand Target"); + builder.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_DESCRIPTION_KEY, "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, builder isn't necessarily the armor stand which opened builder menu."); + builder.add(ArmorStandVanillaTweaksScreen.LOCK_TRANSLATION_KEY, "Lock"); + builder.add(ArmorStandVanillaTweaksScreen.LOCK_DESCRIPTION_KEY, "Locking an armor stand prevents it from being changed using builder menu and disables interaction with the equipment slots."); + builder.add(ArmorStandVanillaTweaksScreen.UNLOCK_TRANSLATION_KEY, "Unlock"); + builder.add(ArmorStandVanillaTweaksScreen.UNLOCK_DESCRIPTION_KEY, "Unlocking an armor stand reverts any adjustments made via a previous lock action."); + builder.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_TRANSLATION_KEY, "Align Tool As Tool Rack"); + builder.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_DESCRIPTION_KEY, "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand."); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY, "Swap Mainhand & Offhand"); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY, "Swaps items between the main hand and off hand equipment slots."); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY, "Swap Mainhand & Helmet"); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY, "Swaps items between the main hand and helmet equipment slots."); + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java new file mode 100644 index 0000000..2dd8af0 --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java @@ -0,0 +1,43 @@ +package fuzs.armorstatues.handler; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.proxy.Proxy; +import fuzs.puzzleslib.api.core.v1.ModLoaderEnvironment; +import fuzs.puzzleslib.api.event.v1.core.EventResultHolder; +import fuzs.statuemenus.api.v1.helper.ArmorStandInteractHelper; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; + +public class ArmorStandInteractHandler { + + public static EventResultHolder onUseEntityAt(Player player, Level level, InteractionHand interactionHand, Entity target, Vec3 hitVector) { + + if (player.getAbilities().mayBuild && target.getType() == EntityType.ARMOR_STAND) { + + boolean clientsideOnly = level.isClientSide && !ModLoaderEnvironment.INSTANCE.isModPresentServerside(ArmorStatues.MOD_ID); + // the menu won't exist in the registry if the mod is missing serverside since Forge syncs registries to clients + MenuType menuType = clientsideOnly ? null : ModRegistry.ARMOR_STAND_MENU_TYPE.value(); + EventResultHolder result = ArmorStandInteractHelper.tryOpenArmorStatueMenu(player, level, interactionHand, (ArmorStand) target, menuType, ModRegistry.ARMOR_STAND_DATA_PROVIDER); + if (result.isInterrupt() && clientsideOnly) { + + Proxy.INSTANCE.openArmorStandScreen((ArmorStand) target, player); + // required so no packet is sent to server when only installed client-side, so the server doesn't change any equipment when we only want to open the screen + // returning InteractionResult.FAIL will miss out on the player arm swing animation, which we manually play here + player.swing(interactionHand); + return EventResultHolder.interrupt(InteractionResult.FAIL); + } + + return result; + } + + return EventResultHolder.pass(); + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java new file mode 100644 index 0000000..c296189 --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java @@ -0,0 +1,32 @@ +package fuzs.armorstatues.init; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.init.v3.registry.RegistryManager; +import fuzs.statuemenus.api.v1.world.entity.decoration.ArmorStandDataProvider; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandMenu; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.core.Holder; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class ModRegistry { + static final RegistryManager REGISTRY = RegistryManager.from(ArmorStatues.MOD_ID); + public static final Holder.Reference> ARMOR_STAND_MENU_TYPE = REGISTRY.registerExtendedMenuType("armor_stand", () -> (containerId, inventory, data) -> { + return ArmorStandMenu.create(ModRegistry.ARMOR_STAND_MENU_TYPE.value(), containerId, inventory, data, ModRegistry.ARMOR_STAND_DATA_PROVIDER); + }); + + public static final ArmorStandScreenType ALIGNMENTS_SCREEN_TYPE = new ArmorStandScreenType("alignments", new ItemStack(Items.DIAMOND_PICKAXE)); + public static final ArmorStandScreenType VANILLA_TWEAKS_SCREEN_TYPE = new ArmorStandScreenType("vanillaTweaks", new ItemStack(Items.WRITTEN_BOOK)); + public static final ArmorStandDataProvider ARMOR_STAND_DATA_PROVIDER = new ArmorStandDataProvider() { + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return new ArmorStandScreenType[]{ArmorStandScreenType.ROTATIONS, ArmorStandScreenType.POSES, ArmorStandScreenType.STYLE, ArmorStandScreenType.POSITION, ModRegistry.ALIGNMENTS_SCREEN_TYPE, ArmorStandScreenType.EQUIPMENT}; + } + }; + + public static void touch() { + // NO-OP + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java new file mode 100644 index 0000000..3d8b59a --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java @@ -0,0 +1,226 @@ +package fuzs.armorstatues.network.client.data; + +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandAlignment; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandPose; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandStyleOption; +import net.minecraft.ChatFormatting; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.FloatTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.function.BiPredicate; + +public class CommandDataSyncHandler implements DataSyncHandler { + public static final String NO_PERMISSION_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noPermission"; + public static final String NO_ARMOR_STAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noArmorStand"; + public static final String OUT_OF_RANGE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.outOfRange"; + public static final String NOT_FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.notFinished"; + public static final String FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.finished"; + public static final String FAILURE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure"; + private static final Queue CLIENT_COMMAND_QUEUE = new ArrayDeque<>(); + + @Nullable + private static ArmorStand queueArmorStand; + private static int itemDequeuedTicks; + + private final ArmorStandHolder holder; + protected final LocalPlayer player; + protected ArmorStandPose lastSyncedPose; + + public CommandDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + this.holder = holder; + this.lastSyncedPose = ArmorStandPose.fromEntity(this.holder.getArmorStand()); + this.player = player; + } + + @Override + public ArmorStandHolder getArmorStandHolder() { + return this.holder; + } + + @Override + public void sendName(String name) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.setCustomArmorStandName(this.getArmorStand(), name); + CompoundTag tag = new CompoundTag(); + tag.putString("CustomName", Component.Serializer.toJson(Component.literal(name))); + this.enqueueEntityData(tag); + this.finalizeCurrentOperation(); + } + + @Override + public final void sendPose(ArmorStandPose pose) { + this.sendPose(pose, true); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + // split this into multiple chat messages as the client chat field has a very low character limit + this.sendPosePart(pose::serializeBodyPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeArmPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeLegPoses, this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + if (finalize) this.finalizeCurrentOperation(); + } + + private void sendPosePart(BiPredicate dataWriter, ArmorStandPose lastSyncedPose) { + CompoundTag tag = new CompoundTag(); + if (dataWriter.test(tag, lastSyncedPose)) { + CompoundTag tagToSend = new CompoundTag(); + tagToSend.put("Pose", tag); + this.enqueueEntityData(tagToSend); + } + } + + @Override + public @Nullable ArmorStandPose getLastSyncedPose() { + return this.lastSyncedPose; + } + + @Override + public final void sendPosition(double posX, double posY, double posZ) { + this.sendPosition(posX, posY, posZ, true); + + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(DoubleTag.valueOf(posX)); + listTag.add(DoubleTag.valueOf(posY)); + listTag.add(DoubleTag.valueOf(posZ)); + CompoundTag tag = new CompoundTag(); + tag.put("Pos", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public final void sendRotation(float rotation) { + this.sendRotation(rotation, true); + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(FloatTag.valueOf(rotation)); + CompoundTag tag = new CompoundTag(); + tag.put("Rotation", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public final void sendStyleOption(ArmorStandStyleOption styleOption, boolean value) { + this.sendStyleOption(styleOption, value, true); + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + CompoundTag tag = new CompoundTag(); + styleOption.toTag(tag, value); + this.enqueueEntityData(tag); + styleOption.setOption(this.getArmorStand(), value); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.super.sendAlignment(alignment); + } + + @Override + public boolean supportsScreenType(ArmorStandScreenType screenType) { + return !screenType.requiresServer(); + } + + @Override + public void tick() { + if (itemDequeuedTicks > 0) itemDequeuedTicks--; + if (itemDequeuedTicks == 0 && queueArmorStand != null && !CLIENT_COMMAND_QUEUE.isEmpty()) { + if (this.testArmorStand(queueArmorStand).right().isPresent()) { + this.player.connection.sendCommand(CLIENT_COMMAND_QUEUE.poll()); + } else { + CLIENT_COMMAND_QUEUE.clear(); + } + itemDequeuedTicks = this.getDequeueDelayTicks(); + } else if (itemDequeuedTicks == 1 && CLIENT_COMMAND_QUEUE.isEmpty()) { + this.sendDisplayMessage(Component.translatable(FINISHED_TRANSLATION_KEY), false); + } + } + + protected int getDequeueDelayTicks() { + return 5; + } + + @Override + public boolean shouldContinueTicking() { + return !CLIENT_COMMAND_QUEUE.isEmpty() || itemDequeuedTicks != 0; + } + + protected boolean isEditingAllowed() { + return this.isEditingAllowed(!ArmorStatues.CONFIG.get(ClientConfig.class).overrideClientPermissionsCheck); + } + + protected final boolean isEditingAllowed(boolean testPermissionLevel) { + if (testPermissionLevel && !this.player.hasPermissions(2)) { + this.sendFailureMessage(Component.translatable(NO_PERMISSION_TRANSLATION_KEY)); + return false; + } + return this.player.getAbilities().mayBuild && this.testArmorStand(this.getArmorStand()).ifLeft(this::sendFailureMessage).right().isPresent(); + } + + protected Either testArmorStand(ArmorStand armorStand) { + return !armorStand.isAlive() ? Either.left(Component.translatable(NO_ARMOR_STAND_TRANSLATION_KEY)) : Either.right(Unit.INSTANCE); + } + + protected boolean enqueueClientCommand(String clientCommand) { + if (CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = null; + } else if (queueArmorStand != null) { + this.sendFailureMessage(Component.translatable(NOT_FINISHED_TRANSLATION_KEY)); + return false; + } + CLIENT_COMMAND_QUEUE.offer(clientCommand); + return true; + } + + @Override + public void finalizeCurrentOperation() { + if (!CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = this.getArmorStand(); + } + } + + protected void sendFailureMessage(Component component) { + this.sendDisplayMessage(Component.translatable(FAILURE_TRANSLATION_KEY, component), true); + } + + protected void sendDisplayMessage(Component component, boolean failure) { + this.player.displayClientMessage(Component.empty().append(component).withStyle(failure ? ChatFormatting.RED : ChatFormatting.GREEN), false); + } + + private void enqueueEntityData(CompoundTag tag) { + this.enqueueClientCommand("data merge entity %s %s".formatted(this.getArmorStand().getStringUUID(), tag.getAsString())); + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java new file mode 100644 index 0000000..dbdc639 --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java @@ -0,0 +1,359 @@ +package fuzs.armorstatues.network.client.data; + +import com.google.common.collect.ImmutableSortedMap; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.*; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.Rotations; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.NavigableMap; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +public class VanillaTweaksDataSyncHandler extends CommandDataSyncHandler { + private static final int MAX_INCREMENTAL_OPERATIONS = 12; + public static final int CHECK_TARGET = 999; + public static final int SWAP_SLOTS_MAINHAND_AND_OFFHAND = 161; + public static final int SWAP_SLOTS_MAINHAND_AND_HEAD = 162; + public static final int MIRROR_ARMS_LEFT_TO_RIGHT = 131; + public static final int MIRROR_ARMS_RIGHT_TO_LEFT = 132; + public static final int MIRROR_LEGS_LEFT_TO_RIGHT = 133; + public static final int MIRROR_LEGS_RIGHT_TO_LEFT = 134; + public static final int UTILITIES_LOCK = 1000; + public static final int UTILITIES_UNLOCK = 1001; + public static final int MIRROR_AND_FLIP_FLIP = 135; + public static final int SHOW_BASE_PLATE_YES = 1; + public static final int SHOW_BASE_PLATE_NO = 2; + public static final int SHOW_ARMS_YES = 3; + public static final int SHOW_ARMS_NO = 4; + public static final int SMALL_STAND_YES = 5; + public static final int SMALL_STAND_NO = 6; + public static final int APPLY_GRAVITY_YES = 7; + public static final int APPLY_GRAVITY_NO = 8; + public static final int STAND_VISIBLE_YES = 9; + public static final int STAND_VISIBLE_NO = 10; + public static final int DISPLAY_NAME_YES = 11; + public static final int DISPLAY_NAME_NO = 12; + public static final int NUDGE_POSITION_X8_NEGATIVE = 40; + public static final int NUDGE_POSITION_X3_NEGATIVE = 101; + public static final int NUDGE_POSITION_X1_NEGATIVE = 102; + public static final int NUDGE_POSITION_X1_POSITIVE = 103; + public static final int NUDGE_POSITION_X3_POSITIVE = 104; + public static final int NUDGE_POSITION_X8_POSITIVE = 43; + public static final int NUDGE_POSITION_Y8_NEGATIVE = 44; + public static final int NUDGE_POSITION_Y3_NEGATIVE = 105; + public static final int NUDGE_POSITION_Y1_NEGATIVE = 106; + public static final int NUDGE_POSITION_Y1_POSITIVE = 107; + public static final int NUDGE_POSITION_Y3_POSITIVE = 108; + public static final int NUDGE_POSITION_Y8_POSITIVE = 47; + public static final int NUDGE_POSITION_Z8_NEGATIVE = 48; + public static final int NUDGE_POSITION_Z3_NEGATIVE = 109; + public static final int NUDGE_POSITION_Z1_NEGATIVE = 110; + public static final int NUDGE_POSITION_Z1_POSITIVE = 111; + public static final int NUDGE_POSITION_Z3_POSITIVE = 112; + public static final int NUDGE_POSITION_Z8_POSITIVE = 51; + public static final int ADJUST_ROTATION_ANGLE_STEP_45 = 120; + public static final int ADJUST_ROTATION_ANGLE_STEP_15 = 121; + public static final int ADJUST_ROTATION_ANGLE_STEP_5 = 122; + public static final int ADJUST_ROTATION_ANGLE_STEP_1 = 123; + public static final int ADJUST_ROTATION_ROTATE_RIGHT = 56; + public static final int ADJUST_ROTATION_ROTATE_LEFT = 57; + public static final int POSE_PRESETS_ATTENTION = 20; + public static final int POSE_PRESETS_WALKING = 21; + public static final int POSE_PRESETS_RUNNING = 22; + public static final int POSE_PRESETS_POINTING = 23; + public static final int POSE_PRESETS_BLOCKING = 24; + public static final int POSE_PRESETS_LUNGEING = 25; + public static final int POSE_PRESETS_WINNING = 26; + public static final int POSE_PRESETS_SITTING = 27; + public static final int POSE_PRESETS_ARABESQUE = 28; + public static final int POSE_PRESETS_CUPID = 29; + public static final int POSE_PRESETS_CONFIDENT = 30; + public static final int POSE_PRESETS_SALUTE = 31; + public static final int POSE_PRESETS_DEATH = 32; + public static final int POSE_PRESETS_FACEPALM = 33; + public static final int POSE_PRESETS_LAZING = 34; + public static final int POSE_PRESETS_CONFUSED = 35; + public static final int POSE_PRESETS_FORMAL = 36; + public static final int POSE_PRESETS_SAD = 37; + public static final int POSE_PRESETS_JOYOUS = 38; + public static final int POSE_PRESETS_STARGAZING = 39; + public static final int AUTO_ALIGNMENT_BLOCK_ON_SURFACE = 151; + public static final int AUTO_ALIGNMENT_ITEM_ON_SURFACE = 152; + public static final int AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE = 153; + public static final int AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE = 154; + public static final int AUTO_ALIGNMENT_TOOL_RACK = 155; + public static final int POSE_ADJUSTMENT_HEAD_X_NEGATIVE = 60; + public static final int POSE_ADJUSTMENT_HEAD_X_POSITIVE = 61; + public static final int POSE_ADJUSTMENT_HEAD_Y_NEGATIVE = 62; + public static final int POSE_ADJUSTMENT_HEAD_Y_POSITIVE = 63; + public static final int POSE_ADJUSTMENT_HEAD_Z_NEGATIVE = 64; + public static final int POSE_ADJUSTMENT_HEAD_Z_POSITIVE = 65; + public static final int POSE_ADJUSTMENT_BODY_X_NEGATIVE = 67; + public static final int POSE_ADJUSTMENT_BODY_X_POSITIVE = 66; + public static final int POSE_ADJUSTMENT_BODY_Y_NEGATIVE = 68; + public static final int POSE_ADJUSTMENT_BODY_Y_POSITIVE = 69; + public static final int POSE_ADJUSTMENT_BODY_Z_NEGATIVE = 70; + public static final int POSE_ADJUSTMENT_BODY_Z_POSITIVE = 71; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE = 72; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE = 73; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE = 74; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE = 75; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE = 77; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE = 76; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE = 78; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE = 79; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE = 81; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE = 80; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE = 82; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE = 83; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE = 84; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE = 85; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE = 87; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE = 86; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE = 89; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE = 88; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE = 90; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE = 91; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE = 92; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE = 93; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE = 94; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE = 95; + private static final int[] POSE_ADJUSTMENT_HEAD = new int[]{POSE_ADJUSTMENT_HEAD_X_NEGATIVE, POSE_ADJUSTMENT_HEAD_X_POSITIVE, POSE_ADJUSTMENT_HEAD_Y_NEGATIVE, POSE_ADJUSTMENT_HEAD_Y_POSITIVE, POSE_ADJUSTMENT_HEAD_Z_NEGATIVE, POSE_ADJUSTMENT_HEAD_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_BODY = new int[]{POSE_ADJUSTMENT_BODY_X_POSITIVE, POSE_ADJUSTMENT_BODY_X_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_POSITIVE, POSE_ADJUSTMENT_BODY_Z_NEGATIVE, POSE_ADJUSTMENT_BODY_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_ARM = new int[]{POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_ARM = new int[]{POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_LEG = new int[]{POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_LEG = new int[]{POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE}; + private static final NavigableMap NUDGE_POSITIONS_X_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_X3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_X8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_X_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_X3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_X8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_POSITIVE); + private static final NavigableMap ADJUST_ROTATION_ANGLE_STEPS = ImmutableSortedMap.of(1.0F, ADJUST_ROTATION_ANGLE_STEP_1, 5.0F, ADJUST_ROTATION_ANGLE_STEP_5, 15.0F, ADJUST_ROTATION_ANGLE_STEP_15, 45.0F, ADJUST_ROTATION_ANGLE_STEP_45); + + public VanillaTweaksDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + super(holder, player); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue = this.getTriggerValueFromPose(pose); + if (triggerValue != -1) { + if (this.enqueueTriggerValue(triggerValue)) { + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + } + } else { + this.tryApplyAllPoseParts(pose); + } + if (finalize) this.finalizeCurrentOperation(); + } + + private int getTriggerValueFromPose(ArmorStandPose pose) { + if (pose.getSourceType() == ArmorStandPose.SourceType.EMPTY) return POSE_PRESETS_ATTENTION; + if (pose.getSourceType() == ArmorStandPose.SourceType.MIRRORED) return MIRROR_AND_FLIP_FLIP; + if (pose.getSourceType() != ArmorStandPose.SourceType.VANILLA_TWEAKS) return -1; + if (pose == ArmorStandPose.WALKING) return POSE_PRESETS_WALKING; + if (pose == ArmorStandPose.RUNNING) return POSE_PRESETS_RUNNING; + if (pose == ArmorStandPose.POINTING) return POSE_PRESETS_POINTING; + if (pose == ArmorStandPose.BLOCKING) return POSE_PRESETS_BLOCKING; + if (pose == ArmorStandPose.LUNGEING) return POSE_PRESETS_LUNGEING; + if (pose == ArmorStandPose.WINNING) return POSE_PRESETS_WINNING; + if (pose == ArmorStandPose.SITTING) return POSE_PRESETS_SITTING; + if (pose == ArmorStandPose.ARABESQUE) return POSE_PRESETS_ARABESQUE; + if (pose == ArmorStandPose.CUPID) return POSE_PRESETS_CUPID; + if (pose == ArmorStandPose.CONFIDENT) return POSE_PRESETS_CONFIDENT; + if (pose == ArmorStandPose.SALUTE) return POSE_PRESETS_SALUTE; + if (pose == ArmorStandPose.DEATH) return POSE_PRESETS_DEATH; + if (pose == ArmorStandPose.FACEPALM) return POSE_PRESETS_FACEPALM; + if (pose == ArmorStandPose.LAZING) return POSE_PRESETS_LAZING; + if (pose == ArmorStandPose.CONFUSED) return POSE_PRESETS_CONFUSED; + if (pose == ArmorStandPose.FORMAL) return POSE_PRESETS_FORMAL; + if (pose == ArmorStandPose.SAD) return POSE_PRESETS_SAD; + if (pose == ArmorStandPose.JOYOUS) return POSE_PRESETS_JOYOUS; + if (pose == ArmorStandPose.STARGAZING) return POSE_PRESETS_STARGAZING; + return -1; + } + + private void tryApplyAllPoseParts(ArmorStandPose pose) { + if (!this.tryApplyPosePart(this.lastSyncedPose.getHeadPose(), pose.getNullableHeadPose(), POSE_ADJUSTMENT_HEAD, this.lastSyncedPose::withHeadPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getBodyPose(), pose.getNullableBodyPose(), POSE_ADJUSTMENT_BODY, this.lastSyncedPose::withBodyPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightArmPose(), pose.getNullableRightArmPose(), POSE_ADJUSTMENT_RIGHT_ARM, this.lastSyncedPose::withRightArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftArmPose(), pose.getNullableLeftArmPose(), POSE_ADJUSTMENT_LEFT_ARM, this.lastSyncedPose::withLeftArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightLegPose(), pose.getNullableRightLegPose(), POSE_ADJUSTMENT_RIGHT_LEG, this.lastSyncedPose::withRightLegPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftLegPose(), pose.getNullableLeftLegPose(), POSE_ADJUSTMENT_LEFT_LEG, this.lastSyncedPose::withLeftLegPose)) + return; + } + + private boolean tryApplyPosePart(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment, Function function) { + if (this.tryApplyPoseAdjustment(oldPose, newPose, poseAdjustment)) { + this.lastSyncedPose = function.apply(newPose != null ? newPose : oldPose); + return true; + } else { + return false; + } + } + + private boolean tryApplyPoseAdjustment(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment) { + if (newPose == null || oldPose.equals(newPose)) return true; + if (!this.applyIncrementsFromSteps(oldPose.getX(), newPose.getX(), poseAdjustment[0], poseAdjustment[1])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getY(), newPose.getY(), poseAdjustment[2], poseAdjustment[3])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getZ(), newPose.getZ(), poseAdjustment[4], poseAdjustment[5])) return false; + return true; + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyPositionIncrements(this.getArmorStand().getX(), posX, NUDGE_POSITIONS_X_POSITIVE, NUDGE_POSITIONS_X_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getY(), posY, NUDGE_POSITIONS_Y_POSITIVE, NUDGE_POSITIONS_Y_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getZ(), posZ, NUDGE_POSITIONS_Z_POSITIVE, NUDGE_POSITIONS_Z_NEGATIVE); + if (finalize) this.finalizeCurrentOperation(); + } + + private void applyPositionIncrements(double oldValue, double newValue, NavigableMap positiveNudgePositions, NavigableMap negativeNudgePositions) { + double value = newValue - oldValue; + double signum = Math.signum(value); + value = Math.abs(value); + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = (signum == -1.0F ? negativeNudgePositions : positiveNudgePositions).floorEntry(value); + if (entry != null) { + value -= entry.getKey(); + if (!this.enqueueTriggerValue(entry.getValue())) { + return; + } + } else { + break; + } + } + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyIncrementsFromSteps(this.getArmorStand().getYRot(), rotation, ADJUST_ROTATION_ROTATE_RIGHT, ADJUST_ROTATION_ROTATE_LEFT); + if (finalize) this.finalizeCurrentOperation(); + } + + private boolean applyIncrementsFromSteps(float oldValue, float newValue, int triggerValueNegative, int triggerValuePositive) { + float value = newValue - oldValue; + float signum = Math.signum(value); + value = Math.abs(value); + float lastIncrement = 0.0F; + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = ADJUST_ROTATION_ANGLE_STEPS.floorEntry(value); + if (entry != null) { + float currentIncrement = entry.getKey(); + value -= currentIncrement; + if (currentIncrement != lastIncrement) { + lastIncrement = currentIncrement; + if (!this.enqueueTriggerValue(entry.getValue())) { + return false; + } + } + if (!this.enqueueTriggerValue(signum == -1.0F ? triggerValuePositive : triggerValueNegative)) { + return false; + } + } else { + break; + } + } + return true; + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue; + if (styleOption == ArmorStandStyleOptions.SHOW_NAME) { + triggerValue = value ? DISPLAY_NAME_YES : DISPLAY_NAME_NO; + } else if (styleOption == ArmorStandStyleOptions.SHOW_ARMS) { + triggerValue = value ? SHOW_ARMS_YES : SHOW_ARMS_NO; + } else if (styleOption == ArmorStandStyleOptions.SMALL) { + triggerValue = value ? SMALL_STAND_YES : SMALL_STAND_NO; + } else if (styleOption == ArmorStandStyleOptions.INVISIBLE) { + triggerValue = value ? STAND_VISIBLE_NO : STAND_VISIBLE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_BASE_PLATE) { + triggerValue = value ? SHOW_BASE_PLATE_NO : SHOW_BASE_PLATE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_GRAVITY) { + triggerValue = value ? APPLY_GRAVITY_NO : APPLY_GRAVITY_YES; + } else { + super.sendStyleOption(styleOption, value, finalize); + return; + } + if (this.sendSingleTriggerValue(triggerValue, finalize)) { + styleOption.setOption(this.getArmorStand(), value); + } + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + int triggerValue = switch (alignment) { + case BLOCK -> AUTO_ALIGNMENT_BLOCK_ON_SURFACE; + case FLOATING_ITEM -> AUTO_ALIGNMENT_ITEM_ON_SURFACE; + case FLAT_ITEM -> AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE; + case TOOL -> AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE; + }; + this.sendSingleTriggerValue(triggerValue, true); + } + + public void sendSingleTriggerValue(int triggerValue) { + if (!this.isEditingAllowed()) return; + this.sendSingleTriggerValue(triggerValue, true); + } + + private boolean sendSingleTriggerValue(int triggerValue, boolean finalize) { + boolean result = this.enqueueTriggerValue(triggerValue); + if (finalize) this.finalizeCurrentOperation(); + return result; + } + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return Stream.concat(Stream.of(super.getScreenTypes()), Stream.of(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE)).toArray(ArmorStandScreenType[]::new); + } + + @Override + protected boolean isEditingAllowed() { + return this.isEditingAllowed(false); + } + + @Override + protected Either testArmorStand(ArmorStand armorStand) { + return super.testArmorStand(armorStand).>map(Optional::of, $ -> { + if (this.player.distanceToSqr(armorStand) < 9.0) { + return Optional.empty(); + } else { + return Optional.of(Component.translatable(OUT_OF_RANGE_TRANSLATION_KEY)); + } + }).>map(Either::left).orElse(Either.right(Unit.INSTANCE)); + } + + @Override + protected int getDequeueDelayTicks() { + return ArmorStatues.CONFIG.get(ClientConfig.class).clientCommandDelay; + } + + private boolean enqueueTriggerValue(int triggerValue) { + return this.enqueueClientCommand("trigger as_trigger set %s".formatted(triggerValue)); + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java new file mode 100644 index 0000000..3689876 --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java @@ -0,0 +1,51 @@ +package fuzs.armorstatues.proxy; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreenFactory; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.entity.decoration.ArmorStandDataProvider; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ClientProxy extends ServerProxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + ArmorStandHolder holder = new ArmorStandHolder() { + + @Override + public ArmorStand getArmorStand() { + return armorStand; + } + + @Override + public ArmorStandDataProvider getDataProvider() { + return ModRegistry.ARMOR_STAND_DATA_PROVIDER; + } + }; + Screen screen = ArmorStandScreenFactory.createLastScreenType(holder, + player.getInventory(), + armorStand.getDisplayName(), + createDataSyncHandler(holder, (LocalPlayer) player) + ); + Minecraft minecraft = Minecraft.getInstance(); + minecraft.setScreen(screen); + } + + private static DataSyncHandler createDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + if ((!player.hasPermissions(2) || ArmorStatues.CONFIG.get(ClientConfig.class).overrideClientPermissionsCheck) && + ArmorStatues.CONFIG.get(ClientConfig.class).useVanillaTweaksTriggers) { + return new VanillaTweaksDataSyncHandler(holder, player); + } else { + return new CommandDataSyncHandler(holder, player); + } + } +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java new file mode 100644 index 0000000..1f5ae83 --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java @@ -0,0 +1,11 @@ +package fuzs.armorstatues.proxy; + +import fuzs.puzzleslib.api.core.v1.ModLoaderEnvironment; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public interface Proxy { + Proxy INSTANCE = ModLoaderEnvironment.INSTANCE.isClient() ? new ClientProxy() : new ServerProxy(); + + void openArmorStandScreen(ArmorStand armorStand, Player player); +} diff --git a/1.20.4/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java b/1.20.4/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java new file mode 100644 index 0000000..f41e246 --- /dev/null +++ b/1.20.4/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java @@ -0,0 +1,12 @@ +package fuzs.armorstatues.proxy; + +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ServerProxy implements Proxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + // NO-OP + } +} diff --git a/1.20.4/Common/src/main/resources/architectury.common.json b/1.20.4/Common/src/main/resources/architectury.common.json new file mode 100644 index 0000000..4cfa289 --- /dev/null +++ b/1.20.4/Common/src/main/resources/architectury.common.json @@ -0,0 +1,3 @@ +{ + "accessWidener": "armorstatues.accesswidener" +} \ No newline at end of file diff --git a/1.20.4/Common/src/main/resources/armorstatues.accesswidener b/1.20.4/Common/src/main/resources/armorstatues.accesswidener new file mode 100644 index 0000000..236e6b1 --- /dev/null +++ b/1.20.4/Common/src/main/resources/armorstatues.accesswidener @@ -0,0 +1 @@ +accessWidener v2 named diff --git a/Forge/src/main/resources/armorstatues.forge.mixins.json b/1.20.4/Common/src/main/resources/common.mixins.json similarity index 58% rename from Forge/src/main/resources/armorstatues.forge.mixins.json rename to 1.20.4/Common/src/main/resources/common.mixins.json index 16fc290..77fc8f4 100644 --- a/Forge/src/main/resources/armorstatues.forge.mixins.json +++ b/1.20.4/Common/src/main/resources/common.mixins.json @@ -2,12 +2,10 @@ "required": true, "minVersion": "0.8", "compatibilityLevel": "JAVA_17", - "package": "fuzs.armorstatues.mixin", - "refmap": "armorstatues.refmap.json", + "package": "${modGroup}.mixin", "mixins": [ ], "client": [ - "client.MultiPlayerGameModeMixin" ], "injectors": { "defaultRequire": 1 diff --git a/1.20.4/Common/src/main/resources/mod_banner.png b/1.20.4/Common/src/main/resources/mod_banner.png new file mode 100644 index 0000000..62a4610 Binary files /dev/null and b/1.20.4/Common/src/main/resources/mod_banner.png differ diff --git a/1.20.4/Common/src/main/resources/mod_logo.png b/1.20.4/Common/src/main/resources/mod_logo.png new file mode 100644 index 0000000..869766b Binary files /dev/null and b/1.20.4/Common/src/main/resources/mod_logo.png differ diff --git a/Common/src/main/resources/pack.mcmeta b/1.20.4/Common/src/main/resources/pack.mcmeta similarity index 60% rename from Common/src/main/resources/pack.mcmeta rename to 1.20.4/Common/src/main/resources/pack.mcmeta index 6221806..546f4f1 100755 --- a/Common/src/main/resources/pack.mcmeta +++ b/1.20.4/Common/src/main/resources/pack.mcmeta @@ -1,6 +1,6 @@ { "pack": { "description": "${modDescription}", - "pack_format": ${packFormat} + "pack_format": ${resourcePackFormat} } } diff --git a/1.20.4/Fabric/build.gradle b/1.20.4/Fabric/build.gradle new file mode 100644 index 0000000..ac4ba5e --- /dev/null +++ b/1.20.4/Fabric/build.gradle @@ -0,0 +1,12 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/v2/fabric.gradle" + +dependencies { + // Fabric Api + modApi libs.fabricapi.fabric + + // Puzzles Lib + modApi libs.puzzleslib.fabric + + // Statue Menus + modApi(include(libs.statuemenus.fabric.get())) +} diff --git a/1.20.4/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java b/1.20.4/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java new file mode 100644 index 0000000..822cef4 --- /dev/null +++ b/1.20.4/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java @@ -0,0 +1,13 @@ +package fuzs.armorstatues.fabric; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.fabricmc.api.ModInitializer; + +public class ArmorStatuesFabric implements ModInitializer { + + @Override + public void onInitialize() { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } +} diff --git a/1.20.4/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java b/1.20.4/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java new file mode 100644 index 0000000..2414106 --- /dev/null +++ b/1.20.4/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java @@ -0,0 +1,14 @@ +package fuzs.armorstatues.fabric.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.client.ArmorStatuesClient; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import net.fabricmc.api.ClientModInitializer; + +public class ArmorStatuesFabricClient implements ClientModInitializer { + + @Override + public void onInitializeClient() { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + } +} diff --git a/1.20.4/Fabric/src/main/resources/fabric.mixins.json b/1.20.4/Fabric/src/main/resources/fabric.mixins.json new file mode 100644 index 0000000..6bf7940 --- /dev/null +++ b/1.20.4/Fabric/src/main/resources/fabric.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.fabric.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "refmap": "${modId}.fabric.refmap.json" +} diff --git a/1.20.4/Fabric/src/main/resources/fabric.mod.json b/1.20.4/Fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..d67e512 --- /dev/null +++ b/1.20.4/Fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,46 @@ +{ + "schemaVersion": 1, + "id": "${modId}", + "version": "${modVersion}", + + "name": "${modName}", + "description": "${modDescription}", + + "authors": [ + "${modAuthor}" + ], + + "contact": { + "homepage": "${modPageUrl}", + "issues": "${modIssueUrl}", + "sources": "${modPageUrl}" + }, + + "license": "${modLicense}", + "icon": "mod_logo.png", + + "environment": "${modFabricEnvironment}", + + "entrypoints": { + "main": [ + "${mainEntryPoint}" + ], + "client": [ + "${clientEntryPoint}" + ] + }, + + "mixins": [ + "${modId}.common.mixins.json", + "${modId}.fabric.mixins.json" + ], + + "depends": { + "fabricloader": ">=${minFabricVersion}", + "fabric-api": ">=${minFabricApiVersion}", + "puzzleslib": ">=${minPuzzlesVersion}", + "statuemenus": "*", + "minecraft": "${minecraftVersion}", + "java": ">=17" + } +} diff --git a/1.20.4/Forge/build.gradle b/1.20.4/Forge/build.gradle new file mode 100644 index 0000000..a0ed80c --- /dev/null +++ b/1.20.4/Forge/build.gradle @@ -0,0 +1,9 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/v2/forge.gradle" + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.forge + + // Statue Menus + modApi(include(libs.statuemenus.forge.get())) +} diff --git a/1.20.4/Forge/gradle.properties b/1.20.4/Forge/gradle.properties new file mode 100644 index 0000000..32f842a --- /dev/null +++ b/1.20.4/Forge/gradle.properties @@ -0,0 +1 @@ +loom.platform=forge \ No newline at end of file diff --git a/1.20.4/Forge/src/main/java/fuzs/armorstatues/forge/ArmorStatuesForge.java b/1.20.4/Forge/src/main/java/fuzs/armorstatues/forge/ArmorStatuesForge.java new file mode 100644 index 0000000..e4cb244 --- /dev/null +++ b/1.20.4/Forge/src/main/java/fuzs/armorstatues/forge/ArmorStatuesForge.java @@ -0,0 +1,17 @@ +package fuzs.armorstatues.forge; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; + +@Mod(ArmorStatues.MOD_ID) +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public class ArmorStatuesForge { + + @SubscribeEvent + public static void onConstructMod(final FMLConstructModEvent evt) { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } +} diff --git a/1.20.4/Forge/src/main/java/fuzs/armorstatues/forge/client/ArmorStatuesForgeClient.java b/1.20.4/Forge/src/main/java/fuzs/armorstatues/forge/client/ArmorStatuesForgeClient.java new file mode 100644 index 0000000..c263dd4 --- /dev/null +++ b/1.20.4/Forge/src/main/java/fuzs/armorstatues/forge/client/ArmorStatuesForgeClient.java @@ -0,0 +1,18 @@ +package fuzs.armorstatues.forge.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.client.ArmorStatuesClient; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; + +@Mod.EventBusSubscriber(modid = ArmorStatues.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) +public class ArmorStatuesForgeClient { + + @SubscribeEvent + public static void onConstructMod(final FMLConstructModEvent evt) { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + } +} diff --git a/1.20.4/Forge/src/main/resources/META-INF/mods.toml b/1.20.4/Forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..3095160 --- /dev/null +++ b/1.20.4/Forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,59 @@ +modLoader = "javafml" +loaderVersion = "*" +license = "${modLicense}" +issueTrackerURL = "${modIssueUrl}" + +[[mods]] +modId = "${modId}" +displayName = "${modName}" +description = "${modDescription}" +version = "${modVersion}" +authors = "${modAuthor}" +logoFile = "mod_banner.png" +logoBlur = false +displayURL = "${modPageUrl}" +updateJSONURL = "${modUpdateUrl}" +displayTest = "${modForgeDisplayTest}" + +[[dependencies.${ modId }]] +modId = "forge" +mandatory = true +type = "required" +versionRange = "[${minForgeVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "minecraft" +mandatory = true +type = "required" +versionRange = "[${minecraftVersion}]" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "forgeconfigapiport" +mandatory = true +type = "required" +versionRange = "*" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "puzzleslib" +mandatory = true +type = "required" +versionRange = "[${minPuzzlesVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "statuemenus" +mandatory = true +type = "required" +versionRange = "*" +ordering = "NONE" +side = "BOTH" + +[modproperties.${ modId }] +catalogueImageIcon = "mod_logo.png" diff --git a/1.20.4/Forge/src/main/resources/forge.mixins.json b/1.20.4/Forge/src/main/resources/forge.mixins.json new file mode 100644 index 0000000..f2b4276 --- /dev/null +++ b/1.20.4/Forge/src/main/resources/forge.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.forge.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "refmap": "${modId}.forge.refmap.json" +} diff --git a/1.20.4/NeoForge/build.gradle b/1.20.4/NeoForge/build.gradle new file mode 100644 index 0000000..07725b4 --- /dev/null +++ b/1.20.4/NeoForge/build.gradle @@ -0,0 +1,9 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/v2/neoforge.gradle" + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.neoforge + + // Statue Menus + modApi(include(libs.statuemenus.neoforge.get())) +} diff --git a/1.20.4/NeoForge/gradle.properties b/1.20.4/NeoForge/gradle.properties new file mode 100644 index 0000000..2914393 --- /dev/null +++ b/1.20.4/NeoForge/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge \ No newline at end of file diff --git a/1.20.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java b/1.20.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java new file mode 100644 index 0000000..1dd837f --- /dev/null +++ b/1.20.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java @@ -0,0 +1,17 @@ +package fuzs.armorstatues.neoforge; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLConstructModEvent; + +@Mod(ArmorStatues.MOD_ID) +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public class ArmorStatuesNeoForge { + + @SubscribeEvent + public static void onConstructMod(final FMLConstructModEvent evt) { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } +} diff --git a/1.20.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java b/1.20.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java new file mode 100644 index 0000000..dde7adc --- /dev/null +++ b/1.20.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java @@ -0,0 +1,21 @@ +package fuzs.armorstatues.neoforge.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.client.ArmorStatuesClient; +import fuzs.armorstatues.data.client.ModLanguageProvider; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import fuzs.puzzleslib.neoforge.api.data.v2.core.DataProviderHelper; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLConstructModEvent; + +@Mod.EventBusSubscriber(modid = ArmorStatues.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) +public class ArmorStatuesNeoForgeClient { + + @SubscribeEvent + public static void onConstructMod(final FMLConstructModEvent evt) { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + DataProviderHelper.registerDataProviders(ArmorStatues.MOD_ID, ModLanguageProvider::new); + } +} diff --git a/1.20.4/NeoForge/src/main/resources/META-INF/mods.toml b/1.20.4/NeoForge/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..432ab6c --- /dev/null +++ b/1.20.4/NeoForge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,57 @@ +modLoader = "javafml" +loaderVersion = "*" +license = "${modLicense}" +issueTrackerURL = "${modIssueUrl}" + +[[mods]] +modId = "${modId}" +displayName = "${modName}" +description = "${modDescription}" +version = "${modVersion}" +authors = "${modAuthor}" +logoFile = "mod_banner.png" +logoBlur = false +displayURL = "${modPageUrl}" +updateJSONURL = "${modUpdateUrl}" +displayTest = "${modForgeDisplayTest}" + +[[mixins]] +config="${modId}.common.mixins.json" + +[[mixins]] +config="${modId}.neoforge.mixins.json" + +[[dependencies.${ modId }]] +modId = "neoforge" +mandatory = true +type = "required" +versionRange = "[${minNeoForgeVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "minecraft" +mandatory = true +type = "required" +versionRange = "[${minecraftVersion}]" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "puzzleslib" +mandatory = true +type = "required" +versionRange = "[${minPuzzlesVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "statuemenus" +mandatory = true +type = "required" +versionRange = "*" +ordering = "NONE" +side = "BOTH" + +[modproperties.${ modId }] +catalogueImageIcon = "mod_logo.png" diff --git a/1.20.4/NeoForge/src/main/resources/neoforge.mixins.json b/1.20.4/NeoForge/src/main/resources/neoforge.mixins.json new file mode 100644 index 0000000..1fb3919 --- /dev/null +++ b/1.20.4/NeoForge/src/main/resources/neoforge.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.neoforge.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.20.4/build.gradle b/1.20.4/build.gradle new file mode 100644 index 0000000..2417d2e --- /dev/null +++ b/1.20.4/build.gradle @@ -0,0 +1,9 @@ +plugins { + alias libs.plugins.architecturyloom apply false + alias libs.plugins.architecturyplugin apply false + alias libs.plugins.shadow apply false + alias libs.plugins.cursegradle apply false + alias libs.plugins.minotaur apply false +} + +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/v2/main.gradle" diff --git a/1.20.4/gradle.properties b/1.20.4/gradle.properties new file mode 100755 index 0000000..00a426e --- /dev/null +++ b/1.20.4/gradle.properties @@ -0,0 +1,43 @@ +org.gradle.jvmargs=-Xmx4G +org.gradle.daemon=false +copyBuildJar=true + +# Mod Attributes +modId=armorstatues +modName=Armor Statues +modVersion=20.4.2 +modAuthor=Fuzs +modDescription=Unlock the full potential of armor stands! Works on vanilla servers, too. +modLicense=MPL-2.0 +modSourceUrl=https://github.com/Fuzss/armorstatues +modIssueUrl=https://github.com/Fuzss/armorstatues/issues +modUpdateUrl=https://raw.githubusercontent.com/Fuzss/modresources/main/update/armorstatues.json +modMavenGroup=fuzs.armorstatues +# "MATCH_VERSION" for a mod required on both sides, "IGNORE_SERVER_VERSION" for a server only mod, "IGNORE_ALL_VERSION" for a client only mod +modForgeDisplayTest=IGNORE_ALL_VERSION +# "*" for a mod loaded on both sides, "server" for a server only mod, "client" for a client only mod +modFabricEnvironment=* + +# Version Catalog +dependenciesVersionCatalog=1.20.4-v35 +#dependenciesPuzzlesLibVersion=20.4.21 +#dependenciesMinPuzzlesLibVersion=20.4.21 + +# Mod Publishing +projectReleaseType=release +projectCurseForgeId=682566 +projectModrinthId=bbGCtEvb + +dependenciesRequiredFabricCurseForge=fabric-api, forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredNeoForgeCurseForge=puzzles-lib +dependenciesRequiredForgeCurseForge=forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredFabricModrinth=fabric-api, forge-config-api-port, puzzles-lib +dependenciesRequiredNeoForgeModrinth=puzzles-lib +dependenciesRequiredForgeModrinth=forge-config-api-port, puzzles-lib + +dependenciesOptionalFabricCurseForge=config-menus-forge +dependenciesOptionalNeoForgeCurseForge=config-menus-forge +dependenciesOptionalForgeCurseForge=config-menus-forge +dependenciesOptionalFabricModrinth=forge-config-screens +dependenciesOptionalNeoForgeModrinth=forge-config-screens +dependenciesOptionalForgeModrinth=forge-config-screens diff --git a/1.20.4/gradle/wrapper/gradle-wrapper.jar b/1.20.4/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..943f0cb Binary files /dev/null and b/1.20.4/gradle/wrapper/gradle-wrapper.jar differ diff --git a/1.20.4/gradle/wrapper/gradle-wrapper.properties b/1.20.4/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3499ded --- /dev/null +++ b/1.20.4/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/1.20.4/gradlew b/1.20.4/gradlew new file mode 100755 index 0000000..65dcd68 --- /dev/null +++ b/1.20.4/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/1.20.4/gradlew.bat b/1.20.4/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/1.20.4/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/1.20.4/settings.gradle b/1.20.4/settings.gradle new file mode 100644 index 0000000..e401f79 --- /dev/null +++ b/1.20.4/settings.gradle @@ -0,0 +1,13 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { url "https://maven.architectury.dev/" } + maven { url "https://maven.fabricmc.net/" } + maven { url "https://maven.neoforged.net/releases/" } + maven { url "https://maven.minecraftforge.net/" } + } +} + +include("Common", "Fabric", "NeoForge", "Forge") + +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/v2/settings.gradle" diff --git a/1.21.1/CHANGELOG.md b/1.21.1/CHANGELOG.md new file mode 100644 index 0000000..e8a7c35 --- /dev/null +++ b/1.21.1/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [v21.1.0-1.21.1] - 2024-09-17 +- Port to Minecraft 1.21.1 diff --git a/1.21.1/Common/build.gradle b/1.21.1/Common/build.gradle new file mode 100644 index 0000000..1a985e0 --- /dev/null +++ b/1.21.1/Common/build.gradle @@ -0,0 +1,13 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/common.gradle" + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.common + + // Statue Menus + modApi libs.statuemenus.common +} + +tasks.withType(net.fabricmc.loom.task.AbstractRemapJarTask).configureEach { + targetNamespace = "named" +} diff --git a/1.21.1/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 b/1.21.1/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 new file mode 100644 index 0000000..bd386df --- /dev/null +++ b/1.21.1/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 @@ -0,0 +1,2 @@ +// 1.21 2024-07-11T14:39:10.467599 Language (en_us) +3b837616894d5f092cf8c1f8e4658d723dbc0320 assets/armorstatues/lang/en_us.json diff --git a/1.21.1/Common/src/generated/resources/assets/armorstatues/lang/en_us.json b/1.21.1/Common/src/generated/resources/assets/armorstatues/lang/en_us.json new file mode 100644 index 0000000..1235c24 --- /dev/null +++ b/1.21.1/Common/src/generated/resources/assets/armorstatues/lang/en_us.json @@ -0,0 +1,23 @@ +{ + "armorstatues.dataSync.failure": "Unable to modify armor stand data: %s", + "armorstatues.dataSync.failure.noArmorStand": "No Valid Armor Stand", + "armorstatues.dataSync.failure.noPermission": "No Permission", + "armorstatues.dataSync.failure.notFinished": "Queue Not Empty", + "armorstatues.dataSync.failure.outOfRange": "Out Of Range", + "armorstatues.dataSync.finished": "Finished sending queued armor stand data", + "armorstatues.screen.vanillaTweaks.checkTarget": "Check Armor Stand Target", + "armorstatues.screen.vanillaTweaks.checkTarget.description": "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, builder isn't necessarily the armor stand which opened builder menu.", + "armorstatues.screen.vanillaTweaks.lock": "Lock", + "armorstatues.screen.vanillaTweaks.lock.description": "Locking an armor stand prevents it from being changed using builder menu and disables interaction with the equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead": "Swap Mainhand & Helmet", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead.description": "Swaps items between the main hand and helmet equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand": "Swap Mainhand & Offhand", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand.description": "Swaps items between the main hand and off hand equipment slots.", + "armorstatues.screen.vanillaTweaks.toolRack": "Align Tool As Tool Rack", + "armorstatues.screen.vanillaTweaks.toolRack.description": "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand.", + "armorstatues.screen.vanillaTweaks.triggerSent": "Sent!", + "armorstatues.screen.vanillaTweaks.unlock": "Unlock", + "armorstatues.screen.vanillaTweaks.unlock.description": "Unlocking an armor stand reverts any adjustments made via a previous lock action.", + "statuemenus.screen.type.alignments": "Alignments", + "statuemenus.screen.type.vanillaTweaks": "Vanilla Tweaks" +} \ No newline at end of file diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java new file mode 100644 index 0000000..70c87b4 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java @@ -0,0 +1,37 @@ +package fuzs.armorstatues; + +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzleslib.api.config.v3.ConfigHolder; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import fuzs.puzzleslib.api.core.v1.utility.ResourceLocationHelper; +import fuzs.puzzleslib.api.event.v1.core.EventPhase; +import fuzs.puzzleslib.api.event.v1.entity.player.PlayerInteractEvents; +import net.minecraft.resources.ResourceLocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ArmorStatues implements ModConstructor { + public static final String MOD_ID = "armorstatues"; + public static final String MOD_NAME = "Armor Statues"; + public static final Logger LOGGER = LoggerFactory.getLogger(MOD_NAME); + + public static final ConfigHolder CONFIG = ConfigHolder.builder(MOD_ID).client(ClientConfig.class); + + @Override + public void onConstructMod() { + ModRegistry.touch(); + registerEventHandlers(); + } + + private static void registerEventHandlers() { + // high priority, so we run before other mods that add armor stand interactions + // we require empty hand + shift, so those other mods can still run their behaviors when those conditions are not met + PlayerInteractEvents.USE_ENTITY_AT.register(EventPhase.BEFORE, ArmorStandInteractHandler::onUseEntityAt); + } + + public static ResourceLocation id(String path) { + return ResourceLocationHelper.fromNamespaceAndPath(MOD_ID, path); + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java new file mode 100644 index 0000000..2b4043c --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java @@ -0,0 +1,51 @@ +package fuzs.armorstatues.client; + +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandAlignmentsScreen; +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.client.handler.ArmorStandTooltipHandler; +import fuzs.armorstatues.client.handler.ClientInteractHandler; +import fuzs.armorstatues.client.handler.DataSyncTickHandler; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import fuzs.puzzleslib.api.client.core.v1.context.MenuScreensContext; +import fuzs.puzzleslib.api.client.event.v1.ClientTickEvents; +import fuzs.puzzleslib.api.client.event.v1.entity.player.InteractionInputEvents; +import fuzs.puzzleslib.api.client.event.v1.gui.ItemTooltipCallback; +import fuzs.puzzleslib.api.client.event.v1.gui.ScreenEvents; +import fuzs.puzzleslib.api.event.v1.core.EventPhase; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreenFactory; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandMenu; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; + +public class ArmorStatuesClient implements ClientModConstructor { + + @Override + public void onConstructMod() { + registerEventHandlers(); + } + + private static void registerEventHandlers() { + ItemTooltipCallback.EVENT.register(ArmorStandTooltipHandler::onItemTooltip); + ClientTickEvents.END.register(DataSyncTickHandler::onEndClientTick); + ScreenEvents.remove(Screen.class).register(DataSyncTickHandler::onRemove); + // event phase must match PlayerInteractEvents#USE_ENTITY_AT as both are implemented using the same event on Fabric + InteractionInputEvents.USE.register(EventPhase.BEFORE, ClientInteractHandler::onUseInteraction); + } + + @Override + public void onClientSetup() { + ArmorStandScreenFactory.register(ModRegistry.ALIGNMENTS_SCREEN_TYPE, ArmorStandAlignmentsScreen::new); + ArmorStandScreenFactory.register(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE, ArmorStandVanillaTweaksScreen::new); + } + + @SuppressWarnings("Convert2MethodRef") + @Override + public void onRegisterMenuScreens(MenuScreensContext context) { + // compiler doesn't like method reference :( + context.registerMenuScreen(ModRegistry.ARMOR_STAND_MENU_TYPE.value(), (ArmorStandMenu menu, Inventory inventory, Component component) -> { + return ArmorStandScreenFactory.createLastScreenType(menu, inventory, component); + }); + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java new file mode 100644 index 0000000..df01829 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java @@ -0,0 +1,54 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandButtonsScreen; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandPositionScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandAlignment; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.phys.Vec3; + +import java.util.EnumSet; +import java.util.List; + +public class ArmorStandAlignmentsScreen extends ArmorStandButtonsScreen { + + public ArmorStandAlignmentsScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new DoubleButtonWidget(Component.translatable(ArmorStandPositionScreen.CENTERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CENTERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)).add(0.5, 0.0, 0.5); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + }, button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + })); + for (ArmorStandAlignment alignment : ArmorStandAlignment.values()) { + widgets.add(new SingleButtonWidget(Component.translatable(alignment.getTranslationKey()), Component.translatable(alignment.getDescriptionsKey()), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + ArmorStandAlignmentsScreen.this.dataSyncHandler.sendAlignment(alignment); + })); + } + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.ALIGNMENTS_SCREEN_TYPE; + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java new file mode 100644 index 0000000..f52054d --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java @@ -0,0 +1,75 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandButtonsScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; + +import java.util.List; + +public class ArmorStandVanillaTweaksScreen extends ArmorStandButtonsScreen { + public static final String TRIGGER_SENT_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.triggerSent"; + public static final String CHECK_TARGET_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget"; + public static final String CHECK_TARGET_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget.description"; + public static final String LOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock"; + public static final String LOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock.description"; + public static final String UNLOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock"; + public static final String UNLOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock.description"; + public static final String TOOL_RACK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack"; + public static final String TOOL_RACK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack.description"; + public static final String SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand"; + public static final String SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand.description"; + public static final String SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead"; + public static final String SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead.description"; + + public ArmorStandVanillaTweaksScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + public VanillaTweaksDataSyncHandler getDataSyncHandler() { + return (VanillaTweaksDataSyncHandler) super.getDataSyncHandler(); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new SingleButtonWidget(Component.translatable(CHECK_TARGET_TRANSLATION_KEY), Component.translatable(CHECK_TARGET_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.CHECK_TARGET); + this.onClose(); + })); + widgets.add(new DoubleButtonWidget(Component.translatable(LOCK_TRANSLATION_KEY), Component.translatable(UNLOCK_TRANSLATION_KEY), Component.translatable(LOCK_DESCRIPTION_KEY), Component.translatable(UNLOCK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_LOCK); + }, button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_UNLOCK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(TOOL_RACK_TRANSLATION_KEY), Component.translatable(TOOL_RACK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.AUTO_ALIGNMENT_TOOL_RACK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_OFFHAND); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_HEAD); + })); + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE; + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java new file mode 100644 index 0000000..a7c16e7 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java @@ -0,0 +1,27 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.puzzleslib.api.core.v1.Proxy; +import fuzs.statuemenus.api.v1.helper.ArmorStandInteractHelper; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.TooltipFlag; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ArmorStandTooltipHandler { + + public static void onItemTooltip(ItemStack itemStack, List lines, Item.TooltipContext tooltipContext, @Nullable Player player, TooltipFlag tooltipFlag) { + if (itemStack.is(Items.ARMOR_STAND)) { + List components = Proxy.INSTANCE.splitTooltipLines(ArmorStandInteractHelper.getArmorStandHoverText()); + if (tooltipFlag.isAdvanced()) { + lines.addAll(lines.size() - (!itemStack.getComponents().isEmpty() ? 2 : 1), components); + } else { + lines.addAll(components); + } + } + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java new file mode 100644 index 0000000..22b26d2 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java @@ -0,0 +1,39 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.puzzleslib.api.event.v1.core.EventResult; +import fuzs.puzzleslib.api.event.v1.core.EventResultHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; + +public class ClientInteractHandler { + + public static EventResult onUseInteraction(Minecraft minecraft, LocalPlayer player, InteractionHand interactionHand, HitResult hitResult) { + + if (hitResult.getType() == HitResult.Type.ENTITY) { + + Entity entity = ((EntityHitResult) hitResult).getEntity(); + Vec3 hitVector = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ()); + EventResultHolder result = ArmorStandInteractHandler.onUseEntityAt(minecraft.player, + minecraft.level, + interactionHand, + entity, + hitVector + ); + + // if InteractionResult.FAIL is returned the mod is missing server-side, and we open the menu client-side without sending a packet to the server, so the server does not try to interact + if (result.filter(interactionResult -> interactionResult == InteractionResult.FAIL).isInterrupt()) { + + return EventResult.INTERRUPT; + } + } + + return EventResult.PASS; + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java new file mode 100644 index 0000000..822390a --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java @@ -0,0 +1,28 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import org.jetbrains.annotations.Nullable; + +public class DataSyncTickHandler { + @Nullable + private static DataSyncHandler dataSyncHandler; + + public static void onRemove(Screen screen) { + if (screen instanceof ArmorStandScreen armorStandScreen && armorStandScreen.getDataSyncHandler().shouldContinueTicking()) { + dataSyncHandler = armorStandScreen.getDataSyncHandler(); + } + } + + public static void onEndClientTick(Minecraft minecraft) { + if (minecraft.player != null && !(minecraft.screen instanceof ArmorStandScreen) && dataSyncHandler != null) { + if (dataSyncHandler.shouldContinueTicking()) { + dataSyncHandler.tick(); + } else { + dataSyncHandler = null; + } + } + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java new file mode 100644 index 0000000..137797d --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java @@ -0,0 +1,15 @@ +package fuzs.armorstatues.config; + +import fuzs.puzzleslib.api.config.v3.Config; +import fuzs.puzzleslib.api.config.v3.ConfigCore; +import fuzs.statuemenus.api.v1.client.gui.screens.AbstractArmorStandScreen; + +public class ClientConfig implements ConfigCore { + @Config(description = {"Allows for using this mod on a server without it (like a vanilla server) when the Vanilla Tweaks Armor Statues data pack is installed without the need for being a server operator.", "Download the Vanilla Tweaks Armor Statues data pack from here: " + AbstractArmorStandScreen.VANILLA_TWEAKS_HOMEPAGE}) + public boolean useVanillaTweaksTriggers = false; + @Config(description = "Do not check if the client has the necessary permission level for executing the '/data' command when trying to edit an armor stand. Useful when the current server is using custom permissions handling.") + public boolean overrideClientPermissionsCheck = false; + @Config(description = "The delay in ticks for sending queued client commands for editing armor stands to the server. Increase this values if commands are sent too quickly and the server fails to process all of them.") + @Config.IntRange(min = 20) + public int clientCommandDelay = 20; +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java new file mode 100644 index 0000000..44c0b50 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java @@ -0,0 +1,39 @@ +package fuzs.armorstatues.data.client; + +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.puzzleslib.api.client.data.v2.AbstractLanguageProvider; +import fuzs.puzzleslib.api.data.v2.core.DataProviderContext; + +public class ModLanguageProvider extends AbstractLanguageProvider { + + public ModLanguageProvider(DataProviderContext context) { + super(context); + } + + @Override + public void addTranslations(TranslationBuilder builder) { + builder.add(CommandDataSyncHandler.FAILURE_TRANSLATION_KEY, "Unable to modify armor stand data: %s"); + builder.add(CommandDataSyncHandler.NO_PERMISSION_TRANSLATION_KEY, "No Permission"); + builder.add(CommandDataSyncHandler.NO_ARMOR_STAND_TRANSLATION_KEY, "No Valid Armor Stand"); + builder.add(CommandDataSyncHandler.OUT_OF_RANGE_TRANSLATION_KEY, "Out Of Range"); + builder.add(CommandDataSyncHandler.NOT_FINISHED_TRANSLATION_KEY, "Queue Not Empty"); + builder.add(CommandDataSyncHandler.FINISHED_TRANSLATION_KEY, "Finished sending queued armor stand data"); + builder.add(ModRegistry.ALIGNMENTS_SCREEN_TYPE.getTranslationKey(), "Alignments"); + builder.add(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE.getTranslationKey(), "Vanilla Tweaks"); + builder.add(ArmorStandVanillaTweaksScreen.TRIGGER_SENT_TRANSLATION_KEY, "Sent!"); + builder.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_TRANSLATION_KEY, "Check Armor Stand Target"); + builder.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_DESCRIPTION_KEY, "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, builder isn't necessarily the armor stand which opened builder menu."); + builder.add(ArmorStandVanillaTweaksScreen.LOCK_TRANSLATION_KEY, "Lock"); + builder.add(ArmorStandVanillaTweaksScreen.LOCK_DESCRIPTION_KEY, "Locking an armor stand prevents it from being changed using builder menu and disables interaction with the equipment slots."); + builder.add(ArmorStandVanillaTweaksScreen.UNLOCK_TRANSLATION_KEY, "Unlock"); + builder.add(ArmorStandVanillaTweaksScreen.UNLOCK_DESCRIPTION_KEY, "Unlocking an armor stand reverts any adjustments made via a previous lock action."); + builder.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_TRANSLATION_KEY, "Align Tool As Tool Rack"); + builder.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_DESCRIPTION_KEY, "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand."); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY, "Swap Mainhand & Offhand"); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY, "Swaps items between the main hand and off hand equipment slots."); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY, "Swap Mainhand & Helmet"); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY, "Swaps items between the main hand and helmet equipment slots."); + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java new file mode 100644 index 0000000..2dd8af0 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java @@ -0,0 +1,43 @@ +package fuzs.armorstatues.handler; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.proxy.Proxy; +import fuzs.puzzleslib.api.core.v1.ModLoaderEnvironment; +import fuzs.puzzleslib.api.event.v1.core.EventResultHolder; +import fuzs.statuemenus.api.v1.helper.ArmorStandInteractHelper; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; + +public class ArmorStandInteractHandler { + + public static EventResultHolder onUseEntityAt(Player player, Level level, InteractionHand interactionHand, Entity target, Vec3 hitVector) { + + if (player.getAbilities().mayBuild && target.getType() == EntityType.ARMOR_STAND) { + + boolean clientsideOnly = level.isClientSide && !ModLoaderEnvironment.INSTANCE.isModPresentServerside(ArmorStatues.MOD_ID); + // the menu won't exist in the registry if the mod is missing serverside since Forge syncs registries to clients + MenuType menuType = clientsideOnly ? null : ModRegistry.ARMOR_STAND_MENU_TYPE.value(); + EventResultHolder result = ArmorStandInteractHelper.tryOpenArmorStatueMenu(player, level, interactionHand, (ArmorStand) target, menuType, ModRegistry.ARMOR_STAND_DATA_PROVIDER); + if (result.isInterrupt() && clientsideOnly) { + + Proxy.INSTANCE.openArmorStandScreen((ArmorStand) target, player); + // required so no packet is sent to server when only installed client-side, so the server doesn't change any equipment when we only want to open the screen + // returning InteractionResult.FAIL will miss out on the player arm swing animation, which we manually play here + player.swing(interactionHand); + return EventResultHolder.interrupt(InteractionResult.FAIL); + } + + return result; + } + + return EventResultHolder.pass(); + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java new file mode 100644 index 0000000..a9aba22 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java @@ -0,0 +1,51 @@ +package fuzs.armorstatues.init; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.init.v3.registry.RegistryManager; +import fuzs.statuemenus.api.v1.world.entity.decoration.ArmorStandDataProvider; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandMenu; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.core.Holder; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class ModRegistry { + static final RegistryManager REGISTRIES = RegistryManager.from(ArmorStatues.MOD_ID); + public static final Holder.Reference> ARMOR_STAND_MENU_TYPE = REGISTRIES.registerExtendedMenuType( + "armor_stand", + () -> (containerId, inventory, data) -> { + return ArmorStandMenu.create(ModRegistry.ARMOR_STAND_MENU_TYPE.value(), + containerId, + inventory, + data, + ModRegistry.ARMOR_STAND_DATA_PROVIDER + ); + } + ); + + public static final ArmorStandScreenType ALIGNMENTS_SCREEN_TYPE = new ArmorStandScreenType("alignments", + new ItemStack(Items.DIAMOND_PICKAXE) + ); + public static final ArmorStandScreenType VANILLA_TWEAKS_SCREEN_TYPE = new ArmorStandScreenType("vanillaTweaks", + new ItemStack(Items.WRITTEN_BOOK) + ); + public static final ArmorStandDataProvider ARMOR_STAND_DATA_PROVIDER = new ArmorStandDataProvider() { + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return new ArmorStandScreenType[]{ + ArmorStandScreenType.ROTATIONS, + ArmorStandScreenType.POSES, + ArmorStandScreenType.STYLE, + ArmorStandScreenType.POSITION, + ModRegistry.ALIGNMENTS_SCREEN_TYPE, + ArmorStandScreenType.EQUIPMENT + }; + } + }; + + public static void touch() { + // NO-OP + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java new file mode 100644 index 0000000..d52bc76 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java @@ -0,0 +1,226 @@ +package fuzs.armorstatues.network.client.data; + +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandAlignment; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandPose; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandStyleOption; +import net.minecraft.ChatFormatting; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.FloatTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.function.BiPredicate; + +public class CommandDataSyncHandler implements DataSyncHandler { + public static final String NO_PERMISSION_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noPermission"; + public static final String NO_ARMOR_STAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noArmorStand"; + public static final String OUT_OF_RANGE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.outOfRange"; + public static final String NOT_FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.notFinished"; + public static final String FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.finished"; + public static final String FAILURE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure"; + private static final Queue CLIENT_COMMAND_QUEUE = new ArrayDeque<>(); + + @Nullable + private static ArmorStand queueArmorStand; + private static int itemDequeuedTicks; + + private final ArmorStandHolder holder; + protected final LocalPlayer player; + protected ArmorStandPose lastSyncedPose; + + public CommandDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + this.holder = holder; + this.lastSyncedPose = ArmorStandPose.fromEntity(this.holder.getArmorStand()); + this.player = player; + } + + @Override + public ArmorStandHolder getArmorStandHolder() { + return this.holder; + } + + @Override + public void sendName(String name) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.setCustomArmorStandName(this.getArmorStand(), name); + CompoundTag tag = new CompoundTag(); + tag.putString("CustomName", Component.Serializer.toJson(Component.literal(name), this.player.registryAccess())); + this.enqueueEntityData(tag); + this.finalizeCurrentOperation(); + } + + @Override + public final void sendPose(ArmorStandPose pose) { + this.sendPose(pose, true); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + // split this into multiple chat messages as the client chat field has a very low character limit + this.sendPosePart(pose::serializeBodyPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeArmPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeLegPoses, this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + if (finalize) this.finalizeCurrentOperation(); + } + + private void sendPosePart(BiPredicate dataWriter, ArmorStandPose lastSyncedPose) { + CompoundTag tag = new CompoundTag(); + if (dataWriter.test(tag, lastSyncedPose)) { + CompoundTag tagToSend = new CompoundTag(); + tagToSend.put("Pose", tag); + this.enqueueEntityData(tagToSend); + } + } + + @Override + public @Nullable ArmorStandPose getLastSyncedPose() { + return this.lastSyncedPose; + } + + @Override + public final void sendPosition(double posX, double posY, double posZ) { + this.sendPosition(posX, posY, posZ, true); + + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(DoubleTag.valueOf(posX)); + listTag.add(DoubleTag.valueOf(posY)); + listTag.add(DoubleTag.valueOf(posZ)); + CompoundTag tag = new CompoundTag(); + tag.put("Pos", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public final void sendRotation(float rotation) { + this.sendRotation(rotation, true); + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(FloatTag.valueOf(rotation)); + CompoundTag tag = new CompoundTag(); + tag.put("Rotation", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public final void sendStyleOption(ArmorStandStyleOption styleOption, boolean value) { + this.sendStyleOption(styleOption, value, true); + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + CompoundTag tag = new CompoundTag(); + styleOption.toTag(tag, value); + this.enqueueEntityData(tag); + styleOption.setOption(this.getArmorStand(), value); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.super.sendAlignment(alignment); + } + + @Override + public boolean supportsScreenType(ArmorStandScreenType screenType) { + return !screenType.requiresServer(); + } + + @Override + public void tick() { + if (itemDequeuedTicks > 0) itemDequeuedTicks--; + if (itemDequeuedTicks == 0 && queueArmorStand != null && !CLIENT_COMMAND_QUEUE.isEmpty()) { + if (this.testArmorStand(queueArmorStand).right().isPresent()) { + this.player.connection.sendCommand(CLIENT_COMMAND_QUEUE.poll()); + } else { + CLIENT_COMMAND_QUEUE.clear(); + } + itemDequeuedTicks = this.getDequeueDelayTicks(); + } else if (itemDequeuedTicks == 1 && CLIENT_COMMAND_QUEUE.isEmpty()) { + this.sendDisplayMessage(Component.translatable(FINISHED_TRANSLATION_KEY), false); + } + } + + protected int getDequeueDelayTicks() { + return 5; + } + + @Override + public boolean shouldContinueTicking() { + return !CLIENT_COMMAND_QUEUE.isEmpty() || itemDequeuedTicks != 0; + } + + protected boolean isEditingAllowed() { + return this.isEditingAllowed(!ArmorStatues.CONFIG.get(ClientConfig.class).overrideClientPermissionsCheck); + } + + protected final boolean isEditingAllowed(boolean testPermissionLevel) { + if (testPermissionLevel && !this.player.hasPermissions(2)) { + this.sendFailureMessage(Component.translatable(NO_PERMISSION_TRANSLATION_KEY)); + return false; + } + return this.player.getAbilities().mayBuild && this.testArmorStand(this.getArmorStand()).ifLeft(this::sendFailureMessage).right().isPresent(); + } + + protected Either testArmorStand(ArmorStand armorStand) { + return !armorStand.isAlive() ? Either.left(Component.translatable(NO_ARMOR_STAND_TRANSLATION_KEY)) : Either.right(Unit.INSTANCE); + } + + protected boolean enqueueClientCommand(String clientCommand) { + if (CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = null; + } else if (queueArmorStand != null) { + this.sendFailureMessage(Component.translatable(NOT_FINISHED_TRANSLATION_KEY)); + return false; + } + CLIENT_COMMAND_QUEUE.offer(clientCommand); + return true; + } + + @Override + public void finalizeCurrentOperation() { + if (!CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = this.getArmorStand(); + } + } + + protected void sendFailureMessage(Component component) { + this.sendDisplayMessage(Component.translatable(FAILURE_TRANSLATION_KEY, component), true); + } + + protected void sendDisplayMessage(Component component, boolean failure) { + this.player.displayClientMessage(Component.empty().append(component).withStyle(failure ? ChatFormatting.RED : ChatFormatting.GREEN), false); + } + + private void enqueueEntityData(CompoundTag tag) { + this.enqueueClientCommand("data merge entity %s %s".formatted(this.getArmorStand().getStringUUID(), tag.getAsString())); + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java new file mode 100644 index 0000000..dbdc639 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java @@ -0,0 +1,359 @@ +package fuzs.armorstatues.network.client.data; + +import com.google.common.collect.ImmutableSortedMap; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.*; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.Rotations; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.NavigableMap; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +public class VanillaTweaksDataSyncHandler extends CommandDataSyncHandler { + private static final int MAX_INCREMENTAL_OPERATIONS = 12; + public static final int CHECK_TARGET = 999; + public static final int SWAP_SLOTS_MAINHAND_AND_OFFHAND = 161; + public static final int SWAP_SLOTS_MAINHAND_AND_HEAD = 162; + public static final int MIRROR_ARMS_LEFT_TO_RIGHT = 131; + public static final int MIRROR_ARMS_RIGHT_TO_LEFT = 132; + public static final int MIRROR_LEGS_LEFT_TO_RIGHT = 133; + public static final int MIRROR_LEGS_RIGHT_TO_LEFT = 134; + public static final int UTILITIES_LOCK = 1000; + public static final int UTILITIES_UNLOCK = 1001; + public static final int MIRROR_AND_FLIP_FLIP = 135; + public static final int SHOW_BASE_PLATE_YES = 1; + public static final int SHOW_BASE_PLATE_NO = 2; + public static final int SHOW_ARMS_YES = 3; + public static final int SHOW_ARMS_NO = 4; + public static final int SMALL_STAND_YES = 5; + public static final int SMALL_STAND_NO = 6; + public static final int APPLY_GRAVITY_YES = 7; + public static final int APPLY_GRAVITY_NO = 8; + public static final int STAND_VISIBLE_YES = 9; + public static final int STAND_VISIBLE_NO = 10; + public static final int DISPLAY_NAME_YES = 11; + public static final int DISPLAY_NAME_NO = 12; + public static final int NUDGE_POSITION_X8_NEGATIVE = 40; + public static final int NUDGE_POSITION_X3_NEGATIVE = 101; + public static final int NUDGE_POSITION_X1_NEGATIVE = 102; + public static final int NUDGE_POSITION_X1_POSITIVE = 103; + public static final int NUDGE_POSITION_X3_POSITIVE = 104; + public static final int NUDGE_POSITION_X8_POSITIVE = 43; + public static final int NUDGE_POSITION_Y8_NEGATIVE = 44; + public static final int NUDGE_POSITION_Y3_NEGATIVE = 105; + public static final int NUDGE_POSITION_Y1_NEGATIVE = 106; + public static final int NUDGE_POSITION_Y1_POSITIVE = 107; + public static final int NUDGE_POSITION_Y3_POSITIVE = 108; + public static final int NUDGE_POSITION_Y8_POSITIVE = 47; + public static final int NUDGE_POSITION_Z8_NEGATIVE = 48; + public static final int NUDGE_POSITION_Z3_NEGATIVE = 109; + public static final int NUDGE_POSITION_Z1_NEGATIVE = 110; + public static final int NUDGE_POSITION_Z1_POSITIVE = 111; + public static final int NUDGE_POSITION_Z3_POSITIVE = 112; + public static final int NUDGE_POSITION_Z8_POSITIVE = 51; + public static final int ADJUST_ROTATION_ANGLE_STEP_45 = 120; + public static final int ADJUST_ROTATION_ANGLE_STEP_15 = 121; + public static final int ADJUST_ROTATION_ANGLE_STEP_5 = 122; + public static final int ADJUST_ROTATION_ANGLE_STEP_1 = 123; + public static final int ADJUST_ROTATION_ROTATE_RIGHT = 56; + public static final int ADJUST_ROTATION_ROTATE_LEFT = 57; + public static final int POSE_PRESETS_ATTENTION = 20; + public static final int POSE_PRESETS_WALKING = 21; + public static final int POSE_PRESETS_RUNNING = 22; + public static final int POSE_PRESETS_POINTING = 23; + public static final int POSE_PRESETS_BLOCKING = 24; + public static final int POSE_PRESETS_LUNGEING = 25; + public static final int POSE_PRESETS_WINNING = 26; + public static final int POSE_PRESETS_SITTING = 27; + public static final int POSE_PRESETS_ARABESQUE = 28; + public static final int POSE_PRESETS_CUPID = 29; + public static final int POSE_PRESETS_CONFIDENT = 30; + public static final int POSE_PRESETS_SALUTE = 31; + public static final int POSE_PRESETS_DEATH = 32; + public static final int POSE_PRESETS_FACEPALM = 33; + public static final int POSE_PRESETS_LAZING = 34; + public static final int POSE_PRESETS_CONFUSED = 35; + public static final int POSE_PRESETS_FORMAL = 36; + public static final int POSE_PRESETS_SAD = 37; + public static final int POSE_PRESETS_JOYOUS = 38; + public static final int POSE_PRESETS_STARGAZING = 39; + public static final int AUTO_ALIGNMENT_BLOCK_ON_SURFACE = 151; + public static final int AUTO_ALIGNMENT_ITEM_ON_SURFACE = 152; + public static final int AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE = 153; + public static final int AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE = 154; + public static final int AUTO_ALIGNMENT_TOOL_RACK = 155; + public static final int POSE_ADJUSTMENT_HEAD_X_NEGATIVE = 60; + public static final int POSE_ADJUSTMENT_HEAD_X_POSITIVE = 61; + public static final int POSE_ADJUSTMENT_HEAD_Y_NEGATIVE = 62; + public static final int POSE_ADJUSTMENT_HEAD_Y_POSITIVE = 63; + public static final int POSE_ADJUSTMENT_HEAD_Z_NEGATIVE = 64; + public static final int POSE_ADJUSTMENT_HEAD_Z_POSITIVE = 65; + public static final int POSE_ADJUSTMENT_BODY_X_NEGATIVE = 67; + public static final int POSE_ADJUSTMENT_BODY_X_POSITIVE = 66; + public static final int POSE_ADJUSTMENT_BODY_Y_NEGATIVE = 68; + public static final int POSE_ADJUSTMENT_BODY_Y_POSITIVE = 69; + public static final int POSE_ADJUSTMENT_BODY_Z_NEGATIVE = 70; + public static final int POSE_ADJUSTMENT_BODY_Z_POSITIVE = 71; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE = 72; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE = 73; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE = 74; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE = 75; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE = 77; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE = 76; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE = 78; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE = 79; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE = 81; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE = 80; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE = 82; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE = 83; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE = 84; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE = 85; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE = 87; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE = 86; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE = 89; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE = 88; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE = 90; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE = 91; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE = 92; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE = 93; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE = 94; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE = 95; + private static final int[] POSE_ADJUSTMENT_HEAD = new int[]{POSE_ADJUSTMENT_HEAD_X_NEGATIVE, POSE_ADJUSTMENT_HEAD_X_POSITIVE, POSE_ADJUSTMENT_HEAD_Y_NEGATIVE, POSE_ADJUSTMENT_HEAD_Y_POSITIVE, POSE_ADJUSTMENT_HEAD_Z_NEGATIVE, POSE_ADJUSTMENT_HEAD_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_BODY = new int[]{POSE_ADJUSTMENT_BODY_X_POSITIVE, POSE_ADJUSTMENT_BODY_X_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_POSITIVE, POSE_ADJUSTMENT_BODY_Z_NEGATIVE, POSE_ADJUSTMENT_BODY_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_ARM = new int[]{POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_ARM = new int[]{POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_LEG = new int[]{POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_LEG = new int[]{POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE}; + private static final NavigableMap NUDGE_POSITIONS_X_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_X3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_X8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_X_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_X3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_X8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_POSITIVE); + private static final NavigableMap ADJUST_ROTATION_ANGLE_STEPS = ImmutableSortedMap.of(1.0F, ADJUST_ROTATION_ANGLE_STEP_1, 5.0F, ADJUST_ROTATION_ANGLE_STEP_5, 15.0F, ADJUST_ROTATION_ANGLE_STEP_15, 45.0F, ADJUST_ROTATION_ANGLE_STEP_45); + + public VanillaTweaksDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + super(holder, player); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue = this.getTriggerValueFromPose(pose); + if (triggerValue != -1) { + if (this.enqueueTriggerValue(triggerValue)) { + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + } + } else { + this.tryApplyAllPoseParts(pose); + } + if (finalize) this.finalizeCurrentOperation(); + } + + private int getTriggerValueFromPose(ArmorStandPose pose) { + if (pose.getSourceType() == ArmorStandPose.SourceType.EMPTY) return POSE_PRESETS_ATTENTION; + if (pose.getSourceType() == ArmorStandPose.SourceType.MIRRORED) return MIRROR_AND_FLIP_FLIP; + if (pose.getSourceType() != ArmorStandPose.SourceType.VANILLA_TWEAKS) return -1; + if (pose == ArmorStandPose.WALKING) return POSE_PRESETS_WALKING; + if (pose == ArmorStandPose.RUNNING) return POSE_PRESETS_RUNNING; + if (pose == ArmorStandPose.POINTING) return POSE_PRESETS_POINTING; + if (pose == ArmorStandPose.BLOCKING) return POSE_PRESETS_BLOCKING; + if (pose == ArmorStandPose.LUNGEING) return POSE_PRESETS_LUNGEING; + if (pose == ArmorStandPose.WINNING) return POSE_PRESETS_WINNING; + if (pose == ArmorStandPose.SITTING) return POSE_PRESETS_SITTING; + if (pose == ArmorStandPose.ARABESQUE) return POSE_PRESETS_ARABESQUE; + if (pose == ArmorStandPose.CUPID) return POSE_PRESETS_CUPID; + if (pose == ArmorStandPose.CONFIDENT) return POSE_PRESETS_CONFIDENT; + if (pose == ArmorStandPose.SALUTE) return POSE_PRESETS_SALUTE; + if (pose == ArmorStandPose.DEATH) return POSE_PRESETS_DEATH; + if (pose == ArmorStandPose.FACEPALM) return POSE_PRESETS_FACEPALM; + if (pose == ArmorStandPose.LAZING) return POSE_PRESETS_LAZING; + if (pose == ArmorStandPose.CONFUSED) return POSE_PRESETS_CONFUSED; + if (pose == ArmorStandPose.FORMAL) return POSE_PRESETS_FORMAL; + if (pose == ArmorStandPose.SAD) return POSE_PRESETS_SAD; + if (pose == ArmorStandPose.JOYOUS) return POSE_PRESETS_JOYOUS; + if (pose == ArmorStandPose.STARGAZING) return POSE_PRESETS_STARGAZING; + return -1; + } + + private void tryApplyAllPoseParts(ArmorStandPose pose) { + if (!this.tryApplyPosePart(this.lastSyncedPose.getHeadPose(), pose.getNullableHeadPose(), POSE_ADJUSTMENT_HEAD, this.lastSyncedPose::withHeadPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getBodyPose(), pose.getNullableBodyPose(), POSE_ADJUSTMENT_BODY, this.lastSyncedPose::withBodyPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightArmPose(), pose.getNullableRightArmPose(), POSE_ADJUSTMENT_RIGHT_ARM, this.lastSyncedPose::withRightArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftArmPose(), pose.getNullableLeftArmPose(), POSE_ADJUSTMENT_LEFT_ARM, this.lastSyncedPose::withLeftArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightLegPose(), pose.getNullableRightLegPose(), POSE_ADJUSTMENT_RIGHT_LEG, this.lastSyncedPose::withRightLegPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftLegPose(), pose.getNullableLeftLegPose(), POSE_ADJUSTMENT_LEFT_LEG, this.lastSyncedPose::withLeftLegPose)) + return; + } + + private boolean tryApplyPosePart(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment, Function function) { + if (this.tryApplyPoseAdjustment(oldPose, newPose, poseAdjustment)) { + this.lastSyncedPose = function.apply(newPose != null ? newPose : oldPose); + return true; + } else { + return false; + } + } + + private boolean tryApplyPoseAdjustment(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment) { + if (newPose == null || oldPose.equals(newPose)) return true; + if (!this.applyIncrementsFromSteps(oldPose.getX(), newPose.getX(), poseAdjustment[0], poseAdjustment[1])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getY(), newPose.getY(), poseAdjustment[2], poseAdjustment[3])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getZ(), newPose.getZ(), poseAdjustment[4], poseAdjustment[5])) return false; + return true; + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyPositionIncrements(this.getArmorStand().getX(), posX, NUDGE_POSITIONS_X_POSITIVE, NUDGE_POSITIONS_X_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getY(), posY, NUDGE_POSITIONS_Y_POSITIVE, NUDGE_POSITIONS_Y_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getZ(), posZ, NUDGE_POSITIONS_Z_POSITIVE, NUDGE_POSITIONS_Z_NEGATIVE); + if (finalize) this.finalizeCurrentOperation(); + } + + private void applyPositionIncrements(double oldValue, double newValue, NavigableMap positiveNudgePositions, NavigableMap negativeNudgePositions) { + double value = newValue - oldValue; + double signum = Math.signum(value); + value = Math.abs(value); + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = (signum == -1.0F ? negativeNudgePositions : positiveNudgePositions).floorEntry(value); + if (entry != null) { + value -= entry.getKey(); + if (!this.enqueueTriggerValue(entry.getValue())) { + return; + } + } else { + break; + } + } + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyIncrementsFromSteps(this.getArmorStand().getYRot(), rotation, ADJUST_ROTATION_ROTATE_RIGHT, ADJUST_ROTATION_ROTATE_LEFT); + if (finalize) this.finalizeCurrentOperation(); + } + + private boolean applyIncrementsFromSteps(float oldValue, float newValue, int triggerValueNegative, int triggerValuePositive) { + float value = newValue - oldValue; + float signum = Math.signum(value); + value = Math.abs(value); + float lastIncrement = 0.0F; + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = ADJUST_ROTATION_ANGLE_STEPS.floorEntry(value); + if (entry != null) { + float currentIncrement = entry.getKey(); + value -= currentIncrement; + if (currentIncrement != lastIncrement) { + lastIncrement = currentIncrement; + if (!this.enqueueTriggerValue(entry.getValue())) { + return false; + } + } + if (!this.enqueueTriggerValue(signum == -1.0F ? triggerValuePositive : triggerValueNegative)) { + return false; + } + } else { + break; + } + } + return true; + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue; + if (styleOption == ArmorStandStyleOptions.SHOW_NAME) { + triggerValue = value ? DISPLAY_NAME_YES : DISPLAY_NAME_NO; + } else if (styleOption == ArmorStandStyleOptions.SHOW_ARMS) { + triggerValue = value ? SHOW_ARMS_YES : SHOW_ARMS_NO; + } else if (styleOption == ArmorStandStyleOptions.SMALL) { + triggerValue = value ? SMALL_STAND_YES : SMALL_STAND_NO; + } else if (styleOption == ArmorStandStyleOptions.INVISIBLE) { + triggerValue = value ? STAND_VISIBLE_NO : STAND_VISIBLE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_BASE_PLATE) { + triggerValue = value ? SHOW_BASE_PLATE_NO : SHOW_BASE_PLATE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_GRAVITY) { + triggerValue = value ? APPLY_GRAVITY_NO : APPLY_GRAVITY_YES; + } else { + super.sendStyleOption(styleOption, value, finalize); + return; + } + if (this.sendSingleTriggerValue(triggerValue, finalize)) { + styleOption.setOption(this.getArmorStand(), value); + } + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + int triggerValue = switch (alignment) { + case BLOCK -> AUTO_ALIGNMENT_BLOCK_ON_SURFACE; + case FLOATING_ITEM -> AUTO_ALIGNMENT_ITEM_ON_SURFACE; + case FLAT_ITEM -> AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE; + case TOOL -> AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE; + }; + this.sendSingleTriggerValue(triggerValue, true); + } + + public void sendSingleTriggerValue(int triggerValue) { + if (!this.isEditingAllowed()) return; + this.sendSingleTriggerValue(triggerValue, true); + } + + private boolean sendSingleTriggerValue(int triggerValue, boolean finalize) { + boolean result = this.enqueueTriggerValue(triggerValue); + if (finalize) this.finalizeCurrentOperation(); + return result; + } + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return Stream.concat(Stream.of(super.getScreenTypes()), Stream.of(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE)).toArray(ArmorStandScreenType[]::new); + } + + @Override + protected boolean isEditingAllowed() { + return this.isEditingAllowed(false); + } + + @Override + protected Either testArmorStand(ArmorStand armorStand) { + return super.testArmorStand(armorStand).>map(Optional::of, $ -> { + if (this.player.distanceToSqr(armorStand) < 9.0) { + return Optional.empty(); + } else { + return Optional.of(Component.translatable(OUT_OF_RANGE_TRANSLATION_KEY)); + } + }).>map(Either::left).orElse(Either.right(Unit.INSTANCE)); + } + + @Override + protected int getDequeueDelayTicks() { + return ArmorStatues.CONFIG.get(ClientConfig.class).clientCommandDelay; + } + + private boolean enqueueTriggerValue(int triggerValue) { + return this.enqueueClientCommand("trigger as_trigger set %s".formatted(triggerValue)); + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java new file mode 100644 index 0000000..3689876 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java @@ -0,0 +1,51 @@ +package fuzs.armorstatues.proxy; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreenFactory; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.entity.decoration.ArmorStandDataProvider; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ClientProxy extends ServerProxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + ArmorStandHolder holder = new ArmorStandHolder() { + + @Override + public ArmorStand getArmorStand() { + return armorStand; + } + + @Override + public ArmorStandDataProvider getDataProvider() { + return ModRegistry.ARMOR_STAND_DATA_PROVIDER; + } + }; + Screen screen = ArmorStandScreenFactory.createLastScreenType(holder, + player.getInventory(), + armorStand.getDisplayName(), + createDataSyncHandler(holder, (LocalPlayer) player) + ); + Minecraft minecraft = Minecraft.getInstance(); + minecraft.setScreen(screen); + } + + private static DataSyncHandler createDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + if ((!player.hasPermissions(2) || ArmorStatues.CONFIG.get(ClientConfig.class).overrideClientPermissionsCheck) && + ArmorStatues.CONFIG.get(ClientConfig.class).useVanillaTweaksTriggers) { + return new VanillaTweaksDataSyncHandler(holder, player); + } else { + return new CommandDataSyncHandler(holder, player); + } + } +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java new file mode 100644 index 0000000..1f5ae83 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java @@ -0,0 +1,11 @@ +package fuzs.armorstatues.proxy; + +import fuzs.puzzleslib.api.core.v1.ModLoaderEnvironment; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public interface Proxy { + Proxy INSTANCE = ModLoaderEnvironment.INSTANCE.isClient() ? new ClientProxy() : new ServerProxy(); + + void openArmorStandScreen(ArmorStand armorStand, Player player); +} diff --git a/1.21.1/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java b/1.21.1/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java new file mode 100644 index 0000000..f41e246 --- /dev/null +++ b/1.21.1/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java @@ -0,0 +1,12 @@ +package fuzs.armorstatues.proxy; + +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ServerProxy implements Proxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + // NO-OP + } +} diff --git a/1.21.1/Common/src/main/resources/architectury.common.json b/1.21.1/Common/src/main/resources/architectury.common.json new file mode 100644 index 0000000..4cfa289 --- /dev/null +++ b/1.21.1/Common/src/main/resources/architectury.common.json @@ -0,0 +1,3 @@ +{ + "accessWidener": "armorstatues.accesswidener" +} \ No newline at end of file diff --git a/1.21.1/Common/src/main/resources/armorstatues.accesswidener b/1.21.1/Common/src/main/resources/armorstatues.accesswidener new file mode 100644 index 0000000..236e6b1 --- /dev/null +++ b/1.21.1/Common/src/main/resources/armorstatues.accesswidener @@ -0,0 +1 @@ +accessWidener v2 named diff --git a/1.21.1/Common/src/main/resources/common.mixins.json b/1.21.1/Common/src/main/resources/common.mixins.json new file mode 100644 index 0000000..77fc8f4 --- /dev/null +++ b/1.21.1/Common/src/main/resources/common.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.21.1/Common/src/main/resources/mod_banner.png b/1.21.1/Common/src/main/resources/mod_banner.png new file mode 100644 index 0000000..62a4610 Binary files /dev/null and b/1.21.1/Common/src/main/resources/mod_banner.png differ diff --git a/1.21.1/Common/src/main/resources/mod_logo.png b/1.21.1/Common/src/main/resources/mod_logo.png new file mode 100644 index 0000000..869766b Binary files /dev/null and b/1.21.1/Common/src/main/resources/mod_logo.png differ diff --git a/1.21.1/Common/src/main/resources/pack.mcmeta b/1.21.1/Common/src/main/resources/pack.mcmeta new file mode 100755 index 0000000..546f4f1 --- /dev/null +++ b/1.21.1/Common/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "${modDescription}", + "pack_format": ${resourcePackFormat} + } +} diff --git a/1.21.1/Fabric/build.gradle b/1.21.1/Fabric/build.gradle new file mode 100644 index 0000000..8ab06f6 --- /dev/null +++ b/1.21.1/Fabric/build.gradle @@ -0,0 +1,12 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/fabric.gradle" + +dependencies { + // Fabric Api + modApi libs.fabricapi.fabric + + // Puzzles Lib + modApi libs.puzzleslib.fabric + + // Statue Menus + modApi(include(libs.statuemenus.fabric.get())) +} diff --git a/1.21.1/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java b/1.21.1/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java new file mode 100644 index 0000000..822cef4 --- /dev/null +++ b/1.21.1/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java @@ -0,0 +1,13 @@ +package fuzs.armorstatues.fabric; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.fabricmc.api.ModInitializer; + +public class ArmorStatuesFabric implements ModInitializer { + + @Override + public void onInitialize() { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } +} diff --git a/1.21.1/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java b/1.21.1/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java new file mode 100644 index 0000000..2414106 --- /dev/null +++ b/1.21.1/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java @@ -0,0 +1,14 @@ +package fuzs.armorstatues.fabric.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.client.ArmorStatuesClient; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import net.fabricmc.api.ClientModInitializer; + +public class ArmorStatuesFabricClient implements ClientModInitializer { + + @Override + public void onInitializeClient() { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + } +} diff --git a/1.21.1/Fabric/src/main/resources/fabric.mixins.json b/1.21.1/Fabric/src/main/resources/fabric.mixins.json new file mode 100644 index 0000000..6bf7940 --- /dev/null +++ b/1.21.1/Fabric/src/main/resources/fabric.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.fabric.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "refmap": "${modId}.fabric.refmap.json" +} diff --git a/1.21.1/Fabric/src/main/resources/fabric.mod.json b/1.21.1/Fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..d67e512 --- /dev/null +++ b/1.21.1/Fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,46 @@ +{ + "schemaVersion": 1, + "id": "${modId}", + "version": "${modVersion}", + + "name": "${modName}", + "description": "${modDescription}", + + "authors": [ + "${modAuthor}" + ], + + "contact": { + "homepage": "${modPageUrl}", + "issues": "${modIssueUrl}", + "sources": "${modPageUrl}" + }, + + "license": "${modLicense}", + "icon": "mod_logo.png", + + "environment": "${modFabricEnvironment}", + + "entrypoints": { + "main": [ + "${mainEntryPoint}" + ], + "client": [ + "${clientEntryPoint}" + ] + }, + + "mixins": [ + "${modId}.common.mixins.json", + "${modId}.fabric.mixins.json" + ], + + "depends": { + "fabricloader": ">=${minFabricVersion}", + "fabric-api": ">=${minFabricApiVersion}", + "puzzleslib": ">=${minPuzzlesVersion}", + "statuemenus": "*", + "minecraft": "${minecraftVersion}", + "java": ">=17" + } +} diff --git a/1.21.1/NeoForge/build.gradle b/1.21.1/NeoForge/build.gradle new file mode 100644 index 0000000..97e8395 --- /dev/null +++ b/1.21.1/NeoForge/build.gradle @@ -0,0 +1,9 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/neoforge.gradle" + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.neoforge + + // Statue Menus + modApi(include(libs.statuemenus.neoforge.get())) +} diff --git a/1.21.1/NeoForge/gradle.properties b/1.21.1/NeoForge/gradle.properties new file mode 100644 index 0000000..2914393 --- /dev/null +++ b/1.21.1/NeoForge/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge \ No newline at end of file diff --git a/1.21.1/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java b/1.21.1/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java new file mode 100644 index 0000000..9f37d35 --- /dev/null +++ b/1.21.1/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java @@ -0,0 +1,13 @@ +package fuzs.armorstatues.neoforge; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.neoforged.fml.common.Mod; + +@Mod(ArmorStatues.MOD_ID) +public class ArmorStatuesNeoForge { + + public ArmorStatuesNeoForge() { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } +} diff --git a/1.21.1/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java b/1.21.1/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java new file mode 100644 index 0000000..53de799 --- /dev/null +++ b/1.21.1/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java @@ -0,0 +1,18 @@ +package fuzs.armorstatues.neoforge.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.client.ArmorStatuesClient; +import fuzs.armorstatues.data.client.ModLanguageProvider; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import fuzs.puzzleslib.neoforge.api.data.v2.core.DataProviderHelper; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.fml.common.Mod; + +@Mod(value = ArmorStatues.MOD_ID, dist = Dist.CLIENT) +public class ArmorStatuesNeoForgeClient { + + public ArmorStatuesNeoForgeClient() { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + DataProviderHelper.registerDataProviders(ArmorStatues.MOD_ID, ModLanguageProvider::new); + } +} diff --git a/1.21.1/NeoForge/src/main/resources/META-INF/neoforge.mods.toml b/1.21.1/NeoForge/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..432ab6c --- /dev/null +++ b/1.21.1/NeoForge/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,57 @@ +modLoader = "javafml" +loaderVersion = "*" +license = "${modLicense}" +issueTrackerURL = "${modIssueUrl}" + +[[mods]] +modId = "${modId}" +displayName = "${modName}" +description = "${modDescription}" +version = "${modVersion}" +authors = "${modAuthor}" +logoFile = "mod_banner.png" +logoBlur = false +displayURL = "${modPageUrl}" +updateJSONURL = "${modUpdateUrl}" +displayTest = "${modForgeDisplayTest}" + +[[mixins]] +config="${modId}.common.mixins.json" + +[[mixins]] +config="${modId}.neoforge.mixins.json" + +[[dependencies.${ modId }]] +modId = "neoforge" +mandatory = true +type = "required" +versionRange = "[${minNeoForgeVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "minecraft" +mandatory = true +type = "required" +versionRange = "[${minecraftVersion}]" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "puzzleslib" +mandatory = true +type = "required" +versionRange = "[${minPuzzlesVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "statuemenus" +mandatory = true +type = "required" +versionRange = "*" +ordering = "NONE" +side = "BOTH" + +[modproperties.${ modId }] +catalogueImageIcon = "mod_logo.png" diff --git a/1.21.1/NeoForge/src/main/resources/neoforge.mixins.json b/1.21.1/NeoForge/src/main/resources/neoforge.mixins.json new file mode 100644 index 0000000..1fb3919 --- /dev/null +++ b/1.21.1/NeoForge/src/main/resources/neoforge.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.neoforge.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.21.1/build.gradle b/1.21.1/build.gradle new file mode 100644 index 0000000..dea2bca --- /dev/null +++ b/1.21.1/build.gradle @@ -0,0 +1,9 @@ +plugins { + alias libs.plugins.architecturyloom apply false + alias libs.plugins.architecturyplugin apply false + alias libs.plugins.shadow apply false + alias libs.plugins.cursegradle apply false + alias libs.plugins.minotaur apply false +} + +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/main.gradle" diff --git a/1.21.1/gradle.properties b/1.21.1/gradle.properties new file mode 100755 index 0000000..c5b4b8e --- /dev/null +++ b/1.21.1/gradle.properties @@ -0,0 +1,45 @@ +org.gradle.jvmargs=-Xmx4G +org.gradle.daemon=false +copyBuildJar=true + +# Mod Attributes +modId=armorstatues +modName=Armor Statues +modVersion=21.1.0 +modAuthor=Fuzs +modDescription=Unlock the full potential of armor stands! Works on vanilla servers, too. +modLicense=MPL-2.0 +modSourceUrl=https://github.com/Fuzss/armorstatues +modIssueUrl=https://github.com/Fuzss/armorstatues/issues +modUpdateUrl=https://raw.githubusercontent.com/Fuzss/modresources/main/update/armorstatues.json +modMavenGroup=fuzs.armorstatues +# "MATCH_VERSION" for a mod required on both sides, "IGNORE_SERVER_VERSION" for a server only mod, "IGNORE_ALL_VERSION" for a client only mod +modForgeDisplayTest=IGNORE_ALL_VERSION +# "*" for a mod loaded on both sides, "server" for a server only mod, "client" for a client only mod +modFabricEnvironment=* + +# Version Catalog +dependenciesVersionCatalog=1.21.1-v6 +dependenciesPuzzlesLibVersion=21.1.8 +dependenciesMinPuzzlesLibVersion=21.1.8 + +# Mod Publishing +projectReleaseType=release +projectCurseForgeId=682566 +projectModrinthId=bbGCtEvb + +# Required Dependencies +dependenciesRequiredFabricCurseForge=fabric-api, forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredNeoForgeCurseForge=puzzles-lib +dependenciesRequiredForgeCurseForge=forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredFabricModrinth=fabric-api, forge-config-api-port, puzzles-lib +dependenciesRequiredNeoForgeModrinth=puzzles-lib +dependenciesRequiredForgeModrinth=forge-config-api-port, puzzles-lib + +# Optional Dependencies +dependenciesOptionalFabricCurseForge=config-menus-forge +dependenciesOptionalNeoForgeCurseForge=config-menus-forge +dependenciesOptionalForgeCurseForge=config-menus-forge +dependenciesOptionalFabricModrinth=forge-config-screens +dependenciesOptionalNeoForgeModrinth=forge-config-screens +dependenciesOptionalForgeModrinth=forge-config-screens diff --git a/1.21.1/gradle/wrapper/gradle-wrapper.jar b/1.21.1/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..943f0cb Binary files /dev/null and b/1.21.1/gradle/wrapper/gradle-wrapper.jar differ diff --git a/1.21.1/gradle/wrapper/gradle-wrapper.properties b/1.21.1/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2617362 --- /dev/null +++ b/1.21.1/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/1.21.1/gradlew b/1.21.1/gradlew new file mode 100755 index 0000000..65dcd68 --- /dev/null +++ b/1.21.1/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/1.21.1/gradlew.bat b/1.21.1/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/1.21.1/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/1.21.1/settings.gradle b/1.21.1/settings.gradle new file mode 100644 index 0000000..5cfe5eb --- /dev/null +++ b/1.21.1/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { url "https://maven.architectury.dev/" } + maven { url "https://maven.fabricmc.net/" } + maven { url "https://maven.neoforged.net/releases/" } + maven { url "https://maven.minecraftforge.net/" } + } +} + +include "Common" +include "Fabric" +include "NeoForge" +//include "Forge" + +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/v2/settings.gradle" diff --git a/1.21.3/CHANGELOG.md b/1.21.3/CHANGELOG.md new file mode 100644 index 0000000..f842cff --- /dev/null +++ b/1.21.3/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [v21.3.2-1.21.3] - 2024-12-22 +- Update bundled Statue Menus library to v21.3.2 + +## [v21.3.1-1.21.3] - 2024-12-10 +- Update bundled Statue Menus library to v21.3.1 + +## [v21.3.0-1.21.3] - 2024-12-09 +- Port to Minecraft 1.21.3 diff --git a/1.21.3/Common/build.gradle b/1.21.3/Common/build.gradle new file mode 100644 index 0000000..1a985e0 --- /dev/null +++ b/1.21.3/Common/build.gradle @@ -0,0 +1,13 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/common.gradle" + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.common + + // Statue Menus + modApi libs.statuemenus.common +} + +tasks.withType(net.fabricmc.loom.task.AbstractRemapJarTask).configureEach { + targetNamespace = "named" +} diff --git a/1.21.3/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 b/1.21.3/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 new file mode 100644 index 0000000..7a63887 --- /dev/null +++ b/1.21.3/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 @@ -0,0 +1,2 @@ +// 1.21.3 2024-12-22T18:05:55.570921 Language (en_us) +35086f8aaffbf594a8a74147938605b772d8cea6 assets/armorstatues/lang/en_us.json diff --git a/1.21.3/Common/src/generated/resources/assets/armorstatues/lang/en_us.json b/1.21.3/Common/src/generated/resources/assets/armorstatues/lang/en_us.json new file mode 100644 index 0000000..c1907a6 --- /dev/null +++ b/1.21.3/Common/src/generated/resources/assets/armorstatues/lang/en_us.json @@ -0,0 +1,24 @@ +{ + "armorstatues.dataSync.failure": "Unable to modify armor stand data: %s", + "armorstatues.dataSync.failure.noArmorStand": "No Valid Armor Stand", + "armorstatues.dataSync.failure.noPermission": "No Permission", + "armorstatues.dataSync.failure.notFinished": "Queue Not Empty", + "armorstatues.dataSync.failure.outOfRange": "Out Of Range", + "armorstatues.dataSync.finished": "Finished sending queued armor stand data", + "armorstatues.screen.vanillaTweaks.checkTarget": "Check Armor Stand Target", + "armorstatues.screen.vanillaTweaks.checkTarget.description": "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, builder isn't necessarily the armor stand which opened builder menu.", + "armorstatues.screen.vanillaTweaks.lock": "Lock", + "armorstatues.screen.vanillaTweaks.lock.description": "Locking an armor stand prevents it from being changed using builder menu and disables interaction with the equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead": "Swap Mainhand & Helmet", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead.description": "Swaps items between the main hand and helmet equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand": "Swap Mainhand & Offhand", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand.description": "Swaps items between the main hand and off hand equipment slots.", + "armorstatues.screen.vanillaTweaks.toolRack": "Align Tool As Tool Rack", + "armorstatues.screen.vanillaTweaks.toolRack.description": "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand.", + "armorstatues.screen.vanillaTweaks.triggerSent": "Sent!", + "armorstatues.screen.vanillaTweaks.unlock": "Unlock", + "armorstatues.screen.vanillaTweaks.unlock.description": "Unlocking an armor stand reverts any adjustments made via a previous lock action.", + "statuemenus.screen.type.alignments": "Alignments", + "statuemenus.screen.type.commandsCompatiblePosition": "Position", + "statuemenus.screen.type.vanillaTweaks": "Vanilla Tweaks" +} \ No newline at end of file diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java new file mode 100644 index 0000000..f260170 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java @@ -0,0 +1,37 @@ +package fuzs.armorstatues; + +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzleslib.api.config.v3.ConfigHolder; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import fuzs.puzzleslib.api.core.v1.utility.ResourceLocationHelper; +import fuzs.puzzleslib.api.event.v1.core.EventPhase; +import fuzs.puzzleslib.api.event.v1.entity.player.PlayerInteractEvents; +import net.minecraft.resources.ResourceLocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ArmorStatues implements ModConstructor { + public static final String MOD_ID = "armorstatues"; + public static final String MOD_NAME = "Armor Statues"; + public static final Logger LOGGER = LoggerFactory.getLogger(MOD_NAME); + + public static final ConfigHolder CONFIG = ConfigHolder.builder(MOD_ID).client(ClientConfig.class); + + @Override + public void onConstructMod() { + ModRegistry.bootstrap(); + registerEventHandlers(); + } + + private static void registerEventHandlers() { + // high priority, so we run before other mods that add armor stand interactions + // we require empty hand + shift, so those other mods can still run their behaviors when those conditions are not met + PlayerInteractEvents.USE_ENTITY_AT.register(EventPhase.BEFORE, ArmorStandInteractHandler::onUseEntityAt); + } + + public static ResourceLocation id(String path) { + return ResourceLocationHelper.fromNamespaceAndPath(MOD_ID, path); + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java new file mode 100644 index 0000000..4413040 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java @@ -0,0 +1,54 @@ +package fuzs.armorstatues.client; + +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandAlignmentsScreen; +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.client.gui.screens.armorstand.CommandsCompatiblePositionScreen; +import fuzs.armorstatues.client.handler.ArmorStandTooltipHandler; +import fuzs.armorstatues.client.handler.ClientInteractHandler; +import fuzs.armorstatues.client.handler.DataSyncTickHandler; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import fuzs.puzzleslib.api.client.core.v1.context.MenuScreensContext; +import fuzs.puzzleslib.api.client.event.v1.ClientTickEvents; +import fuzs.puzzleslib.api.client.event.v1.entity.player.InteractionInputEvents; +import fuzs.puzzleslib.api.client.event.v1.gui.ItemTooltipCallback; +import fuzs.puzzleslib.api.client.event.v1.gui.ScreenEvents; +import fuzs.puzzleslib.api.event.v1.core.EventPhase; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreenFactory; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandMenu; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; + +public class ArmorStatuesClient implements ClientModConstructor { + + @Override + public void onConstructMod() { + registerEventHandlers(); + } + + private static void registerEventHandlers() { + ItemTooltipCallback.EVENT.register(ArmorStandTooltipHandler::onItemTooltip); + ClientTickEvents.END.register(DataSyncTickHandler::onEndClientTick); + ScreenEvents.remove(Screen.class).register(DataSyncTickHandler::onRemove); + // event phase must match PlayerInteractEvents#USE_ENTITY_AT as both are implemented using the same event on Fabric + InteractionInputEvents.USE.register(EventPhase.BEFORE, ClientInteractHandler::onUseInteraction); + } + + @Override + public void onClientSetup() { + ArmorStandScreenFactory.register(ModRegistry.POSITION_SCREEN_TYPE, CommandsCompatiblePositionScreen::new); + ArmorStandScreenFactory.register(ModRegistry.ALIGNMENTS_SCREEN_TYPE, ArmorStandAlignmentsScreen::new); + ArmorStandScreenFactory.register(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE, ArmorStandVanillaTweaksScreen::new); + } + + @SuppressWarnings("Convert2MethodRef") + @Override + public void onRegisterMenuScreens(MenuScreensContext context) { + // compiler doesn't like method reference :( + context.registerMenuScreen(ModRegistry.ARMOR_STAND_MENU_TYPE.value(), + (ArmorStandMenu menu, Inventory inventory, Component component) -> { + return ArmorStandScreenFactory.createLastScreenType(menu, inventory, component); + }); + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java new file mode 100644 index 0000000..df01829 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java @@ -0,0 +1,54 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandButtonsScreen; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandPositionScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandAlignment; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.phys.Vec3; + +import java.util.EnumSet; +import java.util.List; + +public class ArmorStandAlignmentsScreen extends ArmorStandButtonsScreen { + + public ArmorStandAlignmentsScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new DoubleButtonWidget(Component.translatable(ArmorStandPositionScreen.CENTERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CENTERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)).add(0.5, 0.0, 0.5); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + }, button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + })); + for (ArmorStandAlignment alignment : ArmorStandAlignment.values()) { + widgets.add(new SingleButtonWidget(Component.translatable(alignment.getTranslationKey()), Component.translatable(alignment.getDescriptionsKey()), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + ArmorStandAlignmentsScreen.this.dataSyncHandler.sendAlignment(alignment); + })); + } + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.ALIGNMENTS_SCREEN_TYPE; + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java new file mode 100644 index 0000000..f52054d --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java @@ -0,0 +1,75 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandButtonsScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; + +import java.util.List; + +public class ArmorStandVanillaTweaksScreen extends ArmorStandButtonsScreen { + public static final String TRIGGER_SENT_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.triggerSent"; + public static final String CHECK_TARGET_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget"; + public static final String CHECK_TARGET_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget.description"; + public static final String LOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock"; + public static final String LOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock.description"; + public static final String UNLOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock"; + public static final String UNLOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock.description"; + public static final String TOOL_RACK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack"; + public static final String TOOL_RACK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack.description"; + public static final String SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand"; + public static final String SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand.description"; + public static final String SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead"; + public static final String SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead.description"; + + public ArmorStandVanillaTweaksScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + public VanillaTweaksDataSyncHandler getDataSyncHandler() { + return (VanillaTweaksDataSyncHandler) super.getDataSyncHandler(); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new SingleButtonWidget(Component.translatable(CHECK_TARGET_TRANSLATION_KEY), Component.translatable(CHECK_TARGET_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.CHECK_TARGET); + this.onClose(); + })); + widgets.add(new DoubleButtonWidget(Component.translatable(LOCK_TRANSLATION_KEY), Component.translatable(UNLOCK_TRANSLATION_KEY), Component.translatable(LOCK_DESCRIPTION_KEY), Component.translatable(UNLOCK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_LOCK); + }, button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_UNLOCK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(TOOL_RACK_TRANSLATION_KEY), Component.translatable(TOOL_RACK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.AUTO_ALIGNMENT_TOOL_RACK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_OFFHAND); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_HEAD); + })); + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE; + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/CommandsCompatiblePositionScreen.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/CommandsCompatiblePositionScreen.java new file mode 100644 index 0000000..5c68164 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/CommandsCompatiblePositionScreen.java @@ -0,0 +1,80 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandPositionScreen; +import fuzs.statuemenus.api.v1.helper.ScaleAttributeHelper; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Consumer; +import java.util.function.DoubleSupplier; + +public class CommandsCompatiblePositionScreen extends ArmorStandPositionScreen { + protected static final ArmorStandWidgetFactory SCALE_WIDGET_FACTORY = (CommandsCompatiblePositionScreen screen, ArmorStand armorStand) -> { + return screen.new CommandsScaleWidget(Component.translatable(SCALE_TRANSLATION_KEY), + armorStand::getScale, + screen.dataSyncHandler::sendScale); + }; + + public CommandsCompatiblePositionScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + // only move server-side to prevent rubber banding + return buildWidgets(this, + armorStand, + List.of(SCALE_WIDGET_FACTORY, + ROTATION_WIDGET_FACTORY, + POSITION_INCREMENT_WIDGET_FACTORY, + POSITION_X_WIDGET_FACTORY, + POSITION_Y_WIDGET_FACTORY, + POSITION_Z_WIDGET_FACTORY)); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.POSITION_SCREEN_TYPE; + } + + protected class CommandsScaleWidget extends ScaleWidget { + @Nullable + private Boolean hasModifier; + + public CommandsScaleWidget(Component title, DoubleSupplier currentValue, Consumer newValue) { + super(title, currentValue, newValue); + } + + @Override + protected void setNewValue(double newValue) { + if (CommandsCompatiblePositionScreen.this.dataSyncHandler instanceof CommandDataSyncHandler commandDataSyncHandler) { + commandDataSyncHandler.sendScale(this.hasModifier == null || this.hasModifier, + getScaledValue(newValue), + true); + } else { + super.setNewValue(newValue); + } + this.hasModifier = null; + } + + @Override + protected void applyClientValue(double newValue) { + if (this.hasModifier == null) { + this.hasModifier = CommandsCompatiblePositionScreen.this.getHolder() + .getArmorStand() + .getAttributes() + .hasModifier(Attributes.SCALE, ScaleAttributeHelper.SCALE_BONUS_ID); + } + super.applyClientValue(newValue); + } + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java new file mode 100644 index 0000000..a7c16e7 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java @@ -0,0 +1,27 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.puzzleslib.api.core.v1.Proxy; +import fuzs.statuemenus.api.v1.helper.ArmorStandInteractHelper; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.TooltipFlag; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ArmorStandTooltipHandler { + + public static void onItemTooltip(ItemStack itemStack, List lines, Item.TooltipContext tooltipContext, @Nullable Player player, TooltipFlag tooltipFlag) { + if (itemStack.is(Items.ARMOR_STAND)) { + List components = Proxy.INSTANCE.splitTooltipLines(ArmorStandInteractHelper.getArmorStandHoverText()); + if (tooltipFlag.isAdvanced()) { + lines.addAll(lines.size() - (!itemStack.getComponents().isEmpty() ? 2 : 1), components); + } else { + lines.addAll(components); + } + } + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java new file mode 100644 index 0000000..22b26d2 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java @@ -0,0 +1,39 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.puzzleslib.api.event.v1.core.EventResult; +import fuzs.puzzleslib.api.event.v1.core.EventResultHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; + +public class ClientInteractHandler { + + public static EventResult onUseInteraction(Minecraft minecraft, LocalPlayer player, InteractionHand interactionHand, HitResult hitResult) { + + if (hitResult.getType() == HitResult.Type.ENTITY) { + + Entity entity = ((EntityHitResult) hitResult).getEntity(); + Vec3 hitVector = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ()); + EventResultHolder result = ArmorStandInteractHandler.onUseEntityAt(minecraft.player, + minecraft.level, + interactionHand, + entity, + hitVector + ); + + // if InteractionResult.FAIL is returned the mod is missing server-side, and we open the menu client-side without sending a packet to the server, so the server does not try to interact + if (result.filter(interactionResult -> interactionResult == InteractionResult.FAIL).isInterrupt()) { + + return EventResult.INTERRUPT; + } + } + + return EventResult.PASS; + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java new file mode 100644 index 0000000..822390a --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java @@ -0,0 +1,28 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import org.jetbrains.annotations.Nullable; + +public class DataSyncTickHandler { + @Nullable + private static DataSyncHandler dataSyncHandler; + + public static void onRemove(Screen screen) { + if (screen instanceof ArmorStandScreen armorStandScreen && armorStandScreen.getDataSyncHandler().shouldContinueTicking()) { + dataSyncHandler = armorStandScreen.getDataSyncHandler(); + } + } + + public static void onEndClientTick(Minecraft minecraft) { + if (minecraft.player != null && !(minecraft.screen instanceof ArmorStandScreen) && dataSyncHandler != null) { + if (dataSyncHandler.shouldContinueTicking()) { + dataSyncHandler.tick(); + } else { + dataSyncHandler = null; + } + } + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java new file mode 100644 index 0000000..137797d --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java @@ -0,0 +1,15 @@ +package fuzs.armorstatues.config; + +import fuzs.puzzleslib.api.config.v3.Config; +import fuzs.puzzleslib.api.config.v3.ConfigCore; +import fuzs.statuemenus.api.v1.client.gui.screens.AbstractArmorStandScreen; + +public class ClientConfig implements ConfigCore { + @Config(description = {"Allows for using this mod on a server without it (like a vanilla server) when the Vanilla Tweaks Armor Statues data pack is installed without the need for being a server operator.", "Download the Vanilla Tweaks Armor Statues data pack from here: " + AbstractArmorStandScreen.VANILLA_TWEAKS_HOMEPAGE}) + public boolean useVanillaTweaksTriggers = false; + @Config(description = "Do not check if the client has the necessary permission level for executing the '/data' command when trying to edit an armor stand. Useful when the current server is using custom permissions handling.") + public boolean overrideClientPermissionsCheck = false; + @Config(description = "The delay in ticks for sending queued client commands for editing armor stands to the server. Increase this values if commands are sent too quickly and the server fails to process all of them.") + @Config.IntRange(min = 20) + public int clientCommandDelay = 20; +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java new file mode 100644 index 0000000..3a6418f --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java @@ -0,0 +1,46 @@ +package fuzs.armorstatues.data.client; + +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.puzzleslib.api.client.data.v2.AbstractLanguageProvider; +import fuzs.puzzleslib.api.data.v2.core.DataProviderContext; + +public class ModLanguageProvider extends AbstractLanguageProvider { + + public ModLanguageProvider(DataProviderContext context) { + super(context); + } + + @Override + public void addTranslations(TranslationBuilder builder) { + builder.add(CommandDataSyncHandler.FAILURE_TRANSLATION_KEY, "Unable to modify armor stand data: %s"); + builder.add(CommandDataSyncHandler.NO_PERMISSION_TRANSLATION_KEY, "No Permission"); + builder.add(CommandDataSyncHandler.NO_ARMOR_STAND_TRANSLATION_KEY, "No Valid Armor Stand"); + builder.add(CommandDataSyncHandler.OUT_OF_RANGE_TRANSLATION_KEY, "Out Of Range"); + builder.add(CommandDataSyncHandler.NOT_FINISHED_TRANSLATION_KEY, "Queue Not Empty"); + builder.add(CommandDataSyncHandler.FINISHED_TRANSLATION_KEY, "Finished sending queued armor stand data"); + builder.add(ModRegistry.POSITION_SCREEN_TYPE.getTranslationKey(), "Position"); + builder.add(ModRegistry.ALIGNMENTS_SCREEN_TYPE.getTranslationKey(), "Alignments"); + builder.add(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE.getTranslationKey(), "Vanilla Tweaks"); + builder.add(ArmorStandVanillaTweaksScreen.TRIGGER_SENT_TRANSLATION_KEY, "Sent!"); + builder.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_TRANSLATION_KEY, "Check Armor Stand Target"); + builder.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_DESCRIPTION_KEY, + "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, builder isn't necessarily the armor stand which opened builder menu."); + builder.add(ArmorStandVanillaTweaksScreen.LOCK_TRANSLATION_KEY, "Lock"); + builder.add(ArmorStandVanillaTweaksScreen.LOCK_DESCRIPTION_KEY, + "Locking an armor stand prevents it from being changed using builder menu and disables interaction with the equipment slots."); + builder.add(ArmorStandVanillaTweaksScreen.UNLOCK_TRANSLATION_KEY, "Unlock"); + builder.add(ArmorStandVanillaTweaksScreen.UNLOCK_DESCRIPTION_KEY, + "Unlocking an armor stand reverts any adjustments made via a previous lock action."); + builder.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_TRANSLATION_KEY, "Align Tool As Tool Rack"); + builder.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_DESCRIPTION_KEY, + "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand."); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY, "Swap Mainhand & Offhand"); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY, + "Swaps items between the main hand and off hand equipment slots."); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY, "Swap Mainhand & Helmet"); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY, + "Swaps items between the main hand and helmet equipment slots."); + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java new file mode 100644 index 0000000..2dd8af0 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java @@ -0,0 +1,43 @@ +package fuzs.armorstatues.handler; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.proxy.Proxy; +import fuzs.puzzleslib.api.core.v1.ModLoaderEnvironment; +import fuzs.puzzleslib.api.event.v1.core.EventResultHolder; +import fuzs.statuemenus.api.v1.helper.ArmorStandInteractHelper; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; + +public class ArmorStandInteractHandler { + + public static EventResultHolder onUseEntityAt(Player player, Level level, InteractionHand interactionHand, Entity target, Vec3 hitVector) { + + if (player.getAbilities().mayBuild && target.getType() == EntityType.ARMOR_STAND) { + + boolean clientsideOnly = level.isClientSide && !ModLoaderEnvironment.INSTANCE.isModPresentServerside(ArmorStatues.MOD_ID); + // the menu won't exist in the registry if the mod is missing serverside since Forge syncs registries to clients + MenuType menuType = clientsideOnly ? null : ModRegistry.ARMOR_STAND_MENU_TYPE.value(); + EventResultHolder result = ArmorStandInteractHelper.tryOpenArmorStatueMenu(player, level, interactionHand, (ArmorStand) target, menuType, ModRegistry.ARMOR_STAND_DATA_PROVIDER); + if (result.isInterrupt() && clientsideOnly) { + + Proxy.INSTANCE.openArmorStandScreen((ArmorStand) target, player); + // required so no packet is sent to server when only installed client-side, so the server doesn't change any equipment when we only want to open the screen + // returning InteractionResult.FAIL will miss out on the player arm swing animation, which we manually play here + player.swing(interactionHand); + return EventResultHolder.interrupt(InteractionResult.FAIL); + } + + return result; + } + + return EventResultHolder.pass(); + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java new file mode 100644 index 0000000..df8161e --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java @@ -0,0 +1,50 @@ +package fuzs.armorstatues.init; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.init.v3.registry.RegistryManager; +import fuzs.statuemenus.api.v1.world.entity.decoration.ArmorStandDataProvider; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandMenu; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.core.Holder; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class ModRegistry { + static final RegistryManager REGISTRIES = RegistryManager.from(ArmorStatues.MOD_ID); + public static final Holder.Reference> ARMOR_STAND_MENU_TYPE = REGISTRIES.registerExtendedMenuType( + "armor_stand", + () -> (containerId, inventory, data) -> { + return ArmorStandMenu.create(ModRegistry.ARMOR_STAND_MENU_TYPE.value(), + containerId, + inventory, + data, + ModRegistry.ARMOR_STAND_DATA_PROVIDER); + }); + + public static final ArmorStandScreenType POSITION_SCREEN_TYPE = new ArmorStandScreenType( + "commandsCompatiblePosition", + new ItemStack(Items.GRASS_BLOCK)); + public static final ArmorStandScreenType ALIGNMENTS_SCREEN_TYPE = new ArmorStandScreenType("alignments", + new ItemStack(Items.DIAMOND_PICKAXE)); + public static final ArmorStandScreenType VANILLA_TWEAKS_SCREEN_TYPE = new ArmorStandScreenType("vanillaTweaks", + new ItemStack(Items.WRITTEN_BOOK)); + public static final ArmorStandDataProvider ARMOR_STAND_DATA_PROVIDER = new ArmorStandDataProvider() { + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return new ArmorStandScreenType[]{ + ArmorStandScreenType.ROTATIONS, + ArmorStandScreenType.POSES, + ArmorStandScreenType.STYLE, + POSITION_SCREEN_TYPE, + ALIGNMENTS_SCREEN_TYPE, + ArmorStandScreenType.EQUIPMENT + }; + } + }; + + public static void bootstrap() { + // NO-OP + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java new file mode 100644 index 0000000..29bc3b3 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java @@ -0,0 +1,242 @@ +package fuzs.armorstatues.network.client.data; + +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.statuemenus.api.v1.helper.ScaleAttributeHelper; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandAlignment; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandPose; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandStyleOption; +import net.minecraft.ChatFormatting; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.FloatTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.BiPredicate; + +public class CommandDataSyncHandler implements DataSyncHandler { + public static final String NO_PERMISSION_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noPermission"; + public static final String NO_ARMOR_STAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noArmorStand"; + public static final String OUT_OF_RANGE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.outOfRange"; + public static final String NOT_FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.notFinished"; + public static final String FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.finished"; + public static final String FAILURE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure"; + private static final Queue> CLIENT_COMMAND_QUEUE = new ArrayDeque<>(); + + @Nullable + private static ArmorStand queueArmorStand; + private static int itemDequeuedTicks; + + private final ArmorStandHolder holder; + protected final LocalPlayer player; + protected ArmorStandPose lastSyncedPose; + + public CommandDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + this.holder = holder; + this.lastSyncedPose = ArmorStandPose.fromEntity(this.holder.getArmorStand()); + this.player = player; + } + + @Override + public ArmorStandHolder getArmorStandHolder() { + return this.holder; + } + + @Override + public void sendName(String name) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.setCustomArmorStandName(this.getArmorStand(), name); + CompoundTag tag = new CompoundTag(); + tag.putString("CustomName", Component.Serializer.toJson(Component.literal(name), this.player.registryAccess())); + this.enqueueEntityData(tag); + this.finalizeCurrentOperation(); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + // split this into multiple chat messages as the client chat field has a very low character limit + this.sendPosePart(pose::serializeBodyPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeArmPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeLegPoses, this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + if (finalize) this.finalizeCurrentOperation(); + } + + private void sendPosePart(BiPredicate dataWriter, ArmorStandPose lastSyncedPose) { + CompoundTag tag = new CompoundTag(); + if (dataWriter.test(tag, lastSyncedPose)) { + CompoundTag tagToSend = new CompoundTag(); + tagToSend.put("Pose", tag); + this.enqueueEntityData(tagToSend); + } + } + + @Override + public @Nullable ArmorStandPose getLastSyncedPose() { + return this.lastSyncedPose; + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(DoubleTag.valueOf(posX)); + listTag.add(DoubleTag.valueOf(posY)); + listTag.add(DoubleTag.valueOf(posZ)); + CompoundTag tag = new CompoundTag(); + tag.put("Pos", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendScale(float scale, boolean finalize) { + this.sendScale(true, scale, finalize); + } + + public void sendScale(boolean hasModifier, float scale, boolean finalize) { + // track if our scale attribute modifier is already present, so we only try to remove it if necessary + // otherwise an error message from the remove command is displayed which would also be fine if this no longer works + List clientCommands = new ArrayList<>(); + if (hasModifier) { + clientCommands.add("attribute %s %s modifier remove %s".formatted(this.getArmorStand().getStringUUID(), + Attributes.SCALE.unwrapKey().orElseThrow().location(), + ScaleAttributeHelper.SCALE_BONUS_ID)); + } + if (scale != ScaleAttributeHelper.DEFAULT_SCALE) { + clientCommands.add("attribute %s %s modifier add %s %s add_value".formatted(this.getArmorStand() + .getStringUUID(), + Attributes.SCALE.unwrapKey().orElseThrow().location(), + ScaleAttributeHelper.SCALE_BONUS_ID, + scale - ScaleAttributeHelper.DEFAULT_SCALE)); + } + this.enqueueClientCommand(clientCommands); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(FloatTag.valueOf(rotation)); + CompoundTag tag = new CompoundTag(); + tag.put("Rotation", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + CompoundTag tag = new CompoundTag(); + styleOption.toTag(tag, value); + this.enqueueEntityData(tag); + styleOption.setOption(this.getArmorStand(), value); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.super.sendAlignment(alignment); + } + + @Override + public boolean supportsScreenType(ArmorStandScreenType screenType) { + return !screenType.requiresServer(); + } + + @Override + public void tick() { + if (itemDequeuedTicks > 0) itemDequeuedTicks--; + if (itemDequeuedTicks == 0 && queueArmorStand != null && !CLIENT_COMMAND_QUEUE.isEmpty()) { + if (this.testArmorStand(queueArmorStand).right().isPresent()) { + for (String clientCommand : CLIENT_COMMAND_QUEUE.poll()) { + this.player.connection.sendCommand(clientCommand); + } + } else { + CLIENT_COMMAND_QUEUE.clear(); + } + itemDequeuedTicks = this.getDequeueDelayTicks(); + } else if (itemDequeuedTicks == 1 && CLIENT_COMMAND_QUEUE.isEmpty()) { + this.sendDisplayMessage(Component.translatable(FINISHED_TRANSLATION_KEY), false); + } + } + + protected int getDequeueDelayTicks() { + return 5; + } + + @Override + public boolean shouldContinueTicking() { + return !CLIENT_COMMAND_QUEUE.isEmpty() || itemDequeuedTicks != 0; + } + + protected boolean isEditingAllowed() { + return this.isEditingAllowed(!ArmorStatues.CONFIG.get(ClientConfig.class).overrideClientPermissionsCheck); + } + + protected final boolean isEditingAllowed(boolean testPermissionLevel) { + if (testPermissionLevel && !this.player.hasPermissions(2)) { + this.sendFailureMessage(Component.translatable(NO_PERMISSION_TRANSLATION_KEY)); + return false; + } + return this.player.getAbilities().mayBuild && + this.testArmorStand(this.getArmorStand()).ifLeft(this::sendFailureMessage).right().isPresent(); + } + + protected Either testArmorStand(ArmorStand armorStand) { + return !armorStand.isAlive() ? Either.left(Component.translatable(NO_ARMOR_STAND_TRANSLATION_KEY)) : + Either.right(Unit.INSTANCE); + } + + protected boolean enqueueClientCommand(String clientCommand) { + return this.enqueueClientCommand(Collections.singletonList(clientCommand)); + } + + protected boolean enqueueClientCommand(List clientCommand) { + if (CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = null; + } else if (queueArmorStand != null) { + this.sendFailureMessage(Component.translatable(NOT_FINISHED_TRANSLATION_KEY)); + return false; + } + CLIENT_COMMAND_QUEUE.offer(clientCommand); + return true; + } + + @Override + public void finalizeCurrentOperation() { + if (!CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = this.getArmorStand(); + } + } + + protected void sendFailureMessage(Component component) { + this.sendDisplayMessage(Component.translatable(FAILURE_TRANSLATION_KEY, component), true); + } + + protected void sendDisplayMessage(Component component, boolean failure) { + this.player.displayClientMessage(Component.empty() + .append(component) + .withStyle(failure ? ChatFormatting.RED : ChatFormatting.GREEN), false); + } + + private void enqueueEntityData(CompoundTag tag) { + this.enqueueClientCommand("data merge entity %s %s".formatted(this.getArmorStand().getStringUUID(), + tag.getAsString())); + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java new file mode 100644 index 0000000..dbdc639 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java @@ -0,0 +1,359 @@ +package fuzs.armorstatues.network.client.data; + +import com.google.common.collect.ImmutableSortedMap; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.*; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.Rotations; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.NavigableMap; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +public class VanillaTweaksDataSyncHandler extends CommandDataSyncHandler { + private static final int MAX_INCREMENTAL_OPERATIONS = 12; + public static final int CHECK_TARGET = 999; + public static final int SWAP_SLOTS_MAINHAND_AND_OFFHAND = 161; + public static final int SWAP_SLOTS_MAINHAND_AND_HEAD = 162; + public static final int MIRROR_ARMS_LEFT_TO_RIGHT = 131; + public static final int MIRROR_ARMS_RIGHT_TO_LEFT = 132; + public static final int MIRROR_LEGS_LEFT_TO_RIGHT = 133; + public static final int MIRROR_LEGS_RIGHT_TO_LEFT = 134; + public static final int UTILITIES_LOCK = 1000; + public static final int UTILITIES_UNLOCK = 1001; + public static final int MIRROR_AND_FLIP_FLIP = 135; + public static final int SHOW_BASE_PLATE_YES = 1; + public static final int SHOW_BASE_PLATE_NO = 2; + public static final int SHOW_ARMS_YES = 3; + public static final int SHOW_ARMS_NO = 4; + public static final int SMALL_STAND_YES = 5; + public static final int SMALL_STAND_NO = 6; + public static final int APPLY_GRAVITY_YES = 7; + public static final int APPLY_GRAVITY_NO = 8; + public static final int STAND_VISIBLE_YES = 9; + public static final int STAND_VISIBLE_NO = 10; + public static final int DISPLAY_NAME_YES = 11; + public static final int DISPLAY_NAME_NO = 12; + public static final int NUDGE_POSITION_X8_NEGATIVE = 40; + public static final int NUDGE_POSITION_X3_NEGATIVE = 101; + public static final int NUDGE_POSITION_X1_NEGATIVE = 102; + public static final int NUDGE_POSITION_X1_POSITIVE = 103; + public static final int NUDGE_POSITION_X3_POSITIVE = 104; + public static final int NUDGE_POSITION_X8_POSITIVE = 43; + public static final int NUDGE_POSITION_Y8_NEGATIVE = 44; + public static final int NUDGE_POSITION_Y3_NEGATIVE = 105; + public static final int NUDGE_POSITION_Y1_NEGATIVE = 106; + public static final int NUDGE_POSITION_Y1_POSITIVE = 107; + public static final int NUDGE_POSITION_Y3_POSITIVE = 108; + public static final int NUDGE_POSITION_Y8_POSITIVE = 47; + public static final int NUDGE_POSITION_Z8_NEGATIVE = 48; + public static final int NUDGE_POSITION_Z3_NEGATIVE = 109; + public static final int NUDGE_POSITION_Z1_NEGATIVE = 110; + public static final int NUDGE_POSITION_Z1_POSITIVE = 111; + public static final int NUDGE_POSITION_Z3_POSITIVE = 112; + public static final int NUDGE_POSITION_Z8_POSITIVE = 51; + public static final int ADJUST_ROTATION_ANGLE_STEP_45 = 120; + public static final int ADJUST_ROTATION_ANGLE_STEP_15 = 121; + public static final int ADJUST_ROTATION_ANGLE_STEP_5 = 122; + public static final int ADJUST_ROTATION_ANGLE_STEP_1 = 123; + public static final int ADJUST_ROTATION_ROTATE_RIGHT = 56; + public static final int ADJUST_ROTATION_ROTATE_LEFT = 57; + public static final int POSE_PRESETS_ATTENTION = 20; + public static final int POSE_PRESETS_WALKING = 21; + public static final int POSE_PRESETS_RUNNING = 22; + public static final int POSE_PRESETS_POINTING = 23; + public static final int POSE_PRESETS_BLOCKING = 24; + public static final int POSE_PRESETS_LUNGEING = 25; + public static final int POSE_PRESETS_WINNING = 26; + public static final int POSE_PRESETS_SITTING = 27; + public static final int POSE_PRESETS_ARABESQUE = 28; + public static final int POSE_PRESETS_CUPID = 29; + public static final int POSE_PRESETS_CONFIDENT = 30; + public static final int POSE_PRESETS_SALUTE = 31; + public static final int POSE_PRESETS_DEATH = 32; + public static final int POSE_PRESETS_FACEPALM = 33; + public static final int POSE_PRESETS_LAZING = 34; + public static final int POSE_PRESETS_CONFUSED = 35; + public static final int POSE_PRESETS_FORMAL = 36; + public static final int POSE_PRESETS_SAD = 37; + public static final int POSE_PRESETS_JOYOUS = 38; + public static final int POSE_PRESETS_STARGAZING = 39; + public static final int AUTO_ALIGNMENT_BLOCK_ON_SURFACE = 151; + public static final int AUTO_ALIGNMENT_ITEM_ON_SURFACE = 152; + public static final int AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE = 153; + public static final int AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE = 154; + public static final int AUTO_ALIGNMENT_TOOL_RACK = 155; + public static final int POSE_ADJUSTMENT_HEAD_X_NEGATIVE = 60; + public static final int POSE_ADJUSTMENT_HEAD_X_POSITIVE = 61; + public static final int POSE_ADJUSTMENT_HEAD_Y_NEGATIVE = 62; + public static final int POSE_ADJUSTMENT_HEAD_Y_POSITIVE = 63; + public static final int POSE_ADJUSTMENT_HEAD_Z_NEGATIVE = 64; + public static final int POSE_ADJUSTMENT_HEAD_Z_POSITIVE = 65; + public static final int POSE_ADJUSTMENT_BODY_X_NEGATIVE = 67; + public static final int POSE_ADJUSTMENT_BODY_X_POSITIVE = 66; + public static final int POSE_ADJUSTMENT_BODY_Y_NEGATIVE = 68; + public static final int POSE_ADJUSTMENT_BODY_Y_POSITIVE = 69; + public static final int POSE_ADJUSTMENT_BODY_Z_NEGATIVE = 70; + public static final int POSE_ADJUSTMENT_BODY_Z_POSITIVE = 71; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE = 72; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE = 73; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE = 74; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE = 75; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE = 77; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE = 76; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE = 78; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE = 79; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE = 81; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE = 80; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE = 82; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE = 83; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE = 84; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE = 85; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE = 87; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE = 86; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE = 89; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE = 88; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE = 90; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE = 91; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE = 92; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE = 93; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE = 94; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE = 95; + private static final int[] POSE_ADJUSTMENT_HEAD = new int[]{POSE_ADJUSTMENT_HEAD_X_NEGATIVE, POSE_ADJUSTMENT_HEAD_X_POSITIVE, POSE_ADJUSTMENT_HEAD_Y_NEGATIVE, POSE_ADJUSTMENT_HEAD_Y_POSITIVE, POSE_ADJUSTMENT_HEAD_Z_NEGATIVE, POSE_ADJUSTMENT_HEAD_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_BODY = new int[]{POSE_ADJUSTMENT_BODY_X_POSITIVE, POSE_ADJUSTMENT_BODY_X_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_POSITIVE, POSE_ADJUSTMENT_BODY_Z_NEGATIVE, POSE_ADJUSTMENT_BODY_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_ARM = new int[]{POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_ARM = new int[]{POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_LEG = new int[]{POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_LEG = new int[]{POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE}; + private static final NavigableMap NUDGE_POSITIONS_X_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_X3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_X8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_X_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_X3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_X8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_POSITIVE); + private static final NavigableMap ADJUST_ROTATION_ANGLE_STEPS = ImmutableSortedMap.of(1.0F, ADJUST_ROTATION_ANGLE_STEP_1, 5.0F, ADJUST_ROTATION_ANGLE_STEP_5, 15.0F, ADJUST_ROTATION_ANGLE_STEP_15, 45.0F, ADJUST_ROTATION_ANGLE_STEP_45); + + public VanillaTweaksDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + super(holder, player); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue = this.getTriggerValueFromPose(pose); + if (triggerValue != -1) { + if (this.enqueueTriggerValue(triggerValue)) { + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + } + } else { + this.tryApplyAllPoseParts(pose); + } + if (finalize) this.finalizeCurrentOperation(); + } + + private int getTriggerValueFromPose(ArmorStandPose pose) { + if (pose.getSourceType() == ArmorStandPose.SourceType.EMPTY) return POSE_PRESETS_ATTENTION; + if (pose.getSourceType() == ArmorStandPose.SourceType.MIRRORED) return MIRROR_AND_FLIP_FLIP; + if (pose.getSourceType() != ArmorStandPose.SourceType.VANILLA_TWEAKS) return -1; + if (pose == ArmorStandPose.WALKING) return POSE_PRESETS_WALKING; + if (pose == ArmorStandPose.RUNNING) return POSE_PRESETS_RUNNING; + if (pose == ArmorStandPose.POINTING) return POSE_PRESETS_POINTING; + if (pose == ArmorStandPose.BLOCKING) return POSE_PRESETS_BLOCKING; + if (pose == ArmorStandPose.LUNGEING) return POSE_PRESETS_LUNGEING; + if (pose == ArmorStandPose.WINNING) return POSE_PRESETS_WINNING; + if (pose == ArmorStandPose.SITTING) return POSE_PRESETS_SITTING; + if (pose == ArmorStandPose.ARABESQUE) return POSE_PRESETS_ARABESQUE; + if (pose == ArmorStandPose.CUPID) return POSE_PRESETS_CUPID; + if (pose == ArmorStandPose.CONFIDENT) return POSE_PRESETS_CONFIDENT; + if (pose == ArmorStandPose.SALUTE) return POSE_PRESETS_SALUTE; + if (pose == ArmorStandPose.DEATH) return POSE_PRESETS_DEATH; + if (pose == ArmorStandPose.FACEPALM) return POSE_PRESETS_FACEPALM; + if (pose == ArmorStandPose.LAZING) return POSE_PRESETS_LAZING; + if (pose == ArmorStandPose.CONFUSED) return POSE_PRESETS_CONFUSED; + if (pose == ArmorStandPose.FORMAL) return POSE_PRESETS_FORMAL; + if (pose == ArmorStandPose.SAD) return POSE_PRESETS_SAD; + if (pose == ArmorStandPose.JOYOUS) return POSE_PRESETS_JOYOUS; + if (pose == ArmorStandPose.STARGAZING) return POSE_PRESETS_STARGAZING; + return -1; + } + + private void tryApplyAllPoseParts(ArmorStandPose pose) { + if (!this.tryApplyPosePart(this.lastSyncedPose.getHeadPose(), pose.getNullableHeadPose(), POSE_ADJUSTMENT_HEAD, this.lastSyncedPose::withHeadPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getBodyPose(), pose.getNullableBodyPose(), POSE_ADJUSTMENT_BODY, this.lastSyncedPose::withBodyPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightArmPose(), pose.getNullableRightArmPose(), POSE_ADJUSTMENT_RIGHT_ARM, this.lastSyncedPose::withRightArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftArmPose(), pose.getNullableLeftArmPose(), POSE_ADJUSTMENT_LEFT_ARM, this.lastSyncedPose::withLeftArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightLegPose(), pose.getNullableRightLegPose(), POSE_ADJUSTMENT_RIGHT_LEG, this.lastSyncedPose::withRightLegPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftLegPose(), pose.getNullableLeftLegPose(), POSE_ADJUSTMENT_LEFT_LEG, this.lastSyncedPose::withLeftLegPose)) + return; + } + + private boolean tryApplyPosePart(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment, Function function) { + if (this.tryApplyPoseAdjustment(oldPose, newPose, poseAdjustment)) { + this.lastSyncedPose = function.apply(newPose != null ? newPose : oldPose); + return true; + } else { + return false; + } + } + + private boolean tryApplyPoseAdjustment(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment) { + if (newPose == null || oldPose.equals(newPose)) return true; + if (!this.applyIncrementsFromSteps(oldPose.getX(), newPose.getX(), poseAdjustment[0], poseAdjustment[1])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getY(), newPose.getY(), poseAdjustment[2], poseAdjustment[3])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getZ(), newPose.getZ(), poseAdjustment[4], poseAdjustment[5])) return false; + return true; + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyPositionIncrements(this.getArmorStand().getX(), posX, NUDGE_POSITIONS_X_POSITIVE, NUDGE_POSITIONS_X_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getY(), posY, NUDGE_POSITIONS_Y_POSITIVE, NUDGE_POSITIONS_Y_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getZ(), posZ, NUDGE_POSITIONS_Z_POSITIVE, NUDGE_POSITIONS_Z_NEGATIVE); + if (finalize) this.finalizeCurrentOperation(); + } + + private void applyPositionIncrements(double oldValue, double newValue, NavigableMap positiveNudgePositions, NavigableMap negativeNudgePositions) { + double value = newValue - oldValue; + double signum = Math.signum(value); + value = Math.abs(value); + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = (signum == -1.0F ? negativeNudgePositions : positiveNudgePositions).floorEntry(value); + if (entry != null) { + value -= entry.getKey(); + if (!this.enqueueTriggerValue(entry.getValue())) { + return; + } + } else { + break; + } + } + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyIncrementsFromSteps(this.getArmorStand().getYRot(), rotation, ADJUST_ROTATION_ROTATE_RIGHT, ADJUST_ROTATION_ROTATE_LEFT); + if (finalize) this.finalizeCurrentOperation(); + } + + private boolean applyIncrementsFromSteps(float oldValue, float newValue, int triggerValueNegative, int triggerValuePositive) { + float value = newValue - oldValue; + float signum = Math.signum(value); + value = Math.abs(value); + float lastIncrement = 0.0F; + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = ADJUST_ROTATION_ANGLE_STEPS.floorEntry(value); + if (entry != null) { + float currentIncrement = entry.getKey(); + value -= currentIncrement; + if (currentIncrement != lastIncrement) { + lastIncrement = currentIncrement; + if (!this.enqueueTriggerValue(entry.getValue())) { + return false; + } + } + if (!this.enqueueTriggerValue(signum == -1.0F ? triggerValuePositive : triggerValueNegative)) { + return false; + } + } else { + break; + } + } + return true; + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue; + if (styleOption == ArmorStandStyleOptions.SHOW_NAME) { + triggerValue = value ? DISPLAY_NAME_YES : DISPLAY_NAME_NO; + } else if (styleOption == ArmorStandStyleOptions.SHOW_ARMS) { + triggerValue = value ? SHOW_ARMS_YES : SHOW_ARMS_NO; + } else if (styleOption == ArmorStandStyleOptions.SMALL) { + triggerValue = value ? SMALL_STAND_YES : SMALL_STAND_NO; + } else if (styleOption == ArmorStandStyleOptions.INVISIBLE) { + triggerValue = value ? STAND_VISIBLE_NO : STAND_VISIBLE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_BASE_PLATE) { + triggerValue = value ? SHOW_BASE_PLATE_NO : SHOW_BASE_PLATE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_GRAVITY) { + triggerValue = value ? APPLY_GRAVITY_NO : APPLY_GRAVITY_YES; + } else { + super.sendStyleOption(styleOption, value, finalize); + return; + } + if (this.sendSingleTriggerValue(triggerValue, finalize)) { + styleOption.setOption(this.getArmorStand(), value); + } + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + int triggerValue = switch (alignment) { + case BLOCK -> AUTO_ALIGNMENT_BLOCK_ON_SURFACE; + case FLOATING_ITEM -> AUTO_ALIGNMENT_ITEM_ON_SURFACE; + case FLAT_ITEM -> AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE; + case TOOL -> AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE; + }; + this.sendSingleTriggerValue(triggerValue, true); + } + + public void sendSingleTriggerValue(int triggerValue) { + if (!this.isEditingAllowed()) return; + this.sendSingleTriggerValue(triggerValue, true); + } + + private boolean sendSingleTriggerValue(int triggerValue, boolean finalize) { + boolean result = this.enqueueTriggerValue(triggerValue); + if (finalize) this.finalizeCurrentOperation(); + return result; + } + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return Stream.concat(Stream.of(super.getScreenTypes()), Stream.of(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE)).toArray(ArmorStandScreenType[]::new); + } + + @Override + protected boolean isEditingAllowed() { + return this.isEditingAllowed(false); + } + + @Override + protected Either testArmorStand(ArmorStand armorStand) { + return super.testArmorStand(armorStand).>map(Optional::of, $ -> { + if (this.player.distanceToSqr(armorStand) < 9.0) { + return Optional.empty(); + } else { + return Optional.of(Component.translatable(OUT_OF_RANGE_TRANSLATION_KEY)); + } + }).>map(Either::left).orElse(Either.right(Unit.INSTANCE)); + } + + @Override + protected int getDequeueDelayTicks() { + return ArmorStatues.CONFIG.get(ClientConfig.class).clientCommandDelay; + } + + private boolean enqueueTriggerValue(int triggerValue) { + return this.enqueueClientCommand("trigger as_trigger set %s".formatted(triggerValue)); + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java new file mode 100644 index 0000000..3689876 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java @@ -0,0 +1,51 @@ +package fuzs.armorstatues.proxy; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreenFactory; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.entity.decoration.ArmorStandDataProvider; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ClientProxy extends ServerProxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + ArmorStandHolder holder = new ArmorStandHolder() { + + @Override + public ArmorStand getArmorStand() { + return armorStand; + } + + @Override + public ArmorStandDataProvider getDataProvider() { + return ModRegistry.ARMOR_STAND_DATA_PROVIDER; + } + }; + Screen screen = ArmorStandScreenFactory.createLastScreenType(holder, + player.getInventory(), + armorStand.getDisplayName(), + createDataSyncHandler(holder, (LocalPlayer) player) + ); + Minecraft minecraft = Minecraft.getInstance(); + minecraft.setScreen(screen); + } + + private static DataSyncHandler createDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + if ((!player.hasPermissions(2) || ArmorStatues.CONFIG.get(ClientConfig.class).overrideClientPermissionsCheck) && + ArmorStatues.CONFIG.get(ClientConfig.class).useVanillaTweaksTriggers) { + return new VanillaTweaksDataSyncHandler(holder, player); + } else { + return new CommandDataSyncHandler(holder, player); + } + } +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java new file mode 100644 index 0000000..1f5ae83 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java @@ -0,0 +1,11 @@ +package fuzs.armorstatues.proxy; + +import fuzs.puzzleslib.api.core.v1.ModLoaderEnvironment; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public interface Proxy { + Proxy INSTANCE = ModLoaderEnvironment.INSTANCE.isClient() ? new ClientProxy() : new ServerProxy(); + + void openArmorStandScreen(ArmorStand armorStand, Player player); +} diff --git a/1.21.3/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java b/1.21.3/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java new file mode 100644 index 0000000..f41e246 --- /dev/null +++ b/1.21.3/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java @@ -0,0 +1,12 @@ +package fuzs.armorstatues.proxy; + +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ServerProxy implements Proxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + // NO-OP + } +} diff --git a/1.21.3/Common/src/main/resources/architectury.common.json b/1.21.3/Common/src/main/resources/architectury.common.json new file mode 100644 index 0000000..4cfa289 --- /dev/null +++ b/1.21.3/Common/src/main/resources/architectury.common.json @@ -0,0 +1,3 @@ +{ + "accessWidener": "armorstatues.accesswidener" +} \ No newline at end of file diff --git a/1.21.3/Common/src/main/resources/armorstatues.accesswidener b/1.21.3/Common/src/main/resources/armorstatues.accesswidener new file mode 100644 index 0000000..236e6b1 --- /dev/null +++ b/1.21.3/Common/src/main/resources/armorstatues.accesswidener @@ -0,0 +1 @@ +accessWidener v2 named diff --git a/1.21.3/Common/src/main/resources/common.mixins.json b/1.21.3/Common/src/main/resources/common.mixins.json new file mode 100644 index 0000000..77fc8f4 --- /dev/null +++ b/1.21.3/Common/src/main/resources/common.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.21.3/Common/src/main/resources/mod_banner.png b/1.21.3/Common/src/main/resources/mod_banner.png new file mode 100644 index 0000000..62a4610 Binary files /dev/null and b/1.21.3/Common/src/main/resources/mod_banner.png differ diff --git a/1.21.3/Common/src/main/resources/mod_logo.png b/1.21.3/Common/src/main/resources/mod_logo.png new file mode 100644 index 0000000..869766b Binary files /dev/null and b/1.21.3/Common/src/main/resources/mod_logo.png differ diff --git a/1.21.3/Common/src/main/resources/pack.mcmeta b/1.21.3/Common/src/main/resources/pack.mcmeta new file mode 100755 index 0000000..546f4f1 --- /dev/null +++ b/1.21.3/Common/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "${modDescription}", + "pack_format": ${resourcePackFormat} + } +} diff --git a/1.21.3/Fabric/build.gradle b/1.21.3/Fabric/build.gradle new file mode 100644 index 0000000..8ab06f6 --- /dev/null +++ b/1.21.3/Fabric/build.gradle @@ -0,0 +1,12 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/fabric.gradle" + +dependencies { + // Fabric Api + modApi libs.fabricapi.fabric + + // Puzzles Lib + modApi libs.puzzleslib.fabric + + // Statue Menus + modApi(include(libs.statuemenus.fabric.get())) +} diff --git a/1.21.3/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java b/1.21.3/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java new file mode 100644 index 0000000..822cef4 --- /dev/null +++ b/1.21.3/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java @@ -0,0 +1,13 @@ +package fuzs.armorstatues.fabric; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.fabricmc.api.ModInitializer; + +public class ArmorStatuesFabric implements ModInitializer { + + @Override + public void onInitialize() { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } +} diff --git a/1.21.3/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java b/1.21.3/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java new file mode 100644 index 0000000..2414106 --- /dev/null +++ b/1.21.3/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java @@ -0,0 +1,14 @@ +package fuzs.armorstatues.fabric.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.client.ArmorStatuesClient; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import net.fabricmc.api.ClientModInitializer; + +public class ArmorStatuesFabricClient implements ClientModInitializer { + + @Override + public void onInitializeClient() { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + } +} diff --git a/1.21.3/Fabric/src/main/resources/fabric.mixins.json b/1.21.3/Fabric/src/main/resources/fabric.mixins.json new file mode 100644 index 0000000..6bf7940 --- /dev/null +++ b/1.21.3/Fabric/src/main/resources/fabric.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.fabric.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "refmap": "${modId}.fabric.refmap.json" +} diff --git a/1.21.3/Fabric/src/main/resources/fabric.mod.json b/1.21.3/Fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..d67e512 --- /dev/null +++ b/1.21.3/Fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,46 @@ +{ + "schemaVersion": 1, + "id": "${modId}", + "version": "${modVersion}", + + "name": "${modName}", + "description": "${modDescription}", + + "authors": [ + "${modAuthor}" + ], + + "contact": { + "homepage": "${modPageUrl}", + "issues": "${modIssueUrl}", + "sources": "${modPageUrl}" + }, + + "license": "${modLicense}", + "icon": "mod_logo.png", + + "environment": "${modFabricEnvironment}", + + "entrypoints": { + "main": [ + "${mainEntryPoint}" + ], + "client": [ + "${clientEntryPoint}" + ] + }, + + "mixins": [ + "${modId}.common.mixins.json", + "${modId}.fabric.mixins.json" + ], + + "depends": { + "fabricloader": ">=${minFabricVersion}", + "fabric-api": ">=${minFabricApiVersion}", + "puzzleslib": ">=${minPuzzlesVersion}", + "statuemenus": "*", + "minecraft": "${minecraftVersion}", + "java": ">=17" + } +} diff --git a/1.21.3/NeoForge/build.gradle b/1.21.3/NeoForge/build.gradle new file mode 100644 index 0000000..97e8395 --- /dev/null +++ b/1.21.3/NeoForge/build.gradle @@ -0,0 +1,9 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/neoforge.gradle" + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.neoforge + + // Statue Menus + modApi(include(libs.statuemenus.neoforge.get())) +} diff --git a/1.21.3/NeoForge/gradle.properties b/1.21.3/NeoForge/gradle.properties new file mode 100644 index 0000000..2914393 --- /dev/null +++ b/1.21.3/NeoForge/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge \ No newline at end of file diff --git a/1.21.3/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java b/1.21.3/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java new file mode 100644 index 0000000..9f37d35 --- /dev/null +++ b/1.21.3/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java @@ -0,0 +1,13 @@ +package fuzs.armorstatues.neoforge; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.neoforged.fml.common.Mod; + +@Mod(ArmorStatues.MOD_ID) +public class ArmorStatuesNeoForge { + + public ArmorStatuesNeoForge() { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } +} diff --git a/1.21.3/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java b/1.21.3/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java new file mode 100644 index 0000000..53de799 --- /dev/null +++ b/1.21.3/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java @@ -0,0 +1,18 @@ +package fuzs.armorstatues.neoforge.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.client.ArmorStatuesClient; +import fuzs.armorstatues.data.client.ModLanguageProvider; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import fuzs.puzzleslib.neoforge.api.data.v2.core.DataProviderHelper; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.fml.common.Mod; + +@Mod(value = ArmorStatues.MOD_ID, dist = Dist.CLIENT) +public class ArmorStatuesNeoForgeClient { + + public ArmorStatuesNeoForgeClient() { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + DataProviderHelper.registerDataProviders(ArmorStatues.MOD_ID, ModLanguageProvider::new); + } +} diff --git a/1.21.3/NeoForge/src/main/resources/META-INF/neoforge.mods.toml b/1.21.3/NeoForge/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..432ab6c --- /dev/null +++ b/1.21.3/NeoForge/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,57 @@ +modLoader = "javafml" +loaderVersion = "*" +license = "${modLicense}" +issueTrackerURL = "${modIssueUrl}" + +[[mods]] +modId = "${modId}" +displayName = "${modName}" +description = "${modDescription}" +version = "${modVersion}" +authors = "${modAuthor}" +logoFile = "mod_banner.png" +logoBlur = false +displayURL = "${modPageUrl}" +updateJSONURL = "${modUpdateUrl}" +displayTest = "${modForgeDisplayTest}" + +[[mixins]] +config="${modId}.common.mixins.json" + +[[mixins]] +config="${modId}.neoforge.mixins.json" + +[[dependencies.${ modId }]] +modId = "neoforge" +mandatory = true +type = "required" +versionRange = "[${minNeoForgeVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "minecraft" +mandatory = true +type = "required" +versionRange = "[${minecraftVersion}]" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "puzzleslib" +mandatory = true +type = "required" +versionRange = "[${minPuzzlesVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "statuemenus" +mandatory = true +type = "required" +versionRange = "*" +ordering = "NONE" +side = "BOTH" + +[modproperties.${ modId }] +catalogueImageIcon = "mod_logo.png" diff --git a/1.21.3/NeoForge/src/main/resources/neoforge.mixins.json b/1.21.3/NeoForge/src/main/resources/neoforge.mixins.json new file mode 100644 index 0000000..1fb3919 --- /dev/null +++ b/1.21.3/NeoForge/src/main/resources/neoforge.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.neoforge.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.21.3/build.gradle b/1.21.3/build.gradle new file mode 100644 index 0000000..dea2bca --- /dev/null +++ b/1.21.3/build.gradle @@ -0,0 +1,9 @@ +plugins { + alias libs.plugins.architecturyloom apply false + alias libs.plugins.architecturyplugin apply false + alias libs.plugins.shadow apply false + alias libs.plugins.cursegradle apply false + alias libs.plugins.minotaur apply false +} + +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/main.gradle" diff --git a/1.21.3/gradle.properties b/1.21.3/gradle.properties new file mode 100755 index 0000000..10b6068 --- /dev/null +++ b/1.21.3/gradle.properties @@ -0,0 +1,45 @@ +org.gradle.jvmargs=-Xmx4G +org.gradle.daemon=false +copyBuildJar=true + +# Mod Attributes +modId=armorstatues +modName=Armor Statues +modVersion=21.3.2 +modAuthor=Fuzs +modDescription=Unlock the full potential of armor stands! Works on vanilla servers, too. +modLicense=MPL-2.0 +modSourceUrl=https://github.com/Fuzss/armorstatues +modIssueUrl=https://github.com/Fuzss/armorstatues/issues +modUpdateUrl=https://raw.githubusercontent.com/Fuzss/modresources/main/update/armorstatues.json +modMavenGroup=fuzs.armorstatues +# "MATCH_VERSION" for a mod required on both sides, "IGNORE_SERVER_VERSION" for a server only mod, "IGNORE_ALL_VERSION" for a client only mod +modForgeDisplayTest=IGNORE_ALL_VERSION +# "*" for a mod loaded on both sides, "server" for a server only mod, "client" for a client only mod +modFabricEnvironment=* + +# Version Catalog +dependenciesVersionCatalog=1.21.3-v16 +#dependenciesPuzzlesLibVersion=21.3.13 +#dependenciesMinPuzzlesLibVersion=21.3.13 + +# Mod Publishing +projectReleaseType=release +projectCurseForgeId=682566 +projectModrinthId=bbGCtEvb + +# Required Dependencies +dependenciesRequiredFabricCurseForge=fabric-api, forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredNeoForgeCurseForge=puzzles-lib +dependenciesRequiredForgeCurseForge=forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredFabricModrinth=fabric-api, forge-config-api-port, puzzles-lib +dependenciesRequiredNeoForgeModrinth=puzzles-lib +dependenciesRequiredForgeModrinth=forge-config-api-port, puzzles-lib + +# Optional Dependencies +dependenciesOptionalFabricCurseForge=config-menus-forge +dependenciesOptionalNeoForgeCurseForge=config-menus-forge +dependenciesOptionalForgeCurseForge=config-menus-forge +dependenciesOptionalFabricModrinth=forge-config-screens +dependenciesOptionalNeoForgeModrinth=forge-config-screens +dependenciesOptionalForgeModrinth=forge-config-screens diff --git a/1.21.3/gradle/wrapper/gradle-wrapper.jar b/1.21.3/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..943f0cb Binary files /dev/null and b/1.21.3/gradle/wrapper/gradle-wrapper.jar differ diff --git a/1.21.3/gradle/wrapper/gradle-wrapper.properties b/1.21.3/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2617362 --- /dev/null +++ b/1.21.3/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/1.21.3/gradlew b/1.21.3/gradlew new file mode 100755 index 0000000..65dcd68 --- /dev/null +++ b/1.21.3/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/1.21.3/gradlew.bat b/1.21.3/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/1.21.3/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/1.21.3/settings.gradle b/1.21.3/settings.gradle new file mode 100644 index 0000000..5cfe5eb --- /dev/null +++ b/1.21.3/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { url "https://maven.architectury.dev/" } + maven { url "https://maven.fabricmc.net/" } + maven { url "https://maven.neoforged.net/releases/" } + maven { url "https://maven.minecraftforge.net/" } + } +} + +include "Common" +include "Fabric" +include "NeoForge" +//include "Forge" + +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/v2/settings.gradle" diff --git a/1.21.4/CHANGELOG.md b/1.21.4/CHANGELOG.md new file mode 100644 index 0000000..d9b4a0a --- /dev/null +++ b/1.21.4/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [v21.4.0-1.21.4] - 2025-02-05 +- Port to Minecraft 1.21.4 diff --git a/1.21.4/Common/build.gradle b/1.21.4/Common/build.gradle new file mode 100644 index 0000000..1a985e0 --- /dev/null +++ b/1.21.4/Common/build.gradle @@ -0,0 +1,13 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/common.gradle" + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.common + + // Statue Menus + modApi libs.statuemenus.common +} + +tasks.withType(net.fabricmc.loom.task.AbstractRemapJarTask).configureEach { + targetNamespace = "named" +} diff --git a/1.21.4/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 b/1.21.4/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 new file mode 100644 index 0000000..0c41790 --- /dev/null +++ b/1.21.4/Common/src/generated/resources/.cache/190dc8cc9e260d5273a6d094a2d5cfe3ccdbd9b6 @@ -0,0 +1,2 @@ +// 1.21.4 2025-02-05T09:43:56.207924 Language (en_us) +35086f8aaffbf594a8a74147938605b772d8cea6 assets/armorstatues/lang/en_us.json diff --git a/1.21.4/Common/src/generated/resources/assets/armorstatues/lang/en_us.json b/1.21.4/Common/src/generated/resources/assets/armorstatues/lang/en_us.json new file mode 100644 index 0000000..c1907a6 --- /dev/null +++ b/1.21.4/Common/src/generated/resources/assets/armorstatues/lang/en_us.json @@ -0,0 +1,24 @@ +{ + "armorstatues.dataSync.failure": "Unable to modify armor stand data: %s", + "armorstatues.dataSync.failure.noArmorStand": "No Valid Armor Stand", + "armorstatues.dataSync.failure.noPermission": "No Permission", + "armorstatues.dataSync.failure.notFinished": "Queue Not Empty", + "armorstatues.dataSync.failure.outOfRange": "Out Of Range", + "armorstatues.dataSync.finished": "Finished sending queued armor stand data", + "armorstatues.screen.vanillaTweaks.checkTarget": "Check Armor Stand Target", + "armorstatues.screen.vanillaTweaks.checkTarget.description": "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, builder isn't necessarily the armor stand which opened builder menu.", + "armorstatues.screen.vanillaTweaks.lock": "Lock", + "armorstatues.screen.vanillaTweaks.lock.description": "Locking an armor stand prevents it from being changed using builder menu and disables interaction with the equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead": "Swap Mainhand & Helmet", + "armorstatues.screen.vanillaTweaks.swapMainhandAndHead.description": "Swaps items between the main hand and helmet equipment slots.", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand": "Swap Mainhand & Offhand", + "armorstatues.screen.vanillaTweaks.swapMainhandAndOffhand.description": "Swaps items between the main hand and off hand equipment slots.", + "armorstatues.screen.vanillaTweaks.toolRack": "Align Tool As Tool Rack", + "armorstatues.screen.vanillaTweaks.toolRack.description": "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand.", + "armorstatues.screen.vanillaTweaks.triggerSent": "Sent!", + "armorstatues.screen.vanillaTweaks.unlock": "Unlock", + "armorstatues.screen.vanillaTweaks.unlock.description": "Unlocking an armor stand reverts any adjustments made via a previous lock action.", + "statuemenus.screen.type.alignments": "Alignments", + "statuemenus.screen.type.commandsCompatiblePosition": "Position", + "statuemenus.screen.type.vanillaTweaks": "Vanilla Tweaks" +} \ No newline at end of file diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java new file mode 100644 index 0000000..f260170 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/ArmorStatues.java @@ -0,0 +1,37 @@ +package fuzs.armorstatues; + +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzleslib.api.config.v3.ConfigHolder; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import fuzs.puzzleslib.api.core.v1.utility.ResourceLocationHelper; +import fuzs.puzzleslib.api.event.v1.core.EventPhase; +import fuzs.puzzleslib.api.event.v1.entity.player.PlayerInteractEvents; +import net.minecraft.resources.ResourceLocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ArmorStatues implements ModConstructor { + public static final String MOD_ID = "armorstatues"; + public static final String MOD_NAME = "Armor Statues"; + public static final Logger LOGGER = LoggerFactory.getLogger(MOD_NAME); + + public static final ConfigHolder CONFIG = ConfigHolder.builder(MOD_ID).client(ClientConfig.class); + + @Override + public void onConstructMod() { + ModRegistry.bootstrap(); + registerEventHandlers(); + } + + private static void registerEventHandlers() { + // high priority, so we run before other mods that add armor stand interactions + // we require empty hand + shift, so those other mods can still run their behaviors when those conditions are not met + PlayerInteractEvents.USE_ENTITY_AT.register(EventPhase.BEFORE, ArmorStandInteractHandler::onUseEntityAt); + } + + public static ResourceLocation id(String path) { + return ResourceLocationHelper.fromNamespaceAndPath(MOD_ID, path); + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java new file mode 100644 index 0000000..4413040 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/ArmorStatuesClient.java @@ -0,0 +1,54 @@ +package fuzs.armorstatues.client; + +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandAlignmentsScreen; +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.client.gui.screens.armorstand.CommandsCompatiblePositionScreen; +import fuzs.armorstatues.client.handler.ArmorStandTooltipHandler; +import fuzs.armorstatues.client.handler.ClientInteractHandler; +import fuzs.armorstatues.client.handler.DataSyncTickHandler; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import fuzs.puzzleslib.api.client.core.v1.context.MenuScreensContext; +import fuzs.puzzleslib.api.client.event.v1.ClientTickEvents; +import fuzs.puzzleslib.api.client.event.v1.entity.player.InteractionInputEvents; +import fuzs.puzzleslib.api.client.event.v1.gui.ItemTooltipCallback; +import fuzs.puzzleslib.api.client.event.v1.gui.ScreenEvents; +import fuzs.puzzleslib.api.event.v1.core.EventPhase; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreenFactory; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandMenu; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; + +public class ArmorStatuesClient implements ClientModConstructor { + + @Override + public void onConstructMod() { + registerEventHandlers(); + } + + private static void registerEventHandlers() { + ItemTooltipCallback.EVENT.register(ArmorStandTooltipHandler::onItemTooltip); + ClientTickEvents.END.register(DataSyncTickHandler::onEndClientTick); + ScreenEvents.remove(Screen.class).register(DataSyncTickHandler::onRemove); + // event phase must match PlayerInteractEvents#USE_ENTITY_AT as both are implemented using the same event on Fabric + InteractionInputEvents.USE.register(EventPhase.BEFORE, ClientInteractHandler::onUseInteraction); + } + + @Override + public void onClientSetup() { + ArmorStandScreenFactory.register(ModRegistry.POSITION_SCREEN_TYPE, CommandsCompatiblePositionScreen::new); + ArmorStandScreenFactory.register(ModRegistry.ALIGNMENTS_SCREEN_TYPE, ArmorStandAlignmentsScreen::new); + ArmorStandScreenFactory.register(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE, ArmorStandVanillaTweaksScreen::new); + } + + @SuppressWarnings("Convert2MethodRef") + @Override + public void onRegisterMenuScreens(MenuScreensContext context) { + // compiler doesn't like method reference :( + context.registerMenuScreen(ModRegistry.ARMOR_STAND_MENU_TYPE.value(), + (ArmorStandMenu menu, Inventory inventory, Component component) -> { + return ArmorStandScreenFactory.createLastScreenType(menu, inventory, component); + }); + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java new file mode 100644 index 0000000..df01829 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java @@ -0,0 +1,54 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandButtonsScreen; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandPositionScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandAlignment; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.phys.Vec3; + +import java.util.EnumSet; +import java.util.List; + +public class ArmorStandAlignmentsScreen extends ArmorStandButtonsScreen { + + public ArmorStandAlignmentsScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new DoubleButtonWidget(Component.translatable(ArmorStandPositionScreen.CENTERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CENTERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.CORNERED_DESCRIPTION_TRANSLATION_KEY), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)).add(0.5, 0.0, 0.5); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + }, button -> { + Vec3 newPosition = this.holder.getArmorStand().position().align(EnumSet.allOf(Direction.Axis.class)); + this.dataSyncHandler.sendPosition(newPosition.x(), newPosition.y(), newPosition.z()); + })); + for (ArmorStandAlignment alignment : ArmorStandAlignment.values()) { + widgets.add(new SingleButtonWidget(Component.translatable(alignment.getTranslationKey()), Component.translatable(alignment.getDescriptionsKey()), Component.translatable(ArmorStandPositionScreen.ALIGNED_TRANSLATION_KEY), button -> { + ArmorStandAlignmentsScreen.this.dataSyncHandler.sendAlignment(alignment); + })); + } + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.ALIGNMENTS_SCREEN_TYPE; + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java new file mode 100644 index 0000000..f52054d --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/ArmorStandVanillaTweaksScreen.java @@ -0,0 +1,75 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import com.google.common.collect.Lists; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandButtonsScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; + +import java.util.List; + +public class ArmorStandVanillaTweaksScreen extends ArmorStandButtonsScreen { + public static final String TRIGGER_SENT_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.triggerSent"; + public static final String CHECK_TARGET_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget"; + public static final String CHECK_TARGET_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.checkTarget.description"; + public static final String LOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock"; + public static final String LOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.lock.description"; + public static final String UNLOCK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock"; + public static final String UNLOCK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.unlock.description"; + public static final String TOOL_RACK_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack"; + public static final String TOOL_RACK_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.toolRack.description"; + public static final String SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand"; + public static final String SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndOffhand.description"; + public static final String SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead"; + public static final String SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY = ArmorStatues.MOD_ID + ".screen.vanillaTweaks.swapMainhandAndHead.description"; + + public ArmorStandVanillaTweaksScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + public VanillaTweaksDataSyncHandler getDataSyncHandler() { + return (VanillaTweaksDataSyncHandler) super.getDataSyncHandler(); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + List widgets = Lists.newArrayList(); + widgets.add(new SingleButtonWidget(Component.translatable(CHECK_TARGET_TRANSLATION_KEY), Component.translatable(CHECK_TARGET_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.CHECK_TARGET); + this.onClose(); + })); + widgets.add(new DoubleButtonWidget(Component.translatable(LOCK_TRANSLATION_KEY), Component.translatable(UNLOCK_TRANSLATION_KEY), Component.translatable(LOCK_DESCRIPTION_KEY), Component.translatable(UNLOCK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_LOCK); + }, button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.UTILITIES_UNLOCK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(TOOL_RACK_TRANSLATION_KEY), Component.translatable(TOOL_RACK_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.AUTO_ALIGNMENT_TOOL_RACK); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_OFFHAND); + })); + widgets.add(new SingleButtonWidget(Component.translatable(SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY), Component.translatable(SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY), Component.translatable(TRIGGER_SENT_TRANSLATION_KEY), button -> { + this.getDataSyncHandler().sendSingleTriggerValue(VanillaTweaksDataSyncHandler.SWAP_SLOTS_MAINHAND_AND_HEAD); + })); + return widgets; + } + + @Override + protected void init() { + super.init(); + this.addVanillaTweaksCreditsButton(); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE; + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/CommandsCompatiblePositionScreen.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/CommandsCompatiblePositionScreen.java new file mode 100644 index 0000000..5c68164 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/gui/screens/armorstand/CommandsCompatiblePositionScreen.java @@ -0,0 +1,80 @@ +package fuzs.armorstatues.client.gui.screens.armorstand; + +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandPositionScreen; +import fuzs.statuemenus.api.v1.helper.ScaleAttributeHelper; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Consumer; +import java.util.function.DoubleSupplier; + +public class CommandsCompatiblePositionScreen extends ArmorStandPositionScreen { + protected static final ArmorStandWidgetFactory SCALE_WIDGET_FACTORY = (CommandsCompatiblePositionScreen screen, ArmorStand armorStand) -> { + return screen.new CommandsScaleWidget(Component.translatable(SCALE_TRANSLATION_KEY), + armorStand::getScale, + screen.dataSyncHandler::sendScale); + }; + + public CommandsCompatiblePositionScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { + super(holder, inventory, component, dataSyncHandler); + } + + @Override + protected List buildWidgets(ArmorStand armorStand) { + // only move server-side to prevent rubber banding + return buildWidgets(this, + armorStand, + List.of(SCALE_WIDGET_FACTORY, + ROTATION_WIDGET_FACTORY, + POSITION_INCREMENT_WIDGET_FACTORY, + POSITION_X_WIDGET_FACTORY, + POSITION_Y_WIDGET_FACTORY, + POSITION_Z_WIDGET_FACTORY)); + } + + @Override + public ArmorStandScreenType getScreenType() { + return ModRegistry.POSITION_SCREEN_TYPE; + } + + protected class CommandsScaleWidget extends ScaleWidget { + @Nullable + private Boolean hasModifier; + + public CommandsScaleWidget(Component title, DoubleSupplier currentValue, Consumer newValue) { + super(title, currentValue, newValue); + } + + @Override + protected void setNewValue(double newValue) { + if (CommandsCompatiblePositionScreen.this.dataSyncHandler instanceof CommandDataSyncHandler commandDataSyncHandler) { + commandDataSyncHandler.sendScale(this.hasModifier == null || this.hasModifier, + getScaledValue(newValue), + true); + } else { + super.setNewValue(newValue); + } + this.hasModifier = null; + } + + @Override + protected void applyClientValue(double newValue) { + if (this.hasModifier == null) { + this.hasModifier = CommandsCompatiblePositionScreen.this.getHolder() + .getArmorStand() + .getAttributes() + .hasModifier(Attributes.SCALE, ScaleAttributeHelper.SCALE_BONUS_ID); + } + super.applyClientValue(newValue); + } + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java new file mode 100644 index 0000000..a7c16e7 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/handler/ArmorStandTooltipHandler.java @@ -0,0 +1,27 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.puzzleslib.api.core.v1.Proxy; +import fuzs.statuemenus.api.v1.helper.ArmorStandInteractHelper; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.TooltipFlag; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ArmorStandTooltipHandler { + + public static void onItemTooltip(ItemStack itemStack, List lines, Item.TooltipContext tooltipContext, @Nullable Player player, TooltipFlag tooltipFlag) { + if (itemStack.is(Items.ARMOR_STAND)) { + List components = Proxy.INSTANCE.splitTooltipLines(ArmorStandInteractHelper.getArmorStandHoverText()); + if (tooltipFlag.isAdvanced()) { + lines.addAll(lines.size() - (!itemStack.getComponents().isEmpty() ? 2 : 1), components); + } else { + lines.addAll(components); + } + } + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java new file mode 100644 index 0000000..22b26d2 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/handler/ClientInteractHandler.java @@ -0,0 +1,39 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.armorstatues.handler.ArmorStandInteractHandler; +import fuzs.puzzleslib.api.event.v1.core.EventResult; +import fuzs.puzzleslib.api.event.v1.core.EventResultHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; + +public class ClientInteractHandler { + + public static EventResult onUseInteraction(Minecraft minecraft, LocalPlayer player, InteractionHand interactionHand, HitResult hitResult) { + + if (hitResult.getType() == HitResult.Type.ENTITY) { + + Entity entity = ((EntityHitResult) hitResult).getEntity(); + Vec3 hitVector = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ()); + EventResultHolder result = ArmorStandInteractHandler.onUseEntityAt(minecraft.player, + minecraft.level, + interactionHand, + entity, + hitVector + ); + + // if InteractionResult.FAIL is returned the mod is missing server-side, and we open the menu client-side without sending a packet to the server, so the server does not try to interact + if (result.filter(interactionResult -> interactionResult == InteractionResult.FAIL).isInterrupt()) { + + return EventResult.INTERRUPT; + } + } + + return EventResult.PASS; + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java new file mode 100644 index 0000000..822390a --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/client/handler/DataSyncTickHandler.java @@ -0,0 +1,28 @@ +package fuzs.armorstatues.client.handler; + +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreen; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import org.jetbrains.annotations.Nullable; + +public class DataSyncTickHandler { + @Nullable + private static DataSyncHandler dataSyncHandler; + + public static void onRemove(Screen screen) { + if (screen instanceof ArmorStandScreen armorStandScreen && armorStandScreen.getDataSyncHandler().shouldContinueTicking()) { + dataSyncHandler = armorStandScreen.getDataSyncHandler(); + } + } + + public static void onEndClientTick(Minecraft minecraft) { + if (minecraft.player != null && !(minecraft.screen instanceof ArmorStandScreen) && dataSyncHandler != null) { + if (dataSyncHandler.shouldContinueTicking()) { + dataSyncHandler.tick(); + } else { + dataSyncHandler = null; + } + } + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java new file mode 100644 index 0000000..137797d --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/config/ClientConfig.java @@ -0,0 +1,15 @@ +package fuzs.armorstatues.config; + +import fuzs.puzzleslib.api.config.v3.Config; +import fuzs.puzzleslib.api.config.v3.ConfigCore; +import fuzs.statuemenus.api.v1.client.gui.screens.AbstractArmorStandScreen; + +public class ClientConfig implements ConfigCore { + @Config(description = {"Allows for using this mod on a server without it (like a vanilla server) when the Vanilla Tweaks Armor Statues data pack is installed without the need for being a server operator.", "Download the Vanilla Tweaks Armor Statues data pack from here: " + AbstractArmorStandScreen.VANILLA_TWEAKS_HOMEPAGE}) + public boolean useVanillaTweaksTriggers = false; + @Config(description = "Do not check if the client has the necessary permission level for executing the '/data' command when trying to edit an armor stand. Useful when the current server is using custom permissions handling.") + public boolean overrideClientPermissionsCheck = false; + @Config(description = "The delay in ticks for sending queued client commands for editing armor stands to the server. Increase this values if commands are sent too quickly and the server fails to process all of them.") + @Config.IntRange(min = 20) + public int clientCommandDelay = 20; +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java new file mode 100644 index 0000000..3a6418f --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/data/client/ModLanguageProvider.java @@ -0,0 +1,46 @@ +package fuzs.armorstatues.data.client; + +import fuzs.armorstatues.client.gui.screens.armorstand.ArmorStandVanillaTweaksScreen; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.puzzleslib.api.client.data.v2.AbstractLanguageProvider; +import fuzs.puzzleslib.api.data.v2.core.DataProviderContext; + +public class ModLanguageProvider extends AbstractLanguageProvider { + + public ModLanguageProvider(DataProviderContext context) { + super(context); + } + + @Override + public void addTranslations(TranslationBuilder builder) { + builder.add(CommandDataSyncHandler.FAILURE_TRANSLATION_KEY, "Unable to modify armor stand data: %s"); + builder.add(CommandDataSyncHandler.NO_PERMISSION_TRANSLATION_KEY, "No Permission"); + builder.add(CommandDataSyncHandler.NO_ARMOR_STAND_TRANSLATION_KEY, "No Valid Armor Stand"); + builder.add(CommandDataSyncHandler.OUT_OF_RANGE_TRANSLATION_KEY, "Out Of Range"); + builder.add(CommandDataSyncHandler.NOT_FINISHED_TRANSLATION_KEY, "Queue Not Empty"); + builder.add(CommandDataSyncHandler.FINISHED_TRANSLATION_KEY, "Finished sending queued armor stand data"); + builder.add(ModRegistry.POSITION_SCREEN_TYPE.getTranslationKey(), "Position"); + builder.add(ModRegistry.ALIGNMENTS_SCREEN_TYPE.getTranslationKey(), "Alignments"); + builder.add(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE.getTranslationKey(), "Vanilla Tweaks"); + builder.add(ArmorStandVanillaTweaksScreen.TRIGGER_SENT_TRANSLATION_KEY, "Sent!"); + builder.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_TRANSLATION_KEY, "Check Armor Stand Target"); + builder.add(ArmorStandVanillaTweaksScreen.CHECK_TARGET_DESCRIPTION_KEY, + "Highlights the closest armor stand within three blocks of the player which will be adjusted. Due to how data packs work, builder isn't necessarily the armor stand which opened builder menu."); + builder.add(ArmorStandVanillaTweaksScreen.LOCK_TRANSLATION_KEY, "Lock"); + builder.add(ArmorStandVanillaTweaksScreen.LOCK_DESCRIPTION_KEY, + "Locking an armor stand prevents it from being changed using builder menu and disables interaction with the equipment slots."); + builder.add(ArmorStandVanillaTweaksScreen.UNLOCK_TRANSLATION_KEY, "Unlock"); + builder.add(ArmorStandVanillaTweaksScreen.UNLOCK_DESCRIPTION_KEY, + "Unlocking an armor stand reverts any adjustments made via a previous lock action."); + builder.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_TRANSLATION_KEY, "Align Tool As Tool Rack"); + builder.add(ArmorStandVanillaTweaksScreen.TOOL_RACK_DESCRIPTION_KEY, + "Align an armor stand with a tripwire hook on the wall above it so that a tool held by it appears to be hanging up. Also locks the armor stand and disables all slots except the mainhand."); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_TRANSLATION_KEY, "Swap Mainhand & Offhand"); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_OFFHAND_DESCRIPTION_KEY, + "Swaps items between the main hand and off hand equipment slots."); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_TRANSLATION_KEY, "Swap Mainhand & Helmet"); + builder.add(ArmorStandVanillaTweaksScreen.SWAP_MAINHAND_AND_HEAD_DESCRIPTION_KEY, + "Swaps items between the main hand and helmet equipment slots."); + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java new file mode 100644 index 0000000..2dd8af0 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/handler/ArmorStandInteractHandler.java @@ -0,0 +1,43 @@ +package fuzs.armorstatues.handler; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.proxy.Proxy; +import fuzs.puzzleslib.api.core.v1.ModLoaderEnvironment; +import fuzs.puzzleslib.api.event.v1.core.EventResultHolder; +import fuzs.statuemenus.api.v1.helper.ArmorStandInteractHelper; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; + +public class ArmorStandInteractHandler { + + public static EventResultHolder onUseEntityAt(Player player, Level level, InteractionHand interactionHand, Entity target, Vec3 hitVector) { + + if (player.getAbilities().mayBuild && target.getType() == EntityType.ARMOR_STAND) { + + boolean clientsideOnly = level.isClientSide && !ModLoaderEnvironment.INSTANCE.isModPresentServerside(ArmorStatues.MOD_ID); + // the menu won't exist in the registry if the mod is missing serverside since Forge syncs registries to clients + MenuType menuType = clientsideOnly ? null : ModRegistry.ARMOR_STAND_MENU_TYPE.value(); + EventResultHolder result = ArmorStandInteractHelper.tryOpenArmorStatueMenu(player, level, interactionHand, (ArmorStand) target, menuType, ModRegistry.ARMOR_STAND_DATA_PROVIDER); + if (result.isInterrupt() && clientsideOnly) { + + Proxy.INSTANCE.openArmorStandScreen((ArmorStand) target, player); + // required so no packet is sent to server when only installed client-side, so the server doesn't change any equipment when we only want to open the screen + // returning InteractionResult.FAIL will miss out on the player arm swing animation, which we manually play here + player.swing(interactionHand); + return EventResultHolder.interrupt(InteractionResult.FAIL); + } + + return result; + } + + return EventResultHolder.pass(); + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java new file mode 100644 index 0000000..df8161e --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java @@ -0,0 +1,50 @@ +package fuzs.armorstatues.init; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.init.v3.registry.RegistryManager; +import fuzs.statuemenus.api.v1.world.entity.decoration.ArmorStandDataProvider; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandMenu; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import net.minecraft.core.Holder; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class ModRegistry { + static final RegistryManager REGISTRIES = RegistryManager.from(ArmorStatues.MOD_ID); + public static final Holder.Reference> ARMOR_STAND_MENU_TYPE = REGISTRIES.registerExtendedMenuType( + "armor_stand", + () -> (containerId, inventory, data) -> { + return ArmorStandMenu.create(ModRegistry.ARMOR_STAND_MENU_TYPE.value(), + containerId, + inventory, + data, + ModRegistry.ARMOR_STAND_DATA_PROVIDER); + }); + + public static final ArmorStandScreenType POSITION_SCREEN_TYPE = new ArmorStandScreenType( + "commandsCompatiblePosition", + new ItemStack(Items.GRASS_BLOCK)); + public static final ArmorStandScreenType ALIGNMENTS_SCREEN_TYPE = new ArmorStandScreenType("alignments", + new ItemStack(Items.DIAMOND_PICKAXE)); + public static final ArmorStandScreenType VANILLA_TWEAKS_SCREEN_TYPE = new ArmorStandScreenType("vanillaTweaks", + new ItemStack(Items.WRITTEN_BOOK)); + public static final ArmorStandDataProvider ARMOR_STAND_DATA_PROVIDER = new ArmorStandDataProvider() { + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return new ArmorStandScreenType[]{ + ArmorStandScreenType.ROTATIONS, + ArmorStandScreenType.POSES, + ArmorStandScreenType.STYLE, + POSITION_SCREEN_TYPE, + ALIGNMENTS_SCREEN_TYPE, + ArmorStandScreenType.EQUIPMENT + }; + } + }; + + public static void bootstrap() { + // NO-OP + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java new file mode 100644 index 0000000..29bc3b3 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/network/client/data/CommandDataSyncHandler.java @@ -0,0 +1,242 @@ +package fuzs.armorstatues.network.client.data; + +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.statuemenus.api.v1.helper.ScaleAttributeHelper; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandAlignment; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandPose; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandScreenType; +import fuzs.statuemenus.api.v1.world.inventory.data.ArmorStandStyleOption; +import net.minecraft.ChatFormatting; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.FloatTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.BiPredicate; + +public class CommandDataSyncHandler implements DataSyncHandler { + public static final String NO_PERMISSION_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noPermission"; + public static final String NO_ARMOR_STAND_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.noArmorStand"; + public static final String OUT_OF_RANGE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.outOfRange"; + public static final String NOT_FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure.notFinished"; + public static final String FINISHED_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.finished"; + public static final String FAILURE_TRANSLATION_KEY = ArmorStatues.MOD_ID + ".dataSync.failure"; + private static final Queue> CLIENT_COMMAND_QUEUE = new ArrayDeque<>(); + + @Nullable + private static ArmorStand queueArmorStand; + private static int itemDequeuedTicks; + + private final ArmorStandHolder holder; + protected final LocalPlayer player; + protected ArmorStandPose lastSyncedPose; + + public CommandDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + this.holder = holder; + this.lastSyncedPose = ArmorStandPose.fromEntity(this.holder.getArmorStand()); + this.player = player; + } + + @Override + public ArmorStandHolder getArmorStandHolder() { + return this.holder; + } + + @Override + public void sendName(String name) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.setCustomArmorStandName(this.getArmorStand(), name); + CompoundTag tag = new CompoundTag(); + tag.putString("CustomName", Component.Serializer.toJson(Component.literal(name), this.player.registryAccess())); + this.enqueueEntityData(tag); + this.finalizeCurrentOperation(); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + // split this into multiple chat messages as the client chat field has a very low character limit + this.sendPosePart(pose::serializeBodyPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeArmPoses, this.lastSyncedPose); + this.sendPosePart(pose::serializeLegPoses, this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + if (finalize) this.finalizeCurrentOperation(); + } + + private void sendPosePart(BiPredicate dataWriter, ArmorStandPose lastSyncedPose) { + CompoundTag tag = new CompoundTag(); + if (dataWriter.test(tag, lastSyncedPose)) { + CompoundTag tagToSend = new CompoundTag(); + tagToSend.put("Pose", tag); + this.enqueueEntityData(tagToSend); + } + } + + @Override + public @Nullable ArmorStandPose getLastSyncedPose() { + return this.lastSyncedPose; + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(DoubleTag.valueOf(posX)); + listTag.add(DoubleTag.valueOf(posY)); + listTag.add(DoubleTag.valueOf(posZ)); + CompoundTag tag = new CompoundTag(); + tag.put("Pos", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendScale(float scale, boolean finalize) { + this.sendScale(true, scale, finalize); + } + + public void sendScale(boolean hasModifier, float scale, boolean finalize) { + // track if our scale attribute modifier is already present, so we only try to remove it if necessary + // otherwise an error message from the remove command is displayed which would also be fine if this no longer works + List clientCommands = new ArrayList<>(); + if (hasModifier) { + clientCommands.add("attribute %s %s modifier remove %s".formatted(this.getArmorStand().getStringUUID(), + Attributes.SCALE.unwrapKey().orElseThrow().location(), + ScaleAttributeHelper.SCALE_BONUS_ID)); + } + if (scale != ScaleAttributeHelper.DEFAULT_SCALE) { + clientCommands.add("attribute %s %s modifier add %s %s add_value".formatted(this.getArmorStand() + .getStringUUID(), + Attributes.SCALE.unwrapKey().orElseThrow().location(), + ScaleAttributeHelper.SCALE_BONUS_ID, + scale - ScaleAttributeHelper.DEFAULT_SCALE)); + } + this.enqueueClientCommand(clientCommands); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + ListTag listTag = new ListTag(); + listTag.add(FloatTag.valueOf(rotation)); + CompoundTag tag = new CompoundTag(); + tag.put("Rotation", listTag); + this.enqueueEntityData(tag); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + CompoundTag tag = new CompoundTag(); + styleOption.toTag(tag, value); + this.enqueueEntityData(tag); + styleOption.setOption(this.getArmorStand(), value); + if (finalize) this.finalizeCurrentOperation(); + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + DataSyncHandler.super.sendAlignment(alignment); + } + + @Override + public boolean supportsScreenType(ArmorStandScreenType screenType) { + return !screenType.requiresServer(); + } + + @Override + public void tick() { + if (itemDequeuedTicks > 0) itemDequeuedTicks--; + if (itemDequeuedTicks == 0 && queueArmorStand != null && !CLIENT_COMMAND_QUEUE.isEmpty()) { + if (this.testArmorStand(queueArmorStand).right().isPresent()) { + for (String clientCommand : CLIENT_COMMAND_QUEUE.poll()) { + this.player.connection.sendCommand(clientCommand); + } + } else { + CLIENT_COMMAND_QUEUE.clear(); + } + itemDequeuedTicks = this.getDequeueDelayTicks(); + } else if (itemDequeuedTicks == 1 && CLIENT_COMMAND_QUEUE.isEmpty()) { + this.sendDisplayMessage(Component.translatable(FINISHED_TRANSLATION_KEY), false); + } + } + + protected int getDequeueDelayTicks() { + return 5; + } + + @Override + public boolean shouldContinueTicking() { + return !CLIENT_COMMAND_QUEUE.isEmpty() || itemDequeuedTicks != 0; + } + + protected boolean isEditingAllowed() { + return this.isEditingAllowed(!ArmorStatues.CONFIG.get(ClientConfig.class).overrideClientPermissionsCheck); + } + + protected final boolean isEditingAllowed(boolean testPermissionLevel) { + if (testPermissionLevel && !this.player.hasPermissions(2)) { + this.sendFailureMessage(Component.translatable(NO_PERMISSION_TRANSLATION_KEY)); + return false; + } + return this.player.getAbilities().mayBuild && + this.testArmorStand(this.getArmorStand()).ifLeft(this::sendFailureMessage).right().isPresent(); + } + + protected Either testArmorStand(ArmorStand armorStand) { + return !armorStand.isAlive() ? Either.left(Component.translatable(NO_ARMOR_STAND_TRANSLATION_KEY)) : + Either.right(Unit.INSTANCE); + } + + protected boolean enqueueClientCommand(String clientCommand) { + return this.enqueueClientCommand(Collections.singletonList(clientCommand)); + } + + protected boolean enqueueClientCommand(List clientCommand) { + if (CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = null; + } else if (queueArmorStand != null) { + this.sendFailureMessage(Component.translatable(NOT_FINISHED_TRANSLATION_KEY)); + return false; + } + CLIENT_COMMAND_QUEUE.offer(clientCommand); + return true; + } + + @Override + public void finalizeCurrentOperation() { + if (!CLIENT_COMMAND_QUEUE.isEmpty()) { + queueArmorStand = this.getArmorStand(); + } + } + + protected void sendFailureMessage(Component component) { + this.sendDisplayMessage(Component.translatable(FAILURE_TRANSLATION_KEY, component), true); + } + + protected void sendDisplayMessage(Component component, boolean failure) { + this.player.displayClientMessage(Component.empty() + .append(component) + .withStyle(failure ? ChatFormatting.RED : ChatFormatting.GREEN), false); + } + + private void enqueueEntityData(CompoundTag tag) { + this.enqueueClientCommand("data merge entity %s %s".formatted(this.getArmorStand().getStringUUID(), + tag.getAsString())); + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java new file mode 100644 index 0000000..dbdc639 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/network/client/data/VanillaTweaksDataSyncHandler.java @@ -0,0 +1,359 @@ +package fuzs.armorstatues.network.client.data; + +import com.google.common.collect.ImmutableSortedMap; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Unit; +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import fuzs.statuemenus.api.v1.world.inventory.data.*; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.Rotations; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.NavigableMap; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +public class VanillaTweaksDataSyncHandler extends CommandDataSyncHandler { + private static final int MAX_INCREMENTAL_OPERATIONS = 12; + public static final int CHECK_TARGET = 999; + public static final int SWAP_SLOTS_MAINHAND_AND_OFFHAND = 161; + public static final int SWAP_SLOTS_MAINHAND_AND_HEAD = 162; + public static final int MIRROR_ARMS_LEFT_TO_RIGHT = 131; + public static final int MIRROR_ARMS_RIGHT_TO_LEFT = 132; + public static final int MIRROR_LEGS_LEFT_TO_RIGHT = 133; + public static final int MIRROR_LEGS_RIGHT_TO_LEFT = 134; + public static final int UTILITIES_LOCK = 1000; + public static final int UTILITIES_UNLOCK = 1001; + public static final int MIRROR_AND_FLIP_FLIP = 135; + public static final int SHOW_BASE_PLATE_YES = 1; + public static final int SHOW_BASE_PLATE_NO = 2; + public static final int SHOW_ARMS_YES = 3; + public static final int SHOW_ARMS_NO = 4; + public static final int SMALL_STAND_YES = 5; + public static final int SMALL_STAND_NO = 6; + public static final int APPLY_GRAVITY_YES = 7; + public static final int APPLY_GRAVITY_NO = 8; + public static final int STAND_VISIBLE_YES = 9; + public static final int STAND_VISIBLE_NO = 10; + public static final int DISPLAY_NAME_YES = 11; + public static final int DISPLAY_NAME_NO = 12; + public static final int NUDGE_POSITION_X8_NEGATIVE = 40; + public static final int NUDGE_POSITION_X3_NEGATIVE = 101; + public static final int NUDGE_POSITION_X1_NEGATIVE = 102; + public static final int NUDGE_POSITION_X1_POSITIVE = 103; + public static final int NUDGE_POSITION_X3_POSITIVE = 104; + public static final int NUDGE_POSITION_X8_POSITIVE = 43; + public static final int NUDGE_POSITION_Y8_NEGATIVE = 44; + public static final int NUDGE_POSITION_Y3_NEGATIVE = 105; + public static final int NUDGE_POSITION_Y1_NEGATIVE = 106; + public static final int NUDGE_POSITION_Y1_POSITIVE = 107; + public static final int NUDGE_POSITION_Y3_POSITIVE = 108; + public static final int NUDGE_POSITION_Y8_POSITIVE = 47; + public static final int NUDGE_POSITION_Z8_NEGATIVE = 48; + public static final int NUDGE_POSITION_Z3_NEGATIVE = 109; + public static final int NUDGE_POSITION_Z1_NEGATIVE = 110; + public static final int NUDGE_POSITION_Z1_POSITIVE = 111; + public static final int NUDGE_POSITION_Z3_POSITIVE = 112; + public static final int NUDGE_POSITION_Z8_POSITIVE = 51; + public static final int ADJUST_ROTATION_ANGLE_STEP_45 = 120; + public static final int ADJUST_ROTATION_ANGLE_STEP_15 = 121; + public static final int ADJUST_ROTATION_ANGLE_STEP_5 = 122; + public static final int ADJUST_ROTATION_ANGLE_STEP_1 = 123; + public static final int ADJUST_ROTATION_ROTATE_RIGHT = 56; + public static final int ADJUST_ROTATION_ROTATE_LEFT = 57; + public static final int POSE_PRESETS_ATTENTION = 20; + public static final int POSE_PRESETS_WALKING = 21; + public static final int POSE_PRESETS_RUNNING = 22; + public static final int POSE_PRESETS_POINTING = 23; + public static final int POSE_PRESETS_BLOCKING = 24; + public static final int POSE_PRESETS_LUNGEING = 25; + public static final int POSE_PRESETS_WINNING = 26; + public static final int POSE_PRESETS_SITTING = 27; + public static final int POSE_PRESETS_ARABESQUE = 28; + public static final int POSE_PRESETS_CUPID = 29; + public static final int POSE_PRESETS_CONFIDENT = 30; + public static final int POSE_PRESETS_SALUTE = 31; + public static final int POSE_PRESETS_DEATH = 32; + public static final int POSE_PRESETS_FACEPALM = 33; + public static final int POSE_PRESETS_LAZING = 34; + public static final int POSE_PRESETS_CONFUSED = 35; + public static final int POSE_PRESETS_FORMAL = 36; + public static final int POSE_PRESETS_SAD = 37; + public static final int POSE_PRESETS_JOYOUS = 38; + public static final int POSE_PRESETS_STARGAZING = 39; + public static final int AUTO_ALIGNMENT_BLOCK_ON_SURFACE = 151; + public static final int AUTO_ALIGNMENT_ITEM_ON_SURFACE = 152; + public static final int AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE = 153; + public static final int AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE = 154; + public static final int AUTO_ALIGNMENT_TOOL_RACK = 155; + public static final int POSE_ADJUSTMENT_HEAD_X_NEGATIVE = 60; + public static final int POSE_ADJUSTMENT_HEAD_X_POSITIVE = 61; + public static final int POSE_ADJUSTMENT_HEAD_Y_NEGATIVE = 62; + public static final int POSE_ADJUSTMENT_HEAD_Y_POSITIVE = 63; + public static final int POSE_ADJUSTMENT_HEAD_Z_NEGATIVE = 64; + public static final int POSE_ADJUSTMENT_HEAD_Z_POSITIVE = 65; + public static final int POSE_ADJUSTMENT_BODY_X_NEGATIVE = 67; + public static final int POSE_ADJUSTMENT_BODY_X_POSITIVE = 66; + public static final int POSE_ADJUSTMENT_BODY_Y_NEGATIVE = 68; + public static final int POSE_ADJUSTMENT_BODY_Y_POSITIVE = 69; + public static final int POSE_ADJUSTMENT_BODY_Z_NEGATIVE = 70; + public static final int POSE_ADJUSTMENT_BODY_Z_POSITIVE = 71; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE = 72; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE = 73; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE = 74; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE = 75; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE = 77; + public static final int POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE = 76; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE = 78; + public static final int POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE = 79; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE = 81; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE = 80; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE = 82; + public static final int POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE = 83; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE = 84; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE = 85; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE = 87; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE = 86; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE = 89; + public static final int POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE = 88; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE = 90; + public static final int POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE = 91; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE = 92; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE = 93; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE = 94; + public static final int POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE = 95; + private static final int[] POSE_ADJUSTMENT_HEAD = new int[]{POSE_ADJUSTMENT_HEAD_X_NEGATIVE, POSE_ADJUSTMENT_HEAD_X_POSITIVE, POSE_ADJUSTMENT_HEAD_Y_NEGATIVE, POSE_ADJUSTMENT_HEAD_Y_POSITIVE, POSE_ADJUSTMENT_HEAD_Z_NEGATIVE, POSE_ADJUSTMENT_HEAD_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_BODY = new int[]{POSE_ADJUSTMENT_BODY_X_POSITIVE, POSE_ADJUSTMENT_BODY_X_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_NEGATIVE, POSE_ADJUSTMENT_BODY_Y_POSITIVE, POSE_ADJUSTMENT_BODY_Z_NEGATIVE, POSE_ADJUSTMENT_BODY_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_ARM = new int[]{POSE_ADJUSTMENT_RIGHT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_ARM_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_ARM = new int[]{POSE_ADJUSTMENT_LEFT_ARM_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_X_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_ARM_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_ARM_Z_POSITIVE}; + private static final int[] POSE_ADJUSTMENT_RIGHT_LEG = new int[]{POSE_ADJUSTMENT_RIGHT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_X_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_POSITIVE, POSE_ADJUSTMENT_RIGHT_LEG_Z_NEGATIVE}; + private static final int[] POSE_ADJUSTMENT_LEFT_LEG = new int[]{POSE_ADJUSTMENT_LEFT_LEG_X_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_X_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Y_POSITIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_NEGATIVE, POSE_ADJUSTMENT_LEFT_LEG_Z_POSITIVE}; + private static final NavigableMap NUDGE_POSITIONS_X_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_X3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_X8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_X_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_X1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_X3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_X8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Y_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Y1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Y3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Y8_POSITIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_NEGATIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_NEGATIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_NEGATIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_NEGATIVE); + private static final NavigableMap NUDGE_POSITIONS_Z_POSITIVE = ImmutableSortedMap.of(1.0 / 16.0, NUDGE_POSITION_Z1_POSITIVE, 3.0 / 16.0, NUDGE_POSITION_Z3_POSITIVE, 8.0 / 16.0, NUDGE_POSITION_Z8_POSITIVE); + private static final NavigableMap ADJUST_ROTATION_ANGLE_STEPS = ImmutableSortedMap.of(1.0F, ADJUST_ROTATION_ANGLE_STEP_1, 5.0F, ADJUST_ROTATION_ANGLE_STEP_5, 15.0F, ADJUST_ROTATION_ANGLE_STEP_15, 45.0F, ADJUST_ROTATION_ANGLE_STEP_45); + + public VanillaTweaksDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + super(holder, player); + } + + @Override + public void sendPose(ArmorStandPose pose, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue = this.getTriggerValueFromPose(pose); + if (triggerValue != -1) { + if (this.enqueueTriggerValue(triggerValue)) { + this.lastSyncedPose = pose.copyAndFillFrom(this.lastSyncedPose); + pose.applyToEntity(this.getArmorStand()); + } + } else { + this.tryApplyAllPoseParts(pose); + } + if (finalize) this.finalizeCurrentOperation(); + } + + private int getTriggerValueFromPose(ArmorStandPose pose) { + if (pose.getSourceType() == ArmorStandPose.SourceType.EMPTY) return POSE_PRESETS_ATTENTION; + if (pose.getSourceType() == ArmorStandPose.SourceType.MIRRORED) return MIRROR_AND_FLIP_FLIP; + if (pose.getSourceType() != ArmorStandPose.SourceType.VANILLA_TWEAKS) return -1; + if (pose == ArmorStandPose.WALKING) return POSE_PRESETS_WALKING; + if (pose == ArmorStandPose.RUNNING) return POSE_PRESETS_RUNNING; + if (pose == ArmorStandPose.POINTING) return POSE_PRESETS_POINTING; + if (pose == ArmorStandPose.BLOCKING) return POSE_PRESETS_BLOCKING; + if (pose == ArmorStandPose.LUNGEING) return POSE_PRESETS_LUNGEING; + if (pose == ArmorStandPose.WINNING) return POSE_PRESETS_WINNING; + if (pose == ArmorStandPose.SITTING) return POSE_PRESETS_SITTING; + if (pose == ArmorStandPose.ARABESQUE) return POSE_PRESETS_ARABESQUE; + if (pose == ArmorStandPose.CUPID) return POSE_PRESETS_CUPID; + if (pose == ArmorStandPose.CONFIDENT) return POSE_PRESETS_CONFIDENT; + if (pose == ArmorStandPose.SALUTE) return POSE_PRESETS_SALUTE; + if (pose == ArmorStandPose.DEATH) return POSE_PRESETS_DEATH; + if (pose == ArmorStandPose.FACEPALM) return POSE_PRESETS_FACEPALM; + if (pose == ArmorStandPose.LAZING) return POSE_PRESETS_LAZING; + if (pose == ArmorStandPose.CONFUSED) return POSE_PRESETS_CONFUSED; + if (pose == ArmorStandPose.FORMAL) return POSE_PRESETS_FORMAL; + if (pose == ArmorStandPose.SAD) return POSE_PRESETS_SAD; + if (pose == ArmorStandPose.JOYOUS) return POSE_PRESETS_JOYOUS; + if (pose == ArmorStandPose.STARGAZING) return POSE_PRESETS_STARGAZING; + return -1; + } + + private void tryApplyAllPoseParts(ArmorStandPose pose) { + if (!this.tryApplyPosePart(this.lastSyncedPose.getHeadPose(), pose.getNullableHeadPose(), POSE_ADJUSTMENT_HEAD, this.lastSyncedPose::withHeadPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getBodyPose(), pose.getNullableBodyPose(), POSE_ADJUSTMENT_BODY, this.lastSyncedPose::withBodyPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightArmPose(), pose.getNullableRightArmPose(), POSE_ADJUSTMENT_RIGHT_ARM, this.lastSyncedPose::withRightArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftArmPose(), pose.getNullableLeftArmPose(), POSE_ADJUSTMENT_LEFT_ARM, this.lastSyncedPose::withLeftArmPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getRightLegPose(), pose.getNullableRightLegPose(), POSE_ADJUSTMENT_RIGHT_LEG, this.lastSyncedPose::withRightLegPose)) + return; + if (!this.tryApplyPosePart(this.lastSyncedPose.getLeftLegPose(), pose.getNullableLeftLegPose(), POSE_ADJUSTMENT_LEFT_LEG, this.lastSyncedPose::withLeftLegPose)) + return; + } + + private boolean tryApplyPosePart(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment, Function function) { + if (this.tryApplyPoseAdjustment(oldPose, newPose, poseAdjustment)) { + this.lastSyncedPose = function.apply(newPose != null ? newPose : oldPose); + return true; + } else { + return false; + } + } + + private boolean tryApplyPoseAdjustment(Rotations oldPose, @Nullable Rotations newPose, int[] poseAdjustment) { + if (newPose == null || oldPose.equals(newPose)) return true; + if (!this.applyIncrementsFromSteps(oldPose.getX(), newPose.getX(), poseAdjustment[0], poseAdjustment[1])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getY(), newPose.getY(), poseAdjustment[2], poseAdjustment[3])) return false; + if (!this.applyIncrementsFromSteps(oldPose.getZ(), newPose.getZ(), poseAdjustment[4], poseAdjustment[5])) return false; + return true; + } + + @Override + public void sendPosition(double posX, double posY, double posZ, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyPositionIncrements(this.getArmorStand().getX(), posX, NUDGE_POSITIONS_X_POSITIVE, NUDGE_POSITIONS_X_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getY(), posY, NUDGE_POSITIONS_Y_POSITIVE, NUDGE_POSITIONS_Y_NEGATIVE); + this.applyPositionIncrements(this.getArmorStand().getZ(), posZ, NUDGE_POSITIONS_Z_POSITIVE, NUDGE_POSITIONS_Z_NEGATIVE); + if (finalize) this.finalizeCurrentOperation(); + } + + private void applyPositionIncrements(double oldValue, double newValue, NavigableMap positiveNudgePositions, NavigableMap negativeNudgePositions) { + double value = newValue - oldValue; + double signum = Math.signum(value); + value = Math.abs(value); + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = (signum == -1.0F ? negativeNudgePositions : positiveNudgePositions).floorEntry(value); + if (entry != null) { + value -= entry.getKey(); + if (!this.enqueueTriggerValue(entry.getValue())) { + return; + } + } else { + break; + } + } + } + + @Override + public void sendRotation(float rotation, boolean finalize) { + if (!this.isEditingAllowed()) return; + this.applyIncrementsFromSteps(this.getArmorStand().getYRot(), rotation, ADJUST_ROTATION_ROTATE_RIGHT, ADJUST_ROTATION_ROTATE_LEFT); + if (finalize) this.finalizeCurrentOperation(); + } + + private boolean applyIncrementsFromSteps(float oldValue, float newValue, int triggerValueNegative, int triggerValuePositive) { + float value = newValue - oldValue; + float signum = Math.signum(value); + value = Math.abs(value); + float lastIncrement = 0.0F; + for (int i = 0; i < MAX_INCREMENTAL_OPERATIONS; i++) { + Map.Entry entry = ADJUST_ROTATION_ANGLE_STEPS.floorEntry(value); + if (entry != null) { + float currentIncrement = entry.getKey(); + value -= currentIncrement; + if (currentIncrement != lastIncrement) { + lastIncrement = currentIncrement; + if (!this.enqueueTriggerValue(entry.getValue())) { + return false; + } + } + if (!this.enqueueTriggerValue(signum == -1.0F ? triggerValuePositive : triggerValueNegative)) { + return false; + } + } else { + break; + } + } + return true; + } + + @Override + public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value, boolean finalize) { + if (!this.isEditingAllowed()) return; + int triggerValue; + if (styleOption == ArmorStandStyleOptions.SHOW_NAME) { + triggerValue = value ? DISPLAY_NAME_YES : DISPLAY_NAME_NO; + } else if (styleOption == ArmorStandStyleOptions.SHOW_ARMS) { + triggerValue = value ? SHOW_ARMS_YES : SHOW_ARMS_NO; + } else if (styleOption == ArmorStandStyleOptions.SMALL) { + triggerValue = value ? SMALL_STAND_YES : SMALL_STAND_NO; + } else if (styleOption == ArmorStandStyleOptions.INVISIBLE) { + triggerValue = value ? STAND_VISIBLE_NO : STAND_VISIBLE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_BASE_PLATE) { + triggerValue = value ? SHOW_BASE_PLATE_NO : SHOW_BASE_PLATE_YES; + } else if (styleOption == ArmorStandStyleOptions.NO_GRAVITY) { + triggerValue = value ? APPLY_GRAVITY_NO : APPLY_GRAVITY_YES; + } else { + super.sendStyleOption(styleOption, value, finalize); + return; + } + if (this.sendSingleTriggerValue(triggerValue, finalize)) { + styleOption.setOption(this.getArmorStand(), value); + } + } + + @Override + public void sendAlignment(ArmorStandAlignment alignment) { + if (!this.isEditingAllowed()) return; + int triggerValue = switch (alignment) { + case BLOCK -> AUTO_ALIGNMENT_BLOCK_ON_SURFACE; + case FLOATING_ITEM -> AUTO_ALIGNMENT_ITEM_ON_SURFACE; + case FLAT_ITEM -> AUTO_ALIGNMENT_ITEM_FLAT_ON_SURFACE; + case TOOL -> AUTO_ALIGNMENT_TOOL_FLAT_ON_SURFACE; + }; + this.sendSingleTriggerValue(triggerValue, true); + } + + public void sendSingleTriggerValue(int triggerValue) { + if (!this.isEditingAllowed()) return; + this.sendSingleTriggerValue(triggerValue, true); + } + + private boolean sendSingleTriggerValue(int triggerValue, boolean finalize) { + boolean result = this.enqueueTriggerValue(triggerValue); + if (finalize) this.finalizeCurrentOperation(); + return result; + } + + @Override + public ArmorStandScreenType[] getScreenTypes() { + return Stream.concat(Stream.of(super.getScreenTypes()), Stream.of(ModRegistry.VANILLA_TWEAKS_SCREEN_TYPE)).toArray(ArmorStandScreenType[]::new); + } + + @Override + protected boolean isEditingAllowed() { + return this.isEditingAllowed(false); + } + + @Override + protected Either testArmorStand(ArmorStand armorStand) { + return super.testArmorStand(armorStand).>map(Optional::of, $ -> { + if (this.player.distanceToSqr(armorStand) < 9.0) { + return Optional.empty(); + } else { + return Optional.of(Component.translatable(OUT_OF_RANGE_TRANSLATION_KEY)); + } + }).>map(Either::left).orElse(Either.right(Unit.INSTANCE)); + } + + @Override + protected int getDequeueDelayTicks() { + return ArmorStatues.CONFIG.get(ClientConfig.class).clientCommandDelay; + } + + private boolean enqueueTriggerValue(int triggerValue) { + return this.enqueueClientCommand("trigger as_trigger set %s".formatted(triggerValue)); + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java new file mode 100644 index 0000000..3689876 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java @@ -0,0 +1,51 @@ +package fuzs.armorstatues.proxy; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.config.ClientConfig; +import fuzs.armorstatues.init.ModRegistry; +import fuzs.armorstatues.network.client.data.CommandDataSyncHandler; +import fuzs.armorstatues.network.client.data.VanillaTweaksDataSyncHandler; +import fuzs.statuemenus.api.v1.client.gui.screens.ArmorStandScreenFactory; +import fuzs.statuemenus.api.v1.network.client.data.DataSyncHandler; +import fuzs.statuemenus.api.v1.world.entity.decoration.ArmorStandDataProvider; +import fuzs.statuemenus.api.v1.world.inventory.ArmorStandHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ClientProxy extends ServerProxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + ArmorStandHolder holder = new ArmorStandHolder() { + + @Override + public ArmorStand getArmorStand() { + return armorStand; + } + + @Override + public ArmorStandDataProvider getDataProvider() { + return ModRegistry.ARMOR_STAND_DATA_PROVIDER; + } + }; + Screen screen = ArmorStandScreenFactory.createLastScreenType(holder, + player.getInventory(), + armorStand.getDisplayName(), + createDataSyncHandler(holder, (LocalPlayer) player) + ); + Minecraft minecraft = Minecraft.getInstance(); + minecraft.setScreen(screen); + } + + private static DataSyncHandler createDataSyncHandler(ArmorStandHolder holder, LocalPlayer player) { + if ((!player.hasPermissions(2) || ArmorStatues.CONFIG.get(ClientConfig.class).overrideClientPermissionsCheck) && + ArmorStatues.CONFIG.get(ClientConfig.class).useVanillaTweaksTriggers) { + return new VanillaTweaksDataSyncHandler(holder, player); + } else { + return new CommandDataSyncHandler(holder, player); + } + } +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java new file mode 100644 index 0000000..1f5ae83 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/proxy/Proxy.java @@ -0,0 +1,11 @@ +package fuzs.armorstatues.proxy; + +import fuzs.puzzleslib.api.core.v1.ModLoaderEnvironment; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public interface Proxy { + Proxy INSTANCE = ModLoaderEnvironment.INSTANCE.isClient() ? new ClientProxy() : new ServerProxy(); + + void openArmorStandScreen(ArmorStand armorStand, Player player); +} diff --git a/1.21.4/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java b/1.21.4/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java new file mode 100644 index 0000000..f41e246 --- /dev/null +++ b/1.21.4/Common/src/main/java/fuzs/armorstatues/proxy/ServerProxy.java @@ -0,0 +1,12 @@ +package fuzs.armorstatues.proxy; + +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; + +public class ServerProxy implements Proxy { + + @Override + public void openArmorStandScreen(ArmorStand armorStand, Player player) { + // NO-OP + } +} diff --git a/1.21.4/Common/src/main/resources/architectury.common.json b/1.21.4/Common/src/main/resources/architectury.common.json new file mode 100644 index 0000000..4cfa289 --- /dev/null +++ b/1.21.4/Common/src/main/resources/architectury.common.json @@ -0,0 +1,3 @@ +{ + "accessWidener": "armorstatues.accesswidener" +} \ No newline at end of file diff --git a/1.21.4/Common/src/main/resources/armorstatues.accesswidener b/1.21.4/Common/src/main/resources/armorstatues.accesswidener new file mode 100644 index 0000000..236e6b1 --- /dev/null +++ b/1.21.4/Common/src/main/resources/armorstatues.accesswidener @@ -0,0 +1 @@ +accessWidener v2 named diff --git a/1.21.4/Common/src/main/resources/common.mixins.json b/1.21.4/Common/src/main/resources/common.mixins.json new file mode 100644 index 0000000..77fc8f4 --- /dev/null +++ b/1.21.4/Common/src/main/resources/common.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.21.4/Common/src/main/resources/mod_banner.png b/1.21.4/Common/src/main/resources/mod_banner.png new file mode 100644 index 0000000..62a4610 Binary files /dev/null and b/1.21.4/Common/src/main/resources/mod_banner.png differ diff --git a/1.21.4/Common/src/main/resources/mod_logo.png b/1.21.4/Common/src/main/resources/mod_logo.png new file mode 100644 index 0000000..869766b Binary files /dev/null and b/1.21.4/Common/src/main/resources/mod_logo.png differ diff --git a/1.21.4/Common/src/main/resources/pack.mcmeta b/1.21.4/Common/src/main/resources/pack.mcmeta new file mode 100755 index 0000000..546f4f1 --- /dev/null +++ b/1.21.4/Common/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "${modDescription}", + "pack_format": ${resourcePackFormat} + } +} diff --git a/1.21.4/Fabric/build.gradle b/1.21.4/Fabric/build.gradle new file mode 100644 index 0000000..8ab06f6 --- /dev/null +++ b/1.21.4/Fabric/build.gradle @@ -0,0 +1,12 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/fabric.gradle" + +dependencies { + // Fabric Api + modApi libs.fabricapi.fabric + + // Puzzles Lib + modApi libs.puzzleslib.fabric + + // Statue Menus + modApi(include(libs.statuemenus.fabric.get())) +} diff --git a/1.21.4/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java b/1.21.4/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java new file mode 100644 index 0000000..822cef4 --- /dev/null +++ b/1.21.4/Fabric/src/main/java/fuzs/armorstatues/fabric/ArmorStatuesFabric.java @@ -0,0 +1,13 @@ +package fuzs.armorstatues.fabric; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.fabricmc.api.ModInitializer; + +public class ArmorStatuesFabric implements ModInitializer { + + @Override + public void onInitialize() { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } +} diff --git a/1.21.4/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java b/1.21.4/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java new file mode 100644 index 0000000..2414106 --- /dev/null +++ b/1.21.4/Fabric/src/main/java/fuzs/armorstatues/fabric/client/ArmorStatuesFabricClient.java @@ -0,0 +1,14 @@ +package fuzs.armorstatues.fabric.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.client.ArmorStatuesClient; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import net.fabricmc.api.ClientModInitializer; + +public class ArmorStatuesFabricClient implements ClientModInitializer { + + @Override + public void onInitializeClient() { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + } +} diff --git a/1.21.4/Fabric/src/main/resources/fabric.mixins.json b/1.21.4/Fabric/src/main/resources/fabric.mixins.json new file mode 100644 index 0000000..6bf7940 --- /dev/null +++ b/1.21.4/Fabric/src/main/resources/fabric.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.fabric.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "refmap": "${modId}.fabric.refmap.json" +} diff --git a/1.21.4/Fabric/src/main/resources/fabric.mod.json b/1.21.4/Fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..d67e512 --- /dev/null +++ b/1.21.4/Fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,46 @@ +{ + "schemaVersion": 1, + "id": "${modId}", + "version": "${modVersion}", + + "name": "${modName}", + "description": "${modDescription}", + + "authors": [ + "${modAuthor}" + ], + + "contact": { + "homepage": "${modPageUrl}", + "issues": "${modIssueUrl}", + "sources": "${modPageUrl}" + }, + + "license": "${modLicense}", + "icon": "mod_logo.png", + + "environment": "${modFabricEnvironment}", + + "entrypoints": { + "main": [ + "${mainEntryPoint}" + ], + "client": [ + "${clientEntryPoint}" + ] + }, + + "mixins": [ + "${modId}.common.mixins.json", + "${modId}.fabric.mixins.json" + ], + + "depends": { + "fabricloader": ">=${minFabricVersion}", + "fabric-api": ">=${minFabricApiVersion}", + "puzzleslib": ">=${minPuzzlesVersion}", + "statuemenus": "*", + "minecraft": "${minecraftVersion}", + "java": ">=17" + } +} diff --git a/1.21.4/NeoForge/build.gradle b/1.21.4/NeoForge/build.gradle new file mode 100644 index 0000000..97e8395 --- /dev/null +++ b/1.21.4/NeoForge/build.gradle @@ -0,0 +1,9 @@ +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/neoforge.gradle" + +dependencies { + // Puzzles Lib + modApi libs.puzzleslib.neoforge + + // Statue Menus + modApi(include(libs.statuemenus.neoforge.get())) +} diff --git a/1.21.4/NeoForge/gradle.properties b/1.21.4/NeoForge/gradle.properties new file mode 100644 index 0000000..2914393 --- /dev/null +++ b/1.21.4/NeoForge/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge \ No newline at end of file diff --git a/1.21.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java b/1.21.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java new file mode 100644 index 0000000..9f37d35 --- /dev/null +++ b/1.21.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/ArmorStatuesNeoForge.java @@ -0,0 +1,13 @@ +package fuzs.armorstatues.neoforge; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.puzzleslib.api.core.v1.ModConstructor; +import net.neoforged.fml.common.Mod; + +@Mod(ArmorStatues.MOD_ID) +public class ArmorStatuesNeoForge { + + public ArmorStatuesNeoForge() { + ModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatues::new); + } +} diff --git a/1.21.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java b/1.21.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java new file mode 100644 index 0000000..53de799 --- /dev/null +++ b/1.21.4/NeoForge/src/main/java/fuzs/armorstatues/neoforge/client/ArmorStatuesNeoForgeClient.java @@ -0,0 +1,18 @@ +package fuzs.armorstatues.neoforge.client; + +import fuzs.armorstatues.ArmorStatues; +import fuzs.armorstatues.client.ArmorStatuesClient; +import fuzs.armorstatues.data.client.ModLanguageProvider; +import fuzs.puzzleslib.api.client.core.v1.ClientModConstructor; +import fuzs.puzzleslib.neoforge.api.data.v2.core.DataProviderHelper; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.fml.common.Mod; + +@Mod(value = ArmorStatues.MOD_ID, dist = Dist.CLIENT) +public class ArmorStatuesNeoForgeClient { + + public ArmorStatuesNeoForgeClient() { + ClientModConstructor.construct(ArmorStatues.MOD_ID, ArmorStatuesClient::new); + DataProviderHelper.registerDataProviders(ArmorStatues.MOD_ID, ModLanguageProvider::new); + } +} diff --git a/1.21.4/NeoForge/src/main/resources/META-INF/neoforge.mods.toml b/1.21.4/NeoForge/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..432ab6c --- /dev/null +++ b/1.21.4/NeoForge/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,57 @@ +modLoader = "javafml" +loaderVersion = "*" +license = "${modLicense}" +issueTrackerURL = "${modIssueUrl}" + +[[mods]] +modId = "${modId}" +displayName = "${modName}" +description = "${modDescription}" +version = "${modVersion}" +authors = "${modAuthor}" +logoFile = "mod_banner.png" +logoBlur = false +displayURL = "${modPageUrl}" +updateJSONURL = "${modUpdateUrl}" +displayTest = "${modForgeDisplayTest}" + +[[mixins]] +config="${modId}.common.mixins.json" + +[[mixins]] +config="${modId}.neoforge.mixins.json" + +[[dependencies.${ modId }]] +modId = "neoforge" +mandatory = true +type = "required" +versionRange = "[${minNeoForgeVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "minecraft" +mandatory = true +type = "required" +versionRange = "[${minecraftVersion}]" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "puzzleslib" +mandatory = true +type = "required" +versionRange = "[${minPuzzlesVersion},)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.${ modId }]] +modId = "statuemenus" +mandatory = true +type = "required" +versionRange = "*" +ordering = "NONE" +side = "BOTH" + +[modproperties.${ modId }] +catalogueImageIcon = "mod_logo.png" diff --git a/1.21.4/NeoForge/src/main/resources/neoforge.mixins.json b/1.21.4/NeoForge/src/main/resources/neoforge.mixins.json new file mode 100644 index 0000000..1fb3919 --- /dev/null +++ b/1.21.4/NeoForge/src/main/resources/neoforge.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "package": "${modGroup}.neoforge.mixin", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/1.21.4/build.gradle b/1.21.4/build.gradle new file mode 100644 index 0000000..dea2bca --- /dev/null +++ b/1.21.4/build.gradle @@ -0,0 +1,9 @@ +plugins { + alias libs.plugins.architecturyloom apply false + alias libs.plugins.architecturyplugin apply false + alias libs.plugins.shadow apply false + alias libs.plugins.cursegradle apply false + alias libs.plugins.minotaur apply false +} + +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/${libs.versions.minecraft.get()}/main.gradle" diff --git a/1.21.4/gradle.properties b/1.21.4/gradle.properties new file mode 100755 index 0000000..6f24a89 --- /dev/null +++ b/1.21.4/gradle.properties @@ -0,0 +1,45 @@ +org.gradle.jvmargs=-Xmx4G +org.gradle.daemon=false +copyBuildJar=true + +# Mod Attributes +modId=armorstatues +modName=Armor Statues +modVersion=21.4.0 +modAuthor=Fuzs +modDescription=Unlock the full potential of armor stands! Works on vanilla servers, too. +modLicense=MPL-2.0 +modSourceUrl=https://github.com/Fuzss/armorstatues +modIssueUrl=https://github.com/Fuzss/armorstatues/issues +modUpdateUrl=https://raw.githubusercontent.com/Fuzss/modresources/main/update/armorstatues.json +modMavenGroup=fuzs.armorstatues +# "MATCH_VERSION" for a mod required on both sides, "IGNORE_SERVER_VERSION" for a server only mod, "IGNORE_ALL_VERSION" for a client only mod +modForgeDisplayTest=IGNORE_ALL_VERSION +# "*" for a mod loaded on both sides, "server" for a server only mod, "client" for a client only mod +modFabricEnvironment=* + +# Version Catalog +dependenciesVersionCatalog=1.21.4-v5 +#dependenciesPuzzlesLibVersion=21.4.2 +#dependenciesMinPuzzlesLibVersion=21.4.2 + +# Mod Publishing +projectReleaseType=release +projectCurseForgeId=682566 +projectModrinthId=bbGCtEvb + +# Required Dependencies +dependenciesRequiredFabricCurseForge=fabric-api, forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredNeoForgeCurseForge=puzzles-lib +dependenciesRequiredForgeCurseForge=forge-config-api-port-fabric, puzzles-lib +dependenciesRequiredFabricModrinth=fabric-api, forge-config-api-port, puzzles-lib +dependenciesRequiredNeoForgeModrinth=puzzles-lib +dependenciesRequiredForgeModrinth=forge-config-api-port, puzzles-lib + +# Optional Dependencies +dependenciesOptionalFabricCurseForge=config-menus-forge +dependenciesOptionalNeoForgeCurseForge=config-menus-forge +dependenciesOptionalForgeCurseForge=config-menus-forge +dependenciesOptionalFabricModrinth=forge-config-screens +dependenciesOptionalNeoForgeModrinth=forge-config-screens +dependenciesOptionalForgeModrinth=forge-config-screens diff --git a/1.21.4/gradle/wrapper/gradle-wrapper.jar b/1.21.4/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..943f0cb Binary files /dev/null and b/1.21.4/gradle/wrapper/gradle-wrapper.jar differ diff --git a/1.21.4/gradle/wrapper/gradle-wrapper.properties b/1.21.4/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..4eaec46 --- /dev/null +++ b/1.21.4/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/1.21.4/gradlew b/1.21.4/gradlew new file mode 100755 index 0000000..65dcd68 --- /dev/null +++ b/1.21.4/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/1.21.4/gradlew.bat b/1.21.4/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/1.21.4/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/1.21.4/settings.gradle b/1.21.4/settings.gradle new file mode 100644 index 0000000..5cfe5eb --- /dev/null +++ b/1.21.4/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { url "https://maven.architectury.dev/" } + maven { url "https://maven.fabricmc.net/" } + maven { url "https://maven.neoforged.net/releases/" } + maven { url "https://maven.minecraftforge.net/" } + } +} + +include "Common" +include "Fabric" +include "NeoForge" +//include "Forge" + +apply from: "https://raw.githubusercontent.com/Fuzss/modresources/main/gradle/v2/settings.gradle" diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 283da97..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,15 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog]. - -## [v4.0.1-1.19.2] - 2022-10-15 -### Changed -- Updated to Forge 43.1.40+ which is also now required -### Fixed -- Armor stand interactions are now properly disabled when trying to open the menu when the mod is only installed client-side - -## [v4.0.0-1.19.2] - 2022-09-22 -- Initial release - -[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ diff --git a/Common/build.gradle b/Common/build.gradle deleted file mode 100644 index 1ea86ea..0000000 --- a/Common/build.gradle +++ /dev/null @@ -1,140 +0,0 @@ -plugins { - id 'org.quiltmc.loom' version '0.12.+' -} - -archivesBaseName = rootProject.name -version = "v${modVersion}-${minecraftVersion}-Common" -group = modMavenGroup - -dependencies { - // Minecraft - minecraft "com.mojang:minecraft:${minecraftVersion}" - mappings loom.layered() { - officialMojangMappings() - parchment("org.parchmentmc.data:parchment-${minecraftVersion}:${parchmentMappingsVersion}@zip") - } - - // Mixin Dependencies - implementation 'org.ow2.asm:asm-tree:9.2' - implementation 'org.ow2.asm:asm-commons:9.2' - implementation 'org.ow2.asm:asm-util:9.2' - implementation 'org.spongepowered:mixin:0.8.5' - - // Config Dependencies - implementation 'com.electronwill.night-config:core:3.6.5' - implementation 'com.electronwill.night-config:toml:3.6.5' - - // Puzzles Lib - modImplementation "fuzs.puzzleslib:puzzleslib-common:${puzzlesVersion}" -} - -loom { - mixin { - // not sure if this is necessary for common... - defaultRefmapName = "${modId}.refmap.json" - // fix for java.lang.NoClassDefFoundError: org/objectweb/asm/tree/MethodNode - useLegacyMixinAp = false - } - - // this should hopeful prevent an empty run directory being generated in common during initial project setup - runs { - client { - client() - setConfigName("Common Client") - ideConfigGenerated(false) - runDir("../run") - } - server { - server() - setConfigName("Common Server") - ideConfigGenerated(false) - runDir("../run") - } - } -} - -processResources { - duplicatesStrategy DuplicatesStrategy.INCLUDE - - // this will ensure that this task is redone when a value changes - inputs.property "modId", "${modId}" - inputs.property "modVersion", "${modVersion}" - inputs.property "modGroup", project.group - inputs.property "modDescription", "${modDescription}" - inputs.property "packFormat", "${packFormat}" - - // replace stuff in fabric.mod.json and pack.mcmeta - filesMatching ('quilt.mod.json') { - expand ( - 'modId': "${modId}", - 'modVersion': "${modVersion}", - 'modGroup': project.group - ) - } - - // replace stuff in pack.mcmeta - filesMatching ('pack.mcmeta') { - expand ( - 'modDescription': "${modDescription}", - "packFormat": "${packFormat}" - ) - } -} - -publishing { - publications { - mavenJava (MavenPublication) { - artifactId = "${modId}-common" - version = modVersion - from components.java - pom { - name = "${modName} [Common]" - description = "${modDescription}" - url = "${modSourceUrl}" - scm { - url = "${modSourceUrl}" - connection = "${modSourceUrl}".replace("https", "scm:git:git").concat(".git") - developerConnection = "${modSourceUrl}".replace("https://github.com/", "scm:git:git@github.com:").concat(".git") - } - issueManagement { - system = 'github' - url = "${modIssueUrl}" - } - licenses { - license { - name = 'MPL-2' - url = 'https://www.mozilla.org/en-US/MPL/2.0/' - } - } - developers { - developer { - id = "${modAuthor}".toLowerCase() - name = "${modAuthor}" - } - } - } - afterEvaluate { - // exclude certain dependencies when publishing to maven - // from https://stackoverflow.com/a/50121790 - pom.withXml { - asNode().dependencies.dependency.each { dep -> - // use this approach to make excluding dependencies from Curse Maven more convenient - if ([].stream().anyMatch(mod -> "${dep.groupId.last().value().last()}:${dep.artifactId.last().value().last()}".startsWith(mod))) { - assert dep.parent().remove(dep) - } - } - } - } - } - } - repositories { - maven { - name = 'FuzsModResources' - url "file://" + project.hasProperty('modResources') ? "${project.findProperty('modResources')}/maven" : System.getenv('local_maven') - } - } -} - -signing { - sign publishing.publications.mavenJava -} diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/UnboundedSliderButton.java b/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/UnboundedSliderButton.java deleted file mode 100644 index bb20128..0000000 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/components/UnboundedSliderButton.java +++ /dev/null @@ -1,6 +0,0 @@ -package fuzs.armorstatues.api.client.gui.components; - -public interface UnboundedSliderButton { - - boolean isDirty(); -} diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java b/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java deleted file mode 100644 index 094c06b..0000000 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandAlignmentsScreen.java +++ /dev/null @@ -1,118 +0,0 @@ -package fuzs.armorstatues.api.client.gui.screens.armorstand; - -import com.google.common.collect.Lists; -import fuzs.armorstatues.api.client.gui.components.TickButton; -import fuzs.armorstatues.api.client.gui.components.TickingButton; -import fuzs.armorstatues.api.network.client.data.DataSyncHandler; -import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandAlignment; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandStyleOptions; -import net.minecraft.Util; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.components.ImageButton; -import net.minecraft.client.gui.screens.ConfirmLinkScreen; -import net.minecraft.core.Direction; -import net.minecraft.network.chat.CommonComponents; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.decoration.ArmorStand; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.phys.Vec3; - -import java.util.EnumSet; -import java.util.List; - -public class ArmorStandAlignmentsScreen extends ArmorStandWidgetsScreen { - - public ArmorStandAlignmentsScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { - super(holder, inventory, component, dataSyncHandler); - } - - @Override - protected List buildWidgets(ArmorStand armorStand) { - List widgets = Lists.newArrayList(new PositionAlignWidget()); - for (ArmorStandAlignment alignment : ArmorStandAlignment.values()) { - widgets.add(new AlignmentWidget(alignment)); - } - return widgets; - } - - @Override - protected void init() { - super.init(); - this.addRenderableWidget(new ImageButton(this.leftPos + 6, this.topPos + 6, 20, 20, 136, 64, 20, ARMOR_STAND_WIDGETS_LOCATION, 256, 256, button -> { - this.minecraft.setScreen(new ConfirmLinkScreen((bl) -> { - if (bl) Util.getPlatform().openUri("https://vanillatweaks.net/"); - this.minecraft.setScreen(this); - }, "https://vanillatweaks.net/", true)); - }, (button, poseStack, mouseX, mouseY) -> { - this.renderTooltip(poseStack, Component.translatable("armorstatues.screen.alignments.credit"), mouseX, mouseY); - }, CommonComponents.EMPTY)); - } - - @Override - public ArmorStandScreenType getScreenType() { - return ArmorStandScreenType.ALIGNMENTS; - } - - private abstract class BlockPositionWidget extends AbstractPositionScreenWidget { - - public BlockPositionWidget() { - super(Component.empty()); - } - - @Override - public void tick() { - super.tick(); - for (AbstractWidget widget : this.children) { - if (widget instanceof TickingButton tickButton) tickButton.tick(); - } - } - - protected Vec3 getCurrentPosition() { - return ArmorStandAlignmentsScreen.this.holder.getArmorStand().position(); - } - - protected void setNewPosition(Vec3 vec3) { - ArmorStandAlignmentsScreen.this.dataSyncHandler.sendPosition(vec3.x(), vec3.y(), vec3.z()); - } - } - - private class PositionAlignWidget extends BlockPositionWidget { - - @Override - public void init(int posX, int posY) { - super.init(posX, posY); - this.children.add(ArmorStandAlignmentsScreen.this.addRenderableWidget(new TickButton(posX, posY + 1, 94, 20, Component.translatable("armorstatues.screen.position.centered"), Component.translatable("armorstatues.screen.position.aligned"), button -> { - this.setNewPosition(this.getCurrentPosition().align(EnumSet.allOf(Direction.Axis.class)).add(0.5, 0.0, 0.5)); - }))); - this.children.add(ArmorStandAlignmentsScreen.this.addRenderableWidget(new TickButton(posX + 100, posY + 1, 94, 20, Component.translatable("armorstatues.screen.position.cornered"), Component.translatable("armorstatues.screen.position.aligned"), button -> { - this.setNewPosition(this.getCurrentPosition().align(EnumSet.allOf(Direction.Axis.class))); - }))); - } - } - - private class AlignmentWidget extends BlockPositionWidget { - private final ArmorStandAlignment alignment; - - public AlignmentWidget(ArmorStandAlignment alignment) { - this.alignment = alignment; - } - - @Override - public void init(int posX, int posY) { - super.init(posX, posY); - this.children.add(ArmorStandAlignmentsScreen.this.addRenderableWidget(new TickButton(posX, posY + 1, 194, 20, this.alignment.getComponent(), Component.translatable("armorstatues.screen.position.aligned"), button -> { - ArmorStandAlignmentsScreen.this.dataSyncHandler.sendPose(this.alignment.getPose()); - ArmorStand armorStand = ArmorStandAlignmentsScreen.this.holder.getArmorStand(); - this.setNewPosition(this.getCurrentPosition().align(EnumSet.allOf(Direction.Axis.class)).add(0.5, 0.0, 0.5).add(this.alignment.getPosition(armorStand.isSmall()))); - if (!armorStand.isInvisible()) { - ArmorStandAlignmentsScreen.this.dataSyncHandler.sendStyleOption(ArmorStandStyleOptions.INVISIBLE, true); - } - if (!armorStand.isNoGravity()) { - ArmorStandAlignmentsScreen.this.dataSyncHandler.sendStyleOption(ArmorStandStyleOptions.NO_GRAVITY, true); - } - }))); - } - } -} diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPosesScreen.java b/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPosesScreen.java deleted file mode 100644 index fbf1ca3..0000000 --- a/Common/src/main/java/fuzs/armorstatues/api/client/gui/screens/armorstand/ArmorStandPosesScreen.java +++ /dev/null @@ -1,93 +0,0 @@ -package fuzs.armorstatues.api.client.gui.screens.armorstand; - -import com.mojang.blaze3d.vertex.PoseStack; -import fuzs.armorstatues.api.network.client.data.DataSyncHandler; -import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandPose; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.ImageButton; -import net.minecraft.client.gui.screens.inventory.InventoryScreen; -import net.minecraft.network.chat.CommonComponents; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.decoration.ArmorStand; -import net.minecraft.world.entity.player.Inventory; - -import java.util.Optional; - -public class ArmorStandPosesScreen extends AbstractArmorStandScreen { - private static final int POSES_PER_PAGE = 4; - - private static int firstPoseIndex; - - private final AbstractWidget[] cycleButtons = new AbstractWidget[2]; - private final AbstractWidget[] poseButtons = new AbstractWidget[POSES_PER_PAGE]; - - public ArmorStandPosesScreen(ArmorStandHolder holder, Inventory inventory, Component component, DataSyncHandler dataSyncHandler) { - super(holder, inventory, component, dataSyncHandler); - this.inventoryEntityX = 5; - this.inventoryEntityY = 40; - } - - @Override - protected void init() { - super.init(); - this.cycleButtons[0] = this.addRenderableWidget(new ImageButton(this.leftPos + 17, this.topPos + 153, 20, 20, 156, 64, ARMOR_STAND_WIDGETS_LOCATION, button -> { - firstPoseIndex -= POSES_PER_PAGE; - this.toggleCycleButtons(); - })); - this.cycleButtons[1] = this.addRenderableWidget(new ImageButton(this.leftPos + 49, this.topPos + 153, 20, 20, 176, 64, ARMOR_STAND_WIDGETS_LOCATION, button -> { - firstPoseIndex += POSES_PER_PAGE; - this.toggleCycleButtons(); - })); - for (int i = 0; i < this.poseButtons.length; i++) { - final int ii = i; - this.poseButtons[i] = this.addRenderableWidget(new ImageButton(this.leftPos + 83 + i % 2 * 62, this.topPos + 9 + i / 2 * 88, 60, 82, 76, 0, 82, ARMOR_STAND_WIDGETS_LOCATION, 256, 256, button -> { - getPoseAt(ii).ifPresent(this.dataSyncHandler::sendPose); - }, (Button button, PoseStack poseStack, int mouseX, int mouseY) -> { - getPoseAt(ii).ifPresent(pose -> this.renderTooltip(poseStack, pose.getComponent(), mouseX, mouseY)); - }, CommonComponents.EMPTY)); - } - this.toggleCycleButtons(); - } - - private void toggleCycleButtons() { - this.cycleButtons[0].active = firstPoseIndex > 0; - this.cycleButtons[1].active = firstPoseIndex + POSES_PER_PAGE < ArmorStandPose.values().length; - for (int i = 0; i < this.poseButtons.length; i++) { - this.poseButtons[i].visible = getPoseAt(i).isPresent(); - } - } - - @Override - protected void renderBg(PoseStack poseStack, float partialTick, int mouseX, int mouseY) { - super.renderBg(poseStack, partialTick, mouseX, mouseY); - ArmorStand armorStand = this.holder.getArmorStand(); - ArmorStandPose entityPose = ArmorStandPose.fromEntity(armorStand); - for (int i = 0; i < POSES_PER_PAGE; i++) { - Optional pose = getPoseAt(i); - if (pose.isPresent()) { - pose.get().applyToEntity(armorStand); - InventoryScreen.renderEntityInInventory(this.leftPos + 112 + i % 2 * 62, this.topPos + 79 + i / 2 * 88, 30, this.leftPos + 112 + i % 2 * 62 - 10 - this.mouseX, this.topPos + 79 + i / 2 * 88 - 44 - this.mouseY, armorStand); - } - } - entityPose.applyToEntity(armorStand); - } - - @Override - protected boolean withCloseButton() { - return false; - } - - @Override - public ArmorStandScreenType getScreenType() { - return ArmorStandScreenType.POSES; - } - - private static Optional getPoseAt(int index) { - index += firstPoseIndex; - if (index >= ArmorStandPose.values().length) return Optional.empty(); - return Optional.of(ArmorStandPose.values()[index]); - } -} diff --git a/Common/src/main/java/fuzs/armorstatues/api/client/init/ModClientRegistry.java b/Common/src/main/java/fuzs/armorstatues/api/client/init/ModClientRegistry.java deleted file mode 100644 index d6b10b0..0000000 --- a/Common/src/main/java/fuzs/armorstatues/api/client/init/ModClientRegistry.java +++ /dev/null @@ -1,8 +0,0 @@ -package fuzs.armorstatues.api.client.init; - -import com.mojang.blaze3d.platform.InputConstants; -import net.minecraft.client.KeyMapping; - -public class ModClientRegistry { - public static final KeyMapping CYCLE_TABS_KEY_MAPPING = new KeyMapping("key.cycleStatueTabs", InputConstants.KEY_TAB, "key.categories.inventory"); -} diff --git a/Common/src/main/java/fuzs/armorstatues/api/helper/ArmorStandInteractHelper.java b/Common/src/main/java/fuzs/armorstatues/api/helper/ArmorStandInteractHelper.java deleted file mode 100644 index b16f641..0000000 --- a/Common/src/main/java/fuzs/armorstatues/api/helper/ArmorStandInteractHelper.java +++ /dev/null @@ -1,37 +0,0 @@ -package fuzs.armorstatues.api.helper; - -import fuzs.armorstatues.api.world.inventory.ArmorStandMenu; -import fuzs.armorstatues.mixin.accessor.ArmorStandAccessor; -import fuzs.puzzleslib.core.CoreServices; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.SimpleMenuProvider; -import net.minecraft.world.entity.decoration.ArmorStand; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.level.Level; - -import java.util.Optional; - -public class ArmorStandInteractHelper { - - public static Optional tryOpenArmorStatueMenu(Player player, Level level, ArmorStand entity, MenuType menuType) { - if (player.isShiftKeyDown() && (!entity.isInvulnerable() || player.getAbilities().instabuild)) { - openArmorStatueMenu(player, entity, menuType); - return Optional.of(InteractionResult.sidedSuccess(level.isClientSide)); - } - return Optional.empty(); - } - - public static void openArmorStatueMenu(Player player, ArmorStand entity, MenuType menuType) { - if (player instanceof ServerPlayer serverPlayer) { - CoreServices.ABSTRACTIONS.openMenu(serverPlayer, new SimpleMenuProvider((containerId, inventory, player1) -> { - return ArmorStandMenu.create(menuType, containerId, inventory, entity); - }, entity.getDisplayName()), (serverPlayer1, friendlyByteBuf) -> { - friendlyByteBuf.writeInt(entity.getId()); - friendlyByteBuf.writeBoolean(entity.isInvulnerable()); - friendlyByteBuf.writeInt(((ArmorStandAccessor) entity).getDisabledSlots()); - }); - } - } -} diff --git a/Common/src/main/java/fuzs/armorstatues/api/network/client/data/CommandDataSyncHandler.java b/Common/src/main/java/fuzs/armorstatues/api/network/client/data/CommandDataSyncHandler.java deleted file mode 100644 index a766339..0000000 --- a/Common/src/main/java/fuzs/armorstatues/api/network/client/data/CommandDataSyncHandler.java +++ /dev/null @@ -1,132 +0,0 @@ -package fuzs.armorstatues.api.network.client.data; - -import fuzs.armorstatues.api.world.inventory.data.ArmorStandPose; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandStyleOption; -import net.minecraft.ChatFormatting; -import net.minecraft.client.Minecraft; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.DoubleTag; -import net.minecraft.nbt.FloatTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.decoration.ArmorStand; -import net.minecraft.world.entity.player.Player; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.function.BiPredicate; -import java.util.function.Predicate; -import java.util.stream.Stream; - -public class CommandDataSyncHandler implements DataSyncHandler { - @Nullable - static ArmorStandScreenType lastType; - - private final ArmorStand armorStand; - private ArmorStandPose lastSyncedPose; - - public CommandDataSyncHandler(ArmorStand armorStand) { - this.armorStand = armorStand; - this.lastSyncedPose = ArmorStandPose.fromEntity(armorStand); - } - - @Override - public ArmorStand getArmorStand() { - return this.armorStand; - } - - @Override - public void sendName(String name) { - if (!this.testPermissionLevel()) return; - DataSyncHandler.super.sendName(name); - CompoundTag tag = new CompoundTag(); - tag.putString("CustomName", Component.Serializer.toJson(Component.literal(name))); - this.sendCommand(tag); - } - - @Override - public void sendPose(ArmorStandPose currentPose) { - if (!this.testPermissionLevel()) return; - DataSyncHandler.super.sendPose(currentPose); - // split this into multiple chat messages as the client chat field has a very low character limit - this.sendPosePart(currentPose::serializeBodyPoses, this.lastSyncedPose); - this.sendPosePart(currentPose::serializeArmPoses, this.lastSyncedPose); - this.sendPosePart(currentPose::serializeLegPoses, this.lastSyncedPose); - this.lastSyncedPose = currentPose; - } - - private void sendPosePart(BiPredicate dataWriter, ArmorStandPose lastSyncedPose) { - CompoundTag tag = new CompoundTag(); - if (dataWriter.test(tag, lastSyncedPose)) { - CompoundTag tag1 = new CompoundTag(); - tag1.put("Pose", tag); - this.sendCommand(tag1); - } - } - - @Override - public void sendPosition(double posX, double posY, double posZ) { - if (!this.testPermissionLevel()) return; - DataSyncHandler.super.sendPosition(posX, posY, posZ); - ListTag listTag = new ListTag(); - listTag.add(DoubleTag.valueOf(posX)); - listTag.add(DoubleTag.valueOf(posY)); - listTag.add(DoubleTag.valueOf(posZ)); - CompoundTag tag = new CompoundTag(); - tag.put("Pos", listTag); - this.sendCommand(tag); - - } - - @Override - public void sendRotation(float rotation) { - if (!this.testPermissionLevel()) return; - DataSyncHandler.super.sendRotation(rotation); - ListTag listTag = new ListTag(); - listTag.add(FloatTag.valueOf(rotation)); - CompoundTag tag = new CompoundTag(); - tag.put("Rotation", listTag); - this.sendCommand(tag); - } - - @Override - public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value) { - if (!this.testPermissionLevel()) return; - DataSyncHandler.super.sendStyleOption(styleOption, value); - CompoundTag tag = new CompoundTag(); - styleOption.toTag(tag, value); - this.sendCommand(tag); - } - - @Override - public ArmorStandScreenType[] tabs() { - return Stream.of(this.getDataProvider().getScreenTypes()).filter(Predicate.not(ArmorStandScreenType::requiresServer)).toArray(ArmorStandScreenType[]::new); - } - - @Override - public Optional getLastType() { - List screenTypes = Arrays.asList(this.getDataProvider().getScreenTypes()); - return Optional.ofNullable(lastType).filter(screenTypes::contains).filter(Predicate.not(ArmorStandScreenType::requiresServer)); - } - - @Override - public void setLastType(ArmorStandScreenType lastType) { - CommandDataSyncHandler.lastType = lastType; - } - - private boolean testPermissionLevel() { - Player player = Minecraft.getInstance().player; - if (!player.hasPermissions(2)) { - player.displayClientMessage(Component.translatable("armorstatues.screen.noPermission").withStyle(ChatFormatting.RED), false); - return false; - } - return true; - } - - private void sendCommand(CompoundTag tag) { - Minecraft.getInstance().player.commandSigned("data merge entity %s %s".formatted(this.getArmorStand().getStringUUID(), tag.getAsString()), null); - } -} diff --git a/Common/src/main/java/fuzs/armorstatues/api/network/client/data/DataSyncHandler.java b/Common/src/main/java/fuzs/armorstatues/api/network/client/data/DataSyncHandler.java deleted file mode 100644 index 0776524..0000000 --- a/Common/src/main/java/fuzs/armorstatues/api/network/client/data/DataSyncHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -package fuzs.armorstatues.api.network.client.data; - -import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandPose; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandStyleOption; -import net.minecraft.SharedConstants; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.decoration.ArmorStand; - -import java.util.Optional; - -public interface DataSyncHandler extends ArmorStandHolder { - - default void sendName(String name) { - setCustomArmorStandName(this.getArmorStand(), name); - } - - default void sendPose(ArmorStandPose currentPose) { - currentPose.applyToEntity(this.getArmorStand()); - } - - default void sendPosition(double posX, double posY, double posZ) { - - } - - default void sendRotation(float rotation) { - - } - - default void sendStyleOption(ArmorStandStyleOption styleOption, boolean value) { - styleOption.setOption(this.getArmorStand(), value); - } - - ArmorStandScreenType[] tabs(); - - Optional getLastType(); - - void setLastType(ArmorStandScreenType lastType); - - static void setCustomArmorStandName(ArmorStand armorStand, String name) { - String s = SharedConstants.filterText(name); - if (s.length() <= 50) { - boolean remove = s.isBlank() || s.equals(EntityType.ARMOR_STAND.getDescription().getString()); - armorStand.setCustomName(remove ? null : Component.literal(s)); - } - } -} diff --git a/Common/src/main/java/fuzs/armorstatues/api/network/client/data/NetworkDataSyncHandler.java b/Common/src/main/java/fuzs/armorstatues/api/network/client/data/NetworkDataSyncHandler.java deleted file mode 100644 index fa4b3be..0000000 --- a/Common/src/main/java/fuzs/armorstatues/api/network/client/data/NetworkDataSyncHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -package fuzs.armorstatues.api.network.client.data; - -import fuzs.armorstatues.api.ArmorStatuesApi; -import fuzs.armorstatues.api.network.client.*; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandPose; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandScreenType; -import fuzs.armorstatues.api.world.inventory.data.ArmorStandStyleOption; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.entity.decoration.ArmorStand; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -public class NetworkDataSyncHandler implements DataSyncHandler { - @Nullable - private static ArmorStandScreenType lastType; - - private final ArmorStand armorStand; - - public NetworkDataSyncHandler(ArmorStand armorStand) { - this.armorStand = armorStand; - } - - @Override - public ArmorStand getArmorStand() { - return this.armorStand; - } - - @Override - public void sendName(String name) { - DataSyncHandler.super.sendName(name); - ArmorStatuesApi.NETWORK.sendToServer(new C2SArmorStandNameMessage(name)); - } - - @Override - public void sendPose(ArmorStandPose currentPose) { - DataSyncHandler.super.sendPose(currentPose); - CompoundTag tag = new CompoundTag(); - currentPose.serializeAllPoses(tag); - ArmorStatuesApi.NETWORK.sendToServer(new C2SArmorStandPoseMessage(tag)); - } - - @Override - public void sendPosition(double posX, double posY, double posZ) { - DataSyncHandler.super.sendPosition(posX, posY, posZ); - ArmorStatuesApi.NETWORK.sendToServer(new C2SArmorStandPositionMessage(posX, posY, posZ)); - } - - @Override - public void sendRotation(float rotation) { - DataSyncHandler.super.sendRotation(rotation); - ArmorStatuesApi.NETWORK.sendToServer(new C2SArmorStandRotationMessage(rotation)); - } - - @Override - public void sendStyleOption(ArmorStandStyleOption styleOption, boolean value) { - DataSyncHandler.super.sendStyleOption(styleOption, value); - ArmorStatuesApi.NETWORK.sendToServer(new C2SArmorStandStyleMessage(styleOption, value)); - } - - @Override - public ArmorStandScreenType[] tabs() { - return this.getDataProvider().getScreenTypes(); - } - - @Override - public Optional getLastType() { - List screenTypes = Arrays.asList(this.getDataProvider().getScreenTypes()); - return Optional.ofNullable(lastType).filter(screenTypes::contains); - } - - @Override - public void setLastType(ArmorStandScreenType lastType) { - NetworkDataSyncHandler.lastType = CommandDataSyncHandler.lastType = lastType; - } -} diff --git a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandPose.java b/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandPose.java deleted file mode 100644 index 93532bc..0000000 --- a/Common/src/main/java/fuzs/armorstatues/api/world/inventory/data/ArmorStandPose.java +++ /dev/null @@ -1,204 +0,0 @@ -package fuzs.armorstatues.api.world.inventory.data; - -import fuzs.armorstatues.mixin.accessor.ArmorStandAccessor; -import net.minecraft.Util; -import net.minecraft.core.Rotations; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.decoration.ArmorStand; -import org.jetbrains.annotations.Nullable; - -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.util.Locale; -import java.util.Objects; -import java.util.Random; - -public class ArmorStandPose { - public static final ArmorStandPose ATHENA = new ArmorStandPose("athena").withBodyPose(new Rotations(0.0F, 0.0F, 2.0F)).withHeadPose(new Rotations(-5.0F, 0.0F, 0.0F)).withLeftArmPose(new Rotations(10.0F, 0.0F, -5.0F)).withLeftLegPose(new Rotations(-3.0F, -3.0F, -3.0F)).withRightArmPose(new Rotations(-60.0F, 20.0F, -10.0F)).withRightLegPose(new Rotations(3.0F, 3.0F, 3.0F)); - public static final ArmorStandPose BRANDISH = new ArmorStandPose("brandish").withBodyPose(new Rotations(0.0F, 0.0F, -2.0F)).withHeadPose(new Rotations(-15.0F, 0.0F, 0.0F)).withLeftArmPose(new Rotations(20.0F, 0.0F, -10.0F)).withLeftLegPose(new Rotations(5.0F, -3.0F, -3.0F)).withRightArmPose(new Rotations(-110.0F, 50.0F, 0.0F)).withRightLegPose(new Rotations(-5.0F, 3.0F, 3.0F)); - public static final ArmorStandPose CANCAN_A = new ArmorStandPose("cancanA").withBodyPose(new Rotations(0.0F, 22.0F, 0.0F)).withHeadPose(new Rotations(-5.0F, 18.0F, 0.0F)).withLeftArmPose(new Rotations(8.0F, 0.0F, -114.0F)).withLeftLegPose(new Rotations(-111.0F, 55.0F, 0.0F)).withRightArmPose(new Rotations(0.0F, 84.0F, 111.0F)).withRightLegPose(new Rotations(0.0F, 23.0F, -13.0F)); - public static final ArmorStandPose CANCAN_B = new ArmorStandPose("cancanB").withBodyPose(new Rotations(0.0F, -18.0F, 0.0F)).withHeadPose(new Rotations(-10.0F, -20.0F, 0.0F)).withLeftArmPose(new Rotations(0.0F, 0.0F, -112.0F)).withLeftLegPose(new Rotations(0.0F, 0.0F, 13.0F)).withRightArmPose(new Rotations(8.0F, 90.0F, 111.0F)).withRightLegPose(new Rotations(-119.0F, -42.0F, 0.0F)); - public static final ArmorStandPose DEFAULT = new ArmorStandPose("default").withLeftArmPose(new Rotations(-10.0F, 0.0F, -10.0F)).withLeftLegPose(new Rotations(-1.0F, 0.0F, -1.0F)).withRightArmPose(new Rotations(-15.0F, 0.0F, 10.0F)).withRightLegPose(new Rotations(1.0F, 0.0F, 1.0F)); - public static final ArmorStandPose ENTERTAIN = new ArmorStandPose("entertain").withHeadPose(new Rotations(-15.0F, 0.0F, 0.0F)).withLeftArmPose(new Rotations(-110.0F, -35.0F, 0.0F)).withLeftLegPose(new Rotations(5.0F, -3.0F, -3.0F)).withRightArmPose(new Rotations(-110.0F, 35.0F, 0.0F)).withRightLegPose(new Rotations(-5.0F, 3.0F, 3.0F)); - public static final ArmorStandPose HERO = new ArmorStandPose("hero").withBodyPose(new Rotations(0.0F, 8.0F, 0.0F)).withHeadPose(new Rotations(-4.0F, 67.0F, 0.0F)).withLeftArmPose(new Rotations(16.0F, 32.0F, -8.0F)).withLeftLegPose(new Rotations(0.0F, -75.0F, -8.0F)).withRightArmPose(new Rotations(-99.0F, 63.0F, 0.0F)).withRightLegPose(new Rotations(4.0F, 63.0F, 8.0F)); - public static final ArmorStandPose HONOR = new ArmorStandPose("honor").withHeadPose(new Rotations(-15.0F, 0.0F, 0.0F)).withLeftArmPose(new Rotations(-110.0F, 35.0F, 0.0F)).withLeftLegPose(new Rotations(5.0F, -3.0F, -3.0F)).withRightArmPose(new Rotations(-110.0F, -35.0F, 0.0F)).withRightLegPose(new Rotations(-5.0F, 3.0F, 3.0F)); - public static final ArmorStandPose RIPOSTE = new ArmorStandPose("riposte").withHeadPose(new Rotations(16.0F, 20.0F, 0.0F)).withLeftArmPose(new Rotations(4.0F, 8.0F, 237.0F)).withLeftLegPose(new Rotations(-14.0F, -18.0F, -16.0F)).withRightArmPose(new Rotations(246.0F, 0.0F, 89.0F)).withRightLegPose(new Rotations(8.0F, 20.0F, 4.0F)); - public static final ArmorStandPose SALUTE = new ArmorStandPose("salute").withLeftArmPose(new Rotations(10.0F, 0.0F, -5.0F)).withLeftLegPose(new Rotations(-1.0F, 0.0F, -1.0F)).withRightArmPose(new Rotations(-70.0F, -40.0F, 0.0F)).withRightLegPose(new Rotations(1.0F, 0.0F, 1.0F)); - public static final ArmorStandPose SOLEMN = new ArmorStandPose("solemn").withBodyPose(new Rotations(0.0F, 0.0F, 2.0F)).withHeadPose(new Rotations(15.0F, 0.0F, 0.0F)).withLeftArmPose(new Rotations(-30.0F, 15.0F, 15.0F)).withLeftLegPose(new Rotations(-1.0F, 0.0F, -1.0F)).withRightArmPose(new Rotations(-60.0F, -20.0F, -10.0F)).withRightLegPose(new Rotations(1.0F, 0.0F, 1.0F)); - public static final ArmorStandPose ZOMBIE = new ArmorStandPose("zombie").withHeadPose(new Rotations(-10.0F, 0.0F, -5.0F)).withLeftArmPose(new Rotations(-105.0F, 0.0F, 0.0F)).withLeftLegPose(new Rotations(7.0F, 0.0F, 0.0F)).withRightArmPose(new Rotations(-100.0F, 0.0F, 0.0F)).withRightLegPose(new Rotations(-46.0F, 0.0F, 0.0F)); - public static final double DEGREES_SNAP_INTERVAL = 0.125; - public static final DecimalFormat ROTATION_FORMAT = Util.make(new DecimalFormat("#.##"), (decimalFormat) -> { - decimalFormat.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ROOT)); - }); - private static final Random RANDOM = new Random(); - - @Nullable - private final String translationId; - private final Rotations headPose; - private final Rotations bodyPose; - private final Rotations leftArmPose; - private final Rotations rightArmPose; - private final Rotations leftLegPose; - private final Rotations rightLegPose; - - private ArmorStandPose(@Nullable String translationId) { - this(translationId, new Rotations(0.0F, 0.0F, 0.0F), new Rotations(0.0F, 0.0F, 0.0F), new Rotations(0.0F, 0.0F, 0.0F), new Rotations(0.0F, 0.0F, 0.0F), new Rotations(0.0F, 0.0F, 0.0F), new Rotations(0.0F, 0.0F, 0.0F)); - } - - private ArmorStandPose(@Nullable String translationId, Rotations headPose, Rotations bodyPose, Rotations leftArmPose, Rotations rightArmPose, Rotations leftLegPose, Rotations rightLegPose) { - this.translationId = translationId; - this.headPose = headPose; - this.bodyPose = bodyPose; - this.leftArmPose = leftArmPose; - this.rightArmPose = rightArmPose; - this.leftLegPose = leftLegPose; - this.rightLegPose = rightLegPose; - } - - public static ArmorStandPose empty() { - return new ArmorStandPose(null); - } - - @Override - public String toString() { - if (this.translationId != null) { - return this.translationId.toUpperCase(Locale.ROOT); - } - return super.toString(); - } - - public Component getComponent() { - return Component.translatable("armorstatues.entity.armor_stand.pose." + Objects.requireNonNull(this.translationId, "Trying to get component for transient armor stand pose")); - } - - public Rotations getHeadPose() { - return this.headPose; - } - - public Rotations getBodyPose() { - return this.bodyPose; - } - - public Rotations getLeftArmPose() { - return this.leftArmPose; - } - - public Rotations getRightArmPose() { - return this.rightArmPose; - } - - public Rotations getLeftLegPose() { - return this.leftLegPose; - } - - public Rotations getRightLegPose() { - return this.rightLegPose; - } - - public ArmorStandPose withHeadPose(Rotations rotation) { - return new ArmorStandPose(this.translationId, rotation, this.bodyPose, this.leftArmPose, this.rightArmPose, this.leftLegPose, this.rightLegPose); - } - - public ArmorStandPose withBodyPose(Rotations rotation) { - return new ArmorStandPose(this.translationId, this.headPose, rotation, this.leftArmPose, this.rightArmPose, this.leftLegPose, this.rightLegPose); - } - - public ArmorStandPose withLeftArmPose(Rotations rotation) { - return new ArmorStandPose(this.translationId, this.headPose, this.bodyPose, rotation, this.rightArmPose, this.leftLegPose, this.rightLegPose); - } - - public ArmorStandPose withRightArmPose(Rotations rotation) { - return new ArmorStandPose(this.translationId, this.headPose, this.bodyPose, this.leftArmPose, rotation, this.leftLegPose, this.rightLegPose); - } - - public ArmorStandPose withLeftLegPose(Rotations rotation) { - return new ArmorStandPose(this.translationId, this.headPose, this.bodyPose, this.leftArmPose, this.rightArmPose, rotation, this.rightLegPose); - } - - public ArmorStandPose withRightLegPose(Rotations rotation) { - return new ArmorStandPose(this.translationId, this.headPose, this.bodyPose, this.leftArmPose, this.rightArmPose, this.leftLegPose, rotation); - } - - public void applyToEntity(ArmorStand armorStand) { - armorStand.setHeadPose(this.headPose); - armorStand.setBodyPose(this.bodyPose); - armorStand.setLeftArmPose(this.leftArmPose); - armorStand.setRightArmPose(this.rightArmPose); - armorStand.setLeftLegPose(this.leftLegPose); - armorStand.setRightLegPose(this.rightLegPose); - } - - public void serializeAllPoses(CompoundTag tag) { - this.serializeBodyPoses(tag, null); - this.serializeArmPoses(tag, null); - this.serializeLegPoses(tag, null); - } - - public boolean serializeBodyPoses(CompoundTag tag, @Nullable ArmorStandPose lastSentPose) { - if (lastSentPose == null || !this.headPose.equals(lastSentPose.headPose) || !this.bodyPose.equals(lastSentPose.bodyPose)) { - tag.put("Head", this.headPose.save()); - tag.put("Body", this.bodyPose.save()); - return true; - } - return false; - } - - public boolean serializeArmPoses(CompoundTag tag, @Nullable ArmorStandPose lastSentPose) { - if (lastSentPose == null || !this.leftArmPose.equals(lastSentPose.leftArmPose) || !this.rightArmPose.equals(lastSentPose.rightArmPose)) { - tag.put("LeftArm", this.leftArmPose.save()); - tag.put("RightArm", this.rightArmPose.save()); - return true; - } - return false; - } - - public boolean serializeLegPoses(CompoundTag tag, @Nullable ArmorStandPose lastSentPose) { - if (lastSentPose == null || !this.leftLegPose.equals(lastSentPose.leftLegPose) || !this.rightLegPose.equals(lastSentPose.rightLegPose)) { - tag.put("LeftLeg", this.leftLegPose.save()); - tag.put("RightLeg", this.rightLegPose.save()); - return true; - } - return false; - } - - public static ArmorStandPose fromEntity(ArmorStand armorStand) { - return new ArmorStandPose(null, armorStand.getHeadPose(), armorStand.getBodyPose(), armorStand.getLeftArmPose(), armorStand.getRightArmPose(), armorStand.getLeftLegPose(), armorStand.getRightLegPose()); - } - - public static void applyTagToEntity(ArmorStand armorStand, CompoundTag tag) { - ((ArmorStandAccessor) armorStand).callReadPose(tag); - } - - public static ArmorStandPose random(PosePartMutator[] mutators, boolean clampRotations) { - checkMutatorsSize(mutators); - return new ArmorStandPose(null, mutators[0].randomRotations(clampRotations), mutators[1].randomRotations(clampRotations), mutators[2].randomRotations(clampRotations), mutators[3].randomRotations(clampRotations), mutators[4].randomRotations(clampRotations), mutators[5].randomRotations(clampRotations)); - } - - public static ArmorStandPose[] values() { - return new ArmorStandPose[]{DEFAULT, SOLEMN, ATHENA, BRANDISH, HONOR, ENTERTAIN, SALUTE, HERO, RIPOSTE, ZOMBIE, CANCAN_A, CANCAN_B}; - } - - public static ArmorStandPose selectRandomPose() { - ArmorStandPose[] values = values(); - return values[RANDOM.nextInt(values().length)]; - } - - public static void checkMutatorsSize(PosePartMutator[] mutators) { - if (mutators.length != 6) throw new IllegalArgumentException("Invalid mutators size: Expected 6, was %s".formatted(mutators.length)); - } - - public static double snapValue(double value, double snapInterval) { - if (snapInterval > 0.0 && snapInterval < 1.0) { - double currentSnap = 0.0; - while (currentSnap < 1.0) { - double snapRegion = snapInterval * 0.1; - if (value >= currentSnap - snapRegion && value < currentSnap + snapRegion) { - return currentSnap; - } - currentSnap += snapInterval; - } - } - return value; - } -} diff --git a/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java b/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java deleted file mode 100644 index ddb7afa..0000000 --- a/Common/src/main/java/fuzs/armorstatues/init/ModRegistry.java +++ /dev/null @@ -1,23 +0,0 @@ -package fuzs.armorstatues.init; - -import fuzs.armorstatues.ArmorStatues; -import fuzs.armorstatues.api.ArmorStatuesApi; -import fuzs.armorstatues.api.world.inventory.ArmorStandMenu; -import fuzs.puzzleslib.core.CoreServices; -import fuzs.puzzleslib.init.RegistryManager; -import fuzs.puzzleslib.init.RegistryReference; -import fuzs.puzzleslib.init.builder.ExtendedModMenuSupplier; -import net.minecraft.world.inventory.MenuType; - -public class ModRegistry { - private static final RegistryManager REGISTRY = CoreServices.FACTORIES.registration(ArmorStatues.MOD_ID); - public static final RegistryReference> ARMOR_STAND_MENU_TYPE = REGISTRY.registerExtendedMenuTypeSupplier("armor_stand", () -> getArmorStandMenuTypeSupplier()); - - public static void touch() { - - } - - private static ExtendedModMenuSupplier getArmorStandMenuTypeSupplier() { - return (containerId, inventory, data) -> ArmorStandMenu.create(ARMOR_STAND_MENU_TYPE.get(), containerId, inventory, data); - } -} diff --git a/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java b/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java deleted file mode 100644 index c254070..0000000 --- a/Common/src/main/java/fuzs/armorstatues/proxy/ClientProxy.java +++ /dev/null @@ -1,18 +0,0 @@ -package fuzs.armorstatues.proxy; - -import fuzs.armorstatues.api.client.gui.screens.armorstand.ArmorStandScreenFactory; -import fuzs.armorstatues.api.network.client.data.CommandDataSyncHandler; -import fuzs.armorstatues.api.world.inventory.ArmorStandHolder; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.world.entity.decoration.ArmorStand; -import net.minecraft.world.entity.player.Player; - -public class ClientProxy extends ServerProxy { - - @Override - public void openArmorStandScreen(ArmorStand armorStand, Player player) { - Screen screen = ArmorStandScreenFactory.createLastScreenType(ArmorStandHolder.simple(armorStand), player.getInventory(), armorStand.getDisplayName(), new CommandDataSyncHandler(armorStand)); - Minecraft.getInstance().setScreen(screen); - } -} diff --git a/Common/src/main/resources/quilt.mod.json b/Common/src/main/resources/quilt.mod.json deleted file mode 100644 index 878577d..0000000 --- a/Common/src/main/resources/quilt.mod.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "schema_version": 1, - "quilt_loader": { - "group": "${modGroup}", - "id": "${modId}", - "version": "${modVersion}" - } -} diff --git a/Fabric/build.gradle b/Fabric/build.gradle deleted file mode 100644 index ec38e02..0000000 --- a/Fabric/build.gradle +++ /dev/null @@ -1,280 +0,0 @@ -plugins { - id 'fabric-loom' version '0.12-SNAPSHOT' - id 'io.github.juuxel.loom-quiltflower' version '1.7.1' - // this depends on an older version of guava, which loom is incompatible with, so make sure to apply this plugin after loom - // haven't found a proper way to manage plugin dependencies otherwise - id 'me.hypherionmc.cursegradle' version '2.+' - // cannot apply this in the base build.gradle as it'll be the same for all subprojects, only one configuration will work - id 'com.modrinth.minotaur' version '2.+' -} - -archivesBaseName = rootProject.name -version = "v${modVersion}-${minecraftVersion}-Fabric" -group = modMavenGroup - -repositories { - maven { - name = "Modmuss" - url = "https://maven.modmuss50.me/" - } - maven { - name = 'Terraformers' - url = "https://maven.terraformersmc.com/" - } - maven { - name = "ladysnake" - url = 'https://ladysnake.jfrog.io/artifactory/mods' - } - maven { - name = "jamieswhiteshirt" - url = "https://maven.jamieswhiteshirt.com/libs-release/" - } -} - -dependencies { - // Include Common Project - compileOnly project(":Common") - - // Minecraft - minecraft "com.mojang:minecraft:${minecraftVersion}" - mappings loom.layered() { - officialMojangMappings() - parchment("org.parchmentmc.data:parchment-${minecraftVersion}:${parchmentMappingsVersion}@zip") - } - - // Fabric - modImplementation "net.fabricmc:fabric-loader:${fabricVersion}" - modImplementation "net.fabricmc.fabric-api:fabric-api:${fabricApiVersion}" - - // Forge Configs - modImplementation "net.minecraftforge:forgeconfigapiport-fabric:4.2.6" - - // Quality of Life Mods - modRuntimeOnly "com.terraformersmc:modmenu:4.0.6" - modRuntimeOnly "curse.maven:tooltipfix-411557:3820437" - - // Puzzles Lib - modImplementation "fuzs.puzzleslib:puzzleslib-fabric:${puzzlesVersion}" -} - -loom { - mixin.defaultRefmapName = "${modId}.refmap.json" - - runs { - client { - client() - setConfigName("Fabric Client") - ideConfigGenerated(true) - runDir("../run") - vmArg '-Dmixin.debug.export=true' - } - server { - server() - setConfigName("Fabric Server") - ideConfigGenerated(true) - runDir("../run") - vmArg '-Dmixin.debug.export=true' - } - } -} - -processResources { - from(project(":Common").sourceSets.main.resources) { - // we need to have this in common so that in a non-production environment the common jar is correctly deobfuscated - exclude("quilt.mod.json") - } - from(project(":Forge").file('src/generated/resources')) { - exclude('.cache/') - } - // Forge's data gen doesn't work with assets placed in the common project, so we place them in Forge and include them here - from(project(":Forge").sourceSets.main.resources) { - include("assets/") - include("data/") - } - - duplicatesStrategy DuplicatesStrategy.INCLUDE - - // this will ensure that this task is redone when a value changes - inputs.property "modId", "${modId}" - inputs.property "modName", "${modName}" - inputs.property "modVersion", "${modVersion}" - inputs.property "modDescription", "${modDescription}" - inputs.property "modGroup", project.group - inputs.property "modPageUrl", "${modSourceUrl}" - inputs.property "modIssueUrl", "${modIssueUrl}" - inputs.property "modAuthor", "${modAuthor}" - inputs.property "minFabricVersion", "${minFabricVersion}" - inputs.property "minFabricApiVersion", "${minFabricApiVersion}" - inputs.property "minMinecraftVersion", "${minMinecraftVersion}" - inputs.property "nextMinecraftVersion", rootProject.getNextVersion("${minMinecraftVersion}") - inputs.property "minPuzzlesVersion", "${minPuzzlesVersion}" - inputs.property "packFormat", "${packFormat}" - inputs.property "mainEntryPoint", "${project.group}.${rootProject.name}Fabric" - inputs.property "clientEntryPoint", "${project.group}.client.${rootProject.name}FabricClient" - inputs.property "modFabricEnvironment", "${modFabricEnvironment}" - - // replace stuff in fabric.mod.json and pack.mcmeta - filesMatching ('fabric.mod.json') { - expand ( - 'modId': "${modId}", - 'modName': "${modName}", - 'modVersion': "${modVersion}", - 'modDescription': "${modDescription}", - 'modGroup': project.group, - 'modPageUrl': "${modSourceUrl}", - 'modIssueUrl': "${modIssueUrl}", - 'modAuthor': "${modAuthor}", - 'minFabricVersion': "${minFabricVersion}", - 'minFabricApiVersion': "${minFabricApiVersion}", - 'minMinecraftVersion': "${minMinecraftVersion}", - "nextMinecraftVersion": rootProject.getNextVersion("${minMinecraftVersion}"), - "minPuzzlesVersion": "${minPuzzlesVersion}", - "mainEntryPoint": "${project.group}.${rootProject.name}Fabric", - "clientEntryPoint": "${project.group}.client.${rootProject.name}FabricClient", - "modFabricEnvironment": "${modFabricEnvironment}" - ) - } - - filesMatching ('pack.mcmeta') { - expand ( - 'modDescription': "${modDescription}", - "packFormat": "${packFormat}" - ) - } -} - -compileJava { - source project(":Common").sourceSets.main.allSource -} - -sourcesJar { - from project(":Common").sourceSets.main.allJava -} - -javadoc { - source project(":Common").sourceSets.main.allJava -} - -publishing { - publications { - mavenJava (MavenPublication) { - artifactId = "${modId}-fabric" - version = modVersion - from components.java - pom { - name = "${modName} [Fabric]" - description = "${modDescription}" - url = "${modSourceUrl}" - scm { - url = "${modSourceUrl}" - connection = "${modSourceUrl}".replace("https", "scm:git:git").concat(".git") - developerConnection = "${modSourceUrl}".replace("https://github.com/", "scm:git:git@github.com:").concat(".git") - } - issueManagement { - system = 'github' - url = "${modIssueUrl}" - } - licenses { - license { - name = 'MPL-2' - url = 'https://www.mozilla.org/en-US/MPL/2.0/' - } - } - developers { - developer { - id = "${modAuthor}".toLowerCase() - name = "${modAuthor}" - } - } - } - afterEvaluate { - // exclude certain dependencies when publishing to maven - // from https://stackoverflow.com/a/50121790 - pom.withXml { - asNode().dependencies.dependency.each { dep -> - // use this approach to make excluding dependencies from Curse Maven more convenient - if (["curse.maven:", "com.terraformersmc:modmenu"].stream().anyMatch(mod -> "${dep.groupId.last().value().last()}:${dep.artifactId.last().value().last()}".startsWith(mod))) { - assert dep.parent().remove(dep) - } - } - } - } - } - } - repositories { - maven { - name = 'FuzsModResources' - url "file://" + project.hasProperty('modResources') ? "${project.findProperty('modResources')}/maven" : System.getenv('local_maven') - } - } -} - -signing { - sign publishing.publications.mavenJava -} - -curseforge { - if (!file('../CHANGELOG.md').canRead()) { throw new FileNotFoundException("Could not read changelog file") } - apiKey = project.hasProperty('curseApiToken') ? project.findProperty('curseApiToken') : '' - project { - id = projectCurseId - changelogType = 'markdown' - changelog = file('../CHANGELOG.md') - releaseType = projectReleaseType - addGameVersion 'Fabric' - projectGameVersions.split(",").each { - addGameVersion it.trim() - } - mainArtifact(remapJar) { - displayName = "[FABRIC] [${minecraftVersion}] ${rootProject.name}-v${modVersion}" - relations { - requiredDependency 'fabric-api' - requiredDependency 'forge-config-api-port-fabric' - requiredDependency 'puzzles-lib' - } - } - addArtifact sourcesJar - } - options { -// debug = true - javaVersionAutoDetect = false - forgeGradleIntegration = false - } -} - -modrinth { - if (!file('../CHANGELOG.md').canRead()) { throw new FileNotFoundException("Could not read changelog file") } - token = project.hasProperty('modrinthApiToken') ? project.findProperty('modrinthApiToken') : '' - projectId = projectModrinthId - versionNumber = project.version - versionName = "[FABRIC] [${minecraftVersion}] ${rootProject.name}-v${modVersion}" - changelog = file('../CHANGELOG.md').text - versionType = projectReleaseType - uploadFile = remapJar // This is the java jar task - projectGameVersions.split(",").each { - gameVersions.add it.trim() - } - loaders.add 'fabric' - additionalFiles.add file("${project.buildDir}/libs/${project.archivesBaseName}-${project.version}-sources.jar") - dependencies { - required.project 'fabric-api' - required.project 'forge-config-api-port' - required.project 'puzzles-lib' - } -// debugMode = true -} - -import groovy.json.* - -task copyJarToDir(type: Copy) { - onlyIf { project.hasProperty('buildJarOutputDir') && project.hasProperty('uniqueBuildNumber') } - if (project.findProperty('copyBuildJar').toBoolean()) { - from remapJar - into project.findProperty('buildJarOutputDir') - // add build number to be able to distinguish jars when testing thorough official launcher - // build number is stored in global gradle.properties - rename { fileName -> fileName.replace("v${modVersion}", "v${modVersion}.${uniqueBuildNumber}") } - } -} - -build.finalizedBy project.tasks.copyJarToDir, rootProject.tasks.incrementBuildNumber diff --git a/Forge/build.gradle b/Forge/build.gradle deleted file mode 100644 index 44fd0cd..0000000 --- a/Forge/build.gradle +++ /dev/null @@ -1,378 +0,0 @@ -buildscript { - repositories { - mavenCentral() - maven { url = 'https://maven.minecraftforge.net' } - maven { url = 'https://repo.spongepowered.org/repository/maven-public/' } - } - dependencies { - classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true - classpath 'org.spongepowered:mixingradle:0.7-SNAPSHOT' - classpath group: 'org.parchmentmc', name: 'librarian', version: '1.+' - } -} - -plugins { - id 'me.hypherionmc.cursegradle' version '2.+' - // cannot apply this in the base build.gradle as it'll be the same for all subprojects, only one configuration will work - id 'com.modrinth.minotaur' version '2.+' -} - -apply plugin: 'net.minecraftforge.gradle' -apply plugin: 'eclipse' -apply plugin: 'org.spongepowered.mixin' -apply plugin: 'org.parchmentmc.librarian.forgegradle' - -archivesBaseName = rootProject.name -version = "v${modVersion}-${minecraftVersion}-Forge" -group = modMavenGroup - -minecraft { - mappings channel: 'parchment', version: "${parchmentMappingsVersion}-${minecraftVersion}" -// mappings channel: 'official', version: "${minecraftVersion}" - - runs { - client { - workingDirectory project.file('../run') - jvmArgs '-Xms1G', '-Xmx4G' - property 'fml.earlyprogresswindow', 'false' - if (project(":Common").file("src/main/resources/${modId}.common.mixins.json").exists()) { - arg "-mixin.config=${modId}.common.mixins.json" - } - if (project.file("src/main/resources/${modId}.forge.mixins.json").exists()) { - arg "-mixin.config=${modId}.forge.mixins.json" - } - ideaModule "${rootProject.name}.${project.name}.main" - taskName 'Client' - property 'terminal.ansi', 'true' - property 'mixin.env.remapRefMap', 'true' - property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" - property 'mixin.debug.export', 'true' - mods { - modClientRun { - source sourceSets.main - source project(":Common").sourceSets.main - } - } - } - - server { - workingDirectory project.file('../run') - jvmArgs '-Xms1G', '-Xmx4G' - arg 'nogui' - if (project(":Common").file("src/main/resources/${modId}.common.mixins.json").exists()) { - arg "-mixin.config=${modId}.common.mixins.json" - } - if (project.file("src/main/resources/${modId}.forge.mixins.json").exists()) { - arg "-mixin.config=${modId}.forge.mixins.json" - } - ideaModule "${rootProject.name}.${project.name}.main" - taskName 'Server' - property 'terminal.ansi', 'true' - property 'mixin.env.remapRefMap', 'true' - property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" - property 'mixin.debug.export', 'true' - mods { - modServerRun { - source sourceSets.main - source project(":Common").sourceSets.main - } - } - } - - data { - workingDirectory project.file('../run') - jvmArgs '-Xms1G', '-Xmx4G' - if (project(":Common").file("src/main/resources/${modId}.common.mixins.json").exists()) { - arg "-mixin.config=${modId}.common.mixins.json" - } - if (project.file("src/main/resources/${modId}.forge.mixins.json").exists()) { - arg "-mixin.config=${modId}.forge.mixins.json" - } - args '--mod', modId, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') - ideaModule "${rootProject.name}.${project.name}.main" - taskName 'Data' - property 'terminal.ansi', 'true' - property 'mixin.env.remapRefMap', 'true' - property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" - property 'mixin.debug.export', 'true' - mods { - modDataRun { - source sourceSets.main - source project(":Common").sourceSets.main - } - } - } - } -} - -dependencies { - // Include Common Project - compileOnly project(":Common") - - // Minecraft - minecraft "net.minecraftforge:forge:${forgeVersion}" - annotationProcessor 'org.spongepowered:mixin:0.8.5:processor' - - // Quality of Life Mods - runtimeOnly fg.deobf("curse.maven:catalogue-459701:3873264") - runtimeOnly fg.deobf("fuzs.bettermodsbutton:bettermodsbutton-forge:4.2.0") -// runtimeOnly fg.deobf("curse.maven:configmenusforge-544048:3822820") -// runtimeOnly fg.deobf("curse.maven:configured-457570:3903908") - - // Puzzles Lib - implementation fg.deobf("fuzs.puzzleslib:puzzleslib-forge:${puzzlesVersion}") -} - -mixin { - if (project.file("src/main/resources/${modId}.forge.mixins.json").exists() || project(":Common").file("src/main/resources/${modId}.common.mixins.json").exists()) { - add project(":Common").sourceSets.main, "${modId}.refmap.json" - } - if (project(":Common").file("src/main/resources/${modId}.common.mixins.json").exists()) { - config "${modId}.common.mixins.json" - } - if (project.file("src/main/resources/${modId}.forge.mixins.json").exists()) { - config "${modId}.forge.mixins.json" - } -} - -processResources { - from(project(":Common").sourceSets.main.resources) { - // we need to have this in common so that in a non-production environment the common jar is correctly deobfuscated - exclude("quilt.mod.json") - } - from(file('src/generated/resources')) { - exclude('.cache/') - } - - duplicatesStrategy DuplicatesStrategy.INCLUDE - - // this will ensure that this task is redone when a value changes - inputs.property "modId", "${modId}" - inputs.property "modName", "${modName}" - inputs.property "modVersion", "${modVersion}" - inputs.property "modDescription", "${modDescription}" - inputs.property "modGroup", project.group - inputs.property "modPageUrl", "${modSourceUrl}" - inputs.property "modUpdateUrl", "${modUpdateUrl}" - inputs.property "modIssueUrl", "${modIssueUrl}" - inputs.property "modAuthor", "${modAuthor}" - inputs.property "minFMLVersion", "${minForgeVersion}".replaceAll("\\..*", "") - inputs.property "minForgeVersion", "${minForgeVersion}" - inputs.property "minMinecraftVersion", "${minMinecraftVersion}" - inputs.property "nextMinecraftVersion", rootProject.getNextVersion("${minMinecraftVersion}") - inputs.property "minPuzzlesVersion", "${minPuzzlesVersion}" - inputs.property "packFormat", "${packFormat}" - inputs.property "modForgeDisplayTest", "${modForgeDisplayTest}" - - // replace stuff in mods.toml and pack.mcmeta - filesMatching ('META-INF/mods.toml') { - expand ( - 'modId': "${modId}", - 'modName': "${modName}", - 'modVersion': "${modVersion}", - 'modDescription': "${modDescription}", - 'modGroup': project.group, - 'modPageUrl': "${modSourceUrl}", - 'modUpdateUrl': "${modUpdateUrl}", - 'modIssueUrl': "${modIssueUrl}", - 'modAuthor': "${modAuthor}", - 'minFMLVersion': "${minForgeVersion}".replaceAll("\\..*", ""), - 'minForgeVersion': "${minForgeVersion}", - 'minMinecraftVersion': "${minMinecraftVersion}", - 'nextMinecraftVersion': rootProject.getNextVersion("${minMinecraftVersion}"), - 'minPuzzlesVersion': "${minPuzzlesVersion}", - 'modForgeDisplayTest': "${modForgeDisplayTest}" - ) - } - - filesMatching ('pack.mcmeta') { - expand ( - 'modDescription': "${modDescription}", - "packFormat": "${packFormat}" - ) - } -} - -compileJava { - source project(":Common").sourceSets.main.allSource -} - -sourcesJar { - from project(":Common").sourceSets.main.allJava -} - -javadoc { - source project(":Common").sourceSets.main.allJava -} - -// important: the task may not run before 'compileJava', otherwise overridden/shadowed fields and methods in mixin classes will not be reobfuscated -jar.finalizedBy("configureReobfTaskForReobfJar") -jar.finalizedBy('reobfJar') - -publishing { - publications { - mavenJava (MavenPublication) { - artifactId = "${modId}-forge" - version = modVersion - from components.java - // strip Forge dependency from POM - fg.component(it) - pom { - name = "${modName} [Forge]" - description = "${modDescription}" - url = "${modSourceUrl}" - scm { - url = "${modSourceUrl}" - connection = "${modSourceUrl}".replace("https", "scm:git:git").concat(".git") - developerConnection = "${modSourceUrl}".replace("https://github.com/", "scm:git:git@github.com:").concat(".git") - } - issueManagement { - system = 'github' - url = "${modIssueUrl}" - } - licenses { - license { - name = 'MPL-2' - url = 'https://www.mozilla.org/en-US/MPL/2.0/' - } - } - developers { - developer { - id = "${modAuthor}".toLowerCase() - name = "${modAuthor}" - } - } - } - afterEvaluate { - // exclude certain dependencies when publishing to maven - // from https://stackoverflow.com/a/50121790 - pom.withXml { - asNode().dependencies.dependency.each { dep -> - // use this approach to make excluding dependencies from Curse Maven more convenient - if (["curse.maven:", "fuzs.bettermodsbutton:bettermodsbutton-forge"].stream().anyMatch(mod -> "${dep.groupId.last().value().last()}:${dep.artifactId.last().value().last()}".startsWith(mod))) { - assert dep.parent().remove(dep) - } - } - } - } - } - } - repositories { - maven { - name = 'FuzsModResources' - url "file://" + project.hasProperty('modResources') ? "${project.findProperty('modResources')}/maven" : System.getenv('local_maven') - } - } -} - -signing { - sign publishing.publications.mavenJava -} - -task signJar(type: net.minecraftforge.gradle.common.tasks.SignJar, dependsOn: jar) { - onlyIf { project.hasProperty('keyStore') } - keyStore = project.findProperty('keyStore') - alias = project.findProperty('keyStoreAlias') - storePass = project.findProperty('keyStorePass') - keyPass = project.findProperty('keyStoreKeyPass') - inputFile = outputFile = jar.archivePath -} - -jar.finalizedBy 'signJar' -signJar.mustRunAfter 'reobfJar' - -curseforge { - if (!file('../CHANGELOG.md').canRead()) { throw new FileNotFoundException("Could not read changelog file") } - apiKey = project.hasProperty('curseApiToken') ? project.findProperty('curseApiToken') : '' - project { - id = projectCurseId - changelogType = 'markdown' - changelog = file('../CHANGELOG.md') - releaseType = projectReleaseType - addGameVersion 'Forge' - projectGameVersions.split(",").each { - addGameVersion it.trim() - } - mainArtifact(jar) { - displayName = "[FORGE] [${minecraftVersion}] ${rootProject.name}-v${modVersion}" - relations { - requiredDependency 'puzzles-lib' - } - } - addArtifact sourcesJar - } - options { -// debug = true - javaVersionAutoDetect = false - forgeGradleIntegration = false - } -} - -modrinth { - if (!file('../CHANGELOG.md').canRead()) { throw new FileNotFoundException("Could not read changelog file") } - token = project.hasProperty('modrinthApiToken') ? project.findProperty('modrinthApiToken') : '' - projectId = projectModrinthId - versionNumber = project.version - versionName = "[FORGE] [${minecraftVersion}] ${rootProject.name}-v${modVersion}" - changelog = file('../CHANGELOG.md').text - versionType = projectReleaseType - uploadFile = jar // This is the java jar task - projectGameVersions.split(",").each { - gameVersions.add it.trim() - } - loaders.add 'forge' - additionalFiles.add file("${project.buildDir}/libs/${project.archivesBaseName}-${project.version}-sources.jar") - dependencies { - required.project 'puzzles-lib' - } -// debugMode = true -} - -import groovy.json.* - -task copyJarToDir(type: Copy) { - onlyIf { project.hasProperty('buildJarOutputDir') && project.hasProperty('uniqueBuildNumber') } - if (project.findProperty('copyBuildJar').toBoolean()) { - // shortcut for jar.outputs.files - from jar - into project.findProperty('buildJarOutputDir') - // add build number to be able to distinguish jars when testing thorough official launcher - // build number is stored in global gradle.properties - rename { fileName -> fileName.replace("v${modVersion}", "v${modVersion}.${uniqueBuildNumber}") } - } -} - -task refreshUpdateJson { - onlyIf { project.hasProperty('modResources') } - doLast { - def updateFile = file(project.findProperty('modResources').concat(File.separator).concat('update').concat(File.separator).concat("${modId}").concat('.json')) - def updateJson - if (updateFile.exists() && updateFile.canRead()) { - updateJson = new JsonSlurper().parseText(updateFile.text) - "${projectGameVersions}".replaceAll(" ", "").split(",").each { version -> - updateJson['promos']["${version}-latest"] = "${modVersion}" - // alpha and beta releases will contain 'a' or 'b' char respectively, don't update recommended for those - if ("${modVersion}".matches("[^a-zA-Z]+")) { - updateJson['promos']["${version}-recommended"] = "${modVersion}" - } - } - } else { - def builder = new JsonBuilder() - updateJson = builder { - homepage "${modSourceUrl}" - promos { "${projectGameVersions}".replaceAll(" ", "").split(",").each { version -> - "${version}-latest" "${modVersion}" - // alpha and beta releases will contain 'a' or 'b' char respectively, don't update recommended for those - if ("${modVersion}".matches("[^a-zA-Z]+")) { - "${version}-recommended" "${modVersion}" - } - } } - } - } - def output = new JsonOutput() - updateFile.write(output.prettyPrint(output.toJson(updateJson))) - } -} - -build.finalizedBy project.tasks.copyJarToDir, rootProject.tasks.incrementBuildNumber -[tasks.modrinth, tasks.curseforge].each {it.finalizedBy project.tasks.refreshUpdateJson} diff --git a/Forge/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 b/Forge/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 deleted file mode 100644 index be7c05c..0000000 --- a/Forge/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 +++ /dev/null @@ -1,2 +0,0 @@ -// 1.19.2 2022-09-23T20:09:24.900309 Languages: en_us -6e73dfcadb10e01ff872707f9d4a53e8279ebe47 assets/armorstatues/lang/en_us.json diff --git a/Forge/src/generated/resources/assets/armorstatues/lang/en_us.json b/Forge/src/generated/resources/assets/armorstatues/lang/en_us.json deleted file mode 100644 index 2787a16..0000000 --- a/Forge/src/generated/resources/assets/armorstatues/lang/en_us.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "armorstatues.entity.armor_stand.pose.athena": "Athena", - "armorstatues.entity.armor_stand.pose.brandish": "Brandish", - "armorstatues.entity.armor_stand.pose.cancanA": "Cancan #1", - "armorstatues.entity.armor_stand.pose.cancanB": "Cancan #2", - "armorstatues.entity.armor_stand.pose.default": "Default", - "armorstatues.entity.armor_stand.pose.entertain": "Entertain", - "armorstatues.entity.armor_stand.pose.hero": "Hero", - "armorstatues.entity.armor_stand.pose.honor": "Honor", - "armorstatues.entity.armor_stand.pose.none": "None", - "armorstatues.entity.armor_stand.pose.riposte": "Riposte", - "armorstatues.entity.armor_stand.pose.salute": "Salute", - "armorstatues.entity.armor_stand.pose.solemn": "Solemn", - "armorstatues.entity.armor_stand.pose.zombie": "Zombie", - "armorstatues.item.armor_stand.description": "Shift + right-click to open configuration screen when placed.", - "armorstatues.screen.alignments.block": "Align Block", - "armorstatues.screen.alignments.credit": "Alignment values are taken from the Vanilla Tweaks \"Armor Statues\" data pack. Click this button to go to their website!", - "armorstatues.screen.alignments.itemFlat": "Align Item As Flat", - "armorstatues.screen.alignments.itemFloating": "Align Item As Floating", - "armorstatues.screen.alignments.tool": "Align Tool As Flat", - "armorstatues.screen.noPermission": "Unable to set new armor stand data; no permission", - "armorstatues.screen.pose.randomize": "Randomize", - "armorstatues.screen.pose.randomized": "Applied!", - "armorstatues.screen.position.aligned": "Aligned!", - "armorstatues.screen.position.blocks": "%s Block(s)", - "armorstatues.screen.position.centered": "Align Centered", - "armorstatues.screen.position.cornered": "Align Cornered", - "armorstatues.screen.position.decrement": "Decrement By %s", - "armorstatues.screen.position.degrees": "%s°", - "armorstatues.screen.position.increment": "Increment By %s", - "armorstatues.screen.position.moveBy": "Move By:", - "armorstatues.screen.position.pixels": "%s Pixel(s)", - "armorstatues.screen.position.rotation": "Rotation:", - "armorstatues.screen.position.x": "X-Position:", - "armorstatues.screen.position.y": "Y-Position:", - "armorstatues.screen.position.z": "Z-Position:", - "armorstatues.screen.rotations.copy": "Copy", - "armorstatues.screen.rotations.limited": "Limited Rotations", - "armorstatues.screen.rotations.paste": "Paste", - "armorstatues.screen.rotations.pose.body": "Body", - "armorstatues.screen.rotations.pose.head": "Head", - "armorstatues.screen.rotations.pose.leftArm": "Left Arm", - "armorstatues.screen.rotations.pose.leftLeg": "Left Leg", - "armorstatues.screen.rotations.pose.rightArm": "Right Arm", - "armorstatues.screen.rotations.pose.rightLeg": "Right Leg", - "armorstatues.screen.rotations.randomize": "Randomize", - "armorstatues.screen.rotations.reset": "Reset", - "armorstatues.screen.rotations.tip1": "Hold the Shift or Alt key to lock sliders to a single axis!", - "armorstatues.screen.rotations.tip2": "Use the arrow keys to move sliders more precisely! Focus a slider first by clicking.", - "armorstatues.screen.rotations.tip3": "Press the %s key (or Shift + %s) to quickly switch between tabs!", - "armorstatues.screen.rotations.unlimited": "Unlimited Rotations", - "armorstatues.screen.rotations.x": "X: %s", - "armorstatues.screen.rotations.y": "Y: %s", - "armorstatues.screen.rotations.z": "Z: %s", - "armorstatues.screen.style.glowing": "Glowing", - "armorstatues.screen.style.glowing.description": "Adds a glowing outline to the statue, visible through blocks.", - "armorstatues.screen.style.invisible": "Invisible", - "armorstatues.screen.style.invisible.description": "Makes the statue itself invisible, but still shows all equipped items.", - "armorstatues.screen.style.noBasePlate": "No Base Plate", - "armorstatues.screen.style.noBasePlate.description": "Hide the stone base plate at the statue's feet.", - "armorstatues.screen.style.noGravity": "No Gravity", - "armorstatues.screen.style.noGravity.description": "Prevents the statue from falling down, so it may float freely.", - "armorstatues.screen.style.sealed": "Sealed", - "armorstatues.screen.style.sealed.description": "The statue can no longer be broken, equipment cannot be changed. Disallows opening this menu in survival mode.", - "armorstatues.screen.style.showArms": "Show Arms", - "armorstatues.screen.style.showArms.description": "Shows the statue's arms, so it may hold items in both hands.", - "armorstatues.screen.style.showName": "Show Name", - "armorstatues.screen.style.showName.description": "Render the statue's name tag above it's head.", - "armorstatues.screen.style.small": "Small", - "armorstatues.screen.style.small.description": "Makes the statue half it's size like a baby mob.", - "armorstatues.screen.type.alignments": "Alignments", - "armorstatues.screen.type.equipment": "Equipment", - "armorstatues.screen.type.poses": "Poses", - "armorstatues.screen.type.position": "Position", - "armorstatues.screen.type.rotations": "Rotations", - "armorstatues.screen.type.style": "Style", - "key.cycleStatueTabs": "Cycle Statue Tabs" -} \ No newline at end of file diff --git a/Forge/src/main/java/fuzs/armorstatues/client/ArmorStatuesForgeClient.java b/Forge/src/main/java/fuzs/armorstatues/client/ArmorStatuesForgeClient.java deleted file mode 100644 index 195de76..0000000 --- a/Forge/src/main/java/fuzs/armorstatues/client/ArmorStatuesForgeClient.java +++ /dev/null @@ -1,35 +0,0 @@ -package fuzs.armorstatues.client; - -import fuzs.armorstatues.ArmorStatues; -import fuzs.armorstatues.api.ArmorStatuesApi; -import fuzs.armorstatues.api.client.ArmorStatuesApiClient; -import fuzs.armorstatues.client.handler.ArmorStandTooltipHandler; -import fuzs.armorstatues.handler.ArmorStandInteractHandler; -import fuzs.puzzleslib.client.core.ClientCoreServices; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.ClientPlayerNetworkEvent; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.entity.player.ItemTooltipEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; - -@Mod.EventBusSubscriber(modid = ArmorStatues.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) -public class ArmorStatuesForgeClient { - - @SubscribeEvent - public static void onConstructMod(final FMLConstructModEvent evt) { - ClientCoreServices.FACTORIES.clientModConstructor(ArmorStatues.MOD_ID).accept(new ArmorStatuesApiClient()); - ClientCoreServices.FACTORIES.clientModConstructor(ArmorStatues.MOD_ID).accept(new ArmorStatuesClient()); - registerHandlers(); - } - - private static void registerHandlers() { - MinecraftForge.EVENT_BUS.addListener((final ItemTooltipEvent evt) -> { - ArmorStandTooltipHandler.onItemTooltip(evt.getItemStack(), evt.getFlags(), evt.getToolTip()); - }); - MinecraftForge.EVENT_BUS.addListener((final ClientPlayerNetworkEvent.LoggingIn evt) -> { - ArmorStandInteractHandler.clearPresentServerside(); - }); - } -} diff --git a/Forge/src/main/java/fuzs/armorstatues/data/ModLanguageProvider.java b/Forge/src/main/java/fuzs/armorstatues/data/ModLanguageProvider.java deleted file mode 100644 index b27ba2b..0000000 --- a/Forge/src/main/java/fuzs/armorstatues/data/ModLanguageProvider.java +++ /dev/null @@ -1,91 +0,0 @@ -package fuzs.armorstatues.data; - -import net.minecraft.data.DataGenerator; -import net.minecraftforge.common.data.LanguageProvider; - -public class ModLanguageProvider extends LanguageProvider { - - public ModLanguageProvider(DataGenerator gen, String modId) { - super(gen, modId, "en_us"); - } - - @Override - protected void addTranslations() { - this.add("key.cycleStatueTabs", "Cycle Statue Tabs"); - this.add("armorstatues.item.armor_stand.description", "Shift + right-click to open configuration screen when placed."); - this.add("armorstatues.entity.armor_stand.pose.athena", "Athena"); - this.add("armorstatues.entity.armor_stand.pose.brandish", "Brandish"); - this.add("armorstatues.entity.armor_stand.pose.cancanA", "Cancan #1"); - this.add("armorstatues.entity.armor_stand.pose.cancanB", "Cancan #2"); - this.add("armorstatues.entity.armor_stand.pose.default", "Default"); - this.add("armorstatues.entity.armor_stand.pose.entertain", "Entertain"); - this.add("armorstatues.entity.armor_stand.pose.hero", "Hero"); - this.add("armorstatues.entity.armor_stand.pose.honor", "Honor"); - this.add("armorstatues.entity.armor_stand.pose.none", "None"); - this.add("armorstatues.entity.armor_stand.pose.riposte", "Riposte"); - this.add("armorstatues.entity.armor_stand.pose.salute", "Salute"); - this.add("armorstatues.entity.armor_stand.pose.solemn", "Solemn"); - this.add("armorstatues.entity.armor_stand.pose.zombie", "Zombie"); - this.add("armorstatues.screen.type.equipment", "Equipment"); - this.add("armorstatues.screen.type.rotations", "Rotations"); - this.add("armorstatues.screen.type.style", "Style"); - this.add("armorstatues.screen.type.poses", "Poses"); - this.add("armorstatues.screen.type.position", "Position"); - this.add("armorstatues.screen.type.alignments", "Alignments"); - this.add("armorstatues.screen.style.showArms", "Show Arms"); - this.add("armorstatues.screen.style.small", "Small"); - this.add("armorstatues.screen.style.invisible", "Invisible"); - this.add("armorstatues.screen.style.noBasePlate", "No Base Plate"); - this.add("armorstatues.screen.style.showName", "Show Name"); - this.add("armorstatues.screen.style.noGravity", "No Gravity"); - this.add("armorstatues.screen.style.glowing", "Glowing"); - this.add("armorstatues.screen.style.sealed", "Sealed"); - this.add("armorstatues.screen.style.showArms.description", "Shows the statue's arms, so it may hold items in both hands."); - this.add("armorstatues.screen.style.small.description", "Makes the statue half it's size like a baby mob."); - this.add("armorstatues.screen.style.invisible.description", "Makes the statue itself invisible, but still shows all equipped items."); - this.add("armorstatues.screen.style.noBasePlate.description", "Hide the stone base plate at the statue's feet."); - this.add("armorstatues.screen.style.showName.description", "Render the statue's name tag above it's head."); - this.add("armorstatues.screen.style.noGravity.description", "Prevents the statue from falling down, so it may float freely."); - this.add("armorstatues.screen.style.glowing.description", "Adds a glowing outline to the statue, visible through blocks."); - this.add("armorstatues.screen.style.sealed.description", "The statue can no longer be broken, equipment cannot be changed. Disallows opening this menu in survival mode."); - this.add("armorstatues.screen.position.rotation", "Rotation:"); - this.add("armorstatues.screen.position.degrees", "%s\u00B0"); - this.add("armorstatues.screen.position.moveBy", "Move By:"); - this.add("armorstatues.screen.position.pixels", "%s Pixel(s)"); - this.add("armorstatues.screen.position.blocks", "%s Block(s)"); - this.add("armorstatues.screen.position.x", "X-Position:"); - this.add("armorstatues.screen.position.y", "Y-Position:"); - this.add("armorstatues.screen.position.z", "Z-Position:"); - this.add("armorstatues.screen.position.increment", "Increment By %s"); - this.add("armorstatues.screen.position.decrement", "Decrement By %s"); - this.add("armorstatues.screen.position.centered", "Align Centered"); - this.add("armorstatues.screen.position.cornered", "Align Cornered"); - this.add("armorstatues.screen.position.aligned", "Aligned!"); - this.add("armorstatues.screen.pose.randomize", "Randomize"); - this.add("armorstatues.screen.pose.randomized", "Applied!"); - this.add("armorstatues.screen.rotations.pose.head", "Head"); - this.add("armorstatues.screen.rotations.pose.body", "Body"); - this.add("armorstatues.screen.rotations.pose.leftArm", "Left Arm"); - this.add("armorstatues.screen.rotations.pose.rightArm", "Right Arm"); - this.add("armorstatues.screen.rotations.pose.leftLeg", "Left Leg"); - this.add("armorstatues.screen.rotations.pose.rightLeg", "Right Leg"); - this.add("armorstatues.screen.rotations.tip1", "Hold the Shift or Alt key to lock sliders to a single axis!"); - this.add("armorstatues.screen.rotations.tip2", "Use the arrow keys to move sliders more precisely! Focus a slider first by clicking."); - this.add("armorstatues.screen.rotations.tip3", "Press the %s key (or Shift + %s) to quickly switch between tabs!"); - this.add("armorstatues.screen.rotations.reset", "Reset"); - this.add("armorstatues.screen.rotations.randomize", "Randomize"); - this.add("armorstatues.screen.rotations.limited", "Limited Rotations"); - this.add("armorstatues.screen.rotations.unlimited", "Unlimited Rotations"); - this.add("armorstatues.screen.rotations.copy", "Copy"); - this.add("armorstatues.screen.rotations.paste", "Paste"); - this.add("armorstatues.screen.rotations.x", "X: %s"); - this.add("armorstatues.screen.rotations.y", "Y: %s"); - this.add("armorstatues.screen.rotations.z", "Z: %s"); - this.add("armorstatues.screen.alignments.block", "Align Block"); - this.add("armorstatues.screen.alignments.itemFloating", "Align Item As Floating"); - this.add("armorstatues.screen.alignments.itemFlat", "Align Item As Flat"); - this.add("armorstatues.screen.alignments.tool", "Align Tool As Flat"); - this.add("armorstatues.screen.alignments.credit", "Alignment values are taken from the Vanilla Tweaks \"Armor Statues\" data pack. Click this button to go to their website!"); - this.add("armorstatues.screen.noPermission", "Unable to set new armor stand data; no permission"); - } -} diff --git a/Forge/src/main/java/fuzs/armorstatues/mixin/client/MultiPlayerGameModeMixin.java b/Forge/src/main/java/fuzs/armorstatues/mixin/client/MultiPlayerGameModeMixin.java deleted file mode 100644 index 12667d8..0000000 --- a/Forge/src/main/java/fuzs/armorstatues/mixin/client/MultiPlayerGameModeMixin.java +++ /dev/null @@ -1,39 +0,0 @@ -package fuzs.armorstatues.mixin.client; - -import fuzs.armorstatues.handler.ArmorStandInteractHandler; -import net.minecraft.client.multiplayer.ClientPacketListener; -import net.minecraft.client.multiplayer.MultiPlayerGameMode; -import net.minecraft.network.protocol.game.ServerboundInteractPacket; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.phys.EntityHitResult; -import net.minecraft.world.phys.Vec3; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.Optional; - -@Mixin(MultiPlayerGameMode.class) -abstract class MultiPlayerGameModeMixin { - @Shadow - @Final - private ClientPacketListener connection; - - @Inject(method = "interactAt", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/MultiPlayerGameMode;ensureHasSentCarriedItem()V", shift = At.Shift.AFTER), cancellable = true) - public void interactAt(Player player, Entity target, EntityHitResult ray, InteractionHand hand, CallbackInfoReturnable callback) { - Vec3 vec3 = ray.getLocation().subtract(target.getX(), target.getY(), target.getZ()); - Optional result = ArmorStandInteractHandler.onEntityInteract(player, player.level, hand, target, vec3); - if (result.isPresent()) { - if (result.get() == InteractionResult.SUCCESS) { - this.connection.send(ServerboundInteractPacket.createInteractionPacket(target, player.isShiftKeyDown(), hand, vec3)); - } - callback.setReturnValue(result.get()); - } - } -} diff --git a/Forge/src/main/resources/META-INF/mods.toml b/Forge/src/main/resources/META-INF/mods.toml deleted file mode 100644 index 5e945cb..0000000 --- a/Forge/src/main/resources/META-INF/mods.toml +++ /dev/null @@ -1,41 +0,0 @@ -modLoader="javafml" -loaderVersion="[${minFMLVersion},)" -license="MPL-2" -issueTrackerURL="${modIssueUrl}" - -[[mods]] - modId="${modId}" - displayName="${modName}" - description="${modDescription}" - version="${modVersion}" - authors="${modAuthor}" - logoFile="mod_banner.png" - logoBlur=false - displayURL="${modPageUrl}" - updateJSONURL="${modUpdateUrl}" - displayTest="${modForgeDisplayTest}" - -[[dependencies.${modId}]] - modId="forge" - mandatory=true - versionRange="[${minForgeVersion},)" - ordering="NONE" - side="BOTH" - -[[dependencies.${modId}]] - modId="minecraft" - mandatory=true - versionRange="[${minMinecraftVersion},${nextMinecraftVersion})" - ordering="NONE" - side="BOTH" - -[[dependencies.${modId}]] - modId="puzzleslib" - mandatory=true - versionRange="[${minPuzzlesVersion},)" - ordering="NONE" - side="BOTH" - -[modproperties.${modId}] - catalogueImageIcon="mod_logo.png" - configuredBackground="minecraft:textures/block/coarse_dirt.png" diff --git a/Forge/src/main/resources/assets/armorstatues/textures/gui/container/armor_stand/background.png b/Forge/src/main/resources/assets/armorstatues/textures/gui/container/armor_stand/background.png deleted file mode 100644 index 7414c9e..0000000 Binary files a/Forge/src/main/resources/assets/armorstatues/textures/gui/container/armor_stand/background.png and /dev/null differ diff --git a/Forge/src/main/resources/assets/armorstatues/textures/gui/container/armor_stand/widgets.png b/Forge/src/main/resources/assets/armorstatues/textures/gui/container/armor_stand/widgets.png deleted file mode 100644 index 8984ea6..0000000 Binary files a/Forge/src/main/resources/assets/armorstatues/textures/gui/container/armor_stand/widgets.png and /dev/null differ diff --git a/LICENSE-ASSETS.md b/LICENSE-ASSETS.md new file mode 100644 index 0000000..9a3157a --- /dev/null +++ b/LICENSE-ASSETS.md @@ -0,0 +1 @@ +Copyright (c) 2024 @heyitsfuzs. All Rights Reserved. diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md diff --git a/README.md b/README.md index f918472..a75f2c4 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,4 @@ A Minecraft mod. Downloads can be found on [CurseForge](https://www.curseforge.com/members/fuzs_/projects) and [Modrinth](https://modrinth.com/user/Fuzs). -![](https://i.imgur.com/9rluSuU.png) +![](https://raw.githubusercontent.com/Fuzss/modresources/main/pages/data/armorstatues/banner.png) diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 91d8eae..0000000 --- a/build.gradle +++ /dev/null @@ -1,122 +0,0 @@ -apply from: './gradle/tasks.gradle' - -subprojects { - apply plugin: 'java' - apply plugin: 'java-library' - apply plugin: 'maven-publish' - apply plugin: 'signing' - - java.toolchain.languageVersion = JavaLanguageVersion.of(17) - java.withSourcesJar() - java.withJavadocJar() - // silence missing javadoc comments, we just don't care - javadoc.options.addStringOption('Xdoclint:none', '-quiet') - - repositories { - mavenCentral() - mavenLocal() - maven { - name = 'Sponge / Mixin' - url = 'https://repo.spongepowered.org/repository/maven-public/' - } - maven { - name = 'Jared' - url = 'https://maven.blamejared.com/' - } - maven { - name = 'Jitpack' - url = 'https://jitpack.io' - } - maven { - name = 'Shedaniel' - url = 'https://maven.shedaniel.me/' - } - maven { - name = 'Parchment' - url = 'https://maven.parchmentmc.org' - } - maven { - name = 'Curse Maven' - url = 'https://cursemaven.com' - } - maven { - name = "Fuzs Mod Resources" - url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven/" - } - maven { - name = "Progwml6 maven" - url = "https://dvs1.progwml6.com/files/maven/" - } - maven { - name = "ModMaven" - url = "https://modmaven.dev" - } - flatDir { - dirs 'libs' - } - } - - tasks.withType(JavaCompile).configureEach { - // ensure that the encoding is set to UTF-8, no matter what the system default is - // this fixes some edge cases with special characters not displaying correctly - // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html - // If Javadoc is generated, this must be specified in that task too. - options.encoding = 'UTF-8' - options.release = 17 - } - - tasks.withType(Jar).configureEach { - from rootProject.file("LICENSE") - from rootProject.file("CHANGELOG.md") - manifest { - attributes([ - "Specification-Title" : modName, - 'Specification-Version' : modVersion, - "Specification-Vendor" : modAuthor, - 'Implementation-Title' : modName, - 'Implementation-Version' : modVersion, - 'Implementation-Vendor' : modAuthor, - 'Implementation-Timestamp' : new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - 'Implementation-Timestamp-Milli' : System.currentTimeMillis(), - 'Implementation-URL' : modSourceUrl, - 'Built-On-Java' : "${System.getProperty('java.vm.version')} (${System.getProperty('java.vm.vendor')})", - 'Built-On-Minecraft' : minecraftVersion - ]) - } - group 'jar' - } - - tasks.withType(GenerateModuleMetadata) { - // Disables Gradle's custom module metadata from being published to maven. The - // metadata includes mapped dependencies which are not reasonably consumable by - // other mod developers. - enabled = false - } -} - -import groovy.json.* -import java.util.regex.Pattern - -println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) - -task incrementBuildNumber { - onlyIf { project.hasProperty('uniqueBuildNumber') } - doLast { - def propertiesName = 'gradle.properties' - // build number is stored in global gradle.properties - def propertiesFile = new File(project.gradle.gradleUserHomeDir, propertiesName) - if (!propertiesFile.canRead()) { throw new FileNotFoundException("Could not read file ".concat(propertiesName)) } - def buildNumberMatcher = Pattern.compile("uniqueBuildNumber=(\\d+)").matcher(propertiesFile.getText()) - buildNumberMatcher.find() - def versionCode = Integer.parseInt(buildNumberMatcher.group(1)) - def propertiesContent = buildNumberMatcher.replaceAll("uniqueBuildNumber=" + ++versionCode) - propertiesFile.write(propertiesContent) - } -} - -def static getNextVersion(String puzzlesVersion) { - def puzzlesVersionMatcher = Pattern.compile("(\\d+\\.\\d+)").matcher(puzzlesVersion) - puzzlesVersionMatcher.find() - def currentVersion = puzzlesVersionMatcher.group(1) - return currentVersion.substring(0, currentVersion.indexOf(".") + 1).concat(String.valueOf(Integer.parseInt(currentVersion.substring(currentVersion.indexOf(".") + 1, currentVersion.size())) + 1)) -} diff --git a/gradle/tasks.gradle b/gradle/tasks.gradle deleted file mode 100644 index dbd3dc0..0000000 --- a/gradle/tasks.gradle +++ /dev/null @@ -1,232 +0,0 @@ -task forgeClean(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean' - ] -} - -task fabricClean(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Fabric:clean' - ] -} - -task allClean(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Fabric:clean' - ] -} - -task forgeBuild(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Forge:build' - ] -} - -task fabricBuild(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Fabric:clean', - ':Fabric:build' - ] -} - -task allBuild(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Fabric:clean', - ':Common:build', - ':Forge:build', - ':Fabric:build' - ] -} - -task commonPublish(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Common:publishMavenJavaPublicationToFuzsModResourcesRepository' - ] -} - -task forgePublish(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Forge:publishMavenJavaPublicationToFuzsModResourcesRepository' - ] -} - -task fabricPublish(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Fabric:clean', - ':Fabric:publishMavenJavaPublicationToFuzsModResourcesRepository' - ] -} - -task allPublish(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Fabric:clean', - ':Common:publishMavenJavaPublicationToFuzsModResourcesRepository', - ':Forge:publishMavenJavaPublicationToFuzsModResourcesRepository', - ':Fabric:publishMavenJavaPublicationToFuzsModResourcesRepository' - ] -} - -task forgeUploadCurseForge(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Forge:curseforge' - ] -} - -task fabricUploadCurseForge(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Fabric:clean', - ':Fabric:curseforge' - ] -} - -task allUploadCurseForge(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Fabric:clean', - ':Forge:curseforge', - ':Fabric:curseforge' - ] -} - -task forgeUploadModrinth(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Forge:modrinth' - ] -} - -task fabricUploadModrinth(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Fabric:clean', - ':Fabric:modrinth' - ] -} - -task allUploadModrinth(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Fabric:clean', - ':Forge:modrinth', - ':Fabric:modrinth' - ] -} - -task forgeUploadEverywhere(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Forge:curseforge', - ':Forge:modrinth' - ] -} - -task fabricUploadEverywhere(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Fabric:clean', - ':Fabric:curseforge', - ':Fabric:modrinth' - ] -} - -task allUploadEverywhere(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:clean', - ':Forge:clean', - ':Fabric:clean', - ':Forge:curseforge', - ':Forge:modrinth', - ':Fabric:curseforge', - ':Fabric:modrinth' - ] -} - -task commonGenSources(type: GradleBuild) { - group = '_main' - tasks = [ - ':Common:genSourcesWithQuiltflower' - ] -} - -task forgeGenRuns(type: GradleBuild) { - group = '_main' - tasks = [ - ':Forge:genIntellijRuns' - ] -} - -task fabricGenSources(type: GradleBuild) { - group = '_main' - tasks = [ - ':Fabric:genSourcesWithQuiltflower' - ] -} - -task forgeClient(type: GradleBuild) { - group = '_main' - tasks = [ - ':Forge:runClient' - ] -} - -task fabricClient(type: GradleBuild) { - group = '_main' - tasks = [ - ':Fabric:runClient' - ] -} - -task forgeServer(type: GradleBuild) { - group = '_main' - tasks = [ - ':Forge:runServer' - ] -} - -task fabricServer(type: GradleBuild) { - group = '_main' - tasks = [ - ':Fabric:runServer' - ] -} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 41d9927..0000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/run/config/modmenu.json b/run/config/modmenu.json deleted file mode 100644 index c9e0984..0000000 --- a/run/config/modmenu.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "sorting": "ascending", - "count_libraries": true, - "compact_list": false, - "count_children": true, - "mods_button_style": "classic", - "count_hidden_mods": true, - "mod_count_location": "title_screen_and_mods_button", - "hide_mod_links": false, - "show_libraries": false, - "hide_mod_license": false, - "hide_badges": false, - "hide_mod_credits": false, - "easter_eggs": true, - "modify_title_screen": true, - "modify_game_menu": true, - "hide_config_buttons": false, - "hidden_mods": [] -} \ No newline at end of file diff --git a/run/options.txt b/run/options.txt deleted file mode 100644 index 54d0039..0000000 --- a/run/options.txt +++ /dev/null @@ -1,131 +0,0 @@ -version:3120 -autoJump:false -autoSuggestions:true -chatColors:true -chatLinks:true -chatLinksPrompt:true -enableVsync:true -entityShadows:true -forceUnicodeFont:false -discrete_mouse_scroll:false -invertYMouse:false -realmsNotifications:true -reducedDebugInfo:false -showSubtitles:false -directionalAudio:false -touchscreen:false -fullscreen:false -bobView:true -toggleCrouch:false -toggleSprint:false -darkMojangStudiosBackground:false -hideLightningFlashes:false -mouseSensitivity:0.5 -fov:0.0 -screenEffectScale:1.0 -fovEffectScale:1.0 -darknessEffectScale:1.0 -gamma:0.5 -renderDistance:12 -simulationDistance:12 -entityDistanceScaling:1.0 -guiScale:0 -particles:0 -maxFps:120 -graphicsMode:1 -ao:2 -prioritizeChunkUpdates:0 -biomeBlendRadius:2 -renderClouds:"true" -resourcePacks:["Fabric Mods"] -incompatibleResourcePacks:[] -lastServer: -lang:en_us -soundDevice:"" -chatVisibility:0 -chatOpacity:1.0 -chatLineSpacing:0.0 -textBackgroundOpacity:0.5 -backgroundForChatOnly:true -hideServerAddress:false -advancedItemTooltips:false -pauseOnLostFocus:true -overrideWidth:0 -overrideHeight:0 -heldItemTooltips:true -chatHeightFocused:1.0 -chatDelay:0.0 -chatHeightUnfocused:0.4375 -chatScale:1.0 -chatWidth:1.0 -mipmapLevels:4 -useNativeTransport:true -mainHand:"right" -attackIndicator:1 -narrator:0 -tutorialStep:none -mouseWheelSensitivity:1.0 -rawMouseInput:true -glDebugVerbosity:1 -skipMultiplayerWarning:false -skipRealms32bitWarning:false -hideMatchedNames:true -joinedFirstServer:true -hideBundleTutorial:false -syncChunkWrites:false -showAutosaveIndicator:true -allowServerListing:true -chatPreview:1 -onlyShowSecureChat:false -key_key.attack:key.mouse.left -key_key.use:key.mouse.right -key_key.forward:key.keyboard.w -key_key.left:key.keyboard.a -key_key.back:key.keyboard.s -key_key.right:key.keyboard.d -key_key.jump:key.keyboard.space -key_key.sneak:key.keyboard.left.shift -key_key.sprint:key.keyboard.left.control -key_key.drop:key.keyboard.q -key_key.inventory:key.keyboard.e -key_key.chat:key.keyboard.t -key_key.playerlist:key.keyboard.tab -key_key.pickItem:key.mouse.middle -key_key.command:key.keyboard.slash -key_key.socialInteractions:key.keyboard.p -key_key.screenshot:key.keyboard.f2 -key_key.togglePerspective:key.keyboard.f5 -key_key.smoothCamera:key.keyboard.unknown -key_key.fullscreen:key.keyboard.f11 -key_key.spectatorOutlines:key.keyboard.unknown -key_key.swapOffhand:key.keyboard.f -key_key.saveToolbarActivator:key.keyboard.c -key_key.loadToolbarActivator:key.keyboard.x -key_key.advancements:key.keyboard.l -key_key.hotbar.1:key.keyboard.1 -key_key.hotbar.2:key.keyboard.2 -key_key.hotbar.3:key.keyboard.3 -key_key.hotbar.4:key.keyboard.4 -key_key.hotbar.5:key.keyboard.5 -key_key.hotbar.6:key.keyboard.6 -key_key.hotbar.7:key.keyboard.7 -key_key.hotbar.8:key.keyboard.8 -key_key.hotbar.9:key.keyboard.9 -key_key.modmenu.open_menu:key.keyboard.unknown -soundCategory_master:0.5032789 -soundCategory_music:0.0 -soundCategory_record:1.0 -soundCategory_weather:0.15838194 -soundCategory_block:1.0 -soundCategory_hostile:1.0 -soundCategory_neutral:1.0 -soundCategory_player:1.0 -soundCategory_ambient:1.0 -soundCategory_voice:1.0 -modelPart_cape:true -modelPart_jacket:true -modelPart_left_sleeve:true -modelPart_right_sleeve:true -modelPart_left_pants_leg:true -modelPart_right_pants_leg:true -modelPart_hat:true diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 429f534..0000000 --- a/settings.gradle +++ /dev/null @@ -1,28 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - maven { - name = 'Fabric' - url = 'https://maven.fabricmc.net/' - } - maven { - name = 'Sponge Snapshots' - url = 'https://repo.spongepowered.org/repository/maven-public/' - } - maven { - name = 'Quilt (Release)' - url = 'https://maven.quiltmc.org/repository/release' - } - maven { - name = 'Quilt (Snapshot)' - url = 'https://maven.quiltmc.org/repository/snapshot' - } - maven { - name = 'QuiltFlower' - url = 'https://server.bbkr.space/artifactory/libs-release/' - } - } -} - -rootProject.name = "${modName.replaceAll("[^a-zA-Z]", "")}" -include("Common", "Fabric", "Forge")