Skip to content

Commit 172713b

Browse files
committed
Increment version [build] [publish]
1 parent d6e800a commit 172713b

File tree

11 files changed

+349
-3
lines changed

11 files changed

+349
-3
lines changed

changelog.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
* Fix the lock button not immediately sending the update packet when pressed
1+
* Add a button that shows a list of nearby armor stands, allowing you select armor stands that are inside walls / hidden
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.mrbysco.armorposer.client;
2+
3+
import java.util.UUID;
4+
5+
public class GlowHandler {
6+
private static long glowStartTime = 0;
7+
private static UUID glowingStand = null;
8+
9+
public static boolean shouldArmorStandGlow() {
10+
if (glowStartTime == -1) {
11+
return false;
12+
}
13+
boolean notEmpty = glowingStand != null;
14+
if (notEmpty && System.currentTimeMillis() - glowStartTime > 5000) {
15+
glowStartTime = -1;
16+
glowingStand = null;
17+
}
18+
return notEmpty;
19+
}
20+
21+
public static boolean isGlowing(UUID uuid) {
22+
if (!shouldArmorStandGlow())
23+
return false;
24+
else
25+
return glowingStand != null && glowingStand.equals(uuid);
26+
}
27+
28+
public static void startGlowing(UUID uuid) {
29+
glowStartTime = System.currentTimeMillis();
30+
glowingStand = uuid;
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package com.mrbysco.armorposer.client.gui;
2+
3+
import com.mrbysco.armorposer.client.GlowHandler;
4+
import com.mrbysco.armorposer.client.gui.widgets.ArmorGlowWidget;
5+
import net.minecraft.client.Minecraft;
6+
import net.minecraft.client.gui.Font;
7+
import net.minecraft.client.gui.GuiGraphics;
8+
import net.minecraft.client.gui.components.Button;
9+
import net.minecraft.client.gui.components.ObjectSelectionList;
10+
import net.minecraft.client.gui.screens.Screen;
11+
import net.minecraft.commands.arguments.EntityAnchorArgument;
12+
import net.minecraft.network.chat.Component;
13+
import net.minecraft.world.entity.EntitySelector;
14+
import net.minecraft.world.entity.decoration.ArmorStand;
15+
16+
import java.util.Collections;
17+
import java.util.List;
18+
import java.util.function.Consumer;
19+
import java.util.function.Function;
20+
import java.util.stream.Collectors;
21+
22+
public class ArmorGlowScreen extends Screen {
23+
private static final int PADDING = 6;
24+
25+
private ArmorGlowWidget armorListWidget;
26+
private ArmorGlowWidget.ListEntry selected = null;
27+
private List<ArmorStand> armorStands;
28+
private Button locateButton;
29+
private Button modifyButton;
30+
31+
public ArmorStandScreen parentScreen;
32+
33+
public ArmorGlowScreen(ArmorStandScreen parent) {
34+
super(Component.translatable("armorposer.gui.armor_list.list"));
35+
this.parentScreen = parent;
36+
37+
this.minecraft = Minecraft.getInstance();
38+
39+
//Add the armor stands to the list
40+
if (minecraft.player == null)
41+
this.onClose();
42+
43+
List<ArmorStand> armorStands = minecraft.level.getEntitiesOfClass(ArmorStand.class,
44+
minecraft.player.getBoundingBox().inflate(30.0D), EntitySelector.LIVING_ENTITY_STILL_ALIVE).stream().collect(Collectors.toList());
45+
//Sort the list based on how far the armor stand is from the player
46+
armorStands.sort((armorStand, armorStand2) -> {
47+
double distance1 = armorStand.distanceToSqr(minecraft.player);
48+
double distance2 = armorStand2.distanceToSqr(minecraft.player);
49+
return Double.compare(distance1, distance2);
50+
});
51+
this.armorStands = Collections.unmodifiableList(armorStands);
52+
}
53+
54+
@Override
55+
public boolean isPauseScreen() {
56+
return false;
57+
}
58+
59+
@Override
60+
protected void init() {
61+
int centerWidth = this.width / 2;
62+
int listWidth = this.width / 4 + 20;
63+
int structureWidth = this.width - listWidth - (PADDING * 3);
64+
int closeButtonWidth = Math.min(structureWidth, 160);
65+
int y = this.height - 20 - PADDING;
66+
this.addRenderableWidget(Button.builder(Component.translatable("gui.cancel"), b -> ArmorGlowScreen.this.onClose())
67+
.bounds(centerWidth - (closeButtonWidth / 2) + PADDING, y, closeButtonWidth, 20).build());
68+
69+
y -= 18 + PADDING;
70+
int buttonWidth = (closeButtonWidth / 2) - 1;
71+
this.addRenderableWidget(this.locateButton = Button.builder(Component.translatable("armorposer.gui.armor_list.locate"), b -> {
72+
if (selected != null && minecraft.player != null) {
73+
GlowHandler.startGlowing(this.selected.getArmorStand().getUUID());
74+
minecraft.player.lookAt(EntityAnchorArgument.Anchor.EYES, selected.getArmorStand().position());
75+
}
76+
}).bounds(centerWidth - (closeButtonWidth / 2) + PADDING, y, buttonWidth, 20).build());
77+
this.addRenderableWidget(this.modifyButton = Button.builder(Component.translatable("armorposer.gui.armor_list.modify"), b -> {
78+
if (selected != null && minecraft.player != null) {
79+
minecraft.setScreen(new ArmorStandScreen(selected.getArmorStand()));
80+
}
81+
}).bounds(centerWidth - (closeButtonWidth / 2) + PADDING + buttonWidth + 2, y, buttonWidth, 20).build());
82+
83+
int fullButtonHeight = PADDING + 20 + PADDING;
84+
this.armorListWidget = new ArmorGlowWidget(this, Component.translatable("armorposer.gui.armor_list.list"), listWidth, fullButtonHeight, 14 - getScreenFont().lineHeight);
85+
this.armorListWidget.setX(0);
86+
this.armorListWidget.setY(10);
87+
this.armorListWidget.setHeight(this.height);
88+
89+
addWidget(armorListWidget);
90+
91+
updateCache();
92+
}
93+
94+
@Override
95+
public void tick() {
96+
armorListWidget.setSelected(selected);
97+
}
98+
99+
public <T extends ObjectSelectionList.Entry<T>> void buildPositionList(Consumer<T> ListViewConsumer, Function<ArmorStand, T> newEntry) {
100+
armorStands.forEach(stand -> ListViewConsumer.accept(newEntry.apply(stand)));
101+
}
102+
103+
@Override
104+
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
105+
this.armorListWidget.render(guiGraphics, mouseX, mouseY, partialTicks);
106+
super.render(guiGraphics, mouseX, mouseY, partialTicks);
107+
}
108+
109+
@Override
110+
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
111+
return super.keyPressed(keyCode, scanCode, modifiers);
112+
}
113+
114+
@Override
115+
public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
116+
//Nope
117+
}
118+
119+
public void setSelected(ArmorGlowWidget.ListEntry entry) {
120+
this.selected = entry == this.selected ? null : entry;
121+
updateCache();
122+
}
123+
124+
private void updateCache() {
125+
this.locateButton.active = selected != null;
126+
this.modifyButton.active = selected != null;
127+
}
128+
129+
/**
130+
* Clear the search field when right-clicked on it
131+
*/
132+
@Override
133+
public boolean mouseClicked(double mouseX, double mouseY, int button) {
134+
return super.mouseClicked(mouseX, mouseY, button);
135+
}
136+
137+
@Override
138+
public void resize(Minecraft mc, int width, int height) {
139+
ArmorGlowWidget.ListEntry selected = this.selected;
140+
this.init(mc, width, height);
141+
this.selected = selected;
142+
updateCache();
143+
}
144+
145+
@Override
146+
public void onClose() {
147+
this.minecraft.setScreen(parentScreen);
148+
}
149+
150+
public Minecraft getScreenMinecraft() {
151+
return this.minecraft;
152+
}
153+
154+
public Font getScreenFont() {
155+
return this.font;
156+
}
157+
}

