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

MyDimension

【Java】我的维度模组

公开
关注 0 Fork 0 Star 0
UTF-8
package com.xfestudio.mydimension.client;

import com.xfestudio.mydimension.MyDimension;
import com.xfestudio.mydimension.network.ModNetwork;
import com.xfestudio.mydimension.network.TeamMindCommandPacket;
import com.xfestudio.mydimension.network.TeamMindDataPacket;
import com.xfestudio.mydimension.world.MindType;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.function.BooleanSupplier;

public class TeamMindScreen extends Screen {
    private static final int ENTRANCE_COLOR = 0xFF3E8DB8;
    private static final int ADD_COLOR = 0xFF4EBA7A;
    private static final int MANAGE_COLOR = 0xFF8B58C8;
    private static final ResourceLocation RIFT_ICON = new ResourceLocation(MyDimension.MOD_ID, "textures/item/rift.png");

    private final Screen parent;
    private final long openedAt = Util.getMillis();
    private final Set<UUID> requestedPlayers = new HashSet<>();
    private TeamTab tab = TeamTab.ENTRANCES;
    private List<TeamMindDataPacket.PlayerInfo> entrances = List.of();
    private List<TeamMindDataPacket.PlayerInfo> candidates = List.of();
    private List<TeamMindDataPacket.PlayerInfo> guests = List.of();
    private UUID selectedOwner;
    private int listPage;
    private boolean requestedData;

    public TeamMindScreen(Screen parent) {
        super(Component.translatable("screen.mydimension.team.title"));
        this.parent = parent;
    }

