diff --git a/Common/src/main/java/com/mrbysco/armorposer/CommonClass.java b/Common/src/main/java/com/mrbysco/armorposer/CommonClass.java deleted file mode 100644 index d901805..0000000 --- a/Common/src/main/java/com/mrbysco/armorposer/CommonClass.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.mrbysco.armorposer; - -public class CommonClass { - - public static void init() { - - } -} \ No newline at end of file diff --git a/Common/src/main/java/com/mrbysco/armorposer/Reference.java b/Common/src/main/java/com/mrbysco/armorposer/Reference.java deleted file mode 100644 index 75667a4..0000000 --- a/Common/src/main/java/com/mrbysco/armorposer/Reference.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.mrbysco.armorposer; - -import com.mojang.logging.LogUtils; -import net.minecraft.resources.ResourceLocation; -import org.slf4j.Logger; - -public class Reference { - public static final String MOD_ID = "armorposer"; - public static final String MOD_NAME = "Armor Poser"; - public static final Logger LOGGER = LogUtils.getLogger(); - - - public static ResourceLocation SYNC_PACKET_ID = new ResourceLocation(Reference.MOD_ID, "sync_packet"); - public static ResourceLocation SCREEN_PACKET_ID = new ResourceLocation(Reference.MOD_ID, "screen_packet"); -} \ No newline at end of file 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 deleted file mode 100644 index 42bb297..0000000 --- a/Common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorStandScreen.java +++ /dev/null @@ -1,316 +0,0 @@ -package com.mrbysco.armorposer.client.gui; - -import com.mojang.blaze3d.vertex.PoseStack; -import com.mrbysco.armorposer.Reference; -import com.mrbysco.armorposer.client.gui.widgets.NumberFieldWidget; -import com.mrbysco.armorposer.client.gui.widgets.ToggleButton; -import com.mrbysco.armorposer.platform.Services; -import com.mrbysco.armorposer.util.ArmorStandData; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.chat.NarratorChatListener; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.resources.language.I18n; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.FloatTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.TagParser; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.chat.TranslatableComponent; -import net.minecraft.world.entity.decoration.ArmorStand; - -public class ArmorStandScreen extends Screen { - private final ArmorStand entityArmorStand; - private final ArmorStandData armorStandData; - - private final String[] buttonLabels = new String[]{"invisible", "no_base_plate", "no_gravity", "show_arms", "small", "rotation"}; - private final String[] sliderLabels = new String[]{"head", "body", "left_leg", "right_leg", "left_arm", "right_arm"}; - - private NumberFieldWidget rotationTextField; - private final ToggleButton[] toggleButtons = new ToggleButton[5]; - private final NumberFieldWidget[] poseTextFields = new NumberFieldWidget[18]; - - public ArmorStandScreen(ArmorStand entityArmorStand) { - super(NarratorChatListener.NO_TITLE); - this.entityArmorStand = entityArmorStand; - - this.armorStandData = new ArmorStandData(); - this.armorStandData.readFromNBT(entityArmorStand.saveWithoutId(new CompoundTag())); - - for (int i = 0; i < this.buttonLabels.length; i++) - this.buttonLabels[i] = I18n.get(String.format("%s.gui.label." + this.buttonLabels[i], Reference.MOD_ID)); - for (int i = 0; i < this.sliderLabels.length; i++) - this.sliderLabels[i] = I18n.get(String.format("%s.gui.label." + this.sliderLabels[i], Reference.MOD_ID)); - } - - @Override - public boolean isPauseScreen() { - return false; - } - - @Override - public void init() { - this.minecraft.keyboardHandler.setSendRepeatsToGui(true); - super.init(); - - int offsetX = 110; - int offsetY = 50; - - // toggle buttons - for (int i = 0; i < this.toggleButtons.length; i++) { - int x = offsetX; - int y = offsetY + (i * 22); - int width = 40; - int height = 20; - - this.toggleButtons[i] = new ToggleButton(x, y, width, height, this.armorStandData.getBooleanValue(i), (button) -> { - ToggleButton toggleButton = ((ToggleButton) button); - toggleButton.setValue(!toggleButton.getValue()); - this.textFieldUpdated(); - }); - this.addRenderableWidget(this.toggleButtons[i]); - } - - // rotation textbox - this.rotationTextField = new NumberFieldWidget(this.font, 1 + offsetX, 1 + offsetY + (this.toggleButtons.length * 22), 38, 17, new TextComponent("field.rotation")); - this.rotationTextField.setValue(String.valueOf((int) this.armorStandData.rotation)); - this.rotationTextField.setMaxLength(4); - this.addWidget(this.rotationTextField); - - // pose textboxes - offsetX = this.width - 20 - 100; - for (int i = 0; i < this.poseTextFields.length; i++) { - int id = 5 + i; - int x = 1 + offsetX + ((i % 3) * 35); - int y = 1 + offsetY + ((i / 3) * 22); - int width = 28; - int height = 17; - String value = String.valueOf((int) this.armorStandData.pose[i]); - - this.poseTextFields[i] = new NumberFieldWidget(this.font, x, y, width, height, new TextComponent(String.format("field.%s", i))); - this.poseTextFields[i].setValue(value); - this.poseTextFields[i].setMaxLength(4); - this.addWidget(this.poseTextFields[i]); - } - - offsetY = this.height / 4 + 120 + 12; - - // copy & paste buttons - offsetX = 20; - this.addRenderableWidget(new Button(offsetX, offsetY, 64, 20, new TranslatableComponent(String.format("%s.gui.label.copy", Reference.MOD_ID)), (button) -> { - CompoundTag compound = this.writeFieldsToNBT(); - String clipboardData = compound.toString(); - if (this.minecraft != null) { - this.minecraft.keyboardHandler.setClipboard(clipboardData); - } - })); - this.addRenderableWidget(new Button(offsetX + 66, offsetY, 64, 20, new TranslatableComponent(String.format("%s.gui.label.paste", Reference.MOD_ID)), (button) -> { - try { - String clipboardData = null; - if (this.minecraft != null) { - clipboardData = this.minecraft.keyboardHandler.getClipboard(); - } - CompoundTag compound = TagParser.parseTag(clipboardData); - this.readFieldsFromNBT(compound); - this.updateEntity(this.entityArmorStand, compound); - } catch (Exception e) { - //Nope - } - })); - - // done & cancel buttons - offsetX = this.width - 20; - this.addRenderableWidget(new Button(offsetX - ((2 * 96) + 2), offsetY, 96, 20, new TranslatableComponent("gui.done"), (button) -> { - this.updateEntity(this.entityArmorStand, this.writeFieldsToNBT()); - this.minecraft.setScreen((Screen) null); - })); - this.addRenderableWidget(new Button(offsetX - 96, offsetY, 96, 20, new TranslatableComponent("gui.cancel"), (button) -> { - this.updateEntity(this.entityArmorStand, this.armorStandData.writeToNBT()); - this.minecraft.setScreen((Screen) null); - })); - } - - public void removed() { - this.minecraft.keyboardHandler.setSendRepeatsToGui(false); - } - - @Override - public void render(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) { - this.renderBackground(matrixStack); - - // gui title - this.drawCenteredString(matrixStack, this.font, new TranslatableComponent(String.format("%s.gui.title", Reference.MOD_ID)), this.width / 2, 20, 0xFFFFFF); - - // textboxes - this.rotationTextField.render(matrixStack, mouseX, mouseY, partialTicks); - for (EditBox textField : this.poseTextFields) - textField.render(matrixStack, mouseX, mouseY, partialTicks); - - int offsetY = 50; - - // left column labels - int offsetX = 20; - for (int i = 0; i < this.buttonLabels.length; i++) { - int x = offsetX; - int y = offsetY + (i * 22) + (10 - (this.font.lineHeight / 2)); - this.drawString(matrixStack, this.font, this.buttonLabels[i], x, y, 0xA0A0A0); - } - - // right column labels - offsetX = this.width - 20 - 100; - // x, y, z - this.drawString(matrixStack, this.font, "X", offsetX, 37, 0xA0A0A0); - this.drawString(matrixStack, this.font, "Y", offsetX + (35), 37, 0xA0A0A0); - this.drawString(matrixStack, this.font, "Z", offsetX + (2 * 35), 37, 0xA0A0A0); - // pose textboxes - for (int i = 0; i < this.sliderLabels.length; i++) { - int x = offsetX - this.font.width(this.sliderLabels[i]) - 10; - int y = offsetY + (i * 22) + (10 - (this.font.lineHeight / 2)); - this.drawString(matrixStack, this.font, this.sliderLabels[i], x, y, 0xA0A0A0); - } - - super.render(matrixStack, mouseX, mouseY, partialTicks); - } - - @Override - public void tick() { - super.tick(); - this.rotationTextField.tick(); - for (EditBox textField : this.poseTextFields) - textField.tick(); - } - - @Override - public boolean charTyped(char codePoint, int modifiers) { - boolean typed = super.charTyped(codePoint, modifiers); - if (typed) { - this.textFieldUpdated(); - } - return typed; - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == 15) { //Tab - for (int i = 0; i < this.poseTextFields.length; i++) { - if (this.poseTextFields[i].isFocused()) { - this.textFieldUpdated(); - this.poseTextFields[i].moveCursorToEnd(); - this.poseTextFields[i].setFocus(false); - - int j = (!Screen.hasShiftDown() ? (i == this.poseTextFields.length - 1 ? 0 : i + 1) : (i == 0 ? this.poseTextFields.length - 1 : i - 1)); - this.poseTextFields[j].setFocus(true); - this.poseTextFields[j].moveCursorTo(0); - this.poseTextFields[j].setHighlightPos(this.poseTextFields[j].getValue().length()); - } - } - } else { - if (this.rotationTextField.keyPressed(keyCode, scanCode, modifiers)) { - this.textFieldUpdated(); - return true; - } else { - for (NumberFieldWidget textField : this.poseTextFields) { - if (textField.keyPressed(keyCode, scanCode, modifiers)) { - this.textFieldUpdated(); - return true; - } - } - } - } - return super.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - this.rotationTextField.mouseClicked(mouseX, mouseY, button); - for (NumberFieldWidget textField : this.poseTextFields) { - textField.mouseClicked(mouseX, mouseY, button); - this.textFieldUpdated(); - } - - return super.mouseClicked(mouseX, mouseY, button); - } - - protected void textFieldUpdated() { - this.updateEntity(this.entityArmorStand, this.writeFieldsToNBT()); - } - - private CompoundTag writeFieldsToNBT() { - CompoundTag compound = new CompoundTag(); - compound.putBoolean("Invisible", this.toggleButtons[0].getValue()); - compound.putBoolean("NoBasePlate", this.toggleButtons[1].getValue()); - compound.putBoolean("NoGravity", this.toggleButtons[2].getValue()); - compound.putBoolean("ShowArms", this.toggleButtons[3].getValue()); - compound.putBoolean("Small", this.toggleButtons[4].getValue()); - - ListTag rotationTag = new ListTag(); - rotationTag.add(FloatTag.valueOf(Float.valueOf(this.rotationTextField.getFloat()))); - compound.put("Rotation", rotationTag); - - CompoundTag poseTag = new CompoundTag(); - - ListTag poseHeadTag = new ListTag(); - poseHeadTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[0].getFloat()))); - poseHeadTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[1].getFloat()))); - poseHeadTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[2].getFloat()))); - poseTag.put("Head", poseHeadTag); - - ListTag poseBodyTag = new ListTag(); - poseBodyTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[3].getFloat()))); - poseBodyTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[4].getFloat()))); - poseBodyTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[5].getFloat()))); - poseTag.put("Body", poseBodyTag); - - ListTag poseLeftLegTag = new ListTag(); - poseLeftLegTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[6].getFloat()))); - poseLeftLegTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[7].getFloat()))); - poseLeftLegTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[8].getFloat()))); - poseTag.put("LeftLeg", poseLeftLegTag); - - ListTag poseRightLegTag = new ListTag(); - poseRightLegTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[9].getFloat()))); - poseRightLegTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[10].getFloat()))); - poseRightLegTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[11].getFloat()))); - poseTag.put("RightLeg", poseRightLegTag); - - ListTag poseLeftArmTag = new ListTag(); - poseLeftArmTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[12].getFloat()))); - poseLeftArmTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[13].getFloat()))); - poseLeftArmTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[14].getFloat()))); - poseTag.put("LeftArm", poseLeftArmTag); - - ListTag poseRightArmTag = new ListTag(); - poseRightArmTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[15].getFloat()))); - poseRightArmTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[16].getFloat()))); - poseRightArmTag.add(FloatTag.valueOf(Float.valueOf(this.poseTextFields[17].getFloat()))); - poseTag.put("RightArm", poseRightArmTag); - - compound.put("Pose", poseTag); - return compound; - } - - private void readFieldsFromNBT(CompoundTag compound) { - ArmorStandData armorStandData = new ArmorStandData(); - armorStandData.readFromNBT(compound); - - for (int i = 0; i < this.toggleButtons.length; i++) { - this.toggleButtons[i].setValue(armorStandData.getBooleanValue(i)); - } - - this.rotationTextField.setValue(String.valueOf((int) armorStandData.rotation)); - - for (int i = 0; i < this.poseTextFields.length; i++) { - this.poseTextFields[i].setValue(String.valueOf((int) armorStandData.pose[i])); - } - } - - - public static void openScreen(ArmorStand armorStandEntity) { - Minecraft.getInstance().setScreen(new ArmorStandScreen(armorStandEntity)); - } - - public void updateEntity(ArmorStand armorStand, CompoundTag compound) { - Services.PLATFORM.updateEntity(armorStand, compound); - } -} \ No newline at end of file diff --git a/Common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/NumberFieldWidget.java b/Common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/NumberFieldWidget.java deleted file mode 100644 index ac018fe..0000000 --- a/Common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/NumberFieldWidget.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.mrbysco.armorposer.client.gui.widgets; - -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.network.chat.Component; -import org.apache.commons.lang3.math.NumberUtils; - -public class NumberFieldWidget extends EditBox { - public NumberFieldWidget(Font font, int x, int y, int width, int height, Component defaultValue) { - super(font, x, y, width, height, defaultValue); - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - return super.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public void insertText(String textToWrite) { - if (this.isNumeric(textToWrite)) - super.insertText(textToWrite); - - float currentValue = getFloat(); - if(currentValue > 360 || currentValue < -360) { - this.setValue("0"); - } - } - - @Override - public String getValue() { - return (this.isNumeric(super.getValue()) ? super.getValue() : "0"); - } - - public float getFloat() { - return NumberUtils.toFloat(super.getValue(), 0.0F); - } - - @Override - protected void setFocused(boolean focused) { - super.setFocused(focused); - if (!focused) { - this.setHighlightPos(this.getValue().length()); - this.moveCursorToEnd(); - } - } - - private boolean isNumeric(String value) { - return value.equals("-") || NumberUtils.isParsable(value); - } -} \ No newline at end of file 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 deleted file mode 100644 index 0a7d1f3..0000000 --- a/Common/src/main/java/com/mrbysco/armorposer/platform/services/IPlatformHelper.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.mrbysco.armorposer.platform.services; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.entity.decoration.ArmorStand; - -public interface IPlatformHelper { - /** - * Update Armor Stand Entity - */ - void updateEntity(ArmorStand armorStand, CompoundTag compound); -} diff --git a/Common/src/main/resources/assets/armorposer/lang/en_us.json b/Common/src/main/resources/assets/armorposer/lang/en_us.json deleted file mode 100644 index b4228a7..0000000 --- a/Common/src/main/resources/assets/armorposer/lang/en_us.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "_comment": "en_us", - - "armorposer.gui.title": "Armor Stand", - - "armorposer.gui.label.invisible": "Invisible", - "armorposer.gui.label.no_base_plate": "No Base Plate", - "armorposer.gui.label.no_gravity": "No Gravity", - "armorposer.gui.label.show_arms": "Show Arms", - "armorposer.gui.label.small": "Small", - "armorposer.gui.label.rotation": "Rotation", - - "armorposer.gui.label.head": "Head", - "armorposer.gui.label.body": "Body", - "armorposer.gui.label.left_leg": "Left Leg", - "armorposer.gui.label.right_leg": "Right Leg", - "armorposer.gui.label.left_arm": "Left Arm", - "armorposer.gui.label.right_arm": "Right Arm", - - "armorposer.gui.label.copy": "Copy", - "armorposer.gui.label.paste": "Paste", - - - "armorposer.config.enableConfigGui": "Enable GUI", - "armorposer.config.enableConfigGui.tooltip": "Show the Armor Stand configuration GUI on shift right click", - - "armorposer.config.overrideEntityInteract": "Process Interactions", - "armorposer.config.overrideEntityInteract.tooltip": "Use custom logic when right-clicking an Armor Stand", - - "armorposer.key.openArmorStandGui": "Open GUI", - "armorposer.key.categories.general": "Armor Stand Configurator" -} \ No newline at end of file diff --git a/Common/src/main/resources/assets/armorposer/lang/fr_fr.json b/Common/src/main/resources/assets/armorposer/lang/fr_fr.json deleted file mode 100644 index 7a2f383..0000000 --- a/Common/src/main/resources/assets/armorposer/lang/fr_fr.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "_comment": "fr_fr", - - "armorposer.gui.title": "Armor Stand", - - "armorposer.gui.label.invisible": "Invisible", - "armorposer.gui.label.no_base_plate": "Sans base", - "armorposer.gui.label.no_gravity": "Sans gravité", - "armorposer.gui.label.show_arms": "Afficher les bras", - "armorposer.gui.label.small": "Petit", - "armorposer.gui.label.rotation": "Rotation", - - "armorposer.gui.label.head": "Tête", - "armorposer.gui.label.body": "Corps", - "armorposer.gui.label.left_leg": "Jambe gauche", - "armorposer.gui.label.right_leg": "Jambe droite", - "armorposer.gui.label.left_arm": "Bras gauche", - "armorposer.gui.label.right_arm": "Bras droit", - - "armorposer.gui.label.copy": "Copier", - "armorposer.gui.label.paste": "Coller", - - - "armorposer.config.enableConfigGui": "Activer l'interface", - "armorposer.config.enableConfigGui.tooltip": "Afficher l'interface de configuration du porte-armure avec un shift clic-droit", - - "armorposer.config.overrideEntityInteract": "Interaction de processus", - "armorposer.config.overrideEntityInteract.tooltip": "Utilisez une logique personnalisée lorsque vous faites un clic droit sur un prote-armure", - - "armorposer.key.openArmorStandGui": "Ouvrir le menu", - "armorposer.key.categories.general": "Configurateur du porte-armure" -} \ No newline at end of file diff --git a/Common/src/main/resources/assets/armorposer/lang/zh_tw.json b/Common/src/main/resources/assets/armorposer/lang/zh_tw.json deleted file mode 100644 index 3ad7635..0000000 --- a/Common/src/main/resources/assets/armorposer/lang/zh_tw.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "_comment": "zh_tw", - - "armorposer.gui.title": "盔甲座", - - "armorposer.gui.label.invisible": "隱形", - "armorposer.gui.label.no_base_plate": "沒有底盤", - "armorposer.gui.label.no_gravity": "不受重力影響", - "armorposer.gui.label.show_arms": "顯示手臂", - "armorposer.gui.label.small": "小型", - "armorposer.gui.label.rotation": "旋轉", - - "armorposer.gui.label.head": "頭", - "armorposer.gui.label.body": "身體", - "armorposer.gui.label.left_leg": "左腿", - "armorposer.gui.label.right_leg": "右腿", - "armorposer.gui.label.left_arm": "左臂", - "armorposer.gui.label.right_arm": "右臂", - - "armorposer.gui.label.copy": "複製", - "armorposer.gui.label.paste": "貼上", - - "armorposer.config.enableConfigGui": "啟用GUI", - "armorposer.config.enableConfigGui.tooltip": "按\"Shift+右鍵\"點撃盔甲座時顯示盔甲座配置GUI", - "armorposer.config.overrideEntityInteract": "過程交互", - "armorposer.config.overrideEntityInteract.tooltip": "按右鍵點撃盔甲座時使用自定義邏輯", - - "armorposer.key.openArmorStandGui": "打開GUI", - "armorposer.key.categories.general": "盔甲座配置" -} \ No newline at end of file diff --git a/Fabric/src/main/java/com/mrbysco/armorposer/platform/FabricPlatformHelper.java b/Fabric/src/main/java/com/mrbysco/armorposer/platform/FabricPlatformHelper.java deleted file mode 100644 index ed16841..0000000 --- a/Fabric/src/main/java/com/mrbysco/armorposer/platform/FabricPlatformHelper.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.mrbysco.armorposer.platform; - -import com.mrbysco.armorposer.Reference; -import com.mrbysco.armorposer.platform.services.IPlatformHelper; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.world.entity.decoration.ArmorStand; - -public class FabricPlatformHelper implements IPlatformHelper { - @Override - public void updateEntity(ArmorStand armorStand, CompoundTag compound) { - CompoundTag CompoundNBT = armorStand.saveWithoutId(new CompoundTag()).copy(); - CompoundNBT.merge(compound); - armorStand.load(CompoundNBT); - - FriendlyByteBuf buf = PacketByteBufs.create(); - buf.writeUUID(armorStand.getUUID()); - buf.writeNbt(compound); - ClientPlayNetworking.send(Reference.SYNC_PACKET_ID, buf); - } -} diff --git a/Fabric/src/main/resources/armorposer.fabric.mixins.json b/Fabric/src/main/resources/armorposer.fabric.mixins.json deleted file mode 100644 index e90dd51..0000000 --- a/Fabric/src/main/resources/armorposer.fabric.mixins.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "required": true, - "minVersion": "0.8.4", - "package": "com.mrbysco.armorposer.mixin", - "compatibilityLevel": "JAVA_17", - "mixins": [ - "ArmorStandMixin" - ], - "client": [ - ], - "injectors": { - "defaultRequire": 1 - } - } - \ No newline at end of file diff --git a/Forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandSyncMessage.java b/Forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandSyncMessage.java deleted file mode 100644 index c144cb3..0000000 --- a/Forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandSyncMessage.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.mrbysco.armorposer.packets; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.decoration.ArmorStand; -import net.minecraftforge.network.NetworkEvent.Context; - -import java.util.UUID; -import java.util.function.Supplier; - - -public class ArmorStandSyncMessage { - private final UUID entityUUID; - private final CompoundTag data; - - public ArmorStandSyncMessage(UUID playerUUID, CompoundTag tag) { - this.entityUUID = playerUUID; - this.data = tag; - } - - public void encode(FriendlyByteBuf buf) { - buf.writeUUID(entityUUID); - buf.writeNbt(data); - } - - public static ArmorStandSyncMessage decode(final FriendlyByteBuf packetBuffer) { - return new ArmorStandSyncMessage(packetBuffer.readUUID(), packetBuffer.readNbt()); - } - - public void handle(Supplier context) { - Context ctx = context.get(); - ctx.enqueueWork(() -> { - if (ctx.getDirection().getReceptionSide().isServer() && ctx.getSender() != null) { - final ServerLevel world = ctx.getSender().getLevel(); - Entity entity = world.getEntity(this.entityUUID); - if (entity instanceof ArmorStand armorStandEntity) { - - CompoundTag entityTag = armorStandEntity.saveWithoutId(new CompoundTag()); - CompoundTag entityTagCopy = entityTag.copy(); - - if (!this.data.isEmpty()) { - entityTagCopy.merge(this.data); - UUID uuid = armorStandEntity.getUUID(); - armorStandEntity.load(entityTagCopy); - armorStandEntity.setUUID(uuid); - } - } - } - }); - ctx.setPacketHandled(true); - } -} diff --git a/Forge/src/main/java/com/mrbysco/armorposer/platform/ForgePlatformHelper.java b/Forge/src/main/java/com/mrbysco/armorposer/platform/ForgePlatformHelper.java deleted file mode 100644 index eb4201f..0000000 --- a/Forge/src/main/java/com/mrbysco/armorposer/platform/ForgePlatformHelper.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.mrbysco.armorposer.platform; - -import com.mrbysco.armorposer.ArmorPoser; -import com.mrbysco.armorposer.packets.ArmorStandSyncMessage; -import com.mrbysco.armorposer.platform.services.IPlatformHelper; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.entity.decoration.ArmorStand; -import net.minecraftforge.network.PacketDistributor; - -public class ForgePlatformHelper implements IPlatformHelper { - @Override - public void updateEntity(ArmorStand armorStand, CompoundTag compound) { - CompoundTag CompoundNBT = armorStand.saveWithoutId(new CompoundTag()).copy(); - CompoundNBT.merge(compound); - armorStand.load(CompoundNBT); - - ArmorPoser.CHANNEL.send(PacketDistributor.SERVER.noArg(), new ArmorStandSyncMessage(armorStand.getUUID(), compound)); - } -} diff --git a/build.gradle b/build.gradle index 972c2d1..1c9e6c7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.3-SNAPSHOT' apply(false) + id 'fabric-loom' version '1.7-SNAPSHOT' apply(false) id 'net.minecraftforge.gradle' version '[6.0,6.2)' apply(false) id 'org.spongepowered.gradle.vanilla' version '0.2.1-SNAPSHOT' apply(false) id("org.spongepowered.mixin") version "0.7-SNAPSHOT" apply(false) diff --git a/Common/build.gradle b/common/build.gradle similarity index 100% rename from Common/build.gradle rename to common/build.gradle diff --git a/common/src/main/java/com/mrbysco/armorposer/Reference.java b/common/src/main/java/com/mrbysco/armorposer/Reference.java new file mode 100644 index 0000000..a624bfe --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/Reference.java @@ -0,0 +1,73 @@ +package com.mrbysco.armorposer; + +import com.mojang.logging.LogUtils; +import com.mrbysco.armorposer.poses.UserPoseHandler; +import com.mrbysco.armorposer.util.PoseData; +import net.minecraft.nbt.CompoundTag; +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 { + public static final String MOD_ID = "armorposer"; + public static final String MOD_NAME = "Armor Poser"; + public static final Logger LOGGER = LogUtils.getLogger(); + + + public static final ResourceLocation SYNC_PACKET_ID = new ResourceLocation(Reference.MOD_ID, "sync_packet"); + public static final ResourceLocation SWAP_PACKET_ID = new ResourceLocation(Reference.MOD_ID, "swap_packet"); + public static final ResourceLocation SCREEN_PACKET_ID = new ResourceLocation(Reference.MOD_ID, "screen_packet"); + + public static final Map defaultPoseMap = initializePoseMap(); + + 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]},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<>(); + + public static void savePose(String poseName, CompoundTag tag) { + String tagString = tag.toString(); + userPoses.add(new PoseData(poseName, tagString)); + UserPoseHandler.saveUserPoses(); + } + + public static void removePose(String poseName) { + userPoses.removeIf(pose -> pose.name().equalsIgnoreCase(poseName)); + UserPoseHandler.saveUserPoses(); + } + + public static final String alignedBlockPose = "{CustomNameVisible:0b,DisabledSlots:0,Invisible:0b,Invulnerable:0b,Move:[0.0d,0.0d,0.0d],NoBasePlate:0b,NoGravity:1b,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,135.0f,0.0f],RightLeg:[0.0f,0.0f,0.0f]},Rotation:[0.0f],ShowArms:1b,Small:0b}"; + public static final String alignedUprightItemPose = "{CustomNameVisible:0b,DisabledSlots:0,Invisible:1b,Invulnerable:0b,Move:[0.0d,0.0d,0.0d],NoBasePlate:0b,NoGravity:1b,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]},Rotation:[0.0f],ShowArms:1b,Small:0b}"; + public static final String alignedFlatItemPose = "{CustomNameVisible:0b,DisabledSlots:0,Invisible:1b,Invulnerable:0b,Move:[0.0d,0.0d,0.0d],NoBasePlate:0b,NoGravity:1b,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]},Rotation:[0.0f],ShowArms:1b,Small:0b}"; + public static final String alignedToolPose = "{CustomNameVisible:0b,DisabledSlots:0,Invisible:0b,Invulnerable:0b,Move:[0.0d,0.0d,0.0d],NoBasePlate:0b,NoGravity:1b,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:[-10.0f,0.0f,-90.0f],RightLeg:[0.0f,0.0f,0.0f]},Rotation:[0.0f],ShowArms:1b,Small:0b}"; +} \ No newline at end of file diff --git a/common/src/main/java/com/mrbysco/armorposer/client/GlowHandler.java b/common/src/main/java/com/mrbysco/armorposer/client/GlowHandler.java new file mode 100644 index 0000000..9000e58 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/GlowHandler.java @@ -0,0 +1,32 @@ +package com.mrbysco.armorposer.client; + +import java.util.UUID; + +public class GlowHandler { + private static long glowStartTime = 0; + private static UUID glowingStand = null; + + public static boolean shouldArmorStandGlow() { + if (glowStartTime == -1) { + return false; + } + boolean notEmpty = glowingStand != null; + if (notEmpty && System.currentTimeMillis() - glowStartTime > 5000) { + glowStartTime = -1; + glowingStand = null; + } + return notEmpty; + } + + public static boolean isGlowing(UUID uuid) { + if (!shouldArmorStandGlow()) + return false; + else + return glowingStand != null && glowingStand.equals(uuid); + } + + public static void startGlowing(UUID uuid) { + glowStartTime = System.currentTimeMillis(); + glowingStand = uuid; + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorGlowScreen.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorGlowScreen.java new file mode 100644 index 0000000..c91eab0 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorGlowScreen.java @@ -0,0 +1,152 @@ +package com.mrbysco.armorposer.client.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mrbysco.armorposer.client.GlowHandler; +import com.mrbysco.armorposer.client.gui.widgets.ArmorGlowWidget; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.ObjectSelectionList; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.commands.arguments.EntityAnchorArgument; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.world.entity.EntitySelector; +import net.minecraft.world.entity.decoration.ArmorStand; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ArmorGlowScreen extends Screen { + private static final int PADDING = 6; + + private ArmorGlowWidget armorListWidget; + private ArmorGlowWidget.ListEntry selected = null; + private final List armorStands; + private Button locateButton; + private Button modifyButton; + + public ArmorStandScreen parentScreen; + + public ArmorGlowScreen(ArmorStandScreen parent) { + super(new TranslatableComponent("armorposer.gui.armor_list.list")); + this.parentScreen = parent; + + this.minecraft = Minecraft.getInstance(); + + //Add the armor stands to the list + if (minecraft.player == null) + this.onClose(); + + List armorStands = minecraft.level.getEntitiesOfClass(ArmorStand.class, + minecraft.player.getBoundingBox().inflate(30.0D), EntitySelector.LIVING_ENTITY_STILL_ALIVE).stream().collect(Collectors.toList()); + //Sort the list based on how far the armor stand is from the player + armorStands.sort((armorStand, armorStand2) -> { + double distance1 = armorStand.distanceToSqr(minecraft.player); + double distance2 = armorStand2.distanceToSqr(minecraft.player); + return Double.compare(distance1, distance2); + }); + this.armorStands = Collections.unmodifiableList(armorStands); + } + + @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(new Button(centerWidth - (closeButtonWidth / 2) + PADDING, y, closeButtonWidth, 20, + new TranslatableComponent("gui.cancel"), b -> ArmorGlowScreen.this.onClose())); + + y -= 18 + PADDING; + int buttonWidth = (closeButtonWidth / 2) - 1; + this.addRenderableWidget(this.locateButton = new Button(centerWidth - (closeButtonWidth / 2) + PADDING, y, buttonWidth, 20, new TranslatableComponent("armorposer.gui.armor_list.locate"), b -> { + if (selected != null && minecraft.player != null) { + GlowHandler.startGlowing(this.selected.getArmorStand().getUUID()); + minecraft.player.lookAt(EntityAnchorArgument.Anchor.EYES, selected.getArmorStand().position()); + } + })); + this.addRenderableWidget(this.modifyButton = new Button(centerWidth - (closeButtonWidth / 2) + PADDING + buttonWidth + 2, y, buttonWidth, 20, new TranslatableComponent("armorposer.gui.armor_list.modify"), b -> { + if (selected != null && minecraft.player != null) { + minecraft.setScreen(new ArmorStandScreen(selected.getArmorStand())); + } + })); + + int fullButtonHeight = PADDING + 20 + PADDING; + this.armorListWidget = new ArmorGlowWidget(this, new TranslatableComponent("armorposer.gui.armor_list.list"), listWidth, fullButtonHeight, 14 - getScreenFont().lineHeight); + this.armorListWidget.setLeftPos(0); + this.armorListWidget.setY(10); + this.armorListWidget.setHeight(this.height); + + addWidget(armorListWidget); + + updateCache(); + } + + @Override + public void tick() { + armorListWidget.setSelected(selected); + } + + public > void buildPositionList(Consumer ListViewConsumer, Function newEntry) { + armorStands.forEach(stand -> ListViewConsumer.accept(newEntry.apply(stand))); + } + + @Override + public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { + this.armorListWidget.render(poseStack, mouseX, mouseY, partialTicks); + super.render(poseStack, mouseX, mouseY, partialTicks); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + return super.keyPressed(keyCode, scanCode, modifiers); + } + + public void setSelected(ArmorGlowWidget.ListEntry entry) { + this.selected = entry == this.selected ? null : entry; + updateCache(); + } + + private void updateCache() { + this.locateButton.active = selected != null; + this.modifyButton.active = selected != null; + } + + /** + * Clear the search field when right-clicked on it + */ + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public void resize(Minecraft mc, int width, int height) { + ArmorGlowWidget.ListEntry selected = this.selected; + this.init(mc, width, height); + this.selected = selected; + 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/ArmorPosesScreen.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorPosesScreen.java new file mode 100644 index 0000000..b064c40 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorPosesScreen.java @@ -0,0 +1,282 @@ +package com.mrbysco.armorposer.client.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +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.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 net.minecraft.network.chat.TranslatableComponent; +import org.lwjgl.glfw.GLFW; + +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 new TranslatableComponent("armorposer.gui.poses.search." + name().toLowerCase(Locale.ROOT)); + } + } + + private static final int PADDING = 6; + + private final 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(new TranslatableComponent("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(new Button(centerWidth - (closeButtonWidth / 2) + PADDING, y, closeButtonWidth, 20, + new TranslatableComponent("gui.cancel"), b -> ArmorPosesScreen.this.onClose())); + + y -= 18 + PADDING; + this.addRenderableWidget(this.applyButton = new Button(centerWidth - (closeButtonWidth / 2) + PADDING, y, + closeButtonWidth, 20, new TranslatableComponent("armorposer.gui.poses.selection.apply"), b -> { + if (selected != null) { + if (!selected.userAdded() && selected.rawName().equalsIgnoreCase("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)); + this.parentScreen.textFieldUpdated(); + } + } else { + this.parentScreen.readFieldsFromNBT(selected.getTag()); + this.parentScreen.updateEntity(selected.getTag()); + } + } + this.onClose(); + })); + + y -= 14 + PADDING; + search = new EditBox(getScreenFont(), centerWidth - listWidth / 2 + PADDING + 1, y, listWidth - 2, 14, + new TranslatableComponent("armorposer.gui.poses.search")); + + int fullButtonHeight = PADDING + 20 + PADDING; + this.poseListWidget[0] = new PoseListWidget(this, new TranslatableComponent("armorposer.gui.poses.default"), + false, listWidth, fullButtonHeight, search.y - getScreenFont().lineHeight - PADDING); + this.poseListWidget[0].setLeftPos(0); + this.poseListWidget[0].setY(10); + this.poseListWidget[0].setHeight(this.height); + + this.poseListWidget[1] = new PoseListWidget(this, new TranslatableComponent("armorposer.gui.poses.user"), + true, listWidth, fullButtonHeight, search.y - getScreenFont().lineHeight - PADDING); + this.poseListWidget[1].setLeftPos(width - listWidth); + this.poseListWidget[1].setY(10); + this.poseListWidget[1].setHeight(this.height); + + addWidget(search); + addWidget(poseListWidget[0]); + addWidget(poseListWidget[1]); + search.setFocus(false); + search.setCanLoseFocus(true); + + final int width = listWidth / numButtons; + int x = centerWidth + PADDING - width; + addRenderableWidget(SortType.A_TO_Z.button = new Button(x, PADDING, width - buttonMargin, 20, + SortType.A_TO_Z.getButtonText(), b -> resortPoses(SortType.A_TO_Z))); + x += width + buttonMargin; + addRenderableWidget(SortType.Z_TO_A.button = new Button(x, PADDING, width - buttonMargin, 20, + SortType.Z_TO_A.getButtonText(), b -> resortPoses(SortType.Z_TO_A))); + + 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(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { + this.poseListWidget[0].render(poseStack, mouseX, mouseY, partialTicks); + this.poseListWidget[1].render(poseStack, mouseX, mouseY, partialTicks); + super.render(poseStack, mouseX, mouseY, partialTicks); + + Component text = new TranslatableComponent("armorposer.gui.poses.search"); + drawCenteredString(poseStack, getScreenFont(), text, this.width / 2 + PADDING, + search.y - getScreenFont().lineHeight - 2, 0xFFFFFF); + + this.search.render(poseStack, mouseX, mouseY, partialTicks); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_DELETE) { + if (selected != null && selected.userAdded()) { + this.minecraft.setScreen(new DeletePoseScreen(this.parentScreen, selected)); + } + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + 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 new file mode 100644 index 0000000..e7110b3 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorStandScreen.java @@ -0,0 +1,789 @@ +package com.mrbysco.armorposer.client.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.math.Vector3f; +import com.mrbysco.armorposer.Reference; +import com.mrbysco.armorposer.client.gui.widgets.NumberFieldBox; +import com.mrbysco.armorposer.client.gui.widgets.PoseImageButton; +import com.mrbysco.armorposer.client.gui.widgets.ToggleButton; +import com.mrbysco.armorposer.client.gui.widgets.TooltipLockIconButton; +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.Minecraft; +import net.minecraft.client.gui.chat.NarratorChatListener; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.FloatTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.TagParser; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.phys.Vec3; + +import java.util.function.Consumer; + +public class ArmorStandScreen extends Screen { + private final ArmorStand entityArmorStand; + private final ArmorStandData armorStandData; + + private final String[] buttonLabels = new String[]{"invisible", "no_base_plate", "no_gravity", "show_arms", "small", "name_visible", "rotation"}; + private final String[] sliderLabels = new String[]{"head", "body", "left_leg", "right_leg", "left_arm", "right_arm", "position"}; + + private NumberFieldBox rotationTextField; + private final ToggleButton[] toggleButtons = new ToggleButton[6]; + protected final NumberFieldBox[] poseTextFields = new NumberFieldBox[3 * 7]; + private TooltipLockIconButton lockButton; + private final boolean allowScrolling; + + private Vec3 lastSendOffset = new Vec3(0, 0, 0); + + //Cache the tooltip, so we don't have to create a new one every tick + private final NumberFieldBox.OnTooltip yPositionTooltip = createFieldTooltip(new TranslatableComponent("armorposer.gui.tooltip.y_position")); + private final NumberFieldBox.OnTooltip yPositionTooltipDisabled = createFieldTooltip(new TranslatableComponent("armorposer.gui.tooltip.y_position.disabled").withStyle(ChatFormatting.RED)); + + private final int whiteColor = 16777215; + + public ArmorStandScreen(ArmorStand entityArmorStand) { + super(NarratorChatListener.NO_TITLE); + this.entityArmorStand = entityArmorStand; + + this.armorStandData = new ArmorStandData(); + this.armorStandData.readFromNBT(entityArmorStand.saveWithoutId(new CompoundTag())); + + this.allowScrolling = Services.PLATFORM.allowScrolling(); + } + + @Override + public boolean isPauseScreen() { + return false; + } + + @Override + public void init() { + super.init(); + + int offsetX = 110; + int offsetY = 20; + + // toggle buttons + for (int i = 0; i < this.toggleButtons.length; i++) { + int x = offsetX; + int y = offsetY + (i * 22); + int width = 40; + int height = 20; + + this.addRenderableWidget(this.toggleButtons[1] = new ToggleButton(x, y, width, height, this.armorStandData.getBooleanValue(i), (button) -> { + ToggleButton toggleButton = ((ToggleButton) button); + toggleButton.setValue(!toggleButton.getValue()); + this.textFieldUpdated(); + }, createTooltip(new TranslatableComponent("armorposer.gui.tooltip." + buttonLabels[i])))); + } + + // rotation textbox + this.rotationTextField = new NumberFieldBox(this.font, 1 + offsetX, 1 + offsetY + (this.toggleButtons.length * 22), + 38, 17, new TranslatableComponent("armorposer.gui.label.rotation"), + createFieldTooltip(new TranslatableComponent("armorposer.gui.tooltip.rotation"))); + this.rotationTextField.setValue(String.valueOf((int) this.armorStandData.rotation)); + this.rotationTextField.setMaxLength(4); + this.addWidget(this.rotationTextField); + + // pose textboxes + offsetX = this.width - 20 - 100; + for (int i = 0; i < this.poseTextFields.length; i++) { + int x = 1 + offsetX + ((i % 3) * 35); + int y = 1 + offsetY + ((i / 3) * 22); + int width = 28; + int height = 17; + String value = String.valueOf((int) this.armorStandData.pose[i]); + boolean lastRow = i >= 3 * 6 && i < 3 * 7; + + //Create tooltip + NumberFieldBox.OnTooltip tooltip; + if (i % 3 == 0) { + tooltip = createFieldTooltip(new TranslatableComponent("armorposer.gui.tooltip." + (lastRow ? "x_position" : "x_rotation"))); + } else if (i % 3 == 1) { + tooltip = createFieldTooltip(new TranslatableComponent("armorposer.gui.tooltip." + (lastRow ? "y_position" : "y_rotation"))); + } else { + tooltip = createFieldTooltip(new TranslatableComponent("armorposer.gui.tooltip." + (lastRow ? "z_position" : "z_rotation"))); + } + this.poseTextFields[i] = new NumberFieldBox(this.font, x, y, width, height, new TextComponent(value), tooltip); + this.poseTextFields[i].setValue(value); + this.poseTextFields[i].setMaxLength(4); + if (lastRow) { + this.poseTextFields[i].scrollMultiplier = 0.01f; + this.poseTextFields[i].modValue = Integer.MAX_VALUE; + this.poseTextFields[i].decimalPoints = 2; + this.poseTextFields[i].allowDecimal = true; + this.poseTextFields[i].setMaxLength(6); + } + + this.addWidget(this.poseTextFields[i]); + } + + offsetY = this.height / 4 + 134; + + // copy & paste buttons + offsetX = 20; + this.addRenderableWidget(new Button(offsetX, offsetY, 130, 20, new TranslatableComponent("armorposer.gui.label.poses"), (button) -> + this.minecraft.setScreen(new ArmorPosesScreen(this)), + createTooltip(new TranslatableComponent("armorposer.gui.tooltip.poses")))); + this.addRenderableWidget(new Button(offsetX, offsetY + 22, 42, 20, new TranslatableComponent("armorposer.gui.label.copy"), (button) -> { + CompoundTag compound = this.writeFieldsToNBT(); + String clipboardData = compound.toString(); + if (this.minecraft != null) { + this.minecraft.keyboardHandler.setClipboard(clipboardData); + } + }, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.copy")))); + + this.addRenderableWidget(new Button(offsetX + 44, offsetY + 22, 42, 20, new TranslatableComponent("armorposer.gui.label.paste"), (button) -> { + try { + String clipboardData = null; + if (this.minecraft != null) { + clipboardData = this.minecraft.keyboardHandler.getClipboard(); + } + if (clipboardData != null) { + CompoundTag compound = TagParser.parseTag(clipboardData); + this.readFieldsFromNBT(compound); + this.updateEntity(compound); + } + } catch (Exception e) { + //Nope + } + }, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.paste")))); + + this.addRenderableWidget(new Button(offsetX + 88, offsetY + 22, 42, 20, new TranslatableComponent("armorposer.gui.label.save"), (button) -> { + this.minecraft.setScreen(new SavePoseScreen(this)); + }, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.save")))); + + offsetX = this.width - 20; + int buttonsLeft = 9; + int buttonOffset = -4; + PoseImageButton mirrorPose = this.addRenderableWidget(new PoseImageButton(offsetX - (22 * buttonsLeft) - buttonOffset, offsetY, (button) -> { + //Mirror head + float[] head = new float[]{poseTextFields[0].getFloat(), poseTextFields[1].getFloat(), poseTextFields[2].getFloat()}; + poseTextFields[0].setValue(String.valueOf(head[0])); + poseTextFields[1].setValue(String.valueOf(head[1] != 0 ? -head[1] : 0)); + poseTextFields[2].setValue(String.valueOf(head[2] != 0 ? -head[2] : 0)); + + //Mirror head + float[] body = new float[]{poseTextFields[3].getFloat(), poseTextFields[4].getFloat(), poseTextFields[5].getFloat()}; + poseTextFields[3].setValue(String.valueOf(body[0])); + poseTextFields[4].setValue(String.valueOf(body[1] != 0 ? -body[1] : 0)); + poseTextFields[5].setValue(String.valueOf(body[2] != 0 ? -body[2] : 0)); + + //Mirror Legs + float[] leftLeg = new float[]{poseTextFields[6].getFloat(), poseTextFields[7].getFloat(), poseTextFields[8].getFloat()}; + float[] rightLeg = new float[]{poseTextFields[9].getFloat(), poseTextFields[10].getFloat(), poseTextFields[11].getFloat()}; + + //Swap angles and mirror the angles + poseTextFields[6].setValue(String.valueOf(rightLeg[0])); + poseTextFields[7].setValue(String.valueOf(rightLeg[1] != 0 ? -rightLeg[1] : 0)); + poseTextFields[8].setValue(String.valueOf(rightLeg[2] != 0 ? -rightLeg[2] : 0)); + poseTextFields[9].setValue(String.valueOf(leftLeg[0])); + poseTextFields[10].setValue(String.valueOf(leftLeg[1] != 0 ? -leftLeg[1] : 0)); + poseTextFields[11].setValue(String.valueOf(leftLeg[2] != 0 ? -leftLeg[2] : 0)); + + //Mirror Arms + float[] leftArm = new float[]{poseTextFields[12].getFloat(), poseTextFields[13].getFloat(), poseTextFields[14].getFloat()}; + float[] rightArm = new float[]{poseTextFields[15].getFloat(), poseTextFields[16].getFloat(), poseTextFields[17].getFloat()}; + + //Swap angles and mirror the angles + poseTextFields[12].setValue(String.valueOf(rightArm[0])); + poseTextFields[13].setValue(String.valueOf(rightArm[1] != 0 ? -rightArm[1] : 0)); + poseTextFields[14].setValue(String.valueOf(rightArm[2] != 0 ? -rightArm[2] : 0)); + poseTextFields[15].setValue(String.valueOf(leftArm[0])); + poseTextFields[16].setValue(String.valueOf(leftArm[1] != 0 ? -leftArm[1] : 0)); + poseTextFields[17].setValue(String.valueOf(leftArm[2] != 0 ? -leftArm[2] : 0)); + + }, 0, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.mirror")))); + buttonsLeft--; + + PoseImageButton mirrorLegs = this.addRenderableWidget(new PoseImageButton(offsetX - (22 * buttonsLeft) - buttonOffset, offsetY, (button) -> { + //Mirror Legs + float[] leftLeg = new float[]{poseTextFields[6].getFloat(), poseTextFields[7].getFloat(), poseTextFields[8].getFloat()}; + float[] rightLeg = new float[]{poseTextFields[9].getFloat(), poseTextFields[10].getFloat(), poseTextFields[11].getFloat()}; + + //Swap angles and mirror the angles + poseTextFields[6].setValue(String.valueOf(rightLeg[0])); + poseTextFields[7].setValue(String.valueOf(rightLeg[1] != 0 ? -rightLeg[1] : 0)); + poseTextFields[8].setValue(String.valueOf(rightLeg[2] != 0 ? -rightLeg[2] : 0)); + poseTextFields[9].setValue(String.valueOf(leftLeg[0])); + poseTextFields[10].setValue(String.valueOf(leftLeg[1] != 0 ? -leftLeg[1] : 0)); + poseTextFields[11].setValue(String.valueOf(leftLeg[2] != 0 ? -leftLeg[2] : 0)); + }, 1, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.mirror_legs")))); + buttonsLeft--; + + PoseImageButton mirrorArms = this.addRenderableWidget(new PoseImageButton(offsetX - (22 * buttonsLeft) - buttonOffset, offsetY, (button) -> { + //Mirror Arms + float[] leftArm = new float[]{poseTextFields[12].getFloat(), poseTextFields[13].getFloat(), poseTextFields[14].getFloat()}; + float[] rightArm = new float[]{poseTextFields[15].getFloat(), poseTextFields[16].getFloat(), poseTextFields[17].getFloat()}; + + //Swap angles and mirror the angles + poseTextFields[12].setValue(String.valueOf(rightArm[0])); + poseTextFields[13].setValue(String.valueOf(rightArm[1] != 0 ? -rightArm[1] : 0)); + poseTextFields[14].setValue(String.valueOf(rightArm[2] != 0 ? -rightArm[2] : 0)); + poseTextFields[15].setValue(String.valueOf(leftArm[0])); + poseTextFields[16].setValue(String.valueOf(leftArm[1] != 0 ? -leftArm[1] : 0)); + poseTextFields[17].setValue(String.valueOf(leftArm[2] != 0 ? -leftArm[2] : 0)); + }, 2, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.mirror_arms")))); + buttonsLeft--; + + PoseImageButton swapToHead = this.addRenderableWidget(new PoseImageButton(offsetX - (22 * buttonsLeft) - buttonOffset, offsetY, (button) -> { + //Swap item in main hand with head + Services.PLATFORM.swapSlots(this.entityArmorStand, SwapData.Action.SWAP_WITH_HEAD); + + }, 3, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.swap_head")))); + buttonsLeft--; + + PoseImageButton swapHands = this.addRenderableWidget(new PoseImageButton(offsetX - (22 * buttonsLeft) - buttonOffset, offsetY, (button) -> { + //Swap item in main and offhand + Services.PLATFORM.swapSlots(this.entityArmorStand, SwapData.Action.SWAP_HANDS); + + }, 4, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.swap_hands")))); + buttonsLeft--; + + PoseImageButton blockButton = this.addRenderableWidget(new PoseImageButton(offsetX - (22 * buttonsLeft) - buttonOffset, offsetY, (button) -> { + try { + Vec3 pos = this.entityArmorStand.position(); + + //Get the amount subtracted of x to get .0725 + double xDiff = getDesiredOffset(pos.x, 1.0725D); + //Get the amount subtracted of y to get .345 + double yDiff = getDesiredOffset(pos.y, -0.655D); + //Get the amount subtracted of z to get .852 + double zDiff = getDesiredOffset(pos.z, 0.852D); + + Vec3 offset = new Vec3(xDiff, yDiff, zDiff); + int closestDegree = Mth.roundToward((int) this.rotationTextField.getFloat(), 90); + switch (closestDegree) { + case 90: { + //Rotate the desired position to have the correct values + double newX = offset.z - 0.7D; + double newZ = -offset.x + 1.18D; + offset = new Vec3(newX, offset.y, newZ); + break; + } + case -180: { + //Rotate the desired position to have the correct values + double newX = -offset.x; + double newZ = -offset.z; + offset = new Vec3(newX, offset.y, newZ); + break; + } + case -90: { + //Rotate the desired position to have the correct values + double newX = -offset.z + 0.7D; + double newZ = offset.x - 1.18D; + offset = new Vec3(newX, offset.y, newZ); + break; + } + } + + CompoundTag tag = TagParser.parseTag(Reference.alignedBlockPose); + this.readFieldsFromNBT(tag); + this.toggleButtons[0].setValue(true); //Set invisible + this.toggleButtons[2].setValue(true); //Set no gravity + this.toggleButtons[3].setValue(true); //Set show arms + this.rotationTextField.setValue(String.valueOf(closestDegree)); //Set rotation + this.poseTextFields[18].setValue(String.valueOf(offset.x)); //Set X + this.poseTextFields[19].setValue(String.valueOf(offset.y)); //Set Y + this.poseTextFields[20].setValue(String.valueOf(offset.z)); //Set Z + this.textFieldUpdated(); + } catch (CommandSyntaxException e) { + //Nope + } + + }, 5, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.block")))); + buttonsLeft--; + + PoseImageButton itemButton = this.addRenderableWidget(new PoseImageButton(offsetX - (22 * buttonsLeft) - buttonOffset, offsetY, (button) -> { + if (hasShiftDown()) { //If shift is held the item will be upright + try { + Vec3 pos = this.entityArmorStand.position(); + + //Get the amount subtracted of x to get .86 + double xDiff = getDesiredOffset(pos.x, 0.86D); + //Get the amount subtracted of y to get .59 + double yDiff = getDesiredOffset(pos.y, -1.41D); + //Get the amount subtracted of z to get .9375 + double zDiff = getDesiredOffset(pos.z, -0.0625D); + + Vec3 offset = new Vec3(xDiff, yDiff, zDiff); + int closestDegree = Mth.roundToward((int) this.rotationTextField.getFloat(), 90); + switch (closestDegree) { + case 90: { + //Rotate the desired position to have the correct values + double newX = offset.z + 1.12D; + double newZ = -offset.x + 0.74D; + offset = new Vec3(newX, offset.y, newZ); + break; + } + case -180: { + //Rotate the desired position to have the correct values + double newX = -offset.x; + double newZ = -offset.z; + offset = new Vec3(newX, offset.y, newZ); + break; + } + case -90: { + //Rotate the desired position to have the correct values + double newX = -offset.z - 1.12D; + double newZ = offset.x - 0.74D; + offset = new Vec3(newX, offset.y, newZ); + break; + } + } + + CompoundTag tag = TagParser.parseTag(Reference.alignedUprightItemPose); + this.readFieldsFromNBT(tag); + this.toggleButtons[0].setValue(true); //Set invisible + this.toggleButtons[2].setValue(true); //Set no gravity + this.toggleButtons[3].setValue(true); //Set show arms + this.rotationTextField.setValue(String.valueOf(closestDegree)); //Set rotation + this.poseTextFields[18].setValue(String.valueOf(offset.x)); //Set X + this.poseTextFields[19].setValue(String.valueOf(offset.y)); //Set Y + this.poseTextFields[20].setValue(String.valueOf(offset.z)); //Set Z + this.textFieldUpdated(); + } catch (CommandSyntaxException e) { + //Nope + } + } else { + try { + Vec3 pos = this.entityArmorStand.position(); + + //Get the amount subtracted of x to get .886 + double xDiff = getDesiredOffset(pos.x, 0.886D); + //Get the amount subtracted of y to get .22 + double yDiff = getDesiredOffset(pos.y, -0.78D); + //Get the amount subtracted of z to get .205 + double zDiff = getDesiredOffset(pos.z, 0.205D); + + Vec3 offset = new Vec3(xDiff, yDiff, zDiff); + int closestDegree = Mth.roundToward((int) this.rotationTextField.getFloat(), 90); + switch (closestDegree) { + case 90: { + //Rotate the desired position to have the correct values + double newX = offset.z + 0.59D; + double newZ = -offset.x + 0.78D; + offset = new Vec3(newX, offset.y, newZ); + break; + } + case -180: { + //Rotate the desired position to have the correct values + double newX = -offset.x; + double newZ = -offset.z; + offset = new Vec3(newX, offset.y, newZ); + break; + } + case -90: { + //Rotate the desired position to have the correct values + double newX = -offset.z - 0.59D; + double newZ = offset.x - 0.78D; + offset = new Vec3(newX, offset.y, newZ); + break; + } + } + + CompoundTag tag = TagParser.parseTag(Reference.alignedFlatItemPose); + this.readFieldsFromNBT(tag); + this.toggleButtons[0].setValue(true); //Set invisible + this.toggleButtons[2].setValue(true); //Set no gravity + this.toggleButtons[3].setValue(true); //Set show arms + this.rotationTextField.setValue(String.valueOf(closestDegree)); //Set rotation + this.poseTextFields[18].setValue(String.valueOf(offset.x)); //Set X + this.poseTextFields[19].setValue(String.valueOf(offset.y)); //Set Y + this.poseTextFields[20].setValue(String.valueOf(offset.z)); //Set Z + this.textFieldUpdated(); + } catch (CommandSyntaxException e) { + //Nope + } + } + + }, 6, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.item").append("\n").append(new TranslatableComponent("armorposer.gui.tooltip.item2").withStyle(ChatFormatting.GRAY))))); + buttonsLeft--; + PoseImageButton toolButton = this.addRenderableWidget(new PoseImageButton(offsetX - (22 * buttonsLeft) - buttonOffset, offsetY, (button) -> { + try { + Vec3 pos = this.entityArmorStand.position(); + + //Get the amount subtracted of x to get .33 + double xDiff = getDesiredOffset(pos.x, 0.33D); + //Get the amount subtracted of y to get .22 + double yDiff = getDesiredOffset(pos.y, -1.285D); + //Get the amount subtracted of z to get .059999D + double zDiff = getDesiredOffset(pos.z, 0.059999D); + + Vec3 offset = new Vec3(xDiff, yDiff, zDiff); + int closestDegree = Mth.roundToward((int) this.rotationTextField.getFloat(), 90); + switch (closestDegree) { + case 90: { + //Rotate the desired position to have the correct values + double newX = offset.z + 0.88D; + double newZ = -offset.x - 0.34D; + offset = new Vec3(newX, offset.y, newZ); + break; + } + case -180: { + //Rotate the desired position to have the correct values + double newX = -offset.x; + double newZ = -offset.z; + offset = new Vec3(newX, offset.y, newZ); + break; + } + case -90: { + //Rotate the desired position to have the correct values + double newX = -offset.z - 0.88D; + double newZ = offset.x + 0.34D; + offset = new Vec3(newX, offset.y, newZ); + break; + } + } + + CompoundTag tag = TagParser.parseTag(Reference.alignedToolPose); + this.readFieldsFromNBT(tag); + this.toggleButtons[0].setValue(true); //Set invisible + this.toggleButtons[2].setValue(true); //Set no gravity + this.toggleButtons[3].setValue(true); //Set show arms + this.rotationTextField.setValue(String.valueOf(closestDegree)); //Set rotation + this.poseTextFields[18].setValue(String.valueOf(offset.x)); //Set X + this.poseTextFields[19].setValue(String.valueOf(offset.y)); //Set Y + this.poseTextFields[20].setValue(String.valueOf(offset.z)); //Set Z + this.textFieldUpdated(); + } catch (CommandSyntaxException e) { + //Nope + } + + }, 7, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.tool")))); + buttonsLeft--; + + this.addRenderableWidget(this.lockButton = new TooltipLockIconButton(offsetX - (22 * buttonsLeft) - buttonOffset, offsetY, (button) -> { + this.lockButton.setLocked(!this.lockButton.isLocked()); + }, createTooltip(new TranslatableComponent("armorposer.gui.tooltip.lock")))); + this.lockButton.setLocked(this.armorStandData.getBooleanValue(6)); + + // done & cancel buttons + this.addRenderableWidget(new Button(offsetX - ((2 * 96) + 2), offsetY + 22, 97, 20, new TranslatableComponent("gui.done"), (button) -> { + this.textFieldUpdated(); + this.minecraft.setScreen((Screen) null); + })); + this.addRenderableWidget(new Button(offsetX - 95, offsetY + 22, 97, 20, new TranslatableComponent("gui.cancel"), (button) -> { + this.poseTextFields[18].setValue("0"); + this.poseTextFields[19].setValue("0"); + this.poseTextFields[20].setValue("0"); + this.textFieldUpdated(); + this.updateEntity(this.armorStandData.writeToNBT()); + this.minecraft.setScreen((Screen) null); + })); + this.addRenderableWidget(new Button(0, 0, 16, 16, new TextComponent("💡"), (button) -> { + this.minecraft.setScreen(new ArmorGlowScreen(this)); + })); + } + + /** + * Get the desired offset to get the armor stand in the correct position + * + * @param posValue The current position value + * @param desiredValue The desired position value + * @return The amount subtracted from or added to the current position to get the desired position + */ + private double getDesiredOffset(double posValue, double desiredValue) { + double value = posValue - (int) posValue; //Get the decimal value + if (value < 0) { //Make it positive if it's a negative position + value = -value; + } + return desiredValue - value; + } + + @Override + public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { + super.render(poseStack, mouseX, mouseY, partialTicks); + + // Draw gui title + drawCenteredString(poseStack, this.font, new TranslatableComponent("armorposer.gui.title"), this.width / 2, 10, 0xFFFFFF); + + // Draw textboxes + this.rotationTextField.render(poseStack, mouseX, mouseY, partialTicks); + for (EditBox textField : this.poseTextFields) + textField.render(poseStack, mouseX, mouseY, partialTicks); + + int offsetY = 20; + + // left column labels + int offsetX = 20; + for (int i = 0; i < this.buttonLabels.length; i++) { + int x = offsetX; + int y = offsetY + (i * 22) + (10 - (this.font.lineHeight / 2)); + drawString(poseStack, this.font, I18n.get("armorposer.gui.label." + this.buttonLabels[i]), x, y, whiteColor); + } + + // right column labels + offsetX = this.width - 20 - 100; + // x, y, z + drawString(poseStack, this.font, "X", offsetX + 10, 7, whiteColor); + drawString(poseStack, this.font, "Y", offsetX + 45, 7, whiteColor); + drawString(poseStack, this.font, "Z", offsetX + 80, 7, whiteColor); + // 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)); + drawString(poseStack, this.font, translatedLabel, x, y, whiteColor); + } + + if (Services.PLATFORM.allowScrolling()) { + poseStack.pushPose(); + poseStack.mulPose(Vector3f.ZP.rotationDegrees(90.0F)); + drawString(poseStack, this.font, new TranslatableComponent("armorposer.gui.label.scroll"), 21, -width + 10, 11184810); + poseStack.popPose(); + } + } + + @Override + public void tick() { + super.tick(); + this.rotationTextField.tick(); + for (EditBox textField : this.poseTextFields) + if (textField != null) textField.tick(); + + //Disable the Y position field when gravity is enabled (So you can't get it stuck in the ground) + boolean disabledGravity = this.toggleButtons[2].getValue(); + NumberFieldBox yPositionField = this.poseTextFields[19]; + + yPositionField.setEditable(disabledGravity); + if (disabledGravity) { + yPositionField.setTooltip(yPositionTooltip); + } else { + yPositionField.setFocused(false); + //Adjust tooltip to show it's disabled + yPositionField.setTooltip(yPositionTooltipDisabled); + } + } + + @Override + public boolean charTyped(char codePoint, int modifiers) { + boolean typed = super.charTyped(codePoint, modifiers); + if (typed) { + this.textFieldUpdated(); + } + return typed; + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double delta) { + var multiplier = Screen.hasShiftDown() ? 10.0f : 1.0f; + if (allowScrolling && delta > 0) { + //Add 1 to the value + if (rotationTextField.canConsumeInput()) { + float nextValue = (rotationTextField.getFloat() + multiplier * rotationTextField.scrollMultiplier) % rotationTextField.modValue; + rotationTextField.setValue(String.valueOf(nextValue)); + rotationTextField.setCursorPosition(0); + rotationTextField.setHighlightPos(0); + this.textFieldUpdated(); + return true; + } + for (NumberFieldBox textField : this.poseTextFields) { + if (textField.canConsumeInput()) { + float nextValue = (textField.getFloat() + multiplier * textField.scrollMultiplier) % textField.modValue; + textField.setValue(String.valueOf(nextValue)); + textField.setCursorPosition(0); + textField.setHighlightPos(0); + this.textFieldUpdated(); + return true; + } + } + } else if (allowScrolling && delta < 0) { + //Remove 1 to the value + if (rotationTextField.canConsumeInput()) { + float previousValue = (rotationTextField.getFloat() - multiplier * rotationTextField.scrollMultiplier) % rotationTextField.modValue; + rotationTextField.setValue(String.valueOf(previousValue)); + rotationTextField.setCursorPosition(0); + rotationTextField.setHighlightPos(0); + this.textFieldUpdated(); + return true; + } + for (NumberFieldBox textField : this.poseTextFields) { + if (textField.canConsumeInput()) { + float previousValue = (textField.getFloat() - multiplier * textField.scrollMultiplier) % textField.modValue; + textField.setValue(String.valueOf(previousValue)); + textField.setCursorPosition(0); + textField.setHighlightPos(0); + this.textFieldUpdated(); + return true; + } + } + } + return super.mouseScrolled(mouseX, mouseY, delta); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == 15) { //Tab + for (int i = 0; i < this.poseTextFields.length; i++) { + if (this.poseTextFields[i].isFocused()) { + this.textFieldUpdated(); + this.poseTextFields[i].moveCursorToEnd(); + this.poseTextFields[i].setFocused(false); + + int j = (!Screen.hasShiftDown() ? (i == this.poseTextFields.length - 1 ? 0 : i + 1) : (i == 0 ? this.poseTextFields.length - 1 : i - 1)); + this.poseTextFields[j].setFocused(true); + this.poseTextFields[j].moveCursorTo(0); + this.poseTextFields[j].setHighlightPos(this.poseTextFields[j].getValue().length()); + } + } + } else { + if (this.rotationTextField.keyPressed(keyCode, scanCode, modifiers)) { + this.textFieldUpdated(); + return true; + } else { + for (NumberFieldBox textField : this.poseTextFields) { + if (textField.keyPressed(keyCode, scanCode, modifiers)) { + this.textFieldUpdated(); + return true; + } + } + } + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + return super.mouseClicked(mouseX, mouseY, button); + } + + protected void textFieldUpdated() { + this.updateEntity(this.writeFieldsToNBT()); + } + + protected CompoundTag writeFieldsToNBT() { + CompoundTag compound = new CompoundTag(); + compound.putBoolean("Invisible", this.toggleButtons[0].getValue()); + compound.putBoolean("NoBasePlate", this.toggleButtons[1].getValue()); + compound.putBoolean("NoGravity", this.toggleButtons[2].getValue()); + compound.putBoolean("ShowArms", this.toggleButtons[3].getValue()); + compound.putBoolean("Small", this.toggleButtons[4].getValue()); + compound.putBoolean("CustomNameVisible", this.toggleButtons[5].getValue()); + compound.putBoolean("Invulnerable", this.lockButton.isLocked()); + compound.putInt("DisabledSlots", this.lockButton.isLocked() ? 4144959 : 0); + + ListTag rotationTag = new ListTag(); + rotationTag.add(FloatTag.valueOf(this.rotationTextField.getFloat())); + compound.put("Rotation", rotationTag); + + CompoundTag poseTag = new CompoundTag(); + + ListTag poseHeadTag = new ListTag(); + poseHeadTag.add(FloatTag.valueOf(this.poseTextFields[0].getFloat())); + poseHeadTag.add(FloatTag.valueOf(this.poseTextFields[1].getFloat())); + poseHeadTag.add(FloatTag.valueOf(this.poseTextFields[2].getFloat())); + poseTag.put("Head", poseHeadTag); + + ListTag poseBodyTag = new ListTag(); + poseBodyTag.add(FloatTag.valueOf(this.poseTextFields[3].getFloat())); + poseBodyTag.add(FloatTag.valueOf(this.poseTextFields[4].getFloat())); + poseBodyTag.add(FloatTag.valueOf(this.poseTextFields[5].getFloat())); + poseTag.put("Body", poseBodyTag); + + ListTag poseLeftLegTag = new ListTag(); + poseLeftLegTag.add(FloatTag.valueOf(this.poseTextFields[6].getFloat())); + poseLeftLegTag.add(FloatTag.valueOf(this.poseTextFields[7].getFloat())); + poseLeftLegTag.add(FloatTag.valueOf(this.poseTextFields[8].getFloat())); + poseTag.put("LeftLeg", poseLeftLegTag); + + ListTag poseRightLegTag = new ListTag(); + poseRightLegTag.add(FloatTag.valueOf(this.poseTextFields[9].getFloat())); + poseRightLegTag.add(FloatTag.valueOf(this.poseTextFields[10].getFloat())); + poseRightLegTag.add(FloatTag.valueOf(this.poseTextFields[11].getFloat())); + poseTag.put("RightLeg", poseRightLegTag); + + ListTag poseLeftArmTag = new ListTag(); + poseLeftArmTag.add(FloatTag.valueOf(this.poseTextFields[12].getFloat())); + poseLeftArmTag.add(FloatTag.valueOf(this.poseTextFields[13].getFloat())); + poseLeftArmTag.add(FloatTag.valueOf(this.poseTextFields[14].getFloat())); + poseTag.put("LeftArm", poseLeftArmTag); + + ListTag poseRightArmTag = new ListTag(); + poseRightArmTag.add(FloatTag.valueOf(this.poseTextFields[15].getFloat())); + poseRightArmTag.add(FloatTag.valueOf(this.poseTextFields[16].getFloat())); + poseRightArmTag.add(FloatTag.valueOf(this.poseTextFields[17].getFloat())); + poseTag.put("RightArm", poseRightArmTag); + + + var offsetX = this.poseTextFields[18].getFloat(); + var offsetY = this.poseTextFields[19].getFloat(); + var offsetZ = this.poseTextFields[20].getFloat(); + var offsetXDiff = offsetX - this.lastSendOffset.x; + var offsetYDiff = offsetY - this.lastSendOffset.y; + var offsetZDiff = offsetZ - this.lastSendOffset.z; + ListTag positionOffset = new ListTag(); + positionOffset.add(DoubleTag.valueOf(offsetXDiff)); + positionOffset.add(DoubleTag.valueOf(offsetYDiff)); + positionOffset.add(DoubleTag.valueOf(offsetZDiff)); + compound.put("Move", positionOffset); + this.lastSendOffset = new Vec3(offsetX, offsetY, offsetZ); + + compound.put("Pose", poseTag); + return compound; + } + + protected void readFieldsFromNBT(CompoundTag compound) { + CompoundTag armorStandTag = this.armorStandData.writeToNBT(); + armorStandTag.merge(compound); + this.armorStandData.readFromNBT(armorStandTag); + + this.rotationTextField.setValue(String.valueOf((int) armorStandData.rotation)); + for (int i = 0; i < this.poseTextFields.length; i++) { + this.poseTextFields[i].setValue(String.valueOf((int) armorStandData.pose[i])); + } + } + + + public static void openScreen(ArmorStand armorStandEntity) { + Minecraft.getInstance().setScreen(new ArmorStandScreen(armorStandEntity)); + } + + public void updateEntity(CompoundTag compound) { + Services.PLATFORM.updateEntity(this.entityArmorStand, compound); + } + + public Button.OnTooltip createTooltip(Component component) { + return new Button.OnTooltip() { + @Override + public void onTooltip(Button button, PoseStack poseStack, int mouseX, int mouseY) { + if (!button.active) { + ArmorStandScreen.this.renderTooltip(poseStack, component, mouseX, mouseY); + } + } + + @Override + public void narrateTooltip(Consumer componentConsumer) { + componentConsumer.accept(component); + } + }; + } + + public NumberFieldBox.OnTooltip createFieldTooltip(Component component) { + return new NumberFieldBox.OnTooltip() { + @Override + public void onTooltip(NumberFieldBox button, PoseStack poseStack, int mouseX, int mouseY) { + if (!button.active) { + ArmorStandScreen.this.renderTooltip(poseStack, component, mouseX, mouseY); + } + } + + @Override + public void narrateTooltip(Consumer componentConsumer) { + componentConsumer.accept(component); + } + }; + } +} \ No newline at end of file diff --git a/common/src/main/java/com/mrbysco/armorposer/client/gui/DeletePoseScreen.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/DeletePoseScreen.java new file mode 100644 index 0000000..eb6a3f2 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/DeletePoseScreen.java @@ -0,0 +1,46 @@ +package com.mrbysco.armorposer.client.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mrbysco.armorposer.Reference; +import com.mrbysco.armorposer.client.gui.widgets.PoseListWidget; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +public class DeletePoseScreen extends Screen { + private final ArmorStandScreen parentScreen; + private Button deleteButton; + private final PoseListWidget.ListEntry entry; + private final Component warning = new TranslatableComponent("armorposer.gui.delete_poose.message"); + + public DeletePoseScreen(ArmorStandScreen armorStandScreen, PoseListWidget.ListEntry entry) { + super(new TranslatableComponent("armorposer.gui.delete_poose.title")); + this.parentScreen = armorStandScreen; + this.entry = entry; + } + + @Override + protected void init() { + this.addRenderableWidget(this.deleteButton = new Button(this.width / 2 - 66, this.height / 2 + 3, 60, 20, CommonComponents.GUI_YES, (button) -> { + Reference.removePose(entry.rawName()); + this.minecraft.setScreen(parentScreen); + })); + + this.addRenderableWidget(new Button(this.width / 2 - 4, this.height / 2 + 3, 60, 20, CommonComponents.GUI_NO, (button) -> { + this.minecraft.setScreen(parentScreen); + })); + } + + @Override + public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { + this.renderBackground(poseStack); + super.render(poseStack, mouseX, mouseY, partialTicks); + + this.entry.renderPose(poseStack, this.width / 2 - 5, this.height / 2 - 10, 30); + + drawCenteredString(poseStack, this.font, this.title, this.width / 2, 20, 16777215); + drawCenteredString(poseStack, this.font, this.warning, this.width / 2, 40, 11141120); + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/client/gui/SavePoseScreen.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/SavePoseScreen.java new file mode 100644 index 0000000..8499952 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/SavePoseScreen.java @@ -0,0 +1,58 @@ +package com.mrbysco.armorposer.client.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mrbysco.armorposer.Reference; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; + +public class SavePoseScreen extends Screen { + private final ArmorStandScreen parentScreen; + private Button saveButton; + private EditBox nameField; + + public SavePoseScreen(ArmorStandScreen armorStandScreen) { + super(new TranslatableComponent("armorposer.gui.save_pose.title")); + this.parentScreen = armorStandScreen; + } + + @Override + protected void init() { + this.addRenderableWidget(this.saveButton = new Button(this.width / 2 - 66, this.height / 2 + 3, 60, 20, new TranslatableComponent("armorposer.gui.label.save"), (button) -> { + CompoundTag compound = this.parentScreen.writeFieldsToNBT(); + Reference.savePose(this.nameField.getValue(), compound); + this.minecraft.setScreen(parentScreen); + })); + + this.addRenderableWidget(new Button(this.width / 2 - 4, this.height / 2 + 3, 60, 20, CommonComponents.GUI_CANCEL, (button) -> { + this.minecraft.setScreen(parentScreen); + })); + + this.nameField = new EditBox(this.minecraft.font, this.width / 2 - 90, this.height / 2 - 24, 180, 20, new TextComponent("Name")); + this.nameField.setMaxLength(31); + this.nameField.setTextColor(-1); + this.addWidget(this.nameField); + setInitialFocus(nameField); + } + + @Override + public void tick() { + super.tick(); + + this.saveButton.active = !this.nameField.getValue().isEmpty(); + } + + @Override + public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { + this.renderBackground(poseStack); + super.render(poseStack, mouseX, mouseY, partialTicks); + + drawCenteredString(poseStack, this.font, this.title, this.width / 2, 20, 16777215); + + this.nameField.render(poseStack, mouseX, mouseY, partialTicks); + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/ArmorGlowWidget.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/ArmorGlowWidget.java new file mode 100644 index 0000000..45a2c6c --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/ArmorGlowWidget.java @@ -0,0 +1,166 @@ +package com.mrbysco.armorposer.client.gui.widgets; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mrbysco.armorposer.client.gui.ArmorGlowScreen; +import net.minecraft.Util; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.ObjectSelectionList; +import net.minecraft.client.gui.screens.inventory.InventoryScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.decoration.ArmorStand; + +import java.util.Objects; + +public class ArmorGlowWidget extends ObjectSelectionList { + private final ArmorGlowScreen parent; + private final int listWidth; + private final Component title; + + public ArmorGlowWidget(ArmorGlowScreen parent, Component title, int listWidth, int top, int bottom) { + super(parent.getScreenMinecraft(), listWidth, parent.height, top, bottom, parent.getScreenFont().lineHeight * 2 + 8); + this.parent = parent; + this.title = title; + this.listWidth = listWidth; + this.refreshList(); + this.setRenderBackground(false); + } + + private int getX() { + return this.x0; + } + + public void setY(int y) { + this.y0 = y; + this.y1 = y + this.height; + } + + public void setHeight(int height) { + this.height = height; + } + + @Override + protected int getScrollbarPosition() { + return this.getX() + this.listWidth - 6; + } + + @Override + public int getRowWidth() { + return this.listWidth; + } + + public void refreshList() { + this.clearEntries(); + parent.buildPositionList(this::addEntry, location -> new ListEntry(location, this.parent)); + } + + @Override + protected void renderList(PoseStack poseStack, int x, int y, int mouseX, int mouseY, float delta) { + int i = this.getRowLeft(); + int j = this.getRowWidth(); + int k = this.itemHeight - 4; + int l = this.getItemCount(); + + for(int i1 = 0; i1 < l; ++i1) { + int j1 = this.getRowTop(i1); + int k1 = this.getRowTop(i1) + this.itemHeight; + if (k1 >= this.y0 && j1 <= this.y1) { + renderItem(poseStack, mouseX, mouseY, delta, i1, i, j1, j, k); + } + } + } + + protected void renderItem(PoseStack poseStack, int mouseX, int mouseY, float partialTick, int index, + int left, int top, int width, int height) { + ArmorGlowWidget.ListEntry e = this.getEntry(index); + if (this.isSelectedItem(index)) { + int i = this.isFocused() ? -1 : -8355712; + this.renderSelection(poseStack, top, width, height, i, -16777216); + } + + e.render(poseStack, index, top, left, width, height, mouseX, mouseY, Objects.equals(this.getHovered(), e), partialTick); + } + + protected void renderSelection(PoseStack poseStack, 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; + fillGradient(poseStack, xPos, top - 2, xPos2, top + height + 2, -1945083888, -1676648432); + } + + @Override + protected void renderBackground(PoseStack poseStack) { + fillGradient(poseStack, getX(), 0, getX() + this.listWidth, parent.height, -1945104368, -1676668912); + } + + @Override + protected void renderDecorations(PoseStack poseStack, int mouseX, int mouseY) { + super.renderDecorations(poseStack, mouseX, mouseY); + drawCenteredString(poseStack, this.parent.getScreenFont(), title, getX() + this.listWidth / 2, 2, 16777215); + } + + public class ListEntry extends Entry { + private final ArmorGlowScreen parent; + private final ArmorStand armorStand; + + ListEntry(ArmorStand armorStand, ArmorGlowScreen parent) { + this.armorStand = armorStand; + this.parent = parent; + } + + @Override + public void render(PoseStack 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, getPositionComponent(), left + 36, top + 10, left + width - 18, top + 20, 0xFFFFFF); + + renderPose(guiGraphics, left + 16, top + 28, 15); + } + + public ArmorStand getArmorStand() { + return armorStand; + } + + public void renderPose(PoseStack poseStack, int xPos, int yPos, int size) { + if (armorStand != null) { + InventoryScreen.renderEntityInInventory(xPos, yPos, size, + 0F, 0F, this.armorStand); + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + parent.setSelected(this); + ArmorGlowWidget.this.setSelected(this); + return false; + } + + public Component getPositionComponent() { + return new TextComponent(getArmorStand().blockPosition().toShortString()); + } + + @Override + public Component getNarration() { + return getPositionComponent(); + } + + protected void renderScrollingString(PoseStack poseStack, Font font, Component text, int minX, int minY, int maxX, int maxY, int color) { + int $$8 = font.width(text); + int $$9 = (minY + maxY - 9) / 2 + 1; + int $$10 = maxX - minX; + if ($$8 > $$10) { + int $$11 = $$8 - $$10; + double $$12 = (double) Util.getMillis() / 1000.0D; + double $$13 = Math.max((double) $$11 * 0.5D, 3.0D); + double $$14 = Math.sin((Math.PI / 2D) * Math.cos((Math.PI * 2D) * $$12 / $$13)) / 2.0D + 0.5D; + double $$15 = Mth.lerp($$14, 0.0D, (double) $$11); + RenderSystem.enableScissor(minX, minY, maxX, maxY); + drawString(poseStack, font, text, minX - (int) $$15, $$9, color); + RenderSystem.disableScissor(); + } else { + drawCenteredString(poseStack, font, text, (minX + maxX) / 2, $$9, color); + } + } + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/NumberFieldBox.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/NumberFieldBox.java new file mode 100644 index 0000000..a69276d --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/NumberFieldBox.java @@ -0,0 +1,97 @@ +package com.mrbysco.armorposer.client.gui.widgets; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.narration.NarratedElementType; +import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.network.chat.Component; +import org.apache.commons.lang3.math.NumberUtils; + +import java.util.function.Consumer; + +public class NumberFieldBox extends EditBox { + + public float scrollMultiplier = 1; + + public float modValue = 360; + public int decimalPoints = 0; + public boolean allowDecimal = false; + public OnTooltip onTooltip; + + public NumberFieldBox(Font font, int x, int y, int width, int height, Component defaultValue, OnTooltip tooltip) { + super(font, x, y, width, height, defaultValue); + this.onTooltip = tooltip; + } + + public void setTooltip(OnTooltip tooltip) { + this.onTooltip = tooltip; + } + + @Override + public void renderToolTip(PoseStack poseStack, int mouseX, int mouseY) { + onTooltip.onTooltip(this, poseStack, mouseX, mouseY); + } + + @Override + public void updateNarration(NarrationElementOutput elementOutput) { + this.defaultButtonNarrationText(elementOutput); + onTooltip.narrateTooltip(add -> elementOutput.add(NarratedElementType.HINT, add)); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public void insertText(String textToWrite) { + if (this.isNumeric(textToWrite)) super.insertText(textToWrite); + + float currentValue = getFloat(); + if (currentValue > 360 || currentValue < -360) { + this.setValue("0"); + } + } + + @Override + public String getValue() { + return (this.isNumeric(super.getValue()) ? super.getValue() : "0"); + } + + @Override + public void setValue(String value) { + if (value.isEmpty()) { + super.setValue("0"); + } else { + super.setValue(String.format(("%." + decimalPoints + "f"), Float.parseFloat(value))); + } + } + + public float getFloat() { + return NumberUtils.toFloat(super.getValue(), 0.0F); + } + + @Override + public void setFocused(boolean focused) { + super.setFocused(focused); + if (!focused) { + this.setHighlightPos(this.getValue().length()); + this.moveCursorToEnd(); + } + } + + protected boolean isNumeric(String value) { + if (allowDecimal && value.equals(".")) + return true; + else + return value.equals("-") || NumberUtils.isParsable(value); + } + + public interface OnTooltip { + void onTooltip(NumberFieldBox numberFieldBox, PoseStack poseStack, int mouseX, int mouseY); + + default void narrateTooltip(Consumer consumer) { + } + } +} \ No newline at end of file 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/PoseImageButton.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/PoseImageButton.java new file mode 100644 index 0000000..7b7dde2 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/PoseImageButton.java @@ -0,0 +1,34 @@ +package com.mrbysco.armorposer.client.gui.widgets; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mrbysco.armorposer.Reference; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; + +public class PoseImageButton extends Button { + private static final ResourceLocation BUTTON_LOCATION = new ResourceLocation(Reference.MOD_ID, "textures/gui/poser_buttons.png"); + private final int yOffset; + + public PoseImageButton(int x, int y, Button.OnPress onPress, int buttonID, Button.OnTooltip onTooltip) { + super(x, y, 20, 20, TextComponent.EMPTY, onPress, onTooltip); + this.yOffset = buttonID == 0 ? 0 : buttonID * 20; + } + + + @Override + public void renderButton(PoseStack poseStack, int mouseX, int mouseY, float partialTick) { + super.renderButton(poseStack, mouseX, mouseY, partialTick); + int i = 0; + if (this.isHovered) { + i += 20; + } + + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, BUTTON_LOCATION); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + blit(poseStack, this.x, this.y, i, yOffset, 20, 20, 128, 256); + } +} \ No newline at end of file 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..ef320da --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/PoseListWidget.java @@ -0,0 +1,219 @@ +package com.mrbysco.armorposer.client.gui.widgets; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mrbysco.armorposer.Reference; +import com.mrbysco.armorposer.client.gui.ArmorPosesScreen; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +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.network.chat.TextComponent; +import net.minecraft.util.Mth; +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 java.util.Objects; +import java.util.function.Function; + +public class PoseListWidget extends ObjectSelectionList { + 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, parent.height, top, bottom, parent.getScreenFont().lineHeight * 2 + 16); + this.parent = parent; + this.title = title; + this.listWidth = listWidth; + this.refreshList(user); + this.setRenderBackground(false); + } + + private int getX() { + return this.x0; + } + + public void setY(int y) { + this.y0 = y; + this.y1 = y + this.height; + } + + public void setHeight(int height) { + this.height = height; + } + + @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 renderList(PoseStack poseStack, int x, int y, int mouseX, int mouseY, float delta) { + int i = this.getRowLeft(); + int j = this.getRowWidth(); + int k = this.itemHeight - 4; + int l = this.getItemCount(); + + for (int i1 = 0; i1 < l; ++i1) { + int j1 = this.getRowTop(i1); + int k1 = this.getRowTop(i1) + this.itemHeight; + if (k1 >= this.y0 && j1 <= this.y1) { + renderItem(poseStack, mouseX, mouseY, delta, i1, i, j1, j, k); + } + } + } + + protected void renderItem(PoseStack poseStack, int mouseX, int mouseY, float partialTick, int index, + int left, int top, int width, int height) { + PoseListWidget.ListEntry e = this.getEntry(index); + if (this.isSelectedItem(index)) { + int i = this.isFocused() ? -1 : -8355712; + this.renderSelection(poseStack, top, width, height, i, -16777216); + } + + e.render(poseStack, index, top, left, width, height, mouseX, mouseY, Objects.equals(this.getHovered(), e), partialTick); + } + + protected void renderSelection(PoseStack poseStack, 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; + fillGradient(poseStack, xPos, top - 2, xPos2, top + height + 2, -1945083888, -1676648432); + } + + @Override + protected void renderBackground(PoseStack poseStack) { + fillGradient(poseStack, getX(), 0, getX() + this.listWidth, parent.height, -1945104368, -1676668912); + } + + @Override + protected void renderDecorations(PoseStack poseStack, int mouseX, int mouseY) { + super.renderDecorations(poseStack, mouseX, mouseY); + drawCenteredString(poseStack, 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"); + nbt.putBoolean("NoBasePlate", true); + nbt.putBoolean("ShowArms", true); + if (!tag.isEmpty()) { + nbt.merge(tag); + } + ArmorStand armorStand = (ArmorStand) EntityType.loadEntityRecursive(nbt, level, Function.identity()); + if (armorStand != null) { + 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(PoseStack poseStack, int entryIdx, int top, int left, int entryWidth, int entryHeight, + int mouseX, int mouseY, boolean hovered, float partialTicks) { + Font font = this.parent.getScreenFont(); + renderScrollingString(poseStack, font, new TextComponent(getName()), left + 36, top + 10, left + width - 18, top + 20, 0xFFFFFF); + + renderPose(poseStack, left + 16, top + 28, 15); + } + + public void renderPose(PoseStack poseStack, int xPos, int yPos, int size) { + if (cachedEntity != null) { + InventoryScreen.renderEntityInInventory(xPos, yPos, size, + 0F, 0F, this.cachedEntity); + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + parent.setSelected(this); + if (PoseListWidget.this.getSelected() == this) + PoseListWidget.this.setSelected(null); + else + 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 new TextComponent(getName()); + } + + protected void renderScrollingString(PoseStack poseStack, Font font, Component text, int minX, int minY, int maxX, int maxY, int color) { + int $$8 = font.width(text); + int $$9 = (minY + maxY - 9) / 2 + 1; + int $$10 = maxX - minX; + if ($$8 > $$10) { + int $$11 = $$8 - $$10; + double $$12 = (double) Util.getMillis() / 1000.0D; + double $$13 = Math.max((double) $$11 * 0.5D, 3.0D); + double $$14 = Math.sin((Math.PI / 2D) * Math.cos((Math.PI * 2D) * $$12 / $$13)) / 2.0D + 0.5D; + double $$15 = Mth.lerp($$14, 0.0D, (double) $$11); + RenderSystem.enableScissor(minX, minY, maxX, maxY); + drawString(poseStack, font, text, minX - (int) $$15, $$9, color); + RenderSystem.disableScissor(); + } else { + drawCenteredString(poseStack, font, text, (minX + maxX) / 2, $$9, color); + } + } + } +} diff --git a/Common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/ToggleButton.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/ToggleButton.java similarity index 89% rename from Common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/ToggleButton.java rename to common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/ToggleButton.java index 5be9eab..2ba7a62 100644 --- a/Common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/ToggleButton.java +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/ToggleButton.java @@ -7,8 +7,8 @@ public class ToggleButton extends Button { private boolean value; - public ToggleButton(int x, int y, int width, int height, boolean defaultValue, OnPress pressedAction) { - super(x, y, width, height, defaultValue ? new TranslatableComponent("gui.yes") : new TranslatableComponent("gui.no"), pressedAction); + public ToggleButton(int x, int y, int width, int height, boolean defaultValue, OnPress pressedAction, OnTooltip tooltip) { + super(x, y, width, height, defaultValue ? new TranslatableComponent("gui.yes") : new TranslatableComponent("gui.no"), pressedAction, tooltip); this.value = defaultValue; } diff --git a/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/TooltipLockIconButton.java b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/TooltipLockIconButton.java new file mode 100644 index 0000000..7986e74 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/client/gui/widgets/TooltipLockIconButton.java @@ -0,0 +1,80 @@ +package com.mrbysco.armorposer.client.gui.widgets; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TranslatableComponent; + +/** + * A copy of LocKIconButton but allowing the tooltip to be provided + */ +public class TooltipLockIconButton extends Button { + private boolean locked; + + public TooltipLockIconButton(int $$0, int $$1, Button.OnPress $$2, Button.OnTooltip $$3) { + super($$0, $$1, 20, 20, new TranslatableComponent("narrator.button.difficulty_lock"), $$2, $$3); + } + + @Override + protected MutableComponent createNarrationMessage() { + return CommonComponents.joinForNarration( + super.createNarrationMessage(), + this.isLocked() + ? new TranslatableComponent("narrator.button.difficulty_lock.locked") + : new TranslatableComponent("narrator.button.difficulty_lock.unlocked") + ); + } + + public boolean isLocked() { + return this.locked; + } + + public void setLocked(boolean $$0) { + this.locked = $$0; + } + + @Override + public void renderButton(PoseStack $$0, int $$1, int $$2, float $$3) { + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, Button.WIDGETS_LOCATION); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + TooltipLockIconButton.Icon $$4; + if (!this.active) { + $$4 = this.locked ? TooltipLockIconButton.Icon.LOCKED_DISABLED : TooltipLockIconButton.Icon.UNLOCKED_DISABLED; + } else if (this.isHoveredOrFocused()) { + $$4 = this.locked ? TooltipLockIconButton.Icon.LOCKED_HOVER : TooltipLockIconButton.Icon.UNLOCKED_HOVER; + } else { + $$4 = this.locked ? TooltipLockIconButton.Icon.LOCKED : TooltipLockIconButton.Icon.UNLOCKED; + } + + this.blit($$0, this.x, this.y, $$4.getX(), $$4.getY(), this.width, this.height); + } + + static enum Icon { + LOCKED(0, 146), + LOCKED_HOVER(0, 166), + LOCKED_DISABLED(0, 186), + UNLOCKED(20, 146), + UNLOCKED_HOVER(20, 166), + UNLOCKED_DISABLED(20, 186); + + private final int x; + private final int y; + + private Icon(int $$0, int $$1) { + this.x = $$0; + this.y = $$1; + } + + public int getX() { + return this.x; + } + + public int getY() { + return this.y; + } + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/data/SwapData.java b/common/src/main/java/com/mrbysco/armorposer/data/SwapData.java new file mode 100644 index 0000000..10bdea4 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/data/SwapData.java @@ -0,0 +1,42 @@ +package com.mrbysco.armorposer.data; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.item.ItemStack; + +import java.util.UUID; + +public record SwapData(UUID entityUUID, Action action) { + public void encode(FriendlyByteBuf buf) { + buf.writeUUID(entityUUID); + buf.writeEnum(action); + } + + public static SwapData decode(final FriendlyByteBuf packetBuffer) { + return new SwapData(packetBuffer.readUUID(), packetBuffer.readEnum(Action.class)); + } + + public void handleData(ArmorStand armorStand) { + switch (action) { + case SWAP_HANDS: + ItemStack offStack = armorStand.getItemInHand(InteractionHand.OFF_HAND); + armorStand.setItemInHand(InteractionHand.OFF_HAND, armorStand.getItemInHand(InteractionHand.MAIN_HAND)); + armorStand.setItemInHand(InteractionHand.MAIN_HAND, offStack); + return; + case SWAP_WITH_HEAD: + ItemStack headStack = armorStand.getItemBySlot(EquipmentSlot.HEAD); + armorStand.setItemSlot(EquipmentSlot.HEAD, armorStand.getItemBySlot(EquipmentSlot.MAINHAND)); + armorStand.setItemSlot(EquipmentSlot.MAINHAND, headStack); + return; + default: + throw new IllegalArgumentException("Invalid Pose action"); + } + } + + public static enum Action { + SWAP_WITH_HEAD, + SWAP_HANDS + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/data/SyncData.java b/common/src/main/java/com/mrbysco/armorposer/data/SyncData.java new file mode 100644 index 0000000..7fafdd1 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/data/SyncData.java @@ -0,0 +1,40 @@ +package com.mrbysco.armorposer.data; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.decoration.ArmorStand; + +import java.util.UUID; + +public record SyncData(UUID entityUUID, CompoundTag tag) { + public void encode(FriendlyByteBuf buf) { + buf.writeUUID(entityUUID); + buf.writeNbt(tag); + } + + public static SyncData decode(final FriendlyByteBuf packetBuffer) { + return new SyncData(packetBuffer.readUUID(), packetBuffer.readNbt()); + } + + public void handleData(ArmorStand armorStand) { + CompoundTag entityTag = armorStand.saveWithoutId(new CompoundTag()); + CompoundTag entityTagCopy = entityTag.copy(); + + if (!tag.isEmpty()) { + entityTagCopy.merge(tag); + armorStand.load(entityTagCopy); + armorStand.setUUID(entityUUID); + + ListTag tagList = tag.getList("Move", Tag.TAG_DOUBLE); + double x = tagList.getDouble(0); + double y = tagList.getDouble(1); + double z = tagList.getDouble(2); + if (x != 0 || y != 0 || z != 0) + armorStand.setPosRaw(armorStand.getX() + x, + armorStand.getY() + y, + armorStand.getZ() + z); + } + } +} diff --git a/common/src/main/java/com/mrbysco/armorposer/mixin/MinecraftMixin.java b/common/src/main/java/com/mrbysco/armorposer/mixin/MinecraftMixin.java new file mode 100644 index 0000000..189e2e8 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/mixin/MinecraftMixin.java @@ -0,0 +1,21 @@ +package com.mrbysco.armorposer.mixin; + +import com.mrbysco.armorposer.client.GlowHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.decoration.ArmorStand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Minecraft.class) +public class MinecraftMixin { + + @Inject(method = "shouldEntityAppearGlowing(Lnet/minecraft/world/entity/Entity;)Z", + at = @At("HEAD"), cancellable = true) + public void armorposer$shouldEntityAppearGlowing(Entity entity, CallbackInfoReturnable cir) { + if (entity instanceof ArmorStand && GlowHandler.isGlowing(entity.getUUID())) + cir.setReturnValue(true); + } +} diff --git a/Common/src/main/java/com/mrbysco/armorposer/platform/Services.java b/common/src/main/java/com/mrbysco/armorposer/platform/Services.java similarity index 100% rename from Common/src/main/java/com/mrbysco/armorposer/platform/Services.java rename to common/src/main/java/com/mrbysco/armorposer/platform/Services.java 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 new file mode 100644 index 0000000..7b35169 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/platform/services/IPlatformHelper.java @@ -0,0 +1,30 @@ +package com.mrbysco.armorposer.platform.services; + +import com.mrbysco.armorposer.data.SwapData; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.decoration.ArmorStand; + +import java.nio.file.Path; + +public interface IPlatformHelper { + /** + * Update Armor Stand Entity + */ + void updateEntity(ArmorStand armorStand, CompoundTag compound); + + /** + * Update Armor Stand Entity + */ + void swapSlots(ArmorStand armorStand, SwapData.Action action); + + /** + * 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..fce1336 --- /dev/null +++ b/common/src/main/java/com/mrbysco/armorposer/poses/UserPoseHandler.java @@ -0,0 +1,65 @@ +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 saveUserPoses() { + if (!PRESET_FOLDER.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/ArmorStandData.java b/common/src/main/java/com/mrbysco/armorposer/util/ArmorStandData.java similarity index 88% rename from Common/src/main/java/com/mrbysco/armorposer/util/ArmorStandData.java rename to common/src/main/java/com/mrbysco/armorposer/util/ArmorStandData.java index 5f42ccf..d86534e 100644 --- a/Common/src/main/java/com/mrbysco/armorposer/util/ArmorStandData.java +++ b/common/src/main/java/com/mrbysco/armorposer/util/ArmorStandData.java @@ -10,10 +10,12 @@ public class ArmorStandData { public boolean noGravity = false; public boolean showArms = false; public boolean small = false; + public boolean nameVisible = true; + public boolean locked = true; public float rotation = 0F; - public float[] pose = new float[18]; + public final float[] pose = new float[3 * 7]; public boolean getBooleanValue(int index) { @@ -23,6 +25,8 @@ public boolean getBooleanValue(int index) { case 2 -> this.noGravity; case 3 -> this.showArms; case 4 -> this.small; + case 5 -> this.nameVisible; + case 6 -> this.locked; default -> false; }; } @@ -34,6 +38,8 @@ public void readFromNBT(CompoundTag compound) { this.noGravity = compound.getBoolean("NoGravity"); this.showArms = compound.getBoolean("ShowArms"); this.small = compound.getBoolean("Small"); + this.nameVisible = compound.getBoolean("CustomNameVisible"); + this.locked = compound.getBoolean("Invulnerable"); if (compound.contains("Rotation")) { this.rotation = compound.getList("Rotation", CompoundTag.TAG_FLOAT).getFloat(0); @@ -62,6 +68,9 @@ public CompoundTag writeToNBT() { compound.putBoolean("NoGravity", this.noGravity); compound.putBoolean("ShowArms", this.showArms); compound.putBoolean("Small", this.small); + compound.putBoolean("CustomNameVisible", this.nameVisible); + compound.putBoolean("Invulnerable", this.locked); + compound.putInt("DisabledSlots", this.locked ? 4144959 : 0); ListTag rotationTag = new ListTag(); rotationTag.add(FloatTag.valueOf(this.rotation)); 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 new file mode 100644 index 0000000..1b4da20 --- /dev/null +++ b/common/src/main/resources/assets/armorposer/lang/en_us.json @@ -0,0 +1,122 @@ +{ + "_comment": "en_us", + + "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", + "armorposer.gui.label.show_arms": "Show Arms", + "armorposer.gui.label.small": "Small", + "armorposer.gui.label.rotation": "Rotation", + "armorposer.gui.label.name_visible": "Name Visible", + + "armorposer.gui.label.head": "Head", + "armorposer.gui.label.body": "Body", + "armorposer.gui.label.left_leg": "Left Leg", + "armorposer.gui.label.right_leg": "Right Leg", + "armorposer.gui.label.left_arm": "Left Arm", + "armorposer.gui.label.right_arm": "Right Arm", + "armorposer.gui.label.position": "Position", + "armorposer.gui.label.scroll": "Click and scroll", + + "armorposer.gui.label.copy": "Copy", + "armorposer.gui.label.paste": "Paste", + "armorposer.gui.label.save": "Save", + "armorposer.gui.label.poses": "Poses", + + "armorposer.gui.tooltip.invisible": "Toggle invisibility", + "armorposer.gui.tooltip.no_base_plate": "Toggle base plate", + "armorposer.gui.tooltip.no_gravity": "Toggle gravity", + "armorposer.gui.tooltip.show_arms": "Toggle arms", + "armorposer.gui.tooltip.small": "Toggle size", + "armorposer.gui.tooltip.rotation": "Change rotation", + "armorposer.gui.tooltip.name_visible": "Toggle name visibility", + + "armorposer.gui.tooltip.x_rotation": "Adjust X rotation", + "armorposer.gui.tooltip.y_rotation": "Adjust Y rotation", + "armorposer.gui.tooltip.z_rotation": "Adjust Z rotation", + "armorposer.gui.tooltip.x_position": "Adjust X position", + "armorposer.gui.tooltip.y_position": "Adjust Y position", + "armorposer.gui.tooltip.y_position.disabled": "Disable gravity to adjust Y position", + "armorposer.gui.tooltip.z_position": "Adjust Z position", + + "armorposer.gui.tooltip.copy": "Copy the current pose to the clipboard", + "armorposer.gui.tooltip.paste": "Paste the pose from the clipboard", + "armorposer.gui.tooltip.save": "Save the current pose to the user poses", + "armorposer.gui.tooltip.poses": "Check pose presets", + + "armorposer.gui.tooltip.mirror": "Mirror pose", + "armorposer.gui.tooltip.mirror_legs": "Mirror legs", + "armorposer.gui.tooltip.mirror_arms": "Mirror Arms", + "armorposer.gui.tooltip.swap_head": "Swap main hand and head", + "armorposer.gui.tooltip.swap_hands": "Swap main and off hand", + "armorposer.gui.tooltip.block": "Align held block, so it appears on the surface", + "armorposer.gui.tooltip.item": "Align held item, so it appears flat on the surface", + "armorposer.gui.tooltip.item2": "Holding shift aligns the item so it appears upright on the surface", + "armorposer.gui.tooltip.tool": "Align held tool, so it appears flat on the surface", + "armorposer.gui.tooltip.lock": "Lock Interaction", + + "armorposer.gui.pose.attention": "Attention", + "armorposer.gui.pose.walking": "Walking", + "armorposer.gui.pose.running": "Running", + "armorposer.gui.pose.pointing": "Pointing", + "armorposer.gui.pose.blocking": "Blocking", + "armorposer.gui.pose.lunging": "Lunging", + "armorposer.gui.pose.winning": "Winning", + "armorposer.gui.pose.sitting": "Sitting", + "armorposer.gui.pose.arabesque": "Arabesque", + "armorposer.gui.pose.cupid": "Cupid", + "armorposer.gui.pose.point_and_laugh": "Point and Laugh", + "armorposer.gui.pose.confident": "Confident", + "armorposer.gui.pose.salute": "Salute", + "armorposer.gui.pose.death": "Death", + "armorposer.gui.pose.facepalm": "Facepalm", + "armorposer.gui.pose.lazing": "Lazing", + "armorposer.gui.pose.confused": "Confused", + "armorposer.gui.pose.formal": "Formal", + "armorposer.gui.pose.sad": "Sad", + "armorposer.gui.pose.joyous": "Joyous", + "armorposer.gui.pose.stargazing": "Stargazing", + "armorposer.gui.pose.block": "Block", + "armorposer.gui.pose.item": "Item", + "armorposer.gui.pose.random": "Random", + + "armorposer.gui.save_pose.title": "Save pose", + "armorposer.gui.delete_poose.title": "Delete pose", + "armorposer.gui.delete_poose.message": "Are you sure you want to remove this pose?", + + "armorposer.gui.armor_list.list": "Armor Stands", + "armorposer.gui.armor_list.locate": "Locate", + "armorposer.gui.armor_list.modify": "Modify", + + "armorposer.config.enableConfigGui": "Enable GUI", + "armorposer.config.enableConfigGui.tooltip": "Show the Armor Stand configuration GUI on shift right click", + + "armorposer.config.enableNameTags": "Enable Name Tags", + "armorposer.config.enableNameTags.tooltip": "Allow Armor Stand to be renamed using name tags", + + "armorposer.config.allowScrolling": "Allow Scrolling", + "armorposer.config.allowScrolling.tooltip": "Allow scrolling to increase / decrease an angle value in the posing screen", + + "armorposer.key.openArmorStandGui": "Open GUI", + "armorposer.key.categories.general": "Armor Stand Configurator", + + "text.autoconfig.armorposer.title": "Armor Poser", + "text.autoconfig.armorposer.option.general": "General", + "text.autoconfig.armorposer.option.general.enableConfigGui": "Enable Config Gui", + "text.autoconfig.armorposer.option.general.enableNameTags": "Enable Name Tags", + "text.autoconfig.armorposer.option.general.allowScrolling": "Allow Scrolling", + + "armorposer.networking.screen.failed": "Armor poser networking screen packet failed", + "armorposer.networking.swap.failed": "Armor poser networking swap packet failed", + "armorposer.networking.sync.failed": "Armor poser networking sync packet failed" +} \ No newline at end of file diff --git a/common/src/main/resources/assets/armorposer/lang/es_mx.json b/common/src/main/resources/assets/armorposer/lang/es_mx.json new file mode 100644 index 0000000..b109618 --- /dev/null +++ b/common/src/main/resources/assets/armorposer/lang/es_mx.json @@ -0,0 +1,118 @@ +{ + "_comment": "es_mx", + + "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", + "armorposer.gui.label.show_arms": "Mostrar brazos", + "armorposer.gui.label.small": "Pequeño", + "armorposer.gui.label.rotation": "Rotación", + "armorposer.gui.label.name_visible": "Nombre visible", + + "armorposer.gui.label.head": "Cabeza", + "armorposer.gui.label.body": "Cuerpo", + "armorposer.gui.label.left_leg": "Pierna izquierda", + "armorposer.gui.label.right_leg": "Pierna derecha", + "armorposer.gui.label.left_arm": "Brazo izquierdo", + "armorposer.gui.label.right_arm": "Brazo derecho", + "armorposer.gui.label.position": "Posición", + "armorposer.gui.label.scroll": "Click and scroll", + + "armorposer.gui.label.copy": "Copiar", + "armorposer.gui.label.paste": "Pegar", + "armorposer.gui.label.save": "Save", + "armorposer.gui.label.poses": "Posiciones", + + "armorposer.gui.tooltip.invisible": "Alternar invisibilidad", + "armorposer.gui.tooltip.no_base_plate": "Alternar base", + "armorposer.gui.tooltip.no_gravity": "Alternar gravedad", + "armorposer.gui.tooltip.show_arms": "Alternar brazos", + "armorposer.gui.tooltip.small": "Alternar tamaño", + "armorposer.gui.tooltip.rotation": "Cambiar rotación", + "armorposer.gui.tooltip.name_visible": "Alternar visibilidad de nombre", + + "armorposer.gui.tooltip.x_rotation": "Ajustar rotación X", + "armorposer.gui.tooltip.y_rotation": "Ajustar rotación Y", + "armorposer.gui.tooltip.z_rotation": "Ajustar rotación Z", + "armorposer.gui.tooltip.x_position": "Ajustar posición X", + "armorposer.gui.tooltip.y_position": "Ajustar posición Y", + "armorposer.gui.tooltip.y_position.disabled": "Desactiva la gravedad para ajustar la posición Y", + "armorposer.gui.tooltip.z_position": "Ajustar posición Z", + + "armorposer.gui.tooltip.copy": "Copiar la pose actual al portapapeles", + "armorposer.gui.tooltip.paste": "pegar la pose actual desde el portapapeles", + "armorposer.gui.tooltip.save": "Save the current pose to the user poses", + "armorposer.gui.tooltip.poses": "Poses predeterminadas", + + "armorposer.gui.tooltip.mirror": "Postura en espejo", + "armorposer.gui.tooltip.mirror_legs": "Piernas en espejo", + "armorposer.gui.tooltip.mirror_arms": "Brazos en espejo", + "armorposer.gui.tooltip.swap_head": "Intercambiar mano principal y cabeza", + "armorposer.gui.tooltip.swap_hands": "Intercambiar mano principal y secundaria", + "armorposer.gui.tooltip.block": "Align held block, so it appears on the surface", + "armorposer.gui.tooltip.item": "Align held item, so it appears flat on the surface", + "armorposer.gui.tooltip.item2": "Holding shift aligns the item so it appears upright on the surface", + "armorposer.gui.tooltip.tool": "Align held tool, so it appears flat on the surface", + "armorposer.gui.tooltip.lock": "Bloquear interacción", + + "armorposer.gui.pose.attention": "Alerta", + "armorposer.gui.pose.walking": "Caminar", + "armorposer.gui.pose.running": "Correr", + "armorposer.gui.pose.pointing": "Apuntar", + "armorposer.gui.pose.blocking": "Bloquear", + "armorposer.gui.pose.lunging": "Embestir", + "armorposer.gui.pose.winning": "Ganador", + "armorposer.gui.pose.sitting": "Sentado", + "armorposer.gui.pose.arabesque": "Ballet", + "armorposer.gui.pose.cupid": "Cupido", + "armorposer.gui.pose.point_and_laugh": "Señalar y reír", + "armorposer.gui.pose.confident": "Seguro", + "armorposer.gui.pose.salute": "Saludo", + "armorposer.gui.pose.death": "Muerto", + "armorposer.gui.pose.facepalm": "Frustración", + "armorposer.gui.pose.lazing": "Holgazanear", + "armorposer.gui.pose.confused": "Confuso", + "armorposer.gui.pose.formal": "Formal", + "armorposer.gui.pose.sad": "Triste", + "armorposer.gui.pose.joyous": "Jubiloso", + "armorposer.gui.pose.stargazing": "Observando estrellas", + "armorposer.gui.pose.block": "Block", + "armorposer.gui.pose.item": "Objeto", + "armorposer.gui.pose.random": "Aleatorio", + + "armorposer.gui.save_pose.title": "Save pose", + "armorposer.gui.delete_poose.title": "Delete pose", + "armorposer.gui.delete_poose.message": "Are you sure you want to remove this pose?", + + "armorposer.config.enableConfigGui": "Habilitar interfaz", + "armorposer.config.enableConfigGui.tooltip": "Muestra la interfaz de configuración del soporte para armadura al hacer shift y clic con el botón derecho", + + "armorposer.config.enableNameTags": "Habilitar etiquetas", + "armorposer.config.enableNameTags.tooltip": "Permite que se cambie el nombre del soporte para armadura usando etiquetas", + + "armorposer.config.allowScrolling": "Habilitar desplazamiento", + "armorposer.config.allowScrolling.tooltip": "Permite el desplazamiento para aumentar y disminuir un valor en la pantalla de pose", + + "armorposer.key.openArmorStandGui": "Abrir interfaz", + "armorposer.key.categories.general": "Configurador de soporte para armadura", + + "text.autoconfig.armorposer.title": "Armor Poser", + "text.autoconfig.armorposer.option.general": "General", + "text.autoconfig.armorposer.option.general.enableConfigGui": "Enable Config Gui", + "text.autoconfig.armorposer.option.general.enableNameTags": "Enable Name Tags", + "text.autoconfig.armorposer.option.general.allowScrolling": "Allow Scrolling", + + "armorposer.networking.screen.failed": "Armor poser networking screen packet failed", + "armorposer.networking.swap.failed": "Armor poser networking swap packet failed", + "armorposer.networking.sync.failed": "Armor poser networking sync packet failed" +} diff --git a/common/src/main/resources/assets/armorposer/lang/fr_fr.json b/common/src/main/resources/assets/armorposer/lang/fr_fr.json new file mode 100644 index 0000000..bf81441 --- /dev/null +++ b/common/src/main/resources/assets/armorposer/lang/fr_fr.json @@ -0,0 +1,118 @@ +{ + "_comment": "fr_fr", + + "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é", + "armorposer.gui.label.show_arms": "Afficher les bras", + "armorposer.gui.label.small": "Petit", + "armorposer.gui.label.rotation": "Rotation", + "armorposer.gui.label.name_visible": "Name Visible", + + "armorposer.gui.label.head": "Tête", + "armorposer.gui.label.body": "Corps", + "armorposer.gui.label.left_leg": "Jambe gauche", + "armorposer.gui.label.right_leg": "Jambe droite", + "armorposer.gui.label.left_arm": "Bras gauche", + "armorposer.gui.label.right_arm": "Bras droit", + "armorposer.gui.label.position": "Position", + "armorposer.gui.label.scroll": "Click and scroll", + + "armorposer.gui.label.copy": "Copier", + "armorposer.gui.label.paste": "Coller", + "armorposer.gui.label.save": "Save", + "armorposer.gui.label.poses": "Poses", + + "armorposer.gui.tooltip.invisible": "Toggle invisibility", + "armorposer.gui.tooltip.no_base_plate": "Toggle base plate", + "armorposer.gui.tooltip.no_gravity": "Toggle gravity", + "armorposer.gui.tooltip.show_arms": "Toggle arms", + "armorposer.gui.tooltip.small": "Toggle size", + "armorposer.gui.tooltip.rotation": "Change rotation", + "armorposer.gui.tooltip.name_visible": "Toggle name visibility", + + "armorposer.gui.tooltip.x_rotation": "Adjust X rotation", + "armorposer.gui.tooltip.y_rotation": "Adjust Y rotation", + "armorposer.gui.tooltip.z_rotation": "Adjust Z rotation", + "armorposer.gui.tooltip.x_position": "Adjust X position", + "armorposer.gui.tooltip.y_position": "Adjust Y position", + "armorposer.gui.tooltip.y_position.disabled": "Disable gravity to adjust Y position", + "armorposer.gui.tooltip.z_position": "Adjust Z position", + + "armorposer.gui.tooltip.copy": "Copy the current pose to the clipboard", + "armorposer.gui.tooltip.paste": "Paste the pose from the clipboard", + "armorposer.gui.tooltip.save": "Save the current pose to the user poses", + "armorposer.gui.tooltip.poses": "Check built-in poses", + + "armorposer.gui.tooltip.mirror": "Mirror pose", + "armorposer.gui.tooltip.mirror_legs": "Mirror legs", + "armorposer.gui.tooltip.mirror_arms": "Mirror Arms", + "armorposer.gui.tooltip.swap_head": "Swap main hand and head", + "armorposer.gui.tooltip.swap_hands": "Swap main and off hand", + "armorposer.gui.tooltip.block": "Align held block, so it appears on the surface", + "armorposer.gui.tooltip.item": "Align held item, so it appears flat on the surface", + "armorposer.gui.tooltip.item2": "Holding shift aligns the item so it appears upright on the surface", + "armorposer.gui.tooltip.tool": "Align held tool, so it appears flat on the surface", + "armorposer.gui.tooltip.lock": "Lock Interaction", + + "armorposer.gui.pose.attention": "Attention", + "armorposer.gui.pose.walking": "Walking", + "armorposer.gui.pose.running": "Running", + "armorposer.gui.pose.pointing": "Pointing", + "armorposer.gui.pose.blocking": "Blocking", + "armorposer.gui.pose.lunging": "Lunging", + "armorposer.gui.pose.winning": "Winning", + "armorposer.gui.pose.sitting": "Sitting", + "armorposer.gui.pose.arabesque": "Arabesque", + "armorposer.gui.pose.cupid": "Cupid", + "armorposer.gui.pose.point_and_laugh": "Point and Laugh", + "armorposer.gui.pose.confident": "Confident", + "armorposer.gui.pose.salute": "Salute", + "armorposer.gui.pose.death": "Death", + "armorposer.gui.pose.facepalm": "Facepalm", + "armorposer.gui.pose.lazing": "Lazing", + "armorposer.gui.pose.confused": "Confused", + "armorposer.gui.pose.formal": "Formal", + "armorposer.gui.pose.sad": "Sad", + "armorposer.gui.pose.joyous": "Joyous", + "armorposer.gui.pose.stargazing": "Stargazing", + "armorposer.gui.pose.block": "Block", + "armorposer.gui.pose.item": "Item", + "armorposer.gui.pose.random": "Random", + + "armorposer.gui.save_pose.title": "Save pose", + "armorposer.gui.delete_poose.title": "Delete pose", + "armorposer.gui.delete_poose.message": "Are you sure you want to remove this pose?", + + "armorposer.config.enableConfigGui": "Activer l'interface", + "armorposer.config.enableConfigGui.tooltip": "Afficher l'interface de configuration du porte-armure avec un shift clic-droit", + + "armorposer.config.enableNameTags": "Enable Name Tags", + "armorposer.config.enableNameTags.tooltip": "Allow Armor Stand to be renamed using name tags", + + "armorposer.config.allowScrolling": "Allow Scrolling", + "armorposer.config.allowScrolling.tooltip": "Allow scrolling to increase / decrease an angle value in the posing screen", + + "armorposer.key.openArmorStandGui": "Ouvrir le menu", + "armorposer.key.categories.general": "Configurateur du porte-armure", + + "text.autoconfig.armorposer.title": "Armor Poser", + "text.autoconfig.armorposer.option.general": "Général", + "text.autoconfig.armorposer.option.general.enableConfigGui": "Activer l'interface", + "text.autoconfig.armorposer.option.general.enableNameTags": "Activer les balises de nom", + "text.autoconfig.armorposer.option.general.allowScrolling": "Autoriser le défilement", + + "armorposer.networking.screen.failed": "Armor poser networking screen packet failed", + "armorposer.networking.swap.failed": "Armor poser networking swap packet failed", + "armorposer.networking.sync.failed": "Armor poser networking sync packet failed" +} \ No newline at end of file diff --git a/common/src/main/resources/assets/armorposer/lang/ru_ru.json b/common/src/main/resources/assets/armorposer/lang/ru_ru.json new file mode 100644 index 0000000..7cb40c5 --- /dev/null +++ b/common/src/main/resources/assets/armorposer/lang/ru_ru.json @@ -0,0 +1,122 @@ +{ + "_comment": "ru_ru", + + "armorposer.gui.title": "Стойка для брони", + + "armorposer.gui.poses.title": "Поза стойки для брони", + "armorposer.gui.poses.default": "Поза по умолчанию", + "armorposer.gui.poses.user": "Пользовательские позы", + "armorposer.gui.poses.selection.apply": "Применить", + "armorposer.gui.poses.search": "Поиск", + "armorposer.gui.poses.search.a_to_z": "А-Я", + "armorposer.gui.poses.search.z_to_a": "Я-А", + + "armorposer.gui.label.invisible": "Невидимость", + "armorposer.gui.label.no_base_plate": "Без пластины", + "armorposer.gui.label.no_gravity": "Без гравитации", + "armorposer.gui.label.show_arms": "Показать руки", + "armorposer.gui.label.small": "Ребенок", + "armorposer.gui.label.rotation": "Вращать", + "armorposer.gui.label.name_visible": "Отображение имени", + + "armorposer.gui.label.head": "Голова", + "armorposer.gui.label.body": "Тело", + "armorposer.gui.label.left_leg": "Левая нога", + "armorposer.gui.label.right_leg": "Правая нога", + "armorposer.gui.label.left_arm": "Левая рука", + "armorposer.gui.label.right_arm": "Правая рука", + "armorposer.gui.label.position": "Позиция", + "armorposer.gui.label.scroll": "Нажать для вращения", + + "armorposer.gui.label.copy": "Копировать", + "armorposer.gui.label.paste": "Вставить", + "armorposer.gui.label.save": "Сохранить", + "armorposer.gui.label.poses": "Позы", + + "armorposer.gui.tooltip.invisible": "Переключить видимость", + "armorposer.gui.tooltip.no_base_plate": "Переключить отображение пластины", + "armorposer.gui.tooltip.no_gravity": "Переключить гравитацию", + "armorposer.gui.tooltip.show_arms": "Переключить руки", + "armorposer.gui.tooltip.small": "Поменять размер", + "armorposer.gui.tooltip.rotation": "Вращать по горизонтали", + "armorposer.gui.tooltip.name_visible": "Переключить отображение имени", + + "armorposer.gui.tooltip.x_rotation": "Изменить вращение по X", + "armorposer.gui.tooltip.y_rotation": "Изменить вращение по Y", + "armorposer.gui.tooltip.z_rotation": "Изменить вращение по Z", + "armorposer.gui.tooltip.x_position": "Изменить позицию по X", + "armorposer.gui.tooltip.y_position": "Изменить позицию по Y", + "armorposer.gui.tooltip.y_position.disabled": "Отключить гравитацию, чтоы изменить позицию по Y", + "armorposer.gui.tooltip.z_position": "Изменить позицию по Z", + + "armorposer.gui.tooltip.copy": "Скопировать текущую позу в Буфер", + "armorposer.gui.tooltip.paste": "Вставить позу из Буфера", + "armorposer.gui.tooltip.save": "Сохранить текущую позу в пользовательские", + "armorposer.gui.tooltip.poses": "Проверить пресеты поз", + + "armorposer.gui.tooltip.mirror": "Отразить позу", + "armorposer.gui.tooltip.mirror_legs": "Отразить ноги", + "armorposer.gui.tooltip.mirror_arms": "Отразить руки", + "armorposer.gui.tooltip.swap_head": "Поменяйте местами основную руку и голову", + "armorposer.gui.tooltip.swap_hands": "Поменяйте местами основную и дополнительную руку", + "armorposer.gui.tooltip.block": "Выровнить удерживаемый блок так, чтобы он появился на поверхности", + "armorposer.gui.tooltip.item": "Выровнить удерживаемый предмет так, чтобы он выглядел плоским на поверхности", + "armorposer.gui.tooltip.item2": "Удерживая клавишу Shift, вы выравниваете элемент так, чтобы он располагался на поверхности вертикально", + "armorposer.gui.tooltip.tool": "Выровнить удерживаемый инструмент так, чтобы он лежал ровно на поверхности", + "armorposer.gui.tooltip.lock": "Заблокировать взаимодействие", + + "armorposer.gui.pose.attention": "Внимание", + "armorposer.gui.pose.walking": "Походка", + "armorposer.gui.pose.running": "Бег", + "armorposer.gui.pose.pointing": "Указывать", + "armorposer.gui.pose.blocking": "Блокировка", + "armorposer.gui.pose.lunging": "Выпад", + "armorposer.gui.pose.winning": "Победа", + "armorposer.gui.pose.sitting": "Сидеть", + "armorposer.gui.pose.arabesque": "Арабески", + "armorposer.gui.pose.cupid": "Купидон", + "armorposer.gui.pose.point_and_laugh": "Насмешка", + "armorposer.gui.pose.confident": "Уверенный", + "armorposer.gui.pose.salute": "Press F", + "armorposer.gui.pose.death": "Смерть", + "armorposer.gui.pose.facepalm": "РукаЛицо", + "armorposer.gui.pose.lazing": "Ленивый", + "armorposer.gui.pose.confused": "Смущенный", + "armorposer.gui.pose.formal": "Формальный", + "armorposer.gui.pose.sad": "Грустный", + "armorposer.gui.pose.joyous": "Радостный", + "armorposer.gui.pose.stargazing": "Наблюдать на звёзды", + "armorposer.gui.pose.block": "Защита", + "armorposer.gui.pose.item": "Предмет", + "armorposer.gui.pose.random": "Случайная", + + "armorposer.gui.save_pose.title": "Сохранить позу", + "armorposer.gui.delete_poose.title": "Удалить позу", + "armorposer.gui.delete_poose.message": "Вы уверены, что хотите удалить эту позу?", + + "armorposer.gui.armor_list.list": "Стойка для брони", + "armorposer.gui.armor_list.locate": "Найти", + "armorposer.gui.armor_list.modify": "Изменить", + + "armorposer.config.enableConfigGui": "Включить графический интерфейс", + "armorposer.config.enableConfigGui.tooltip": "Показывать графический интерфейс конфигурации Стойки для брони при нажатии ПКМ", + + "armorposer.config.enableNameTags": "Включить теги имен", + "armorposer.config.enableNameTags.tooltip": "Разрешить переименование Стойки для брони с использованием тегов имен", + + "armorposer.config.allowScrolling": "Разрешить прокрутку", + "armorposer.config.allowScrolling.tooltip": "Разрешить прокрутку для увеличения/уменьшения значения угла на экране позирования.", + + "armorposer.key.openArmorStandGui": "Открыть графический интерфейс", + "armorposer.key.categories.general": "Настройки Стойки для брони", + + "text.autoconfig.armorposer.title": "Стойка для брони", + "text.autoconfig.armorposer.option.general": "Общий", + "text.autoconfig.armorposer.option.general.enableConfigGui": "Включить настройки графического интерфейса", + "text.autoconfig.armorposer.option.general.enableNameTags": "Включить теги имен", + "text.autoconfig.armorposer.option.general.allowScrolling": "Разрешить прокрутку", + + "armorposer.networking.screen.failed": "Ошибка пакета сетевого экрана Стойки для брони", + "armorposer.networking.swap.failed": "Сбой пакета подкачки сети Стойки для брони", + "armorposer.networking.sync.failed": "Не удалось выполнить пакет синхронизации сети Стойки для брони" +} \ No newline at end of file diff --git a/Common/src/main/resources/assets/armorposer/lang/zh_cn.json b/common/src/main/resources/assets/armorposer/lang/zh_cn.json similarity index 100% rename from Common/src/main/resources/assets/armorposer/lang/zh_cn.json rename to common/src/main/resources/assets/armorposer/lang/zh_cn.json diff --git a/common/src/main/resources/assets/armorposer/lang/zh_tw.json b/common/src/main/resources/assets/armorposer/lang/zh_tw.json new file mode 100644 index 0000000..090e374 --- /dev/null +++ b/common/src/main/resources/assets/armorposer/lang/zh_tw.json @@ -0,0 +1,118 @@ +{ + "_comment": "zh_tw", + + "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": "不受重力影響", + "armorposer.gui.label.show_arms": "顯示手臂", + "armorposer.gui.label.small": "小型", + "armorposer.gui.label.rotation": "旋轉", + "armorposer.gui.label.name_visible": "Name Visible", + + "armorposer.gui.label.head": "頭", + "armorposer.gui.label.body": "身體", + "armorposer.gui.label.left_leg": "左腿", + "armorposer.gui.label.right_leg": "右腿", + "armorposer.gui.label.left_arm": "左臂", + "armorposer.gui.label.right_arm": "右臂", + "armorposer.gui.label.position": "Position", + "armorposer.gui.label.scroll": "Click and scroll", + + "armorposer.gui.label.copy": "複製", + "armorposer.gui.label.paste": "貼上", + "armorposer.gui.label.save": "Save", + "armorposer.gui.label.poses": "Poses", + + "armorposer.gui.tooltip.invisible": "Toggle invisibility", + "armorposer.gui.tooltip.no_base_plate": "Toggle base plate", + "armorposer.gui.tooltip.no_gravity": "Toggle gravity", + "armorposer.gui.tooltip.show_arms": "Toggle arms", + "armorposer.gui.tooltip.small": "Toggle size", + "armorposer.gui.tooltip.rotation": "Change rotation", + "armorposer.gui.tooltip.name_visible": "Toggle name visibility", + + "armorposer.gui.tooltip.x_rotation": "Adjust X rotation", + "armorposer.gui.tooltip.y_rotation": "Adjust Y rotation", + "armorposer.gui.tooltip.z_rotation": "Adjust Z rotation", + "armorposer.gui.tooltip.x_position": "Adjust X position", + "armorposer.gui.tooltip.y_position": "Adjust Y position", + "armorposer.gui.tooltip.y_position.disabled": "Disable gravity to adjust Y position", + "armorposer.gui.tooltip.z_position": "Adjust Z position", + + "armorposer.gui.tooltip.copy": "Copy the current pose to the clipboard", + "armorposer.gui.tooltip.paste": "Paste the pose from the clipboard", + "armorposer.gui.tooltip.save": "Save the current pose to the user poses", + "armorposer.gui.tooltip.poses": "Check built-in poses", + + "armorposer.gui.tooltip.mirror": "Mirror pose", + "armorposer.gui.tooltip.mirror_legs": "Mirror legs", + "armorposer.gui.tooltip.mirror_arms": "Mirror Arms", + "armorposer.gui.tooltip.swap_head": "Swap main hand and head", + "armorposer.gui.tooltip.swap_hands": "Swap main and off hand", + "armorposer.gui.tooltip.block": "Align held block, so it appears on the surface", + "armorposer.gui.tooltip.item": "Align held item, so it appears flat on the surface", + "armorposer.gui.tooltip.item2": "Holding shift aligns the item so it appears upright on the surface", + "armorposer.gui.tooltip.tool": "Align held tool, so it appears flat on the surface", + "armorposer.gui.tooltip.lock": "Lock Interaction", + + "armorposer.gui.pose.attention": "Attention", + "armorposer.gui.pose.walking": "Walking", + "armorposer.gui.pose.running": "Running", + "armorposer.gui.pose.pointing": "Pointing", + "armorposer.gui.pose.blocking": "Blocking", + "armorposer.gui.pose.lunging": "Lunging", + "armorposer.gui.pose.winning": "Winning", + "armorposer.gui.pose.sitting": "Sitting", + "armorposer.gui.pose.arabesque": "Arabesque", + "armorposer.gui.pose.cupid": "Cupid", + "armorposer.gui.pose.point_and_laugh": "Point and Laugh", + "armorposer.gui.pose.confident": "Confident", + "armorposer.gui.pose.salute": "Salute", + "armorposer.gui.pose.death": "Death", + "armorposer.gui.pose.facepalm": "Facepalm", + "armorposer.gui.pose.lazing": "Lazing", + "armorposer.gui.pose.confused": "Confused", + "armorposer.gui.pose.formal": "Formal", + "armorposer.gui.pose.sad": "Sad", + "armorposer.gui.pose.joyous": "Joyous", + "armorposer.gui.pose.stargazing": "Stargazing", + "armorposer.gui.pose.block": "Block", + "armorposer.gui.pose.item": "Item", + "armorposer.gui.pose.random": "Random", + + "armorposer.gui.save_pose.title": "Save pose", + "armorposer.gui.delete_poose.title": "Delete pose", + "armorposer.gui.delete_poose.message": "Are you sure you want to remove this pose?", + + "armorposer.config.enableConfigGui": "啟用GUI", + "armorposer.config.enableConfigGui.tooltip": "按\"Shift+右鍵\"點撃盔甲座時顯示盔甲座配置GUI", + + "armorposer.config.enableNameTags": "啟用命名功能", + "armorposer.config.enableNameTags.tooltip": "允許盔甲座使用命名牌重新命名", + + "armorposer.config.allowScrolling": "Allow Scrolling", + "armorposer.config.allowScrolling.tooltip": "Allow scrolling to increase / decrease an angle value in the posing screen", + + "armorposer.key.openArmorStandGui": "打開GUI", + "armorposer.key.categories.general": "盔甲座配置", + + "text.autoconfig.armorposer.title": "Armor Poser", + "text.autoconfig.armorposer.option.general": "General", + "text.autoconfig.armorposer.option.general.enableConfigGui": "Enable Config Gui", + "text.autoconfig.armorposer.option.general.enableNameTags": "Enable Name Tags", + "text.autoconfig.armorposer.option.general.allowScrolling": "Allow Scrolling", + + "armorposer.networking.screen.failed": "Armor poser networking screen packet failed", + "armorposer.networking.swap.failed": "Armor poser networking swap packet failed", + "armorposer.networking.sync.failed": "Armor poser networking sync packet failed" +} \ No newline at end of file diff --git a/common/src/main/resources/assets/armorposer/textures/gui/poser_buttons.png b/common/src/main/resources/assets/armorposer/textures/gui/poser_buttons.png new file mode 100644 index 0000000..3b73ca3 Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/poser_buttons.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/block.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/block.png new file mode 100644 index 0000000..a07ad9f Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/block.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/block_highlighted.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/block_highlighted.png new file mode 100644 index 0000000..eb96184 Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/block_highlighted.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/item.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/item.png new file mode 100644 index 0000000..34200af Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/item.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/item_highlighted.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/item_highlighted.png new file mode 100644 index 0000000..eb303df Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/item_highlighted.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_arms.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_arms.png new file mode 100644 index 0000000..3fbf6cf Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_arms.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_arms_highlighted.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_arms_highlighted.png new file mode 100644 index 0000000..5d6165a Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_arms_highlighted.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_hands.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_hands.png new file mode 100644 index 0000000..c186944 Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_hands.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_hands_highlighted.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_hands_highlighted.png new file mode 100644 index 0000000..486dd54 Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_hands_highlighted.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_legs.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_legs.png new file mode 100644 index 0000000..7703298 Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_legs.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_legs_highlighted.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_legs_highlighted.png new file mode 100644 index 0000000..dc56bf3 Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_legs_highlighted.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_pose.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_pose.png new file mode 100644 index 0000000..53214f7 Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_pose.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_pose_highlighted.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_pose_highlighted.png new file mode 100644 index 0000000..e88e214 Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/mirror_pose_highlighted.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/swap_to_head.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/swap_to_head.png new file mode 100644 index 0000000..206d53a Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/swap_to_head.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/swap_to_head_highlighted.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/swap_to_head_highlighted.png new file mode 100644 index 0000000..420f96c Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/swap_to_head_highlighted.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/tool.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/tool.png new file mode 100644 index 0000000..1a6fd9d Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/tool.png differ diff --git a/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/tool_highlighted.png b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/tool_highlighted.png new file mode 100644 index 0000000..817c9b2 Binary files /dev/null and b/common/src/main/resources/assets/armorposer/textures/gui/sprites/widget/tool_highlighted.png differ diff --git a/Common/src/main/resources/pack.mcmeta b/common/src/main/resources/pack.mcmeta similarity index 100% rename from Common/src/main/resources/pack.mcmeta rename to common/src/main/resources/pack.mcmeta diff --git a/Fabric/build.gradle b/fabric/build.gradle similarity index 100% rename from Fabric/build.gradle rename to fabric/build.gradle diff --git a/Fabric/src/main/java/com/mrbysco/armorposer/ArmorPoser.java b/fabric/src/main/java/com/mrbysco/armorposer/ArmorPoser.java similarity index 62% rename from Fabric/src/main/java/com/mrbysco/armorposer/ArmorPoser.java rename to fabric/src/main/java/com/mrbysco/armorposer/ArmorPoser.java index 3c08983..cb0f56c 100644 --- a/Fabric/src/main/java/com/mrbysco/armorposer/ArmorPoser.java +++ b/fabric/src/main/java/com/mrbysco/armorposer/ArmorPoser.java @@ -1,49 +1,49 @@ package com.mrbysco.armorposer; import com.mrbysco.armorposer.config.PoserConfig; +import com.mrbysco.armorposer.data.SwapData; +import com.mrbysco.armorposer.data.SyncData; import com.mrbysco.armorposer.handlers.EventHandler; import me.shedaniel.autoconfig.AutoConfig; import me.shedaniel.autoconfig.serializer.Toml4jConfigSerializer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.player.UseItemCallback; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.decoration.ArmorStand; -import java.util.UUID; - public class ArmorPoser implements ModInitializer { @Override public void onInitialize() { AutoConfig.register(PoserConfig.class, Toml4jConfigSerializer::new); - CommonClass.init(); - UseItemCallback.EVENT.register((player, world, hand) -> EventHandler.onPlayerRightClickItem(player, hand)); ServerPlayNetworking.registerGlobalReceiver(Reference.SYNC_PACKET_ID, (server, player, handler, buf, responseSender) -> { final ServerLevel world = player.getLevel(); - UUID standUUID = buf.readUUID(); - CompoundTag data = buf.readNbt(); + SyncData syncData = SyncData.decode(buf); server.execute(() -> { - Entity entity = world.getEntity(standUUID); + Entity entity = world.getEntity(syncData.entityUUID()); if (entity instanceof ArmorStand armorStandEntity) { + syncData.handleData(armorStandEntity); + } + }); + }); - CompoundTag entityTag = armorStandEntity.saveWithoutId(new CompoundTag()); - CompoundTag entityTagCopy = entityTag.copy(); - if (!data.isEmpty()) { - entityTagCopy.merge(data); - UUID uuid = armorStandEntity.getUUID(); - armorStandEntity.load(entityTagCopy); - armorStandEntity.setUUID(uuid); - } + ServerPlayNetworking.registerGlobalReceiver(Reference.SWAP_PACKET_ID, (server, player, handler, buf, responseSender) -> { + final ServerLevel world = player.getLevel(); + + SwapData swapData = SwapData.decode(buf); + + server.execute(() -> { + Entity entity = world.getEntity(swapData.entityUUID()); + if (entity instanceof ArmorStand armorStandEntity) { + swapData.handleData(armorStandEntity); } }); }); diff --git a/Fabric/src/main/java/com/mrbysco/armorposer/ArmorPoserClient.java b/fabric/src/main/java/com/mrbysco/armorposer/ArmorPoserClient.java similarity index 100% rename from Fabric/src/main/java/com/mrbysco/armorposer/ArmorPoserClient.java rename to fabric/src/main/java/com/mrbysco/armorposer/ArmorPoserClient.java diff --git a/Fabric/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java b/fabric/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java similarity index 85% rename from Fabric/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java rename to fabric/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java index b33ce7f..b403cef 100644 --- a/Fabric/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java +++ b/fabric/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java @@ -16,5 +16,7 @@ public static class General { public boolean enableConfigGui = true; @Comment("Allow Armor Stand to be renamed using name tags") public boolean enableNameTags = true; + @Comment("Allow scrolling to add / decrease an angle value in the posing screen") + public boolean allowScrolling = true; } } diff --git a/Fabric/src/main/java/com/mrbysco/armorposer/config/PoserModMenuIntegration.java b/fabric/src/main/java/com/mrbysco/armorposer/config/PoserModMenuIntegration.java similarity index 100% rename from Fabric/src/main/java/com/mrbysco/armorposer/config/PoserModMenuIntegration.java rename to fabric/src/main/java/com/mrbysco/armorposer/config/PoserModMenuIntegration.java diff --git a/Fabric/src/main/java/com/mrbysco/armorposer/handlers/EventHandler.java b/fabric/src/main/java/com/mrbysco/armorposer/handlers/EventHandler.java similarity index 100% rename from Fabric/src/main/java/com/mrbysco/armorposer/handlers/EventHandler.java rename to fabric/src/main/java/com/mrbysco/armorposer/handlers/EventHandler.java diff --git a/Fabric/src/main/java/com/mrbysco/armorposer/mixin/ArmorStandMixin.java b/fabric/src/main/java/com/mrbysco/armorposer/mixin/ArmorStandMixin.java similarity index 100% rename from Fabric/src/main/java/com/mrbysco/armorposer/mixin/ArmorStandMixin.java rename to fabric/src/main/java/com/mrbysco/armorposer/mixin/ArmorStandMixin.java diff --git a/fabric/src/main/java/com/mrbysco/armorposer/platform/FabricPlatformHelper.java b/fabric/src/main/java/com/mrbysco/armorposer/platform/FabricPlatformHelper.java new file mode 100644 index 0000000..da4995a --- /dev/null +++ b/fabric/src/main/java/com/mrbysco/armorposer/platform/FabricPlatformHelper.java @@ -0,0 +1,49 @@ +package com.mrbysco.armorposer.platform; + +import com.mrbysco.armorposer.Reference; +import com.mrbysco.armorposer.config.PoserConfig; +import com.mrbysco.armorposer.data.SwapData; +import com.mrbysco.armorposer.data.SyncData; +import com.mrbysco.armorposer.platform.services.IPlatformHelper; +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) { + CompoundTag CompoundNBT = armorStand.saveWithoutId(new CompoundTag()).copy(); + CompoundNBT.merge(compound); + armorStand.load(CompoundNBT); + + FriendlyByteBuf buf = PacketByteBufs.create(); + SyncData data = new SyncData(armorStand.getUUID(), compound); + data.encode(buf); + ClientPlayNetworking.send(Reference.SYNC_PACKET_ID, buf); + } + + @Override + public void swapSlots(ArmorStand armorStand, SwapData.Action action) { + FriendlyByteBuf buf = PacketByteBufs.create(); + SwapData data = new SwapData(armorStand.getUUID(), action); + data.encode(buf); + ClientPlayNetworking.send(Reference.SWAP_PACKET_ID, buf); + } + + @Override + 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/Fabric/src/main/resources/META-INF/services/com.mrbysco.armorposer.platform.services.IPlatformHelper b/fabric/src/main/resources/META-INF/services/com.mrbysco.armorposer.platform.services.IPlatformHelper similarity index 100% rename from Fabric/src/main/resources/META-INF/services/com.mrbysco.armorposer.platform.services.IPlatformHelper rename to fabric/src/main/resources/META-INF/services/com.mrbysco.armorposer.platform.services.IPlatformHelper diff --git a/fabric/src/main/resources/armorposer.fabric.mixins.json b/fabric/src/main/resources/armorposer.fabric.mixins.json new file mode 100644 index 0000000..fe1fda1 --- /dev/null +++ b/fabric/src/main/resources/armorposer.fabric.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "minVersion": "0.8.4", + "package": "com.mrbysco.armorposer.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "ArmorStandMixin" + ], + "client": [ + "MinecraftMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} + \ No newline at end of file diff --git a/Fabric/src/main/resources/assets/armorposer/icon.png b/fabric/src/main/resources/assets/armorposer/icon.png similarity index 100% rename from Fabric/src/main/resources/assets/armorposer/icon.png rename to fabric/src/main/resources/assets/armorposer/icon.png diff --git a/Fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json similarity index 95% rename from Fabric/src/main/resources/fabric.mod.json rename to fabric/src/main/resources/fabric.mod.json index 140c62c..76c0f46 100644 --- a/Fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -35,9 +35,6 @@ "minecraft": "1.18.x", "java": ">=17", "cloth-config": ">=6.0.42" - }, - "suggests": { - "another-mod": "*" } } \ No newline at end of file diff --git a/Forge/build.gradle b/forge/build.gradle similarity index 100% rename from Forge/build.gradle rename to forge/build.gradle diff --git a/Forge/src/main/java/com/mrbysco/armorposer/ArmorPoser.java b/forge/src/main/java/com/mrbysco/armorposer/ArmorPoser.java similarity index 85% rename from Forge/src/main/java/com/mrbysco/armorposer/ArmorPoser.java rename to forge/src/main/java/com/mrbysco/armorposer/ArmorPoser.java index dc35b47..6f582d0 100644 --- a/Forge/src/main/java/com/mrbysco/armorposer/ArmorPoser.java +++ b/forge/src/main/java/com/mrbysco/armorposer/ArmorPoser.java @@ -3,6 +3,7 @@ import com.mrbysco.armorposer.config.PoserConfig; import com.mrbysco.armorposer.packets.ArmorStandScreenMessage; +import com.mrbysco.armorposer.packets.ArmorStandSwapMessage; import com.mrbysco.armorposer.packets.ArmorStandSyncMessage; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.eventbus.api.IEventBus; @@ -31,13 +32,12 @@ public ArmorPoser() { ModLoadingContext.get().registerConfig(Type.COMMON, PoserConfig.commonSpec); eventBus.register(PoserConfig.class); - CommonClass.init(); - eventBus.addListener(this::setup); } private void setup(final FMLCommonSetupEvent event) { CHANNEL.registerMessage(0, ArmorStandSyncMessage.class, ArmorStandSyncMessage::encode, ArmorStandSyncMessage::decode, ArmorStandSyncMessage::handle); - CHANNEL.registerMessage(1, ArmorStandScreenMessage.class, ArmorStandScreenMessage::encode, ArmorStandScreenMessage::decode, ArmorStandScreenMessage::handle); + CHANNEL.registerMessage(1, ArmorStandSwapMessage.class, ArmorStandSwapMessage::encode, ArmorStandSwapMessage::decode, ArmorStandSwapMessage::handle); + CHANNEL.registerMessage(2, ArmorStandScreenMessage.class, ArmorStandScreenMessage::encode, ArmorStandScreenMessage::decode, ArmorStandScreenMessage::handle); } } \ No newline at end of file diff --git a/Forge/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java b/forge/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java similarity index 86% rename from Forge/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java rename to forge/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java index 181c310..6215b7f 100644 --- a/Forge/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java +++ b/forge/src/main/java/com/mrbysco/armorposer/config/PoserConfig.java @@ -13,6 +13,7 @@ public class PoserConfig { public static class Common { public final BooleanValue enableConfigGui; public final BooleanValue enableNameTags; + public final BooleanValue allowScrolling; Common(ForgeConfigSpec.Builder builder) { builder.comment("General settings") @@ -28,6 +29,11 @@ public static class Common { .translation("armorposer.config.enableNameTags.tooltip") .define("enableNameTags", true); + allowScrolling = builder + .comment("Allow scrolling to increase / decrease an angle value in the posing screen") + .translation("armorposer.config.allowScrolling.tooltip") + .define("allowScrolling", true); + builder.pop(); } diff --git a/Forge/src/main/java/com/mrbysco/armorposer/handlers/EventHandler.java b/forge/src/main/java/com/mrbysco/armorposer/handlers/EventHandler.java similarity index 100% rename from Forge/src/main/java/com/mrbysco/armorposer/handlers/EventHandler.java rename to forge/src/main/java/com/mrbysco/armorposer/handlers/EventHandler.java diff --git a/Forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandScreenMessage.java b/forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandScreenMessage.java similarity index 100% rename from Forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandScreenMessage.java rename to forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandScreenMessage.java diff --git a/forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandSwapMessage.java b/forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandSwapMessage.java new file mode 100644 index 0000000..9e6dfd8 --- /dev/null +++ b/forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandSwapMessage.java @@ -0,0 +1,41 @@ +package com.mrbysco.armorposer.packets; + +import com.mrbysco.armorposer.data.SwapData; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraftforge.network.NetworkEvent.Context; + +import java.util.function.Supplier; + + +public class ArmorStandSwapMessage { + private final SwapData data; + + public ArmorStandSwapMessage(SwapData syncData) { + this.data = syncData; + } + + public void encode(FriendlyByteBuf buf) { + data.encode(buf); + } + + public static ArmorStandSwapMessage decode(final FriendlyByteBuf packetBuffer) { + return new ArmorStandSwapMessage(SwapData.decode(packetBuffer)); + } + + public void handle(Supplier context) { + Context ctx = context.get(); + ctx.enqueueWork(() -> { + if (ctx.getDirection().getReceptionSide().isServer() && ctx.getSender() != null) { + final ServerLevel serverLevel = ctx.getSender().getLevel(); + Entity entity = serverLevel.getEntity(data.entityUUID()); + if (entity instanceof ArmorStand armorStandEntity) { + data.handleData(armorStandEntity); + } + } + }); + ctx.setPacketHandled(true); + } +} diff --git a/forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandSyncMessage.java b/forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandSyncMessage.java new file mode 100644 index 0000000..5a63df8 --- /dev/null +++ b/forge/src/main/java/com/mrbysco/armorposer/packets/ArmorStandSyncMessage.java @@ -0,0 +1,41 @@ +package com.mrbysco.armorposer.packets; + +import com.mrbysco.armorposer.data.SyncData; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraftforge.network.NetworkEvent.Context; + +import java.util.function.Supplier; + + +public class ArmorStandSyncMessage { + private final SyncData data; + + public ArmorStandSyncMessage(SyncData syncData) { + this.data = syncData; + } + + public void encode(FriendlyByteBuf buf) { + data.encode(buf); + } + + public static ArmorStandSyncMessage decode(final FriendlyByteBuf packetBuffer) { + return new ArmorStandSyncMessage(SyncData.decode(packetBuffer)); + } + + public void handle(Supplier context) { + Context ctx = context.get(); + ctx.enqueueWork(() -> { + if (ctx.getDirection().getReceptionSide().isServer() && ctx.getSender() != null) { + final ServerLevel serverLevel = ctx.getSender().getLevel(); + Entity entity = serverLevel.getEntity(data.entityUUID()); + if (entity instanceof ArmorStand armorStandEntity) { + data.handleData(armorStandEntity); + } + } + }); + ctx.setPacketHandled(true); + } +} diff --git a/forge/src/main/java/com/mrbysco/armorposer/platform/ForgePlatformHelper.java b/forge/src/main/java/com/mrbysco/armorposer/platform/ForgePlatformHelper.java new file mode 100644 index 0000000..fa1f503 --- /dev/null +++ b/forge/src/main/java/com/mrbysco/armorposer/platform/ForgePlatformHelper.java @@ -0,0 +1,41 @@ +package com.mrbysco.armorposer.platform; + +import com.mrbysco.armorposer.ArmorPoser; +import com.mrbysco.armorposer.config.PoserConfig; +import com.mrbysco.armorposer.data.SwapData; +import com.mrbysco.armorposer.data.SyncData; +import com.mrbysco.armorposer.packets.ArmorStandSwapMessage; +import com.mrbysco.armorposer.packets.ArmorStandSyncMessage; +import com.mrbysco.armorposer.platform.services.IPlatformHelper; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraftforge.fml.loading.FMLPaths; +import net.minecraftforge.network.PacketDistributor; + +import java.nio.file.Path; + +public class ForgePlatformHelper implements IPlatformHelper { + @Override + public void updateEntity(ArmorStand armorStand, CompoundTag compound) { + CompoundTag CompoundNBT = armorStand.saveWithoutId(new CompoundTag()).copy(); + CompoundNBT.merge(compound); + armorStand.load(CompoundNBT); + + ArmorPoser.CHANNEL.send(PacketDistributor.SERVER.noArg(), new ArmorStandSyncMessage(new SyncData(armorStand.getUUID(), compound))); + } + + @Override + public void swapSlots(ArmorStand armorStand, SwapData.Action action) { + ArmorPoser.CHANNEL.send(PacketDistributor.SERVER.noArg(), new ArmorStandSwapMessage(new SwapData(armorStand.getUUID(), action))); + } + + @Override + public boolean allowScrolling() { + return PoserConfig.COMMON.allowScrolling.get(); + } + + @Override + public Path getUserPresetFolder() { + return FMLPaths.CONFIGDIR.get(); + } +} diff --git a/Forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml similarity index 100% rename from Forge/src/main/resources/META-INF/mods.toml rename to forge/src/main/resources/META-INF/mods.toml diff --git a/Forge/src/main/resources/META-INF/services/com.mrbysco.armorposer.platform.services.IPlatformHelper b/forge/src/main/resources/META-INF/services/com.mrbysco.armorposer.platform.services.IPlatformHelper similarity index 100% rename from Forge/src/main/resources/META-INF/services/com.mrbysco.armorposer.platform.services.IPlatformHelper rename to forge/src/main/resources/META-INF/services/com.mrbysco.armorposer.platform.services.IPlatformHelper diff --git a/forge/src/main/resources/armorposer.forge.mixins.json b/forge/src/main/resources/armorposer.forge.mixins.json new file mode 100644 index 0000000..e8a2016 --- /dev/null +++ b/forge/src/main/resources/armorposer.forge.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "minVersion": "0.8.4", + "package": "com.mrbysco.armorposer.mixin", + "refmap": "${mod_id}.refmap.json", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + "MinecraftMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index d87d61a..5a04d29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,8 +14,8 @@ forge_version=40.2.1 //forge_ats_enabled=true # Fabric -fabric_version=0.76.0+1.18.2 -fabric_loader_version=0.14.19 +fabric_version=0.77.0+1.18.2 +fabric_loader_version=0.16.5 cloth_config_version=6.2.62 mod_menu_version=3.2.1