diff --git a/common/src/main/java/com/mrbysco/armorposer/Reference.java b/common/src/main/java/com/mrbysco/armorposer/Reference.java index 56fafc9..9592fc4 100644 --- a/common/src/main/java/com/mrbysco/armorposer/Reference.java +++ b/common/src/main/java/com/mrbysco/armorposer/Reference.java @@ -1,10 +1,13 @@ package com.mrbysco.armorposer; import com.mojang.logging.LogUtils; +import com.mrbysco.armorposer.util.PoseData; import net.minecraft.resources.ResourceLocation; import org.slf4j.Logger; +import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; public class Reference { @@ -21,30 +24,32 @@ public class Reference { private static Map initializePoseMap() { Map poseMap = new LinkedHashMap<>(); - poseMap.put("attention", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[0.0f,0.0f,0.0f],LeftLeg:[0.0f,0.0f,0.0f],RightArm:[0.0f,0.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]}}"); - poseMap.put("walking", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[-20.0f,0.0f,-10.0f],LeftLeg:[20.0f,0.0f,0.0f],RightArm:[20.0f,0.0f,10.0f],RightLeg:[-20.0f,0.0f,0.0f]}}"); - poseMap.put("running", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[40.0f,0.0f,-10.0f],LeftLeg:[-40.0f,0.0f,0.0f],RightArm:[-40.0f,0.0f,10.0f],RightLeg:[40.0f,0.0f,0.0f]}}"); - poseMap.put("pointing", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,20.0f,0.0f],LeftArm:[0.0f,0.0f,-10.0f],LeftLeg:[0.0f,0.0f,0.0f],RightArm:[-90.0f,18.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]}}"); - poseMap.put("blocking", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[-50.0f,50.0f,0.0f],LeftLeg:[20.0f,0.0f,0.0f],RightArm:[-20.0f,-20.0f,0.0f],RightLeg:[-20.0f,0.0f,0.0f]}}"); - poseMap.put("lunging", "{Pose:{Body:[15.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[10.0f,0.0f,-10.0f],LeftLeg:[30.0f,0.0f,0.0f],RightArm:[-60.0f,-10.0f,0.0f],RightLeg:[-15.0f,0.0f,0.0f]}}"); - poseMap.put("winning", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[-15.0f,0.0f,0.0f],LeftArm:[10.0f,0.0f,-10.0f],LeftLeg:[15.0f,0.0f,0.0f],RightArm:[-120.0f,-10.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]}}"); - poseMap.put("sitting", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[-80.0f,-20.0f,0.0f],LeftLeg:[-90.0f,-10.0f,0.0f],RightArm:[-80.0f,20.0f,0.0f],RightLeg:[-90.0f,10.0f,0.0f]}}"); - poseMap.put("arabesque", "{Pose:{Body:[10.0f,0.0f,0.0f],Head:[-15.0f,0.0f,0.0f],LeftArm:[70.0f,0.0f,-10.0f],LeftLeg:[75.0f,0.0f,0.0f],RightArm:[-140.0f,-10.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]}}"); - poseMap.put("cupid", "{Pose:{Body:[10.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[-75.0f,0.0f,10.0f],LeftLeg:[75.0f,0.0f,0.0f],RightArm:[-90.0f,-10.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]}}"); - poseMap.put("point_and_laugh", "{Pose:{Body:[10.0f,7.0f,8.0f],Head:[25.0f,17.0f,-8.0f],LeftArm:[-90.0f,0.0f,20.0f],LeftLeg:[20.0f,30.0f,-10.0f],RightArm:[-8.0f,0.0f,-77.0f],RightLeg:[20.0f,-10.0f,20.0f]}}"); - poseMap.put("confident", "{Pose:{Body:[-2.0f,0.0f,0.0f],Head:[-10.0f,20.0f,0.0f],LeftArm:[5.0f,0.0f,0.0f],LeftLeg:[0.0f,-10.0f,-4.0f],RightArm:[5.0f,0.0f,0.0f],RightLeg:[16.0f,2.0f,10.0f]}}"); - poseMap.put("salute", "{Pose:{Body:[5.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[29.0f,0.0f,25.0f],LeftLeg:[0.0f,4.0f,2.0f],RightArm:[-124.0f,-51.0f,-35.0f],RightLeg:[0.0f,-4.0f,-2.0f]}}"); - poseMap.put("death", "{Pose:{Body:[-90.0f,0.0f,0.0f],Head:[-85.0f,0.0f,0.0f],LeftArm:[-90.0f,-10.0f,0.0f],LeftLeg:[0.0f,0.0f,0.0f],RightArm:[-90.0f,10.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]}}"); - poseMap.put("facepalm", "{Pose:{Body:[10.0f,0.0f,0.0f],Head:[45.0f,-4.0f,1.0f],LeftArm:[-72.0f,24.0f,47.0f],LeftLeg:[-4.0f,-6.0f,-2.0f],RightArm:[18.0f,-14.0f,0.0f],RightLeg:[25.0f,-2.0f,0.0f]}}"); - poseMap.put("lazing", "{Pose:{Body:[5.0f,0.0f,0.0f],Head:[14.0f,-12.0f,6.0f],LeftArm:[-4.0f,-20.0f,-10.0f],LeftLeg:[-88.0f,46.0f,0.0f],RightArm:[-40.0f,20.0f,0.0f],RightLeg:[-88.0f,71.0f,0.0f]}}"); - poseMap.put("confused", "{Pose:{Body:[0.0f,13.0f,0.0f],Head:[0.0f,30.0f,0.0f],LeftArm:[145.0f,22.0f,-49.0f],LeftLeg:[-6.0f,0.0f,0.0f],RightArm:[-22.0f,31.0f,10.0f],RightLeg:[6.0f,-20.0f,0.0f]}}"); - poseMap.put("formal", "{Pose:{Body:[4.0f,0.0f,0.0f],Head:[4.0f,0.0f,0.0f],LeftArm:[30.0f,-20.0f,21.0f],LeftLeg:[0.0f,0.0f,-5.0f],RightArm:[30.0f,22.0f,-20.0f],RightLeg:[0.0f,0.0f,5.0f]}}"); - poseMap.put("sad", "{Pose:{Body:[10.0f,0.0f,0.0f],Head:[63.0f,0.0f,0.0f],LeftArm:[-5.0f,0.0f,-5.0f],LeftLeg:[-5.0f,16.0f,-5.0f],RightArm:[-5.0f,0.0f,5.0f],RightLeg:[-5.0f,-10.0f,5.0f]}}"); - poseMap.put("joyous", "{Pose:{Body:[-4.0f,0.0f,0.0f],Head:[-11.0f,0.0f,0.0f],LeftArm:[0.0f,0.0f,-100.0f],LeftLeg:[-8.0f,0.0f,-60.0f],RightArm:[0.0f,0.0f,100.0f],RightLeg:[-8.0f,0.0f,60.0f]}}"); - poseMap.put("stargazing", "{Pose:{Body:[-4.0f,10.0f,0.0f],Head:[-22.0f,25.0f,0.0f],LeftArm:[4.0f,18.0f,0.0f],LeftLeg:[6.0f,24.0f,0.0f],RightArm:[-153.0f,34.0f,-3.0f],RightLeg:[-4.0f,17.0f,2.0f]}}"); - poseMap.put("block", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[0.0f,0.0f,0.0f],LeftLeg:[0.0f,0.0f,0.0f],RightArm:[-15.0f,-45.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]}}"); - poseMap.put("item", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[0.0f,0.0f,0.0f],LeftLeg:[0.0f,0.0f,0.0f],RightArm:[-90.0f,0.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]}}"); - poseMap.put("random", "{Pose:{Body:[0.0f,90.0f,0.0f],Head:[25.0f,0.0f,0.0f],LeftArm:[0.0f,0.0f,-50.0f],LeftLeg:[0.0f,0.0f,-50.0f],RightArm:[0.0f,0.0f,50.0f],RightLeg:[0.0f,0.0f,50.0f]}"); + poseMap.put("attention", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[0.0f,0.0f,0.0f],LeftLeg:[0.0f,0.0f,0.0f],RightArm:[0.0f,0.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("walking", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[-20.0f,0.0f,-10.0f],LeftLeg:[20.0f,0.0f,0.0f],RightArm:[20.0f,0.0f,10.0f],RightLeg:[-20.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("running", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[40.0f,0.0f,-10.0f],LeftLeg:[-40.0f,0.0f,0.0f],RightArm:[-40.0f,0.0f,10.0f],RightLeg:[40.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("pointing", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,20.0f,0.0f],LeftArm:[0.0f,0.0f,-10.0f],LeftLeg:[0.0f,0.0f,0.0f],RightArm:[-90.0f,18.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("blocking", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[-50.0f,50.0f,0.0f],LeftLeg:[20.0f,0.0f,0.0f],RightArm:[-20.0f,-20.0f,0.0f],RightLeg:[-20.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("lunging", "{Pose:{Body:[15.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[10.0f,0.0f,-10.0f],LeftLeg:[30.0f,0.0f,0.0f],RightArm:[-60.0f,-10.0f,0.0f],RightLeg:[-15.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("winning", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[-15.0f,0.0f,0.0f],LeftArm:[10.0f,0.0f,-10.0f],LeftLeg:[15.0f,0.0f,0.0f],RightArm:[-120.0f,-10.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("sitting", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[-80.0f,-20.0f,0.0f],LeftLeg:[-90.0f,-10.0f,0.0f],RightArm:[-80.0f,20.0f,0.0f],RightLeg:[-90.0f,10.0f,0.0f]},ShowArms:1b}"); + poseMap.put("arabesque", "{Pose:{Body:[10.0f,0.0f,0.0f],Head:[-15.0f,0.0f,0.0f],LeftArm:[70.0f,0.0f,-10.0f],LeftLeg:[75.0f,0.0f,0.0f],RightArm:[-140.0f,-10.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("cupid", "{Pose:{Body:[10.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[-75.0f,0.0f,10.0f],LeftLeg:[75.0f,0.0f,0.0f],RightArm:[-90.0f,-10.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("point_and_laugh", "{Pose:{Body:[10.0f,7.0f,8.0f],Head:[25.0f,17.0f,-8.0f],LeftArm:[-90.0f,0.0f,20.0f],LeftLeg:[20.0f,30.0f,-10.0f],RightArm:[-8.0f,0.0f,-77.0f],RightLeg:[20.0f,-10.0f,20.0f]},ShowArms:1b}"); + poseMap.put("confident", "{Pose:{Body:[-2.0f,0.0f,0.0f],Head:[-10.0f,20.0f,0.0f],LeftArm:[5.0f,0.0f,0.0f],LeftLeg:[0.0f,-10.0f,-4.0f],RightArm:[5.0f,0.0f,0.0f],RightLeg:[16.0f,2.0f,10.0f]},ShowArms:1b}"); + poseMap.put("salute", "{Pose:{Body:[5.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[29.0f,0.0f,25.0f],LeftLeg:[0.0f,4.0f,2.0f],RightArm:[-124.0f,-51.0f,-35.0f],RightLeg:[0.0f,-4.0f,-2.0f]},ShowArms:1b}"); + poseMap.put("death", "{Pose:{Body:[-90.0f,0.0f,0.0f],Head:[-85.0f,0.0f,0.0f],LeftArm:[-90.0f,-10.0f,0.0f],LeftLeg:[0.0f,0.0f,0.0f],RightArm:[-90.0f,10.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("facepalm", "{Pose:{Body:[10.0f,0.0f,0.0f],Head:[45.0f,-4.0f,1.0f],LeftArm:[-72.0f,24.0f,47.0f],LeftLeg:[-4.0f,-6.0f,-2.0f],RightArm:[18.0f,-14.0f,0.0f],RightLeg:[25.0f,-2.0f,0.0f]},ShowArms:1b}"); + poseMap.put("lazing", "{Pose:{Body:[5.0f,0.0f,0.0f],Head:[14.0f,-12.0f,6.0f],LeftArm:[-4.0f,-20.0f,-10.0f],LeftLeg:[-88.0f,46.0f,0.0f],RightArm:[-40.0f,20.0f,0.0f],RightLeg:[-88.0f,71.0f,0.0f]},ShowArms:1b}"); + poseMap.put("confused", "{Pose:{Body:[0.0f,13.0f,0.0f],Head:[0.0f,30.0f,0.0f],LeftArm:[145.0f,22.0f,-49.0f],LeftLeg:[-6.0f,0.0f,0.0f],RightArm:[-22.0f,31.0f,10.0f],RightLeg:[6.0f,-20.0f,0.0f]},ShowArms:1b}"); + poseMap.put("formal", "{Pose:{Body:[4.0f,0.0f,0.0f],Head:[4.0f,0.0f,0.0f],LeftArm:[30.0f,-20.0f,21.0f],LeftLeg:[0.0f,0.0f,-5.0f],RightArm:[30.0f,22.0f,-20.0f],RightLeg:[0.0f,0.0f,5.0f]},ShowArms:1b}"); + poseMap.put("sad", "{Pose:{Body:[10.0f,0.0f,0.0f],Head:[63.0f,0.0f,0.0f],LeftArm:[-5.0f,0.0f,-5.0f],LeftLeg:[-5.0f,16.0f,-5.0f],RightArm:[-5.0f,0.0f,5.0f],RightLeg:[-5.0f,-10.0f,5.0f]},ShowArms:1b}"); + poseMap.put("joyous", "{Pose:{Body:[-4.0f,0.0f,0.0f],Head:[-11.0f,0.0f,0.0f],LeftArm:[0.0f,0.0f,-100.0f],LeftLeg:[-8.0f,0.0f,-60.0f],RightArm:[0.0f,0.0f,100.0f],RightLeg:[-8.0f,0.0f,60.0f]},ShowArms:1b}"); + poseMap.put("stargazing", "{Pose:{Body:[-4.0f,10.0f,0.0f],Head:[-22.0f,25.0f,0.0f],LeftArm:[4.0f,18.0f,0.0f],LeftLeg:[6.0f,24.0f,0.0f],RightArm:[-153.0f,34.0f,-3.0f],RightLeg:[-4.0f,17.0f,2.0f]},ShowArms:1b}"); + poseMap.put("block", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[0.0f,0.0f,0.0f],LeftLeg:[0.0f,0.0f,0.0f],RightArm:[-15.0f,-45.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("item", "{Pose:{Body:[0.0f,0.0f,0.0f],Head:[0.0f,0.0f,0.0f],LeftArm:[0.0f,0.0f,0.0f],LeftLeg:[0.0f,0.0f,0.0f],RightArm:[-90.0f,0.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]},ShowArms:1b}"); + poseMap.put("random", "{Pose:{Body:[0.0f,90.0f,0.0f],Head:[25.0f,0.0f,0.0f],LeftArm:[0.0f,0.0f,-50.0f],LeftLeg:[0.0f,0.0f,-50.0f],RightArm:[0.0f,0.0f,50.0f],RightLeg:[0.0f,0.0f,50.0f]},ShowArms:1b}"); return poseMap; } + + public static final List userPoses = new ArrayList<>(); } \ No newline at end of file diff --git a/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorPosesScreen.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorPosesScreen.java new file mode 100644 index 0000000..baa1aea --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorPosesScreen.java @@ -0,0 +1,273 @@ +package com.mrbysco.armorposer.client.gui; + +import com.mrbysco.armorposer.Reference; +import com.mrbysco.armorposer.client.gui.widgets.PoseEntry; +import com.mrbysco.armorposer.client.gui.widgets.PoseListWidget; +import com.mrbysco.armorposer.poses.UserPoseHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.ObjectSelectionList; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ArmorPosesScreen extends Screen { + private enum SortType { + NORMAL, + A_TO_Z, + Z_TO_A; + + Button button; + + Component getButtonText() { + return Component.translatable("armorposer.gui.poses.search." + name().toLowerCase(Locale.ROOT)); + } + } + + private static final int PADDING = 6; + + private PoseListWidget[] poseListWidget = new PoseListWidget[2]; + private PoseListWidget.ListEntry selected = null; + private List poses; + private final List unsortedPoses; + private List userPoses; + private final List unsortedUserPoses; + private Button applyButton; + + private final int buttonMargin = 1; + private final int numButtons = SortType.values().length; + private String lastFilterText = ""; + + private EditBox search; + private boolean sorted = false; + private SortType sortType = SortType.NORMAL; + + public ArmorStandScreen parentScreen; + + public ArmorPosesScreen(ArmorStandScreen parent) { + super(Component.translatable("armorposer.gui.poses.title")); + this.parentScreen = parent; + + //Add default poses + List rawPoses = Reference.defaultPoseMap.entrySet().stream() + .map(entry -> new PoseEntry(entry.getKey(), entry.getValue(), false)).collect(Collectors.toList()); + this.unsortedPoses = Collections.unmodifiableList(rawPoses); + Collections.sort(rawPoses); + this.poses = Collections.unmodifiableList(rawPoses); + + //Add user added poses + UserPoseHandler.loadUserPoses(); + List rawUserPoses = Reference.userPoses.stream().map(entry -> new PoseEntry(entry, true)).collect(Collectors.toList()); + this.unsortedUserPoses = Collections.unmodifiableList(rawUserPoses); + Collections.sort(rawUserPoses); + this.userPoses = Collections.unmodifiableList(rawUserPoses); + } + + @Override + public boolean isPauseScreen() { + return false; + } + + @Override + protected void init() { + int centerWidth = this.width / 2; + int listWidth = this.width / 4 + 20; + int structureWidth = this.width - listWidth - (PADDING * 3); + int closeButtonWidth = Math.min(structureWidth, 160); + int y = this.height - 20 - PADDING; + this.addRenderableWidget(Button.builder(Component.translatable("gui.cancel"), b -> ArmorPosesScreen.this.onClose()) + .bounds(centerWidth - (closeButtonWidth / 2) + PADDING, y, closeButtonWidth, 20).build()); + + y -= 18 + PADDING; + this.addRenderableWidget(this.applyButton = Button.builder(Component.translatable("armorposer.gui.poses.selection.apply"), b -> { + if (selected != null) { + if (!selected.userAdded() && selected.rawName().equals("random")) { + //Randomize all fields but the last 3 (as those are position) but don't make the rotations too crazy + for (int i = 0; i < this.parentScreen.poseTextFields.length - 3; i++) { + //generate a random number between -35 and 35 + float randomRotation = (float) (Math.random() * 70 - 35); + this.parentScreen.poseTextFields[i].setValue(String.valueOf((int) randomRotation)); + } + } else { + this.parentScreen.readFieldsFromNBT(selected.getTag()); + } + this.parentScreen.updateEntity(selected.getTag()); + } + this.onClose(); + }).bounds(centerWidth - (closeButtonWidth / 2) + PADDING, y, closeButtonWidth, 20).build()); + + y -= 14 + PADDING; + search = new EditBox(getScreenFont(), centerWidth - listWidth / 2 + PADDING + 1, y, listWidth - 2, 14, + Component.translatable("armorposer.gui.poses.search")); + + int fullButtonHeight = PADDING + 20 + PADDING; + this.poseListWidget[0] = new PoseListWidget(this, Component.translatable("armorposer.gui.poses.default"), false, listWidth, fullButtonHeight, search.getY() - getScreenFont().lineHeight - PADDING); + this.poseListWidget[0].setX(0); + this.poseListWidget[0].setY(10); + this.poseListWidget[0].setHeight(this.height); + + this.poseListWidget[1] = new PoseListWidget(this, Component.translatable("armorposer.gui.poses.user"), true, listWidth, fullButtonHeight, search.getY() - getScreenFont().lineHeight - PADDING); + this.poseListWidget[1].setX(width - listWidth); + this.poseListWidget[1].setY(10); + this.poseListWidget[1].setHeight(this.height); + + addWidget(search); + addWidget(poseListWidget[0]); + addWidget(poseListWidget[1]); + search.setFocused(false); + search.setCanLoseFocus(true); + + final int width = listWidth / numButtons; + int x = centerWidth + PADDING - width; + addRenderableWidget(SortType.A_TO_Z.button = Button.builder(SortType.A_TO_Z.getButtonText(), b -> + resortPoses(SortType.A_TO_Z)) + .bounds(x, PADDING, width - buttonMargin, 20).build()); + x += width + buttonMargin; + addRenderableWidget(SortType.Z_TO_A.button = Button.builder(SortType.Z_TO_A.getButtonText(), b -> + resortPoses(SortType.Z_TO_A)) + .bounds(x, PADDING, width - buttonMargin, 20).build()); + + resortPoses(SortType.A_TO_Z); + updateCache(); + } + + @Override + public void tick() { + if (poseListWidget[0].children().contains(selected)) { + poseListWidget[0].setSelected(selected); + poseListWidget[1].setSelected(null); + } else if (poseListWidget[1].children().contains(selected)) { + poseListWidget[0].setSelected(null); + poseListWidget[1].setSelected(selected); + } + + if (!search.getValue().equals(lastFilterText)) { + reloadPoses(); + sorted = false; + } + + if (!sorted) { + reloadPoses(); + if (sortType == SortType.A_TO_Z) { + Collections.sort(poses); + Collections.sort(userPoses); + } else if (sortType == SortType.Z_TO_A) { + poses.sort(Collections.reverseOrder()); + userPoses.sort(Collections.reverseOrder()); + } + poseListWidget[0].refreshList(false); + poseListWidget[1].refreshList(true); + if (selected != null) { + selected = poseListWidget[0].children().stream().filter(e -> e == selected).findFirst() + .orElse(poseListWidget[1].children().stream().filter(e -> e == selected).findFirst().orElse(null)); + } + sorted = true; + } + } + + private void reloadPoses() { + this.poses = this.unsortedPoses.stream(). + filter(entry -> entry.getName().toLowerCase(Locale.ROOT).contains(search.getValue().toLowerCase(Locale.ROOT))) + .collect(Collectors.toList()); + + this.userPoses = this.unsortedUserPoses.stream(). + filter(entry -> entry.getName().toLowerCase(Locale.ROOT).contains(search.getValue().toLowerCase(Locale.ROOT))) + .collect(Collectors.toList()); + + lastFilterText = search.getValue(); + } + + public > void buildPoseList(Consumer ListViewConsumer, Function newEntry) { + poses.forEach(mod -> ListViewConsumer.accept(newEntry.apply(mod))); + } + + public > void buildUserPoseList(Consumer ListViewConsumer, Function newEntry) { + userPoses.forEach(mod -> ListViewConsumer.accept(newEntry.apply(mod))); + } + + private void resortPoses(SortType newSort) { + this.sortType = newSort; + + for (SortType sort : SortType.values()) { + if (sort.button != null) + sort.button.active = sortType != sort; + } + sorted = false; + } + + @Override + public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { + this.poseListWidget[0].render(guiGraphics, mouseX, mouseY, partialTicks); + this.poseListWidget[1].render(guiGraphics, mouseX, mouseY, partialTicks); + super.render(guiGraphics, mouseX, mouseY, partialTicks); + + Component text = Component.translatable("armorposer.gui.poses.search"); + guiGraphics.drawCenteredString(getScreenFont(), text, this.width / 2 + PADDING, + search.getY() - getScreenFont().lineHeight - 2, 0xFFFFFF); + + this.search.render(guiGraphics, mouseX, mouseY, partialTicks); + } + + @Override + public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { + //Nope + } + + public void setSelected(PoseListWidget.ListEntry entry) { + this.selected = entry == this.selected ? null : entry; + updateCache(); + } + + private void updateCache() { + this.applyButton.active = selected != null; + } + + /** + * Clear the search field when right-clicked on it + */ + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + boolean flag = super.mouseClicked(mouseX, mouseY, button); + if (button == 1 && search.isMouseOver(mouseX, mouseY)) { + search.setValue(""); + } + return flag; + } + + @Override + public void resize(Minecraft mc, int width, int height) { + String s = this.search.getValue(); + SortType sort = this.sortType; + PoseListWidget.ListEntry selected = this.selected; + this.init(mc, width, height); + this.search.setValue(s); + this.selected = selected; + if (!this.search.getValue().isEmpty()) + reloadPoses(); + if (sort != SortType.NORMAL) + resortPoses(sort); + updateCache(); + } + + @Override + public void onClose() { + this.minecraft.setScreen(parentScreen); + } + + public Minecraft getScreenMinecraft() { + return this.minecraft; + } + + public Font getScreenFont() { + return this.font; + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorStandScreen.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorStandScreen.java index 2b99486..e3fa652 100644 --- a/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorStandScreen.java +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorStandScreen.java @@ -1,15 +1,12 @@ package com.mrbysco.armorposer.client.gui; -import com.mrbysco.armorposer.Reference; import com.mrbysco.armorposer.client.gui.widgets.NumberFieldBox; -import com.mrbysco.armorposer.client.gui.widgets.PoseButton; import com.mrbysco.armorposer.client.gui.widgets.PoseImageButton; import com.mrbysco.armorposer.client.gui.widgets.ToggleButton; import com.mrbysco.armorposer.data.SwapData; import com.mrbysco.armorposer.platform.Services; import com.mrbysco.armorposer.util.ArmorStandData; import net.minecraft.ChatFormatting; -import net.minecraft.client.GameNarrator; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; @@ -27,8 +24,6 @@ import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.phys.Vec3; -import java.util.Map; - public class ArmorStandScreen extends Screen { private final ArmorStand entityArmorStand; private final ArmorStandData armorStandData; @@ -38,11 +33,9 @@ public class ArmorStandScreen extends Screen { private NumberFieldBox rotationTextField; private final ToggleButton[] toggleButtons = new ToggleButton[6]; - private final NumberFieldBox[] poseTextFields = new NumberFieldBox[3 * 7]; - private final PoseButton[] poseButtons = new PoseButton[Reference.defaultPoseMap.size()]; + protected final NumberFieldBox[] poseTextFields = new NumberFieldBox[3 * 7]; private LockIconButton lockButton; private final boolean allowScrolling; - private boolean poseTabVisible = false; private Vec3 lastSendOffset = new Vec3(0, 0, 0); @@ -51,7 +44,7 @@ public class ArmorStandScreen extends Screen { private final Tooltip yPositionTooltipDisabled = Tooltip.create(Component.translatable("armorposer.gui.tooltip.y_position.disabled").withStyle(ChatFormatting.RED)); public ArmorStandScreen(ArmorStand entityArmorStand) { - super(GameNarrator.NO_TITLE); + super(Component.translatable("armorposer.gui.title")); this.entityArmorStand = entityArmorStand; this.armorStandData = new ArmorStandData(); @@ -132,8 +125,7 @@ public void init() { // copy & paste buttons offsetX = 20; - this.addRenderableWidget(Button.builder(Component.translatable("armorposer.gui.label.poses"), (button) -> - this.poseTabVisible = !this.poseTabVisible) + this.addRenderableWidget(Button.builder(Component.translatable("armorposer.gui.label.poses"), (button) -> this.minecraft.setScreen(new ArmorPosesScreen(this))) .bounds(offsetX, offsetY, 130, 20) .tooltip(Tooltip.create(Component.translatable("armorposer.gui.tooltip.poses"))).build()); this.addRenderableWidget(Button.builder(Component.translatable("armorposer.gui.label.copy"), (button) -> { @@ -253,53 +245,6 @@ public void init() { this.updateEntity(this.armorStandData.writeToNBT()); this.minecraft.setScreen((Screen) null); }).bounds(offsetX - 96, offsetY + 22, 96, 20).build()); - - - //Setup pose buttons - int centerX = this.width / 2; - int exclusionLeft = centerX - 60; - int exclusionRight = centerX + 80; - offsetX = 10; - offsetY = 10; - - int id = 0; - for (Map.Entry entry : Reference.defaultPoseMap.entrySet()) { - String poseID = entry.getKey(); - String tag = entry.getValue(); - - final int buttonEnd = offsetX + 40; - if (buttonEnd >= exclusionLeft && buttonEnd <= exclusionRight) { - //Move the button until it's no longer in the exclusion zone - offsetX = exclusionRight; - } - - this.addRenderableWidget(this.poseButtons[id] = new PoseButton.Builder(poseID, tag, (button) -> { - PoseButton poseButton = ((PoseButton) button); - if (poseButton.getPoseID().equals("random")) { - //Randomize all fields but the last 3 (as those are position) but don't make the rotations too crazy - for (int i = 0; i < this.poseTextFields.length - 3; i++) { - //generate a random number between -35 and 35 - float randomRotation = (float) (Math.random() * 70 - 35); - this.poseTextFields[i].setValue(String.valueOf((int) randomRotation)); - } - } else { - this.readFieldsFromNBT(poseButton.getTag()); - } - this.updateEntity(poseButton.getTag()); - }).pos(offsetX, offsetY).build()); - //Disable visibility of the buttons at first - this.poseButtons[id].visible = this.poseTabVisible; - - // Adjust the offsetX for the next button - offsetX += 40 + 4; - id++; - - // Move to the next row if necessary - if (offsetX + 40 > this.width) { - offsetX = 10; // Reset the X position - offsetY += 40 + 4; // Move to the next row - } - } } @Override @@ -321,24 +266,21 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia for (int i = 0; i < this.buttonLabels.length; i++) { int x = offsetX; int y = offsetY + (i * 22) + (10 - (this.font.lineHeight / 2)); - if (!poseTabVisible) - guiGraphics.drawString(this.font, I18n.get("armorposer.gui.label." + this.buttonLabels[i]), x, y, 0xA0A0A0, false); + guiGraphics.drawString(this.font, I18n.get("armorposer.gui.label." + this.buttonLabels[i]), x, y, 0xA0A0A0, false); } // right column labels offsetX = this.width - 20 - 100; // x, y, z - if (!poseTabVisible) { - guiGraphics.drawString(this.font, "X", offsetX + 10, 7, 0xA0A0A0, false); - guiGraphics.drawString(this.font, "Y", offsetX + 45, 7, 0xA0A0A0, false); - guiGraphics.drawString(this.font, "Z", offsetX + 80, 7, 0xA0A0A0, false); - } + guiGraphics.drawString(this.font, "X", offsetX + 10, 7, 0xA0A0A0, false); + guiGraphics.drawString(this.font, "Y", offsetX + 45, 7, 0xA0A0A0, false); + guiGraphics.drawString(this.font, "Z", offsetX + 80, 7, 0xA0A0A0, false); // pose textboxes for (int i = 0; i < this.sliderLabels.length; i++) { String translatedLabel = I18n.get("armorposer.gui.label." + this.sliderLabels[i]); int x = offsetX - this.font.width(translatedLabel) - 10; int y = offsetY + (i * 22) + (10 - (this.font.lineHeight / 2)); - if (!poseTabVisible) guiGraphics.drawString(this.font, translatedLabel, x, y, 0xA0A0A0, false); + guiGraphics.drawString(this.font, translatedLabel, x, y, 0xA0A0A0, false); } } @@ -358,34 +300,6 @@ public void tick() { //Adjust tooltip to show it's disabled yPositionField.setTooltip(yPositionTooltipDisabled); } - - if (poseTabVisible) { - for (PoseButton poseButton : this.poseButtons) { - poseButton.visible = true; - } - - //Show the rest of the fields - rotationTextField.visible = false; - for (ToggleButton toggleButton : toggleButtons) { - toggleButton.visible = false; - } - for (NumberFieldBox textField : poseTextFields) { - textField.visible = false; - } - } else { - for (PoseButton poseButton : this.poseButtons) { - poseButton.visible = false; - } - - //Show the rest of the fields - rotationTextField.visible = true; - for (ToggleButton toggleButton : toggleButtons) { - toggleButton.visible = true; - } - for (NumberFieldBox textField : poseTextFields) { - textField.visible = true; - } - } } @Override @@ -558,7 +472,7 @@ private CompoundTag writeFieldsToNBT() { return compound; } - private void readFieldsFromNBT(CompoundTag compound) { + protected void readFieldsFromNBT(CompoundTag compound) { CompoundTag armorStandTag = this.armorStandData.writeToNBT(); armorStandTag.merge(compound); this.armorStandData.readFromNBT(armorStandTag); diff --git a/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/PoseEntry.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/PoseEntry.java new file mode 100644 index 0000000..5f9c67b --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/PoseEntry.java @@ -0,0 +1,32 @@ +package com.mrbysco.armorposer.client.gui.widgets; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mrbysco.armorposer.util.PoseData; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; +import org.jetbrains.annotations.NotNull; + +public record PoseEntry(PoseData pose, boolean userAdded) implements Comparable { + + public PoseEntry(String name, String data, boolean userAdded) { + this(new PoseData(name, data), userAdded); + } + + public String getName() { + return userAdded() ? pose().name() : I18n.get("armorposer.gui.pose." + pose().name()); + } + + public CompoundTag getTag() { + try { + return TagParser.parseTag(pose().data()); + } catch (CommandSyntaxException e) { + return null; + } + } + + @Override + public int compareTo(@NotNull PoseEntry o) { + return getName().compareTo(o.getName()); + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/PoseListWidget.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/PoseListWidget.java new file mode 100644 index 0000000..0c379cf --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/PoseListWidget.java @@ -0,0 +1,156 @@ +package com.mrbysco.armorposer.client.gui.widgets; + +import com.mrbysco.armorposer.Reference; +import com.mrbysco.armorposer.client.gui.ArmorPosesScreen; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.ObjectSelectionList; +import net.minecraft.client.gui.screens.inventory.InventoryScreen; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.level.Level; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.function.Function; + +public class PoseListWidget extends ObjectSelectionList { + private static final Vector3f ARMOR_STAND_TRANSLATION = new Vector3f(); + private static final Quaternionf ARMOR_STAND_ANGLE = new Quaternionf().rotationXYZ(0.43633232F, 0.0F, (float) Math.PI); + + private final ArmorPosesScreen parent; + private final int listWidth; + private final Component title; + + public PoseListWidget(ArmorPosesScreen parent, Component title, boolean user, int listWidth, int top, int bottom) { + super(parent.getScreenMinecraft(), listWidth, bottom - top, top, parent.getScreenFont().lineHeight * 2 + 16); + this.parent = parent; + this.title = title; + this.listWidth = listWidth; + this.refreshList(user); + this.setRenderBackground(false); + } + + @Override + protected int getScrollbarPosition() { + return this.getX() + this.listWidth - 6; + } + + @Override + public int getRowWidth() { + return this.listWidth; + } + + public void refreshList(boolean user) { + this.clearEntries(); + if (user) + parent.buildUserPoseList(this::addEntry, location -> new ListEntry(location, this.parent)); + else + parent.buildPoseList(this::addEntry, location -> new ListEntry(location, this.parent)); + } + + @Override + protected void renderSelection(GuiGraphics guiGraphics, int top, int width, int height, int outerColor, int innerColor) { + int xPos = this.getX() + (this.width - width) / 2; + int xPos2 = this.getX() + (this.width + width) / 2; + guiGraphics.fillGradient(xPos, top - 2, xPos2, top + height + 2, -1945083888, -1676648432); + } + + @Override + public void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { + guiGraphics.fillGradient(getX(), 0, getX() + this.listWidth, parent.height, -1945104368, -1676668912); + super.renderWidget(guiGraphics, mouseX, mouseY, partialTicks); + guiGraphics.drawCenteredString(this.parent.getScreenFont(), title, getX() + this.listWidth / 2, 2, 16777215); + } + + public class ListEntry extends Entry { + private final PoseEntry poseEntry; + private final ArmorPosesScreen parent; + private LivingEntity cachedEntity; + + ListEntry(PoseEntry entry, ArmorPosesScreen parent) { + this.poseEntry = entry; + this.parent = parent; + + Minecraft mc = parent.getScreenMinecraft(); + if (mc == null) { + Reference.LOGGER.error("Minecraft is null, cannot create pose entry {}", entry.pose().name()); + return; + } + Level level = mc.hasSingleplayerServer() && mc.getSingleplayerServer() != null ? mc.getSingleplayerServer().getAllLevels().iterator().next() : mc.level; + if (level != null) { + try { + CompoundTag tag = TagParser.parseTag(entry.pose().data()); + + CompoundTag nbt = new CompoundTag(); + nbt.putString("id", "minecraft:armor_stand"); + if (!tag.isEmpty()) { + nbt.merge(tag); + } + ArmorStand armorStand = (ArmorStand) EntityType.loadEntityRecursive(nbt, level, Function.identity()); + if (armorStand != null) { + armorStand.setNoBasePlate(true); + armorStand.setShowArms(true); + armorStand.yBodyRot = 210.0F; + armorStand.setXRot(25.0F); + armorStand.yHeadRot = armorStand.getYRot(); + armorStand.yHeadRotO = armorStand.getYRot(); + this.cachedEntity = armorStand; + } + } catch (Exception e) { + Reference.LOGGER.error("Unable to parse nbt pose {}", e.getMessage()); + } + } + } + + @Override + public void render(GuiGraphics guiGraphics, int entryIdx, int top, int left, int entryWidth, int entryHeight, + int mouseX, int mouseY, boolean hovered, float partialTicks) { + Font font = this.parent.getScreenFont(); + renderScrollingString(guiGraphics, font, Component.literal(getName()), left + 36, top + 10, left + width - 18, top + 20, 0xFFFFFF); + + if (cachedEntity != null) { + InventoryScreen.renderEntityInInventory(guiGraphics, left + 16, top + 28, 15, + ARMOR_STAND_TRANSLATION, ARMOR_STAND_ANGLE, (Quaternionf) null, this.cachedEntity); + } + } + + @Override + public void renderBack(GuiGraphics guiGraphics, int mouseX, int mouseY, int $$3, int $$4, int $$5, int $$6, int $$7, boolean $$8, float $$9) { + super.renderBack(guiGraphics, mouseX, mouseY, $$3, $$4, $$5, $$6, $$7, $$8, $$9); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + parent.setSelected(this); + PoseListWidget.this.setSelected(this); + return false; + } + + public CompoundTag getTag() { + return poseEntry.getTag(); + } + + public String getName() { + return poseEntry.getName(); + } + + public boolean userAdded() { + return poseEntry.userAdded(); + } + + public String rawName() { + return poseEntry.pose().name(); + } + + @Override + public Component getNarration() { + return Component.literal(getName()); + } + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/platform/services/IPlatformHelper.java b/common/src/main/java/com/mrbysco/armorposer/platform/services/IPlatformHelper.java index a7a81a2..7b35169 100644 --- a/common/src/main/java/com/mrbysco/armorposer/platform/services/IPlatformHelper.java +++ b/common/src/main/java/com/mrbysco/armorposer/platform/services/IPlatformHelper.java @@ -4,6 +4,8 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.decoration.ArmorStand; +import java.nio.file.Path; + public interface IPlatformHelper { /** * Update Armor Stand Entity @@ -19,4 +21,10 @@ public interface IPlatformHelper { * Allow scrolling to increase/decrease the angle of text fields */ boolean allowScrolling(); + + /** + * Get the user preset folder + * @return The user preset folder + */ + Path getUserPresetFolder(); } diff --git a/common/src/main/java/com/mrbysco/armorposer/poses/UserPoseHandler.java b/common/src/main/java/com/mrbysco/armorposer/poses/UserPoseHandler.java new file mode 100644 index 0000000..d321924 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/poses/UserPoseHandler.java @@ -0,0 +1,51 @@ +package com.mrbysco.armorposer.poses; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.mrbysco.armorposer.Reference; +import com.mrbysco.armorposer.platform.Services; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +public class UserPoseHandler { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + public static final File PRESET_FOLDER = new File(Services.PLATFORM.getUserPresetFolder().toFile() + "/armorposer"); + public static final File PRESET_FILE = new File(PRESET_FOLDER, "User_poses.json"); + + public static void initializePresets() { + if (!PRESET_FOLDER.exists() || !PRESET_FILE.exists()) { + PRESET_FOLDER.mkdirs(); + + UserPoses userPresets = new UserPoses(Reference.userPoses); + try (FileWriter writer = new FileWriter(PRESET_FILE)) { + GSON.toJson(userPresets, writer); + writer.flush(); + } catch (IOException e) { + Reference.LOGGER.error("Failed to user presets {}", e.getMessage()); + } + } + } + + public static void loadUserPoses() { + if (!PRESET_FOLDER.exists() || !PRESET_FILE.exists()) { + initializePresets(); + } + + Reference.userPoses.clear(); + String fileName = PRESET_FILE.getName(); + try (FileReader json = new FileReader(PRESET_FILE)) { + final UserPoses userPoses = GSON.fromJson(json, UserPoses.class); + if (userPoses != null) { + Reference.userPoses.addAll(userPoses.userPoses()); + } else { + Reference.LOGGER.error("Could not load user poses from {}.", fileName); + } + } catch (final Exception e) { + Reference.LOGGER.error("Unable to load file {}. Please make sure it's a valid json.", fileName); + Reference.LOGGER.trace("Exception: ", e); + } + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/poses/UserPoses.java b/common/src/main/java/com/mrbysco/armorposer/poses/UserPoses.java new file mode 100644 index 0000000..f1711be --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/poses/UserPoses.java @@ -0,0 +1,37 @@ +package com.mrbysco.armorposer.poses; + +import com.mrbysco.armorposer.util.PoseData; + +import java.util.List; +import java.util.Objects; + +public class UserPoses { + private final List poses; + + public UserPoses(List initialList) { + this.poses = initialList; + } + + public List userPoses() { + return poses; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (UserPoses) obj; + return Objects.equals(this.poses, that.poses); + } + + @Override + public int hashCode() { + return Objects.hash(poses); + } + + @Override + public String toString() { + return "UserPoses[" + + "poses=" + poses + ']'; + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/util/PoseData.java b/common/src/main/java/com/mrbysco/armorposer/util/PoseData.java new file mode 100644 index 0000000..0ab2b2b --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/util/PoseData.java @@ -0,0 +1,4 @@ +package com.mrbysco.armorposer.util; + +public record PoseData(String name, String data) { +} diff --git a/common/src/main/resources/assets/armorposer/lang/en_us.json b/common/src/main/resources/assets/armorposer/lang/en_us.json index 55eae51..d50b133 100644 --- a/common/src/main/resources/assets/armorposer/lang/en_us.json +++ b/common/src/main/resources/assets/armorposer/lang/en_us.json @@ -3,6 +3,14 @@ "armorposer.gui.title": "Armor Stand", + "armorposer.gui.poses.title": "Armor Stand Poses", + "armorposer.gui.poses.default": "Default Poses", + "armorposer.gui.poses.user": "User Poses", + "armorposer.gui.poses.selection.apply": "Apply", + "armorposer.gui.poses.search": "Search", + "armorposer.gui.poses.search.a_to_z": "A-Z", + "armorposer.gui.poses.search.z_to_a": "Z-A", + "armorposer.gui.label.invisible": "Invisible", "armorposer.gui.label.no_base_plate": "No Base Plate", "armorposer.gui.label.no_gravity": "No Gravity", @@ -41,7 +49,7 @@ "armorposer.gui.tooltip.copy": "Copy the current pose to the clipboard", "armorposer.gui.tooltip.paste": "Paste the pose from the clipboard", - "armorposer.gui.tooltip.poses": "Check built-in poses", + "armorposer.gui.tooltip.poses": "Check pose presets", "armorposer.gui.tooltip.mirror": "Mirror pose", "armorposer.gui.tooltip.mirror_legs": "Mirror legs", diff --git a/common/src/main/resources/assets/armorposer/lang/es_mx.json b/common/src/main/resources/assets/armorposer/lang/es_mx.json index 04cd010..e025533 100644 --- a/common/src/main/resources/assets/armorposer/lang/es_mx.json +++ b/common/src/main/resources/assets/armorposer/lang/es_mx.json @@ -3,6 +3,14 @@ "armorposer.gui.title": "Soporte para armaduras", + "armorposer.gui.poses.title": "Armor Stand Poses", + "armorposer.gui.poses.default": "Default Poses", + "armorposer.gui.poses.user": "User Poses", + "armorposer.gui.poses.selection.apply": "Apply", + "armorposer.gui.poses.search": "Search", + "armorposer.gui.poses.search.a_to_z": "A-Z", + "armorposer.gui.poses.search.z_to_a": "Z-A", + "armorposer.gui.label.invisible": "Invisible", "armorposer.gui.label.no_base_plate": "Sin base", "armorposer.gui.label.no_gravity": "Sin gravedad", diff --git a/common/src/main/resources/assets/armorposer/lang/fr_fr.json b/common/src/main/resources/assets/armorposer/lang/fr_fr.json index 03f4e75..19f34b8 100644 --- a/common/src/main/resources/assets/armorposer/lang/fr_fr.json +++ b/common/src/main/resources/assets/armorposer/lang/fr_fr.json @@ -3,6 +3,14 @@ "armorposer.gui.title": "Armor Stand", + "armorposer.gui.poses.title": "Armor Stand Poses", + "armorposer.gui.poses.default": "Default Poses", + "armorposer.gui.poses.user": "User Poses", + "armorposer.gui.poses.selection.apply": "Apply", + "armorposer.gui.poses.search": "Search", + "armorposer.gui.poses.search.a_to_z": "A-Z", + "armorposer.gui.poses.search.z_to_a": "Z-A", + "armorposer.gui.label.invisible": "Invisible", "armorposer.gui.label.no_base_plate": "Sans base", "armorposer.gui.label.no_gravity": "Sans gravité", diff --git a/common/src/main/resources/assets/armorposer/lang/zh_tw.json b/common/src/main/resources/assets/armorposer/lang/zh_tw.json index 194c428..708264a 100644 --- a/common/src/main/resources/assets/armorposer/lang/zh_tw.json +++ b/common/src/main/resources/assets/armorposer/lang/zh_tw.json @@ -3,6 +3,14 @@ "armorposer.gui.title": "盔甲座", + "armorposer.gui.poses.title": "Armor Stand Poses", + "armorposer.gui.poses.default": "Default Poses", + "armorposer.gui.poses.user": "User Poses", + "armorposer.gui.poses.selection.apply": "Apply", + "armorposer.gui.poses.search": "Search", + "armorposer.gui.poses.search.a_to_z": "A-Z", + "armorposer.gui.poses.search.z_to_a": "Z-A", + "armorposer.gui.label.invisible": "隱形", "armorposer.gui.label.no_base_plate": "沒有底盤", "armorposer.gui.label.no_gravity": "不受重力影響", diff --git a/fabric/src/main/java/com/mrbysco/armorposer/platform/FabricPlatformHelper.java b/fabric/src/main/java/com/mrbysco/armorposer/platform/FabricPlatformHelper.java index 37baa3f..da4995a 100644 --- a/fabric/src/main/java/com/mrbysco/armorposer/platform/FabricPlatformHelper.java +++ b/fabric/src/main/java/com/mrbysco/armorposer/platform/FabricPlatformHelper.java @@ -8,10 +8,13 @@ import me.shedaniel.autoconfig.AutoConfig; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.entity.decoration.ArmorStand; +import java.nio.file.Path; + public class FabricPlatformHelper implements IPlatformHelper { @Override public void updateEntity(ArmorStand armorStand, CompoundTag compound) { @@ -38,4 +41,9 @@ public boolean allowScrolling() { PoserConfig config = AutoConfig.getConfigHolder(PoserConfig.class).getConfig(); return config.general.allowScrolling; } + + @Override + public Path getUserPresetFolder() { + return FabricLoader.getInstance().getConfigDir(); + } } diff --git a/forge/src/main/java/com/mrbysco/armorposer/packets/handler/ServerPayloadHandler.java b/forge/src/main/java/com/mrbysco/armorposer/packets/handler/ServerPayloadHandler.java index c864c2f..7ec63bc 100644 --- a/forge/src/main/java/com/mrbysco/armorposer/packets/handler/ServerPayloadHandler.java +++ b/forge/src/main/java/com/mrbysco/armorposer/packets/handler/ServerPayloadHandler.java @@ -16,7 +16,7 @@ public static ServerPayloadHandler getInstance() { } public void handleSwapData(final ArmorStandSwapPayload swapData, final PlayPayloadContext context) { - // Do something with the data, on the main thread + // Do something with the pose, on the main thread context.workHandler().submitAsync(() -> { if (context.level().isPresent() && context.level().get() instanceof ServerLevel serverLevel) { Entity entity = serverLevel.getEntity(swapData.data().entityUUID()); @@ -33,7 +33,7 @@ public void handleSwapData(final ArmorStandSwapPayload swapData, final PlayPaylo } public void handleSyncData(final ArmorStandSyncPayload syncData, final PlayPayloadContext context) { - // Do something with the data, on the main thread + // Do something with the pose, on the main thread context.workHandler().submitAsync(() -> { if (context.level().isPresent() && context.level().get() instanceof ServerLevel serverLevel) { Entity entity = serverLevel.getEntity(syncData.data().entityUUID()); diff --git a/forge/src/main/java/com/mrbysco/armorposer/platform/ForgePlatformHelper.java b/forge/src/main/java/com/mrbysco/armorposer/platform/ForgePlatformHelper.java index 507a734..1df89b4 100644 --- a/forge/src/main/java/com/mrbysco/armorposer/platform/ForgePlatformHelper.java +++ b/forge/src/main/java/com/mrbysco/armorposer/platform/ForgePlatformHelper.java @@ -8,8 +8,11 @@ import com.mrbysco.armorposer.platform.services.IPlatformHelper; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.decoration.ArmorStand; +import net.neoforged.fml.loading.FMLPaths; import net.neoforged.neoforge.network.PacketDistributor; +import java.nio.file.Path; + public class ForgePlatformHelper implements IPlatformHelper { @Override public void updateEntity(ArmorStand armorStand, CompoundTag compound) { @@ -29,4 +32,9 @@ public void swapSlots(ArmorStand armorStand, SwapData.Action action) { public boolean allowScrolling() { return PoserConfig.COMMON.allowScrolling.get(); } + + @Override + public Path getUserPresetFolder() { + return FMLPaths.CONFIGDIR.get(); + } }