    @Override
    protected void init() {
        Layout layout = createLayout();
        addTabButtons(layout);
        switch (tab) {
            case ENTRANCES -> addEntranceWidgets(layout);
            case ADD -> addPlayerRows(layout, candidates, RowAction.REQUEST);
            case MANAGE -> addPlayerRows(layout, guests, RowAction.REVOKE);
        }
        addFooterButtons(layout);

        if (!requestedData) {
            requestedData = true;
            ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.refresh());
        }
    }

    public void updateData(TeamMindDataPacket packet) {
        entrances = packet.entrances();
        candidates = packet.candidates();
        guests = packet.guests();
        requestedPlayers.clear();
        if (selectedOwner == null || entrances.stream().noneMatch(player -> player.id().equals(selectedOwner))) {
            selectedOwner = entrances.isEmpty() ? null : entrances.get(0).id();
        }
        clampPage();
        rebuildWidgets();
    }

    @Override
    public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
        graphics.fillGradient(0, 0, width, height, 0xD0101218, 0xE0181026);
        Layout layout = createLayout();
        float progress = easeOut(Mth.clamp((Util.getMillis() - openedAt) / 260.0F, 0.0F, 1.0F));
        fillRoundedRect(graphics, layout.left(), layout.top(), layout.right(), layout.bottom(), 8,
                multiplyAlpha(0xD0060A12, progress));
        drawRoundedBorder(graphics, layout.left(), layout.top(), layout.right(), layout.bottom(), 8,
                multiplyAlpha(0xFF85D6F7, progress));
        graphics.fill(layout.left() + 12, layout.top() + 1, layout.left() + 78, layout.top() + 3,
                multiplyAlpha(tab.color(), progress));
        graphics.drawCenteredString(font, title, width / 2, layout.top() + 13, multiplyAlpha(0xFFEAFBFF, progress));

        renderContentLabels(graphics, layout, progress);
        super.render(graphics, mouseX, mouseY, partialTick);
    }

    @Override
    public void onClose() {
        if (minecraft != null) {
            minecraft.setScreen(parent);
        }
    }

    @Override
    public boolean isPauseScreen() {
        return false;
    }

    private Layout createLayout() {
        int panelWidth = Math.min(500, Math.max(300, width - 24));
        int panelHeight = Math.min(252, Math.max(190, height - 12));
        int left = (width - panelWidth) / 2;
        int top = (height - panelHeight) / 2;
        return new Layout(left, top, left + panelWidth, top + panelHeight, panelWidth, panelHeight);
    }

    private void addTabButtons(Layout layout) {
        TeamTab[] tabs = TeamTab.values();
        int gap = 7;
        int tabWidth = Math.min(118, (layout.width() - 36 - gap * (tabs.length - 1)) / tabs.length);
        int totalWidth = tabWidth * tabs.length + gap * (tabs.length - 1);
        int x = width / 2 - totalWidth / 2;
        int y = layout.top() + 34;

        for (int i = 0; i < tabs.length; i++) {
            TeamTab target = tabs[i];
            addRenderableWidget(new TeamButton(x, y, tabWidth, 22, target.title(), target.color(),
                    () -> tab == target, i, button -> {
                tab = target;
                listPage = 0;
                rebuildWidgets();
            }));
            x += tabWidth + gap;
        }
    }

    private void addEntranceWidgets(Layout layout) {
        int contentTop = layout.top() + 66;
        int leftWidth = Math.min(156, Math.max(112, layout.width() / 3));
        int left = layout.left() + 16;
        int from = listPage * rowsPerPage();
        int to = Math.min(entrances.size(), from + rowsPerPage());

        for (int index = from; index < to; index++) {
            TeamMindDataPacket.PlayerInfo owner = entrances.get(index);
            int y = contentTop + (index - from) * 25;
            addRenderableWidget(new TeamButton(left, y, leftWidth, 23, Component.literal(owner.name()), ENTRANCE_COLOR,
                    () -> owner.id().equals(selectedOwner), index - from + 3, button -> {
                selectedOwner = owner.id();
                rebuildWidgets();
            }));
        }

        if (selectedOwner == null) {
            return;
        }

        int right = left + leftWidth + 16;
        int availableWidth = layout.right() - 16 - right;
        int gap = 8;
        int buttonWidth = (availableWidth - gap) / 2;
        MindType[] mindTypes = MindType.values();
        for (int i = 0; i < mindTypes.length; i++) {
            MindType mindType = mindTypes[i];
            int x = right + (i % 2) * (buttonWidth + gap);
            int y = contentTop + (i / 2) * 31;
            addRenderableWidget(new MindButton(x, y, buttonWidth, 25, mindType, i + 5));
        }
        addPaginationButtons(layout, entrances);
    }

    private void addPlayerRows(Layout layout, List<TeamMindDataPacket.PlayerInfo> players, RowAction action) {
        int rowsPerPage = rowsPerPage();
        int from = listPage * rowsPerPage;
        int to = Math.min(players.size(), from + rowsPerPage);
        int left = layout.left() + 24;
        int right = layout.right() - 24;
        int top = layout.top() + 66;
        int actionWidth = 78;

        for (int index = from; index < to; index++) {
            TeamMindDataPacket.PlayerInfo player = players.get(index);
            int row = index - from;
            int y = top + row * 25;
            addRenderableWidget(new TeamButton(left, y, right - left - actionWidth - 8, 23,
                    Component.literal(player.name()), tab.color(), () -> false, row + 3, button -> {
            }));

            boolean pending = action == RowAction.REQUEST && requestedPlayers.contains(player.id());
            Component label = pending
                    ? Component.translatable("screen.mydimension.team.requested")
                    : Component.translatable(action.translationKey());
            TeamButton actionButton = new TeamButton(right - actionWidth, y, actionWidth, 23, label,
                    action == RowAction.REQUEST ? ADD_COLOR : 0xFFC95D66, () -> false, row + 4, button -> {
                if (action == RowAction.REQUEST) {
                    requestedPlayers.add(player.id());
                    ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.requestAccess(player.id()));
                    rebuildWidgets();
                } else {
                    ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.revoke(player.id()));
                }
            });
            actionButton.active = !pending;
            addRenderableWidget(actionButton);
        }

        addPaginationButtons(layout, players);
    }

    private void addFooterButtons(Layout layout) {
        int y = layout.bottom() - 29;
        addRenderableWidget(new TeamButton(layout.left() + 12, y, 62, 20,
                Component.translatable("gui.back"), 0xFF47677A, () -> false, 10, button -> onClose()));
        addRenderableWidget(new TeamButton(layout.right() - 74, y, 62, 20,
                Component.translatable("screen.mydimension.team.refresh"), 0xFF47677A, () -> false, 10, button -> {
            ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.refresh());
        }));
    }

    private void renderContentLabels(GuiGraphics graphics, Layout layout, float progress) {
        int color = multiplyAlpha(0xFFA9C9D8, progress);
        if (tab == TeamTab.ENTRANCES) {
            if (entrances.isEmpty()) {
                graphics.drawCenteredString(font, Component.translatable("screen.mydimension.team.no_entrances"),
                        width / 2, layout.top() + 119, color);
                return;
            }
            TeamMindDataPacket.PlayerInfo owner = selectedOwnerInfo();
            if (owner != null) {
                int leftWidth = Math.min(156, Math.max(112, layout.width() / 3));
                int center = layout.left() + 16 + leftWidth + 16 + (layout.right() - 16 - (layout.left() + 16 + leftWidth + 16)) / 2;
                graphics.drawCenteredString(font, Component.translatable("screen.mydimension.team.owner_minds", owner.name()),
                        center, layout.top() + 62, color);
            }
            renderPageNumber(graphics, layout, entrances, color);
        } else {
            List<TeamMindDataPacket.PlayerInfo> players = currentList();
            if (players.isEmpty()) {
                Component empty = Component.translatable(tab == TeamTab.ADD
                        ? "screen.mydimension.team.no_candidates"
                        : "screen.mydimension.team.no_guests");
                graphics.drawCenteredString(font, empty, width / 2, layout.top() + 119, color);
            }
            renderPageNumber(graphics, layout, players, color);
        }
    }

    private TeamMindDataPacket.PlayerInfo selectedOwnerInfo() {
        return entrances.stream().filter(player -> player.id().equals(selectedOwner)).findFirst().orElse(null);
    }

    private List<TeamMindDataPacket.PlayerInfo> currentList() {
        return switch (tab) {
            case ENTRANCES -> entrances;
            case ADD -> candidates;
            case MANAGE -> guests;
        };
    }

    private void addPaginationButtons(Layout layout, List<?> values) {
        int pages = pageCount(values);
        if (pages <= 1) {
            return;
        }

        int y = layout.bottom() - 39;
        TeamButton previous = new TeamButton(width / 2 - 54, y, 24, 20, Component.literal("<"), tab.color(),
                () -> false, 9, button -> changePage(-1));
        previous.active = listPage > 0;
        addRenderableWidget(previous);
        TeamButton next = new TeamButton(width / 2 + 30, y, 24, 20, Component.literal(">"), tab.color(),
                () -> false, 9, button -> changePage(1));
        next.active = listPage + 1 < pages;
        addRenderableWidget(next);
    }

    private void renderPageNumber(GuiGraphics graphics, Layout layout, List<?> values, int color) {
        int pages = pageCount(values);
        if (pages > 1) {
            graphics.drawCenteredString(font, Component.literal((listPage + 1) + " / " + pages),
                    width / 2, layout.bottom() - 34, color);
        }
    }

    private void changePage(int amount) {
        listPage = Mth.clamp(listPage + amount, 0, Math.max(0, pageCount(currentList()) - 1));
        if (tab == TeamTab.ENTRANCES && !entrances.isEmpty()) {
            selectedOwner = entrances.get(Math.min(entrances.size() - 1, listPage * rowsPerPage())).id();
        }
        rebuildWidgets();
    }

    private void clampPage() {
        listPage = Mth.clamp(listPage, 0, Math.max(0, pageCount(currentList()) - 1));
    }

    private static int pageCount(List<?> values) {
        int rowsPerPage = rowsPerPage();
        return Math.max(1, (values.size() + rowsPerPage - 1) / rowsPerPage);
    }

    private static int rowsPerPage() {
        Minecraft minecraft = Minecraft.getInstance();
        return minecraft.getWindow().getGuiScaledHeight() < 232 ? 3 : 5;
    }

    private static float easeOut(float value) {
        float inverse = 1.0F - value;
        return 1.0F - inverse * inverse * inverse;
    }

    private static int multiplyAlpha(int color, float multiplier) {
        int alpha = Math.round(((color >>> 24) & 0xFF) * Mth.clamp(multiplier, 0.0F, 1.0F));
        return (alpha << 24) | (color & 0x00FFFFFF);
    }

    private static int withAlpha(int color, int alpha) {
        return (Mth.clamp(alpha, 0, 255) << 24) | (color & 0x00FFFFFF);
    }

    private static void fillRoundedRect(GuiGraphics graphics, int left, int top, int right, int bottom, int radius, int color) {
        graphics.fill(left + radius, top, right - radius, bottom, color);
        graphics.fill(left, top + radius, right, bottom - radius, color);
        graphics.fill(left + 1, top + 1, right - 1, top + radius, color);
        graphics.fill(left + 1, bottom - radius, right - 1, bottom - 1, color);
    }

    private static void drawRoundedBorder(GuiGraphics graphics, int left, int top, int right, int bottom, int radius, int color) {
        graphics.fill(left + radius, top, right - radius, top + 1, color);
        graphics.fill(left + radius, bottom - 1, right - radius, bottom, color);
        graphics.fill(left, top + radius, left + 1, bottom - radius, color);
        graphics.fill(right - 1, top + radius, right, bottom - radius, color);
        graphics.fill(left + 1, top + 1, left + radius, top + 2, color);
        graphics.fill(right - radius, top + 1, right - 1, top + 2, color);
        graphics.fill(left + 1, bottom - 2, left + radius, bottom - 1, color);
        graphics.fill(right - radius, bottom - 2, right - 1, bottom - 1, color);
    }

    private enum TeamTab {
        ENTRANCES("screen.mydimension.team.tab.entrances", ENTRANCE_COLOR),
        ADD("screen.mydimension.team.tab.add", ADD_COLOR),
        MANAGE("screen.mydimension.team.tab.manage", MANAGE_COLOR);

        private final String translationKey;
        private final int color;

        TeamTab(String translationKey, int color) {
            this.translationKey = translationKey;
            this.color = color;
        }

        private Component title() {
            return Component.translatable(translationKey);
        }

        private int color() {
            return color;
        }
    }

    private enum RowAction {
        REQUEST("screen.mydimension.team.request"),
        REVOKE("screen.mydimension.team.revoke");

        private final String translationKey;

        RowAction(String translationKey) {
            this.translationKey = translationKey;
        }

        private String translationKey() {
            return translationKey;
        }
    }

    private record Layout(int left, int top, int right, int bottom, int width, int height) {
    }

    private class MindButton extends TeamButton {
        private final MindType mindType;

        private MindButton(int x, int y, int width, int height, MindType mindType, int animationOrder) {
            super(x, y, width, height, mindType.displayName(), ENTRANCE_COLOR, () -> false, animationOrder,
                    button -> TeamMindScreen.this.selectMind(mindType));
            this.mindType = mindType;
        }

        @Override
        protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
            super.renderWidget(graphics, mouseX, mouseY, partialTick);
            int y = getY() + (getHeight() - 16) / 2;
            graphics.blit(RIFT_ICON, getX() + 8, y, 0, 0, 16, 16, 16, 16);
        }

    }

    private void selectMind(MindType mindType) {
        if (selectedOwner == null || minecraft == null) {
            return;
        }
        ModNetwork.CHANNEL.sendToServer(TeamMindCommandPacket.select(selectedOwner, mindType));
        minecraft.setScreen(null);
    }

    private class TeamButton extends Button {
        private final int color;
        private final BooleanSupplier selected;
        private final int animationOrder;
        private float hoverProgress;

        private TeamButton(int x, int y, int width, int height, Component message, int color,
                           BooleanSupplier selected, int animationOrder, OnPress onPress) {
            super(x, y, width, height, message, onPress, DEFAULT_NARRATION);
            this.color = color;
            this.selected = selected;
            this.animationOrder = animationOrder;
        }

        @Override
        protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
            hoverProgress = Mth.clamp(hoverProgress + (isHoveredOrFocused() ? 0.15F : -0.15F), 0.0F, 1.0F);
            float progress = easeOut(Mth.clamp((Util.getMillis() - openedAt - animationOrder * 28L) / 220.0F, 0.0F, 1.0F));
            int top = getY() + Math.round((1.0F - progress) * 7.0F);
            int fillAlpha = active ? Math.round(Mth.lerp(hoverProgress, 150.0F, 220.0F)) : 75;
            int edge = selected.getAsBoolean() ? 0xFFFFF4A8 : (active ? 0xFFB8F0FF : 0xFF6A747B);

            fillRoundedRect(graphics, getX(), top, getX() + getWidth(), top + getHeight(), 6,
                    multiplyAlpha(withAlpha(color, fillAlpha), progress));
            drawRoundedBorder(graphics, getX(), top, getX() + getWidth(), top + getHeight(), 6,
                    multiplyAlpha(edge, progress));
            int textLeft = this instanceof MindButton ? getX() + 29 : getX();
            int textWidth = this instanceof MindButton ? getWidth() - 34 : getWidth();
            drawCenteredFittingText(graphics, Minecraft.getInstance().font, getMessage(), textLeft, top,
                    textWidth, getHeight(), multiplyAlpha(active ? 0xFFEAFBFF : 0xFF8A969C, progress));
        }
    }

    private static void drawCenteredFittingText(GuiGraphics graphics, Font font, Component text,
                                                int left, int top, int width, int height, int color) {
        String label = font.plainSubstrByWidth(text.getString(), Math.max(4, width - 8));
        graphics.drawCenteredString(font, label, left + width / 2, top + (height - 8) / 2, color);
    }
}