XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

MyDimension

【Java】我的维度模组

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/MyDimension

新增团队心境访问功能

f39926c
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

16 个文件 +1659 -108
Modified gradle.properties +1 -1
@@ -11,7 +11,7 @@ loader_version_range=[47,)
11 11 mod_id=mydimension
12 12 mod_name=\u6211\u7684\u7ef4\u5ea6
13 13 mod_license=CC0-1.0
14 mod_version=2.2.3
14 mod_version=2.3.0
15 15 mod_group_id=com.xfestudio.mydimension
16 16 maven_group=com.xfestudio
17 17 archives_base_name=mydimension
Added src/main/java/com/xfestudio/mydimension/client/MindAccessRequestScreen.java +87 -0
@@ -0,0 +1,87 @@
1 package com.xfestudio.mydimension.client;
2
3 import com.xfestudio.mydimension.network.ModNetwork;
4 import com.xfestudio.mydimension.network.TeamMindCommandPacket;
5 import net.minecraft.Util;
6 import net.minecraft.client.gui.GuiGraphics;
7 import net.minecraft.client.gui.components.Button;
8 import net.minecraft.client.gui.screens.Screen;
9 import net.minecraft.network.chat.Component;
10 import net.minecraft.util.Mth;
11
12 import java.util.UUID;
13
14 public class MindAccessRequestScreen extends Screen {
15 private final Screen parent;
16 private final UUID requesterId;
17 private final String requesterName;
18 private final long openedAt = Util.getMillis();
19 private boolean responded;
20
21 public MindAccessRequestScreen(Screen parent, UUID requesterId, String requesterName) {
22 super(Component.translatable("screen.mydimension.team.request_title"));
23 this.parent = parent;
24 this.requesterId = requesterId;
25 this.requesterName = requesterName;
26 }
27
28 @Override
29 protected void init() {
30 int centerX = width / 2;
31 int centerY = height / 2;
32 addRenderableWidget(Button.builder(Component.translatable("screen.mydimension.team.accept"), button -> respond(true))
33 .bounds(centerX - 106, centerY + 24, 98, 22).build());
34 addRenderableWidget(Button.builder(Component.translatable("screen.mydimension.team.deny"), button -> respond(false))
35 .bounds(centerX + 8, centerY + 24, 98, 22).build());
36 }
37
38 @Override
39 public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
40 graphics.fillGradient(0, 0, width, height, 0xD0101218, 0xE0181026);
41 float progress = easeOut(Mth.clamp((Util.getMillis() - openedAt) / 220.0F, 0.0F, 1.0F));
42 int centerX = width / 2;
43 int centerY = height / 2;
44 int offset = Math.round((1.0F - progress) * 8.0F);
45 graphics.fill(centerX - 130, centerY - 45 + offset, centerX + 130, centerY + 56 + offset,
46 multiplyAlpha(0xE0060A12, progress));
47 graphics.renderOutline(centerX - 130, centerY - 45 + offset, 260, 101, multiplyAlpha(0xFF85D6F7, progress));
48 graphics.drawCenteredString(font, title, centerX, centerY - 31 + offset, multiplyAlpha(0xFFEAFBFF, progress));
49 Component prompt = Component.translatable("screen.mydimension.team.request_prompt", requesterName);
50 String fittedPrompt = font.plainSubstrByWidth(prompt.getString(), 236);
51 graphics.drawCenteredString(font, fittedPrompt, centerX, centerY - 7 + offset, multiplyAlpha(0xFFC5E7F3, progress));
52 super.render(graphics, mouseX, mouseY, partialTick);
53 }
54
55 @Override
56 public void onClose() {
57 if (!responded) {
58 respond(false);
59 }
60 }
61
62 @Override
63 public boolean isPauseScreen() {
64 return false;
65 }
66
67 private void respond(boolean accepted) {
68 if (responded) {
69 return;
70 }
71 responded = true;
72 ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.respond(requesterId, accepted));
73 if (minecraft != null) {
74 minecraft.setScreen(parent);
75 }
76 }
77
78 private static float easeOut(float value) {
79 float inverse = 1.0F - value;
80 return 1.0F - inverse * inverse * inverse;
81 }
82
83 private static int multiplyAlpha(int color, float multiplier) {
84 int alpha = Math.round(((color >>> 24) & 0xFF) * Mth.clamp(multiplier, 0.0F, 1.0F));
85 return (alpha << 24) | (color & 0x00FFFFFF);
86 }
87 }
Modified src/main/java/com/xfestudio/mydimension/client/RiftActionScreen.java +355 -100
@@ -1,5 +1,6 @@
1 1 package com.xfestudio.mydimension.client;
2 2
3 import com.mojang.blaze3d.systems.RenderSystem;
3 4 import com.xfestudio.mydimension.MyDimension;
4 5 import com.xfestudio.mydimension.item.RiftAction;
5 6 import com.xfestudio.mydimension.item.RiftItem;
@@ -8,6 +9,7 @@ import com.xfestudio.mydimension.network.SetRiftActionPacket;
8 9 import com.xfestudio.mydimension.registry.ModItems;
9 10 import com.xfestudio.mydimension.world.ModDimensions;
10 11 import com.xfestudio.mydimension.world.PrivateMindFeature;
12 import net.minecraft.Util;
11 13 import net.minecraft.client.Minecraft;
12 14 import net.minecraft.client.gui.Font;
13 15 import net.minecraft.client.gui.GuiGraphics;
@@ -20,18 +22,21 @@ import net.minecraft.world.item.ItemStack;
20 22
21 23 import java.util.ArrayList;
22 24 import java.util.List;
25 import java.util.function.BooleanSupplier;
23 26
24 27 public class RiftActionScreen extends Screen {
25 private static final int BUTTON_HEIGHT = 26;
26 private static final int BUTTON_GAP = 18;
28 private static final int GRID_GAP = 10;
27 29 private static final int TRAVEL_COLOR = 0xFF3E8DB8;
28 30 private static final int MOB_COLOR = 0xFF8B58C8;
29 31 private static final int ANCHOR_COLOR = 0xFFD6A23E;
30 32 private static final int COPY_COLOR = 0xFF4EBA7A;
33 private static final int TEAM_COLOR = 0xFF4FB5A5;
31 34 private static final ResourceLocation RIFT_ICON = new ResourceLocation(MyDimension.MOD_ID, "textures/item/rift.png");
32 35 private static final ResourceLocation PORTAL_ICON = new ResourceLocation("minecraft", "textures/block/nether_portal.png");
33 36 private static final ResourceLocation ANCHOR_ICON = new ResourceLocation(MyDimension.MOD_ID, "textures/gui/rift_anchor_icon.png");
34 37
38 private final long openedAt;
39 private long contentAnimationAt;
35 40 private Page page = Page.TRAVEL_PRIVATE;
36 41 private boolean inMindDimension;
37 42 private boolean creative;
@@ -39,6 +44,8 @@ public class RiftActionScreen extends Screen {
39 44
40 45 public RiftActionScreen() {
41 46 super(Component.translatable("screen.mydimension.rift_actions"));
47 openedAt = Util.getMillis();
48 contentAnimationAt = openedAt;
42 49 }
43 50
44 51 @Override
@@ -46,20 +53,24 @@ public class RiftActionScreen extends Screen {
46 53 inMindDimension = minecraft != null && minecraft.player != null && ModDimensions.isMindDimension(minecraft.player.level().dimension());
47 54 creative = minecraft != null && minecraft.player != null && minecraft.player.isCreative();
48 55 privateMindsEnabled = PrivateMindFeature.isEnabled();
56 normalizePage();
49 57
50 int centerX = width / 2;
51 int centerY = height / 2;
52 addTabs(centerX, centerY - 108);
53 addActionGrid(currentActions(), centerY - 36);
58 Layout layout = createLayout();
59 addModeTabs(layout);
60 if (showsScopeSelector()) {
61 addScopeTabs(layout);
62 }
63 addActionGrid(currentActions(), layout);
54 64 if (inMindDimension) {
55 addCenteredActionButton(RiftAction.SET_ANCHOR, centerY + 88);
65 addAnchorButton(layout);
56 66 }
57 67 }
58 68
59 69 @Override
60 70 public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
61 71 graphics.fillGradient(0, 0, width, height, 0xD0101218, 0xE0181026);
62 renderCenterPanel(graphics);
72 renderMotes(graphics);
73 renderCenterPanel(graphics, createLayout());
63 74 super.render(graphics, mouseX, mouseY, partialTick);
64 75 }
65 76
@@ -68,31 +79,161 @@ public class RiftActionScreen extends Screen {
68 79 return false;
69 80 }
70 81
71 private void addTabs(int centerX, int y) {
72 List<Page> pages = visiblePages();
73 int tabWidth = Math.max(68, Math.min(94, (width - 48 - (pages.size() - 1) * 8) / pages.size()));
74 int totalWidth = pages.size() * tabWidth + (pages.size() - 1) * 8;
75 int x = centerX - totalWidth / 2;
76 for (Page tab : pages) {
77 addRenderableWidget(new PageButton(x, y, tabWidth, tab));
78 x += tabWidth + 8;
82 private Layout createLayout() {
83 int desiredHeight = inMindDimension ? 220 : 190;
84 int panelHeight = Math.min(desiredHeight, Math.max(156, height - 12));
85 int panelWidth = Math.min(470, Math.max(280, width - 24));
86 int left = (width - panelWidth) / 2;
87 int top = (height - panelHeight) / 2;
88 return new Layout(left, top, left + panelWidth, top + panelHeight, panelWidth, panelHeight < 205);
89 }
90
91 private void addModeTabs(Layout layout) {
92 List<MenuMode> modes = visibleModes();
93 int height = layout.compact() ? 18 : 22;
94 int gap = 7;
95 int maxWidth = (layout.width() - 28 - gap * (modes.size() - 1)) / modes.size();
96 int tabWidth = Mth.clamp(maxWidth, 74, 112);
97 int totalWidth = modes.size() * tabWidth + (modes.size() - 1) * gap;
98 int x = width / 2 - totalWidth / 2;
99 int y = layout.top() + (layout.compact() ? 28 : 34);
100
101 for (int i = 0; i < modes.size(); i++) {
102 MenuMode mode = modes.get(i);
103 addRenderableWidget(new SegmentButton(x, y, tabWidth, height, mode.title(), button -> switchMode(mode),
104 () -> currentMode() == mode, i));
105 x += tabWidth + gap;
79 106 }
80 107 }
81 108
82 private List<Page> visiblePages() {
83 List<Page> pages = privateMindsEnabled
84 ? new ArrayList<>(List.of(Page.TRAVEL_PRIVATE, Page.TRAVEL_SHARED, Page.MOB_PRIVATE, Page.MOB_SHARED))
85 : new ArrayList<>(List.of(Page.TRAVEL_SHARED, Page.MOB_SHARED));
109 private void addScopeTabs(Layout layout) {
110 int tabWidth = Math.min(116, (layout.width() - 42) / 2);
111 int gap = 6;
112 int totalWidth = tabWidth * 2 + gap;
113 int x = width / 2 - totalWidth / 2;
114 int y = layout.top() + (layout.compact() ? 50 : 62);
115 int height = layout.compact() ? 18 : 20;
116
117 addRenderableWidget(new SegmentButton(x, y, tabWidth, height,
118 Component.translatable("screen.mydimension.rift_actions.scope.private"), button -> switchScope(false),
119 () -> !isSharedPage(), 3));
120 addRenderableWidget(new SegmentButton(x + tabWidth + gap, y, tabWidth, height,
121 Component.translatable("screen.mydimension.rift_actions.scope.shared"), button -> switchScope(true),
122 this::isSharedPage, 4));
123 }
124
125 private void addActionGrid(RiftAction[] actions, Layout layout) {
126 int columns = 3;
127 int horizontalPadding = 18;
128 int buttonWidth = (layout.width() - horizontalPadding * 2 - GRID_GAP * (columns - 1)) / columns;
129 int buttonHeight = layout.compact() ? 24 : 30;
130 int rowGap = layout.compact() ? 6 : 10;
131 int left = layout.left() + horizontalPadding;
132 int top = layout.top() + (showsScopeSelector() ? (layout.compact() ? 72 : 90) : (layout.compact() ? 52 : 68));
133 RiftAction selectedAction = getSelectedAction();
86 134
135 for (int i = 0; i < actions.length; i++) {
136 int x = left + (i % columns) * (buttonWidth + GRID_GAP);
137 int y = top + (i / columns) * (buttonHeight + rowGap);
138 addRenderableWidget(new ActionButton(x, y, buttonWidth, buttonHeight, actions[i],
139 selectedAction == actions[i], i, actionLabel(actions[i])));
140 }
141 }
142
143 private void addAnchorButton(Layout layout) {
144 int buttonWidth = Math.min(210, layout.width() - 56);
145 int buttonHeight = layout.compact() ? 24 : 28;
146 int y = layout.bottom() - buttonHeight - (layout.compact() ? 7 : 10);
147 addRenderableWidget(new ActionButton(width / 2 - buttonWidth / 2, y, buttonWidth, buttonHeight,
148 RiftAction.SET_ANCHOR, getSelectedAction() == RiftAction.SET_ANCHOR, 6, RiftAction.SET_ANCHOR.displayName()));
149 }
150
151 private List<MenuMode> visibleModes() {
152 List<MenuMode> modes = new ArrayList<>();
153 modes.add(MenuMode.TRAVEL);
154 if (!inMindDimension) {
155 modes.add(MenuMode.MOB);
156 }
87 157 if (privateMindsEnabled && creative) {
88 pages.add(Page.COPY_SHARED);
158 modes.add(MenuMode.COPY);
159 }
160 if (privateMindsEnabled) {
161 modes.add(MenuMode.TEAM);
162 }
163 return modes;
164 }
165
166 private void normalizePage() {
167 if (!privateMindsEnabled) {
168 page = switch (page) {
169 case MOB_PRIVATE, MOB_SHARED -> inMindDimension ? Page.TRAVEL_SHARED : Page.MOB_SHARED;
170 default -> Page.TRAVEL_SHARED;
171 };
172 return;
89 173 }
90 174
91 if (!pages.contains(page)) {
92 page = pages.get(0);
175 if (!creative && page == Page.COPY_SHARED) {
176 page = Page.TRAVEL_PRIVATE;
177 }
178 if (inMindDimension && (page == Page.MOB_PRIVATE || page == Page.MOB_SHARED)) {
179 page = page == Page.MOB_SHARED ? Page.TRAVEL_SHARED : Page.TRAVEL_PRIVATE;
93 180 }
181 }
94 182
95 return pages;
183 private boolean showsScopeSelector() {
184 return privateMindsEnabled && currentMode() != MenuMode.COPY;
185 }
186
187 private MenuMode currentMode() {
188 return switch (page) {
189 case TRAVEL_PRIVATE, TRAVEL_SHARED -> MenuMode.TRAVEL;
190 case MOB_PRIVATE, MOB_SHARED -> MenuMode.MOB;
191 case COPY_SHARED -> MenuMode.COPY;
192 };
193 }
194
195 private boolean isSharedPage() {
196 return page == Page.TRAVEL_SHARED || page == Page.MOB_SHARED || page == Page.COPY_SHARED;
197 }
198
199 private void switchMode(MenuMode mode) {
200 if (mode == MenuMode.TEAM) {
201 if (minecraft != null) {
202 minecraft.setScreen(new TeamMindScreen(this));
203 }
204 return;
205 }
206 if (mode == currentMode()) {
207 return;
208 }
209
210 boolean shared = !privateMindsEnabled || isSharedPage();
211 page = switch (mode) {
212 case TRAVEL -> shared ? Page.TRAVEL_SHARED : Page.TRAVEL_PRIVATE;
213 case MOB -> shared ? Page.MOB_SHARED : Page.MOB_PRIVATE;
214 case COPY -> Page.COPY_SHARED;
215 case TEAM -> page;
216 };
217 animatePageChange();
218 }
219
220 private void switchScope(boolean shared) {
221 if (shared == isSharedPage() || currentMode() == MenuMode.COPY) {
222 return;
223 }
224
225 page = switch (currentMode()) {
226 case TRAVEL -> shared ? Page.TRAVEL_SHARED : Page.TRAVEL_PRIVATE;
227 case MOB -> shared ? Page.MOB_SHARED : Page.MOB_PRIVATE;
228 case COPY -> Page.COPY_SHARED;
229 case TEAM -> page;
230 };
231 animatePageChange();
232 }
233
234 private void animatePageChange() {
235 contentAnimationAt = Util.getMillis();
236 rebuildWidgets();
96 237 }
97 238
98 239 private RiftAction[] currentActions() {
@@ -105,41 +246,62 @@ public class RiftActionScreen extends Screen {
105 246 };
106 247 }
107 248
108 private void addActionGrid(RiftAction[] actions, int top) {
109 int columns = 3;
110 int buttonWidth = rowButtonWidth(columns);
111 int totalWidth = buttonWidth * columns + BUTTON_GAP * (columns - 1);
112 int left = width / 2 - totalWidth / 2;
113 for (int i = 0; i < actions.length; i++) {
114 int x = left + (i % columns) * (buttonWidth + BUTTON_GAP);
115 int y = top + (i / columns) * 42;
116 addRenderableWidget(new ActionButton(x, y, buttonWidth, actions[i], getSelectedAction() == actions[i]));
249 private Component actionLabel(RiftAction action) {
250 if (privateMindsEnabled) {
251 return action.displayName();
117 252 }
118 }
119 253
120 private void addCenteredActionButton(RiftAction action, int y) {
121 int buttonWidth = Mth.clamp(width / 4, 140, 190);
122 addRenderableWidget(new ActionButton(width / 2 - buttonWidth / 2, y, buttonWidth, action, getSelectedAction() == action));
254 RiftAction simplified = switch (action) {
255 case SHARED_ETHEREAL_MIND -> RiftAction.ETHEREAL_MIND;
256 case SHARED_MIRROR_MIND -> RiftAction.MIRROR_MIND;
257 case SHARED_WATER_MIND -> RiftAction.WATER_MIND;
258 case SHARED_NATURE_MIND -> RiftAction.NATURE_MIND;
259 case SHARED_SOARING_MIND -> RiftAction.SOARING_MIND;
260 case SEND_MOB_SHARED_ETHEREAL -> RiftAction.SEND_MOB_ETHEREAL;
261 case SEND_MOB_SHARED_MIRROR -> RiftAction.SEND_MOB_MIRROR;
262 case SEND_MOB_SHARED_WATER -> RiftAction.SEND_MOB_WATER;
263 case SEND_MOB_SHARED_NATURE -> RiftAction.SEND_MOB_NATURE;
264 case SEND_MOB_SHARED_SOARING -> RiftAction.SEND_MOB_SOARING;
265 default -> action;
266 };
267 return simplified.displayName();
123 268 }
124 269
125 private int rowButtonWidth(int columns) {
126 int available = Math.max(96, width - 64);
127 int maxByWidth = (available - (columns - 1) * BUTTON_GAP) / columns;
128 return Mth.clamp(maxByWidth, 64, 150);
270 private void renderCenterPanel(GuiGraphics graphics, Layout layout) {
271 float progress = openingProgress(0, 260);
272 int titleOffset = Math.round((1.0F - easeOut(progress)) * 7.0F);
273 int panelFill = multiplyAlpha(0xC0060A12, progress);
274 int panelEdge = multiplyAlpha(0xFF85D6F7, progress);
275
276 fillRoundedRect(graphics, layout.left(), layout.top(), layout.right(), layout.bottom(), 8, panelFill);
277 drawRoundedBorder(graphics, layout.left(), layout.top(), layout.right(), layout.bottom(), 8, panelEdge);
278 graphics.fill(layout.left() + 12, layout.top() + 1, layout.left() + 72, layout.top() + 3,
279 multiplyAlpha(currentMode().accentColor(), progress));
280 graphics.drawCenteredString(font, title, width / 2, layout.top() + (layout.compact() ? 9 : 13) + titleOffset,
281 multiplyAlpha(0xFFEAFBFF, progress));
282
283 if (!layout.compact()) {
284 int footerY = inMindDimension ? layout.bottom() - 55 : layout.bottom() - 19;
285 Component selected = Component.translatable("screen.mydimension.rift_actions.selected", actionLabel(getSelectedAction()));
286 graphics.drawCenteredString(font, selected, width / 2, footerY, multiplyAlpha(0xFFA9C9D8, progress));
287 }
129 288 }
130 289
131 private void renderCenterPanel(GuiGraphics graphics) {
132 int centerX = width / 2;
133 int centerY = height / 2;
134 int left = centerX - 180;
135 int top = centerY - 72;
136 int right = centerX + 180;
137 int bottom = centerY + 72;
138
139 fillRoundedRect(graphics, left, top, right, bottom, 7, 0x99060A12);
140 drawRoundedBorder(graphics, left, top, right, bottom, 7, 0xFF85D6F7);
141 graphics.drawCenteredString(font, title, centerX, centerY - 61, 0xFFEAFBFF);
142 graphics.drawCenteredString(font, page.title(), centerX, centerY + 52, page == Page.COPY_SHARED ? 0xFFB8FFD2 : 0xFF9DDBFF);
290 private void renderMotes(GuiGraphics graphics) {
291 long elapsed = Util.getMillis() - openedAt;
292 int verticalSpan = Math.max(1, height + 36);
293 int horizontalSpan = Math.max(1, width - 20);
294 float progress = openingProgress(80, 420);
295
296 for (int i = 0; i < 16; i++) {
297 int x = 10 + Math.floorMod(i * 83 + (i % 3) * 29, horizontalSpan);
298 int drift = (int) (elapsed * (0.006F + (i % 4) * 0.0015F));
299 int y = Math.floorMod(i * 47 - drift, verticalSpan) - 18;
300 int size = i % 5 == 0 ? 2 : 1;
301 int color = i % 3 == 0 ? 0xFF9A6CE0 : 0xFF70D4F4;
302 int alpha = 28 + (i % 4) * 9;
303 graphics.fill(x, y, x + size, y + size, multiplyAlpha(withAlpha(color, alpha), progress));
304 }
143 305 }
144 306
145 307 private RiftAction getSelectedAction() {
@@ -153,7 +315,8 @@ public class RiftActionScreen extends Screen {
153 315 }
154 316
155 317 ItemStack offHand = minecraft.player.getOffhandItem();
156 return offHand.is(ModItems.RIFT.get()) ? RiftItem.getSelectedAction(offHand) : (privateMindsEnabled ? RiftAction.ETHEREAL_MIND : RiftAction.SHARED_ETHEREAL_MIND);
318 return offHand.is(ModItems.RIFT.get()) ? RiftItem.getSelectedAction(offHand)
319 : (privateMindsEnabled ? RiftAction.ETHEREAL_MIND : RiftAction.SHARED_ETHEREAL_MIND);
157 320 }
158 321
159 322 private void select(RiftAction action) {
@@ -163,6 +326,50 @@ public class RiftActionScreen extends Screen {
163 326 }
164 327 }
165 328
329 private float openingProgress(int delayMillis, int durationMillis) {
330 return animationProgress(openedAt, delayMillis, durationMillis);
331 }
332
333 private float contentProgress(int order) {
334 return easeOut(animationProgress(contentAnimationAt, order * 34, 210));
335 }
336
337 private float animationProgress(long startedAt, int delayMillis, int durationMillis) {
338 float elapsed = Util.getMillis() - startedAt - delayMillis;
339 return Mth.clamp(elapsed / durationMillis, 0.0F, 1.0F);
340 }
341
342 private static float easeOut(float value) {
343 float inverse = 1.0F - value;
344 return 1.0F - inverse * inverse * inverse;
345 }
346
347 private static int multiplyAlpha(int color, float multiplier) {
348 int alpha = Math.round(((color >>> 24) & 0xFF) * Mth.clamp(multiplier, 0.0F, 1.0F));
349 return (alpha << 24) | (color & 0x00FFFFFF);
350 }
351
352 private static int withAlpha(int color, int alpha) {
353 return (Mth.clamp(alpha, 0, 255) << 24) | (color & 0x00FFFFFF);
354 }
355
356 private static int mixColor(int from, int to, float amount) {
357 float value = Mth.clamp(amount, 0.0F, 1.0F);
358 int alpha = Math.round(Mth.lerp(value, (from >>> 24) & 0xFF, (to >>> 24) & 0xFF));
359 int red = Math.round(Mth.lerp(value, (from >>> 16) & 0xFF, (to >>> 16) & 0xFF));
360 int green = Math.round(Mth.lerp(value, (from >>> 8) & 0xFF, (to >>> 8) & 0xFF));
361 int blue = Math.round(Mth.lerp(value, from & 0xFF, to & 0xFF));
362 return (alpha << 24) | (red << 16) | (green << 8) | blue;
363 }
364
365 private static int darken(int color) {
366 int alpha = color & 0xFF000000;
367 int red = (int) (((color >> 16) & 0xFF) * 0.58F);
368 int green = (int) (((color >> 8) & 0xFF) * 0.58F);
369 int blue = (int) ((color & 0xFF) * 0.58F);
370 return alpha | (red << 16) | (green << 8) | blue;
371 }
372
166 373 private static void fillRoundedRect(GuiGraphics graphics, int left, int top, int right, int bottom, int radius, int color) {
167 374 graphics.fill(left + radius, top, right - radius, bottom, color);
168 375 graphics.fill(left, top + radius, right, bottom - radius, color);
@@ -183,76 +390,133 @@ public class RiftActionScreen extends Screen {
183 390 }
184 391
185 392 private enum Page {
186 TRAVEL_PRIVATE("screen.mydimension.rift_actions.page.travel_private"),
187 TRAVEL_SHARED("screen.mydimension.rift_actions.page.travel_shared"),
188 MOB_PRIVATE("screen.mydimension.rift_actions.page.mob_private"),
189 MOB_SHARED("screen.mydimension.rift_actions.page.mob_shared"),
190 COPY_SHARED("screen.mydimension.rift_actions.page.copy_shared");
393 TRAVEL_PRIVATE,
394 TRAVEL_SHARED,
395 MOB_PRIVATE,
396 MOB_SHARED,
397 COPY_SHARED
398 }
399
400 private enum MenuMode {
401 TRAVEL("screen.mydimension.rift_actions.travel", TRAVEL_COLOR),
402 MOB("screen.mydimension.rift_actions.mob", MOB_COLOR),
403 COPY("screen.mydimension.rift_actions.copy", COPY_COLOR),
404 TEAM("screen.mydimension.rift_actions.team", TEAM_COLOR);
191 405
192 406 private final String translationKey;
407 private final int accentColor;
193 408
194 Page(String translationKey) {
409 MenuMode(String translationKey, int accentColor) {
195 410 this.translationKey = translationKey;
411 this.accentColor = accentColor;
196 412 }
197 413
198 414 private Component title() {
199 415 return Component.translatable(translationKey);
200 416 }
417
418 private int accentColor() {
419 return accentColor;
420 }
201 421 }
202 422
203 private class PageButton extends Button {
204 private final Page targetPage;
423 private record Layout(int left, int top, int right, int bottom, int width, boolean compact) {
424 }
425
426 private class SegmentButton extends Button {
427 private final BooleanSupplier selected;
428 private final int animationOrder;
429 private float hoverProgress;
205 430
206 private PageButton(int x, int y, int width, Page targetPage) {
207 super(x, y, width, 20, targetPage.title(), button -> {
208 page = targetPage;
209 rebuildWidgets();
210 }, DEFAULT_NARRATION);
211 this.targetPage = targetPage;
431 private SegmentButton(int x, int y, int width, int height, Component message, OnPress onPress,
432 BooleanSupplier selected, int animationOrder) {
433 super(x, y, width, height, message, onPress, DEFAULT_NARRATION);
434 this.selected = selected;
435 this.animationOrder = animationOrder;
212 436 }
213 437
214 438 @Override
215 439 protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
216 int fill = targetPage == page ? 0xD6D6A23E : (isHoveredOrFocused() ? 0xC83E8DB8 : 0x963E8DB8);
217 fillRoundedRect(graphics, getX(), getY(), getX() + getWidth(), getY() + getHeight(), 5, fill);
218 drawRoundedBorder(graphics, getX(), getY(), getX() + getWidth(), getY() + getHeight(), 5, targetPage == page ? 0xFFFFF4A8 : 0xFFB8F0FF);
219 drawCenteredFittingText(graphics, Minecraft.getInstance().font, getMessage(), getX(), getY(), getWidth(), getHeight(), 0xFFEAFBFF);
440 hoverProgress = Mth.clamp(hoverProgress + (isHoveredOrFocused() ? 0.16F : -0.16F), 0.0F, 1.0F);
441 float progress = easeOut(openingProgress(animationOrder * 24, 220));
442 int offset = Math.round((1.0F - progress) * 5.0F);
443 int left = getX();
444 int top = getY() + offset;
445 int right = left + getWidth();
446 int bottom = top + getHeight();
447 boolean active = selected.getAsBoolean();
448 int activeColor = currentMode().accentColor();
449 int fill = active ? withAlpha(activeColor, 218) : mixColor(0xA62A4354, 0xD0487894, hoverProgress);
450 int edge = active ? 0xFFFFEEA3 : mixColor(0xFF7096AA, 0xFFC9F5FF, hoverProgress);
451
452 fillRoundedRect(graphics, left, top, right, bottom, 5, multiplyAlpha(fill, progress));
453 drawRoundedBorder(graphics, left, top, right, bottom, 5, multiplyAlpha(edge, progress));
454 if (active) {
455 int lineWidth = Math.max(12, getWidth() / 3);
456 graphics.fill(left + (getWidth() - lineWidth) / 2, bottom - 2,
457 right - (getWidth() - lineWidth) / 2, bottom - 1, multiplyAlpha(0xFFFFF3B0, progress));
458 }
459 drawCenteredFittingText(graphics, Minecraft.getInstance().font, getMessage(), left, top, getWidth(), getHeight(),
460 multiplyAlpha(active || hoverProgress > 0.15F ? 0xFFFFFFFF : 0xFFD8E9F0, progress));
220 461 }
221 462 }
222 463
223 464 private class ActionButton extends Button {
224 465 private final RiftAction action;
225 466 private final boolean selected;
467 private final int animationOrder;
468 private float hoverProgress;
226 469
227 private ActionButton(int x, int y, int width, RiftAction action, boolean selected) {
228 super(x, y, width, BUTTON_HEIGHT, action.displayName(), button -> select(action), DEFAULT_NARRATION);
470 private ActionButton(int x, int y, int width, int height, RiftAction action, boolean selected,
471 int animationOrder, Component message) {
472 super(x, y, width, height, message, button -> select(action), DEFAULT_NARRATION);
229 473 this.action = action;
230 474 this.selected = selected;
475 this.animationOrder = animationOrder;
231 476 }
232 477
233 478 @Override
234 479 protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
480 hoverProgress = Mth.clamp(hoverProgress + (isHoveredOrFocused() ? 0.14F : -0.14F), 0.0F, 1.0F);
481 float progress = contentProgress(animationOrder);
482 if (progress <= 0.01F) {
483 return;
484 }
485
235 486 int left = getX();
236 int top = getY();
487 int top = getY() + Math.round((1.0F - progress) * 9.0F);
237 488 int right = left + getWidth();
238 489 int bottom = top + getHeight();
239 int base = action == RiftAction.SET_ANCHOR ? ANCHOR_COLOR : (action.copiesSharedDimension() ? COPY_COLOR : (action.sendsMob() ? MOB_COLOR : TRAVEL_COLOR));
240 int fill = withAlpha(base, isHoveredOrFocused() ? 226 : 174);
241 int edge = selected ? 0xFFFFF4A8 : (action == RiftAction.SET_ANCHOR ? 0xFFFFE0A3 : (action.copiesSharedDimension() ? 0xFFB8FFD2 : (action.sendsMob() ? 0xFFD8C1FF : 0xFFB8F0FF)));
490 int base = action == RiftAction.SET_ANCHOR ? ANCHOR_COLOR
491 : (action.copiesSharedDimension() ? COPY_COLOR : (action.sendsMob() ? MOB_COLOR : TRAVEL_COLOR));
492 int fill = withAlpha(base, Math.round(Mth.lerp(hoverProgress, 172.0F, 232.0F)));
493 int edge = selected ? 0xFFFFF4A8
494 : (action == RiftAction.SET_ANCHOR ? 0xFFFFE0A3
495 : (action.copiesSharedDimension() ? 0xFFB8FFD2 : (action.sendsMob() ? 0xFFD8C1FF : 0xFFB8F0FF)));
496
497 fillRoundedRect(graphics, left, top, right, bottom, 7, multiplyAlpha(darken(fill), progress));
498 fillRoundedRect(graphics, left + 1, top + 1, right - 1, bottom - 1, 6, multiplyAlpha(fill, progress));
499 drawRoundedBorder(graphics, left, top, right, bottom, 7, multiplyAlpha(edge, progress));
242 500
243 fillRoundedRect(graphics, left, top, right, bottom, 7, darken(fill));
244 fillRoundedRect(graphics, left + 1, top + 1, right - 1, bottom - 1, 6, fill);
245 drawRoundedBorder(graphics, left, top, right, bottom, 7, edge);
246 501 if (selected) {
247 fillRoundedRect(graphics, left + 4, top + 5, left + 8, bottom - 5, 2, 0xFFFFF4A8);
502 float pulse = 0.72F + (Mth.sin(Util.getMillis() / 210.0F) + 1.0F) * 0.12F;
503 fillRoundedRect(graphics, left + 4, top + 5, left + 7, bottom - 5, 2,
504 multiplyAlpha(0xFFFFF4A8, progress * pulse));
505 }
506 if (hoverProgress > 0.01F) {
507 int glintWidth = Math.round((getWidth() - 18) * hoverProgress);
508 graphics.fill(left + 9, bottom - 3, left + 9 + glintWidth, bottom - 2,
509 multiplyAlpha(edge, progress * hoverProgress * 0.85F));
248 510 }
249 511
250 renderIcon(graphics, left + 10, top + 5);
251 int textColor = isHoveredOrFocused() ? 0xFFFFFFFF : 0xFFEAFBFF;
252 drawFittingText(graphics, Minecraft.getInstance().font, getMessage(), left + 32, top, getWidth() - 38, getHeight(), textColor);
512 renderIcon(graphics, left + 9 + Math.round(hoverProgress * 2.0F), top + (getHeight() - 16) / 2, progress);
513 int textColor = mixColor(0xFFEAFBFF, 0xFFFFFFFF, hoverProgress);
514 drawFittingText(graphics, Minecraft.getInstance().font, getMessage(), left + 32, top,
515 getWidth() - 38, getHeight(), multiplyAlpha(textColor, progress));
253 516 }
254 517
255 private void renderIcon(GuiGraphics graphics, int x, int y) {
518 private void renderIcon(GuiGraphics graphics, int x, int y, float opacity) {
519 RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, opacity);
256 520 if (action == RiftAction.SET_ANCHOR) {
257 521 graphics.blit(ANCHOR_ICON, x, y, 0, 0, 16, 16, 16, 16);
258 522 } else if (action.sendsMob()) {
@@ -260,27 +524,18 @@ public class RiftActionScreen extends Screen {
260 524 } else {
261 525 graphics.blit(RIFT_ICON, x, y, 0, 0, 16, 16, 16, 16);
262 526 }
263 }
264
265 private int withAlpha(int color, int alpha) {
266 return (Mth.clamp(alpha, 0, 255) << 24) | (color & 0x00FFFFFF);
267 }
268
269 private int darken(int color) {
270 int alpha = color & 0xFF000000;
271 int red = (int) (((color >> 16) & 0xFF) * 0.58F);
272 int green = (int) (((color >> 8) & 0xFF) * 0.58F);
273 int blue = (int) ((color & 0xFF) * 0.58F);
274 return alpha | (red << 16) | (green << 8) | blue;
527 RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
275 528 }
276 529 }
277 530
278 private static void drawCenteredFittingText(GuiGraphics graphics, Font font, Component text, int left, int top, int width, int height, int color) {
531 private static void drawCenteredFittingText(GuiGraphics graphics, Font font, Component text,
532 int left, int top, int width, int height, int color) {
279 533 String label = font.plainSubstrByWidth(text.getString(), width - 8);
280 534 graphics.drawCenteredString(font, label, left + width / 2, top + (height - 8) / 2, color);
281 535 }
282 536
283 private static void drawFittingText(GuiGraphics graphics, Font font, Component text, int left, int top, int width, int height, int color) {
537 private static void drawFittingText(GuiGraphics graphics, Font font, Component text,
538 int left, int top, int width, int height, int color) {
284 539 String label = font.plainSubstrByWidth(text.getString(), width);
285 540 graphics.drawString(font, label, left, top + (height - 8) / 2, color);
286 541 }
Modified src/main/java/com/xfestudio/mydimension/client/RiftClient.java +13 -0
@@ -1,9 +1,22 @@
1 1 package com.xfestudio.mydimension.client;
2 2
3 import com.xfestudio.mydimension.network.MindAccessPromptPacket;
4 import com.xfestudio.mydimension.network.TeamMindDataPacket;
3 5 import net.minecraft.client.Minecraft;
4 6
5 7 public class RiftClient {
6 8 public static void openActionScreen() {
7 9 Minecraft.getInstance().setScreen(new RiftActionScreen());
8 10 }
11
12 public static void handleTeamMindData(TeamMindDataPacket packet) {
13 if (Minecraft.getInstance().screen instanceof TeamMindScreen screen) {
14 screen.updateData(packet);
15 }
16 }
17
18 public static void openMindAccessPrompt(MindAccessPromptPacket packet) {
19 Minecraft minecraft = Minecraft.getInstance();
20 minecraft.setScreen(new MindAccessRequestScreen(minecraft.screen, packet.requesterId(), packet.requesterName()));
21 }
9 22 }
Added src/main/java/com/xfestudio/mydimension/client/TeamMindScreen.java +438 -0
@@ -0,0 +1,438 @@
1 package com.xfestudio.mydimension.client;
2
3 import com.xfestudio.mydimension.MyDimension;
4 import com.xfestudio.mydimension.network.ModNetwork;
5 import com.xfestudio.mydimension.network.TeamMindCommandPacket;
6 import com.xfestudio.mydimension.network.TeamMindDataPacket;
7 import com.xfestudio.mydimension.world.MindType;
8 import net.minecraft.Util;
9 import net.minecraft.client.Minecraft;
10 import net.minecraft.client.gui.Font;
11 import net.minecraft.client.gui.GuiGraphics;
12 import net.minecraft.client.gui.components.Button;
13 import net.minecraft.client.gui.screens.Screen;
14 import net.minecraft.network.chat.Component;
15 import net.minecraft.resources.ResourceLocation;
16 import net.minecraft.util.Mth;
17
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Set;
21 import java.util.UUID;
22 import java.util.function.BooleanSupplier;
23
24 public class TeamMindScreen extends Screen {
25 private static final int ENTRANCE_COLOR = 0xFF3E8DB8;
26 private static final int ADD_COLOR = 0xFF4EBA7A;
27 private static final int MANAGE_COLOR = 0xFF8B58C8;
28 private static final ResourceLocation RIFT_ICON = new ResourceLocation(MyDimension.MOD_ID, "textures/item/rift.png");
29
30 private final Screen parent;
31 private final long openedAt = Util.getMillis();
32 private final Set<UUID> requestedPlayers = new HashSet<>();
33 private TeamTab tab = TeamTab.ENTRANCES;
34 private List<TeamMindDataPacket.PlayerInfo> entrances = List.of();
35 private List<TeamMindDataPacket.PlayerInfo> candidates = List.of();
36 private List<TeamMindDataPacket.PlayerInfo> guests = List.of();
37 private UUID selectedOwner;
38 private int listPage;
39 private boolean requestedData;
40
41 public TeamMindScreen(Screen parent) {
42 super(Component.translatable("screen.mydimension.team.title"));
43 this.parent = parent;
44 }
45
46 @Override
47 protected void init() {
48 Layout layout = createLayout();
49 addTabButtons(layout);
50 switch (tab) {
51 case ENTRANCES -> addEntranceWidgets(layout);
52 case ADD -> addPlayerRows(layout, candidates, RowAction.REQUEST);
53 case MANAGE -> addPlayerRows(layout, guests, RowAction.REVOKE);
54 }
55 addFooterButtons(layout);
56
57 if (!requestedData) {
58 requestedData = true;
59 ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.refresh());
60 }
61 }
62
63 public void updateData(TeamMindDataPacket packet) {
64 entrances = packet.entrances();
65 candidates = packet.candidates();
66 guests = packet.guests();
67 requestedPlayers.clear();
68 if (selectedOwner == null || entrances.stream().noneMatch(player -> player.id().equals(selectedOwner))) {
69 selectedOwner = entrances.isEmpty() ? null : entrances.get(0).id();
70 }
71 clampPage();
72 rebuildWidgets();
73 }
74
75 @Override
76 public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
77 graphics.fillGradient(0, 0, width, height, 0xD0101218, 0xE0181026);
78 Layout layout = createLayout();
79 float progress = easeOut(Mth.clamp((Util.getMillis() - openedAt) / 260.0F, 0.0F, 1.0F));
80 fillRoundedRect(graphics, layout.left(), layout.top(), layout.right(), layout.bottom(), 8,
81 multiplyAlpha(0xD0060A12, progress));
82 drawRoundedBorder(graphics, layout.left(), layout.top(), layout.right(), layout.bottom(), 8,
83 multiplyAlpha(0xFF85D6F7, progress));
84 graphics.fill(layout.left() + 12, layout.top() + 1, layout.left() + 78, layout.top() + 3,
85 multiplyAlpha(tab.color(), progress));
86 graphics.drawCenteredString(font, title, width / 2, layout.top() + 13, multiplyAlpha(0xFFEAFBFF, progress));
87
88 renderContentLabels(graphics, layout, progress);
89 super.render(graphics, mouseX, mouseY, partialTick);
90 }
91
92 @Override
93 public void onClose() {
94 if (minecraft != null) {
95 minecraft.setScreen(parent);
96 }
97 }
98
99 @Override
100 public boolean isPauseScreen() {
101 return false;
102 }
103
104 private Layout createLayout() {
105 int panelWidth = Math.min(500, Math.max(300, width - 24));
106 int panelHeight = Math.min(252, Math.max(190, height - 12));
107 int left = (width - panelWidth) / 2;
108 int top = (height - panelHeight) / 2;
109 return new Layout(left, top, left + panelWidth, top + panelHeight, panelWidth, panelHeight);
110 }
111
112 private void addTabButtons(Layout layout) {
113 TeamTab[] tabs = TeamTab.values();
114 int gap = 7;
115 int tabWidth = Math.min(118, (layout.width() - 36 - gap * (tabs.length - 1)) / tabs.length);
116 int totalWidth = tabWidth * tabs.length + gap * (tabs.length - 1);
117 int x = width / 2 - totalWidth / 2;
118 int y = layout.top() + 34;
119
120 for (int i = 0; i < tabs.length; i++) {
121 TeamTab target = tabs[i];
122 addRenderableWidget(new TeamButton(x, y, tabWidth, 22, target.title(), target.color(),
123 () -> tab == target, i, button -> {
124 tab = target;
125 listPage = 0;
126 rebuildWidgets();
127 }));
128 x += tabWidth + gap;
129 }
130 }
131
132 private void addEntranceWidgets(Layout layout) {
133 int contentTop = layout.top() + 66;
134 int leftWidth = Math.min(156, Math.max(112, layout.width() / 3));
135 int left = layout.left() + 16;
136 int from = listPage * rowsPerPage();
137 int to = Math.min(entrances.size(), from + rowsPerPage());
138
139 for (int index = from; index < to; index++) {
140 TeamMindDataPacket.PlayerInfo owner = entrances.get(index);
141 int y = contentTop + (index - from) * 25;
142 addRenderableWidget(new TeamButton(left, y, leftWidth, 23, Component.literal(owner.name()), ENTRANCE_COLOR,
143 () -> owner.id().equals(selectedOwner), index - from + 3, button -> {
144 selectedOwner = owner.id();
145 rebuildWidgets();
146 }));
147 }
148
149 if (selectedOwner == null) {
150 return;
151 }
152
153 int right = left + leftWidth + 16;
154 int availableWidth = layout.right() - 16 - right;
155 int gap = 8;
156 int buttonWidth = (availableWidth - gap) / 2;
157 MindType[] mindTypes = MindType.values();
158 for (int i = 0; i < mindTypes.length; i++) {
159 MindType mindType = mindTypes[i];
160 int x = right + (i % 2) * (buttonWidth + gap);
161 int y = contentTop + (i / 2) * 31;
162 addRenderableWidget(new MindButton(x, y, buttonWidth, 25, mindType, i + 5));
163 }
164 addPaginationButtons(layout, entrances);
165 }
166
167 private void addPlayerRows(Layout layout, List<TeamMindDataPacket.PlayerInfo> players, RowAction action) {
168 int rowsPerPage = rowsPerPage();
169 int from = listPage * rowsPerPage;
170 int to = Math.min(players.size(), from + rowsPerPage);
171 int left = layout.left() + 24;
172 int right = layout.right() - 24;
173 int top = layout.top() + 66;
174 int actionWidth = 78;
175
176 for (int index = from; index < to; index++) {
177 TeamMindDataPacket.PlayerInfo player = players.get(index);
178 int row = index - from;
179 int y = top + row * 25;
180 addRenderableWidget(new TeamButton(left, y, right - left - actionWidth - 8, 23,
181 Component.literal(player.name()), tab.color(), () -> false, row + 3, button -> {
182 }));
183
184 boolean pending = action == RowAction.REQUEST && requestedPlayers.contains(player.id());
185 Component label = pending
186 ? Component.translatable("screen.mydimension.team.requested")
187 : Component.translatable(action.translationKey());
188 TeamButton actionButton = new TeamButton(right - actionWidth, y, actionWidth, 23, label,
189 action == RowAction.REQUEST ? ADD_COLOR : 0xFFC95D66, () -> false, row + 4, button -> {
190 if (action == RowAction.REQUEST) {
191 requestedPlayers.add(player.id());
192 ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.requestAccess(player.id()));
193 rebuildWidgets();
194 } else {
195 ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.revoke(player.id()));
196 }
197 });
198 actionButton.active = !pending;
199 addRenderableWidget(actionButton);
200 }
201
202 addPaginationButtons(layout, players);
203 }
204
205 private void addFooterButtons(Layout layout) {
206 int y = layout.bottom() - 29;
207 addRenderableWidget(new TeamButton(layout.left() + 12, y, 62, 20,
208 Component.translatable("gui.back"), 0xFF47677A, () -> false, 10, button -> onClose()));
209 addRenderableWidget(new TeamButton(layout.right() - 74, y, 62, 20,
210 Component.translatable("screen.mydimension.team.refresh"), 0xFF47677A, () -> false, 10, button -> {
211 ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.refresh());
212 }));
213 }
214
215 private void renderContentLabels(GuiGraphics graphics, Layout layout, float progress) {
216 int color = multiplyAlpha(0xFFA9C9D8, progress);
217 if (tab == TeamTab.ENTRANCES) {
218 if (entrances.isEmpty()) {
219 graphics.drawCenteredString(font, Component.translatable("screen.mydimension.team.no_entrances"),
220 width / 2, layout.top() + 119, color);
221 return;
222 }
223 TeamMindDataPacket.PlayerInfo owner = selectedOwnerInfo();
224 if (owner != null) {
225 int leftWidth = Math.min(156, Math.max(112, layout.width() / 3));
226 int center = layout.left() + 16 + leftWidth + 16 + (layout.right() - 16 - (layout.left() + 16 + leftWidth + 16)) / 2;
227 graphics.drawCenteredString(font, Component.translatable("screen.mydimension.team.owner_minds", owner.name()),
228 center, layout.top() + 62, color);
229 }
230 renderPageNumber(graphics, layout, entrances, color);
231 } else {
232 List<TeamMindDataPacket.PlayerInfo> players = currentList();
233 if (players.isEmpty()) {
234 Component empty = Component.translatable(tab == TeamTab.ADD
235 ? "screen.mydimension.team.no_candidates"
236 : "screen.mydimension.team.no_guests");
237 graphics.drawCenteredString(font, empty, width / 2, layout.top() + 119, color);
238 }
239 renderPageNumber(graphics, layout, players, color);
240 }
241 }
242
243 private TeamMindDataPacket.PlayerInfo selectedOwnerInfo() {
244 return entrances.stream().filter(player -> player.id().equals(selectedOwner)).findFirst().orElse(null);
245 }
246
247 private List<TeamMindDataPacket.PlayerInfo> currentList() {
248 return switch (tab) {
249 case ENTRANCES -> entrances;
250 case ADD -> candidates;
251 case MANAGE -> guests;
252 };
253 }
254
255 private void addPaginationButtons(Layout layout, List<?> values) {
256 int pages = pageCount(values);
257 if (pages <= 1) {
258 return;
259 }
260
261 int y = layout.bottom() - 39;
262 TeamButton previous = new TeamButton(width / 2 - 54, y, 24, 20, Component.literal("<"), tab.color(),
263 () -> false, 9, button -> changePage(-1));
264 previous.active = listPage > 0;
265 addRenderableWidget(previous);
266 TeamButton next = new TeamButton(width / 2 + 30, y, 24, 20, Component.literal(">"), tab.color(),
267 () -> false, 9, button -> changePage(1));
268 next.active = listPage + 1 < pages;
269 addRenderableWidget(next);
270 }
271
272 private void renderPageNumber(GuiGraphics graphics, Layout layout, List<?> values, int color) {
273 int pages = pageCount(values);
274 if (pages > 1) {
275 graphics.drawCenteredString(font, Component.literal((listPage + 1) + " / " + pages),
276 width / 2, layout.bottom() - 34, color);
277 }
278 }
279
280 private void changePage(int amount) {
281 listPage = Mth.clamp(listPage + amount, 0, Math.max(0, pageCount(currentList()) - 1));
282 if (tab == TeamTab.ENTRANCES && !entrances.isEmpty()) {
283 selectedOwner = entrances.get(Math.min(entrances.size() - 1, listPage * rowsPerPage())).id();
284 }
285 rebuildWidgets();
286 }
287
288 private void clampPage() {
289 listPage = Mth.clamp(listPage, 0, Math.max(0, pageCount(currentList()) - 1));
290 }
291
292 private static int pageCount(List<?> values) {
293 int rowsPerPage = rowsPerPage();
294 return Math.max(1, (values.size() + rowsPerPage - 1) / rowsPerPage);
295 }
296
297 private static int rowsPerPage() {
298 Minecraft minecraft = Minecraft.getInstance();
299 return minecraft.getWindow().getGuiScaledHeight() < 232 ? 3 : 5;
300 }
301
302 private static float easeOut(float value) {
303 float inverse = 1.0F - value;
304 return 1.0F - inverse * inverse * inverse;
305 }
306
307 private static int multiplyAlpha(int color, float multiplier) {
308 int alpha = Math.round(((color >>> 24) & 0xFF) * Mth.clamp(multiplier, 0.0F, 1.0F));
309 return (alpha << 24) | (color & 0x00FFFFFF);
310 }
311
312 private static int withAlpha(int color, int alpha) {
313 return (Mth.clamp(alpha, 0, 255) << 24) | (color & 0x00FFFFFF);
314 }
315
316 private static void fillRoundedRect(GuiGraphics graphics, int left, int top, int right, int bottom, int radius, int color) {
317 graphics.fill(left + radius, top, right - radius, bottom, color);
318 graphics.fill(left, top + radius, right, bottom - radius, color);
319 graphics.fill(left + 1, top + 1, right - 1, top + radius, color);
320 graphics.fill(left + 1, bottom - radius, right - 1, bottom - 1, color);
321 }
322
323 private static void drawRoundedBorder(GuiGraphics graphics, int left, int top, int right, int bottom, int radius, int color) {
324 graphics.fill(left + radius, top, right - radius, top + 1, color);
325 graphics.fill(left + radius, bottom - 1, right - radius, bottom, color);
326 graphics.fill(left, top + radius, left + 1, bottom - radius, color);
327 graphics.fill(right - 1, top + radius, right, bottom - radius, color);
328 graphics.fill(left + 1, top + 1, left + radius, top + 2, color);
329 graphics.fill(right - radius, top + 1, right - 1, top + 2, color);
330 graphics.fill(left + 1, bottom - 2, left + radius, bottom - 1, color);
331 graphics.fill(right - radius, bottom - 2, right - 1, bottom - 1, color);
332 }
333
334 private enum TeamTab {
335 ENTRANCES("screen.mydimension.team.tab.entrances", ENTRANCE_COLOR),
336 ADD("screen.mydimension.team.tab.add", ADD_COLOR),
337 MANAGE("screen.mydimension.team.tab.manage", MANAGE_COLOR);
338
339 private final String translationKey;
340 private final int color;
341
342 TeamTab(String translationKey, int color) {
343 this.translationKey = translationKey;
344 this.color = color;
345 }
346
347 private Component title() {
348 return Component.translatable(translationKey);
349 }
350
351 private int color() {
352 return color;
353 }
354 }
355
356 private enum RowAction {
357 REQUEST("screen.mydimension.team.request"),
358 REVOKE("screen.mydimension.team.revoke");
359
360 private final String translationKey;
361
362 RowAction(String translationKey) {
363 this.translationKey = translationKey;
364 }
365
366 private String translationKey() {
367 return translationKey;
368 }
369 }
370
371 private record Layout(int left, int top, int right, int bottom, int width, int height) {
372 }
373
374 private class MindButton extends TeamButton {
375 private final MindType mindType;
376
377 private MindButton(int x, int y, int width, int height, MindType mindType, int animationOrder) {
378 super(x, y, width, height, mindType.displayName(), ENTRANCE_COLOR, () -> false, animationOrder,
379 button -> TeamMindScreen.this.selectMind(mindType));
380 this.mindType = mindType;
381 }
382
383 @Override
384 protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
385 super.renderWidget(graphics, mouseX, mouseY, partialTick);
386 int y = getY() + (getHeight() - 16) / 2;
387 graphics.blit(RIFT_ICON, getX() + 8, y, 0, 0, 16, 16, 16, 16);
388 }
389
390 }
391
392 private void selectMind(MindType mindType) {
393 if (selectedOwner == null || minecraft == null) {
394 return;
395 }
396 ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.select(selectedOwner, mindType));
397 minecraft.setScreen(null);
398 }
399
400 private class TeamButton extends Button {
401 private final int color;
402 private final BooleanSupplier selected;
403 private final int animationOrder;
404 private float hoverProgress;
405
406 private TeamButton(int x, int y, int width, int height, Component message, int color,
407 BooleanSupplier selected, int animationOrder, OnPress onPress) {
408 super(x, y, width, height, message, onPress, DEFAULT_NARRATION);
409 this.color = color;
410 this.selected = selected;
411 this.animationOrder = animationOrder;
412 }
413
414 @Override
415 protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
416 hoverProgress = Mth.clamp(hoverProgress + (isHoveredOrFocused() ? 0.15F : -0.15F), 0.0F, 1.0F);
417 float progress = easeOut(Mth.clamp((Util.getMillis() - openedAt - animationOrder * 28L) / 220.0F, 0.0F, 1.0F));
418 int top = getY() + Math.round((1.0F - progress) * 7.0F);
419 int fillAlpha = active ? Math.round(Mth.lerp(hoverProgress, 150.0F, 220.0F)) : 75;
420 int edge = selected.getAsBoolean() ? 0xFFFFF4A8 : (active ? 0xFFB8F0FF : 0xFF6A747B);
421
422 fillRoundedRect(graphics, getX(), top, getX() + getWidth(), top + getHeight(), 6,
423 multiplyAlpha(withAlpha(color, fillAlpha), progress));
424 drawRoundedBorder(graphics, getX(), top, getX() + getWidth(), top + getHeight(), 6,
425 multiplyAlpha(edge, progress));
426 int textLeft = this instanceof MindButton ? getX() + 29 : getX();
427 int textWidth = this instanceof MindButton ? getWidth() - 34 : getWidth();
428 drawCenteredFittingText(graphics, Minecraft.getInstance().font, getMessage(), textLeft, top,
429 textWidth, getHeight(), multiplyAlpha(active ? 0xFFEAFBFF : 0xFF8A969C, progress));
430 }
431 }
432
433 private static void drawCenteredFittingText(GuiGraphics graphics, Font font, Component text,
434 int left, int top, int width, int height, int color) {
435 String label = font.plainSubstrByWidth(text.getString(), Math.max(4, width - 8));
436 graphics.drawCenteredString(font, label, left + width / 2, top + (height - 8) / 2, color);
437 }
438 }
Modified src/main/java/com/xfestudio/mydimension/item/RiftAction.java +2 -1
@@ -33,7 +33,8 @@ public enum RiftAction {
33 33 COPY_SHARED_WATER("copy_shared_water", "action.mydimension.copy_shared_water", ModDimensions.WATER_MIND, false, false, true),
34 34 COPY_SHARED_NATURE("copy_shared_nature", "action.mydimension.copy_shared_nature", ModDimensions.NATURE_MIND, false, false, true),
35 35 COPY_SHARED_SOARING("copy_shared_soaring", "action.mydimension.copy_shared_soaring", ModDimensions.SOARING_MIND, false, false, true),
36 SET_ANCHOR("set_anchor", "action.mydimension.set_anchor", null, false, false, false);
36 SET_ANCHOR("set_anchor", "action.mydimension.set_anchor", null, false, false, false),
37 VISIT_TEAM_MIND("visit_team_mind", "action.mydimension.visit_team_mind", null, false, false, false);
37 38
38 39 private final String id;
39 40 private final String translationKey;
Modified src/main/java/com/xfestudio/mydimension/item/RiftItem.java +76 -1
Added src/main/java/com/xfestudio/mydimension/network/MindAccessPromptPacket.java +27 -0
Modified src/main/java/com/xfestudio/mydimension/network/ModNetwork.java +49 -2
Added src/main/java/com/xfestudio/mydimension/network/TeamMindCommandPacket.java +160 -0
Added src/main/java/com/xfestudio/mydimension/network/TeamMindDataPacket.java +87 -0
Modified src/main/java/com/xfestudio/mydimension/world/MindInstances.java +22 -2
Added src/main/java/com/xfestudio/mydimension/world/MindTeamAccess.java +236 -0
Added src/main/java/com/xfestudio/mydimension/world/MindType.java +29 -0
Modified src/main/resources/assets/mydimension/lang/en_us.json +39 -1
Modified src/main/resources/assets/mydimension/lang/zh_cn.json +38 -0