common/src/main/java/com/mrbysco/armorposer/client/gui/ArmorStandScreen.java

+5
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import com.mojang.brigadier.exceptions.CommandSyntaxException;
55
import com.mojang.math.Axis;
66
import com.mrbysco.armorposer.Reference;
7+
import com.mrbysco.armorposer.client.GlowHandler;
78
import com.mrbysco.armorposer.client.gui.widgets.NumberFieldBox;
89
import com.mrbysco.armorposer.client.gui.widgets.ToggleButton;
910
import com.mrbysco.armorposer.data.SwapData;
1011
import com.mrbysco.armorposer.platform.Services;
1112
import com.mrbysco.armorposer.util.ArmorStandData;
1213
import net.minecraft.ChatFormatting;
14+
import net.minecraft.Util;
1315
import net.minecraft.client.Minecraft;
1416
import net.minecraft.client.gui.GuiGraphics;
1517
import net.minecraft.client.gui.components.Button;
@@ -516,6 +518,9 @@ public void init() {
516518
this.updateEntity(this.armorStandData.writeToNBT());
517519
this.minecraft.setScreen((Screen) null);
518520
}).bounds(offsetX - 95, offsetY + 22, 97, 20).build());
521+
this.addRenderableWidget(Button.builder(Component.literal("💡"), (button) -> {
522+
this.minecraft.setScreen(new ArmorGlowScreen(this));
523+
}).bounds(0, 0, 16, 16).build());
519524
}
520525

521526
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package com.mrbysco.armorposer.client.gui.widgets;
2+
3+
import com.mrbysco.armorposer.client.gui.ArmorGlowScreen;
4+
import net.minecraft.client.gui.Font;
5+
import net.minecraft.client.gui.GuiGraphics;
6+
import net.minecraft.client.gui.components.ObjectSelectionList;
7+
import net.minecraft.client.gui.screens.inventory.InventoryScreen;
8+
import net.minecraft.network.chat.Component;
9+
import net.minecraft.world.entity.decoration.ArmorStand;
10+
import org.joml.Quaternionf;
11+
import org.joml.Vector3f;
12+
13+
public class ArmorGlowWidget extends ObjectSelectionList<ArmorGlowWidget.ListEntry> {
14+
private static final Vector3f ARMOR_STAND_TRANSLATION = new Vector3f();
15+
private static final Quaternionf ARMOR_STAND_ANGLE = new Quaternionf().rotationXYZ(0.43633232F, 0.0F, (float) Math.PI);
16+
17+
private final ArmorGlowScreen parent;
18+
private final int listWidth;
19+
private final Component title;
20+
21+
public ArmorGlowWidget(ArmorGlowScreen parent, Component title, int listWidth, int top, int bottom) {
22+
super(parent.getScreenMinecraft(), listWidth, bottom - top, top, parent.getScreenFont().lineHeight * 2 + 16);
23+
this.parent = parent;
24+
this.title = title;
25+
this.listWidth = listWidth;
26+
this.refreshList();
27+
this.setRenderBackground(false);
28+
}
29+
30+
@Override
31+
protected int getScrollbarPosition() {
32+
return this.getX() + this.listWidth - 6;
33+
}
34+
35+
@Override
36+
public int getRowWidth() {
37+
return this.listWidth;
38+
}
39+
40+
public void refreshList() {
41+
this.clearEntries();
42+
parent.buildPositionList(this::addEntry, location -> new ListEntry(location, this.parent));
43+
}
44+
45+
@Override
46+
protected void renderSelection(GuiGraphics guiGraphics, int top, int width, int height, int outerColor, int innerColor) {
47+
int xPos = this.getX() + (this.width - width) / 2;
48+
int xPos2 = this.getX() + (this.width + width) / 2;
49+
guiGraphics.fillGradient(xPos, top - 2, xPos2, top + height + 2, -1945083888, -1676648432);
50+
}
51+
52+
@Override
53+
public void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
54+
guiGraphics.fillGradient(getX(), 0, getX() + this.listWidth, parent.height, -1945104368, -1676668912);
55+
super.renderWidget(guiGraphics, mouseX, mouseY, partialTicks);
56+
guiGraphics.drawCenteredString(this.parent.getScreenFont(), title, getX() + this.listWidth / 2, 2, 16777215);
57+
}
58+
59+
public class ListEntry extends Entry<ListEntry> {
60+
private final ArmorGlowScreen parent;
61+
private ArmorStand armorStand;
62+
63+
ListEntry(ArmorStand armorStand, ArmorGlowScreen parent) {
64+
this.armorStand = armorStand;
65+
this.parent = parent;
66+
}
67+
68+
@Override
69+
public void render(GuiGraphics guiGraphics, int entryIdx, int top, int left, int entryWidth, int entryHeight,
70+
int mouseX, int mouseY, boolean hovered, float partialTicks) {
71+
Font font = this.parent.getScreenFont();
72+
renderScrollingString(guiGraphics, font, getPositionComponent(), left + 36, top + 10, left + width - 18, top + 20, 0xFFFFFF);
73+
74+
renderPose(guiGraphics, left + 16, top + 28, 15);
75+
}
76+
77+
public ArmorStand getArmorStand() {
78+
return armorStand;
79+
}
80+
81+
public void renderPose(GuiGraphics guiGraphics, int xPos, int yPos, int size) {
82+
if (armorStand != null) {
83+
InventoryScreen.renderEntityInInventory(guiGraphics, xPos, yPos, size,
84+
ARMOR_STAND_TRANSLATION, ARMOR_STAND_ANGLE, (Quaternionf) null, this.armorStand);
85+
}
86+
}
87+
88+
@Override
89+
public void renderBack(GuiGraphics guiGraphics, int mouseX, int mouseY, int $$3, int $$4, int $$5, int $$6, int $$7, boolean $$8, float $$9) {
90+
super.renderBack(guiGraphics, mouseX, mouseY, $$3, $$4, $$5, $$6, $$7, $$8, $$9);
91+
}
92+
93+
@Override
94+
public boolean mouseClicked(double mouseX, double mouseY, int button) {
95+
parent.setSelected(this);
96+
ArmorGlowWidget.this.setSelected(this);
97+
return false;
98+
}
99+
100+
public Component getPositionComponent() {
101+
return Component.literal(getArmorStand().blockPosition().toShortString());
102+
}
103+
104+
@Override
105+
public Component getNarration() {
106+
return getPositionComponent();
107+
}
108+
}
109+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.mrbysco.armorposer.mixin;
2+
3+
import com.mrbysco.armorposer.client.GlowHandler;
4+
import net.minecraft.client.Minecraft;
5+
import net.minecraft.world.entity.Entity;
6+
import net.minecraft.world.entity.decoration.ArmorStand;
7+
import org.spongepowered.asm.mixin.Mixin;
8+
import org.spongepowered.asm.mixin.injection.At;
9+
import org.spongepowered.asm.mixin.injection.Inject;
10+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
11+
12+
@Mixin(Minecraft.class)
13+
public class MinecraftMixin {
14+
15+
@Inject(method = "shouldEntityAppearGlowing(Lnet/minecraft/world/entity/Entity;)Z",
16+
at = @At("HEAD"), cancellable = true)
17+
public void armorposer$shouldEntityAppearGlowing(Entity entity, CallbackInfoReturnable<Boolean> cir) {
18+
if (entity instanceof ArmorStand && GlowHandler.isGlowing(entity.getUUID()))
19+
cir.setReturnValue(true);
20+
}
21+
}

common/src/main/resources/assets/armorposer/lang/en_us.json

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@
9494
"armorposer.gui.delete_poose.title": "Delete pose",
9595
"armorposer.gui.delete_poose.message": "Are you sure you want to remove this pose?",
9696

97+
"armorposer.gui.armor_list.list": "Armor Stands",
98+
"armorposer.gui.armor_list.locate": "Locate",
99+
"armorposer.gui.armor_list.modify": "Modify",
100+
97101
"armorposer.config.enableConfigGui": "Enable GUI",
98102
"armorposer.config.enableConfigGui.tooltip": "Show the Armor Stand configuration GUI on shift right click",
99103

fabric/src/main/resources/armorposer.fabric.mixins.json

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"ArmorStandMixin"
99
],
1010
"client": [
11+
"MinecraftMixin"
1112
],
1213
"injectors": {
1314
"defaultRequire": 1

forge/src/main/resources/META-INF/mods.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ modId="neoforge"
2525
type="REQUIRED"
2626
versionRange="[20.4.70-beta,)"
2727
ordering="NONE"
28-
side="BOTH"
28+
side="BOTH"
29+
[[mixins]]
30+
config="armorposer.neoforge.mixins.json"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"required": true,
3+
"minVersion": "0.8.4",
4+
"package": "com.mrbysco.armorposer.mixin",
5+
"compatibilityLevel": "JAVA_17",
6+
"mixins": [
7+
],
8+
"client": [
9+
"MinecraftMixin"
10+
],
11+
"injectors": {
12+
"defaultRequire": 1
13+
}
14+
}
15+

0 commit comments

Comments
 (0)