Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Часть функционала SwapWhell | ExosWare 1 21 4

  • Автор темы Автор темы Its3afif
  • Дата начала Дата начала
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
25 Дек 2023
Сообщения
15
Реакции
0
Выберите загрузчик игры
  1. Fabric
Клесо свапа шаров таликов и тд сфер, фото прикреплю ниже вот код , жду del

Swap:
Expand Collapse Copy
package ru.levin.modules.player;

import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.render.*;
import net.minecraft.client.util.InputUtil;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.screen.slot.SlotActionType;
import org.joml.Matrix4f;
import ru.levin.events.Event;
import ru.levin.events.impl.input.EventKey;
import ru.levin.events.impl.input.EventMouse;
import ru.levin.events.impl.render.EventRender2D;
import ru.levin.modules.Function;
import ru.levin.modules.FunctionAnnotation;
import ru.levin.modules.Type;
import ru.levin.modules.setting.BindSetting;
import ru.levin.modules.setting.BooleanSetting;
import ru.levin.modules.setting.ModeSetting;
import ru.levin.util.player.InventoryUtil;
import ru.levin.util.player.TimerUtil;
import ru.levin.util.render.RenderUtil;

import java.awt.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;

@FunctionAnnotation(name = "SwapWheel", desc = "Круговое меню для быстрой смены предметов", type = Type.Render)
public class SwapWheel extends Function {

    private final BindSetting wheelBind = new BindSetting("Клавиша открытия", 0);
    private final ModeSetting show = new ModeSetting("Показывать", "Тотемы и головы", "Тотемы", "Головы", "Тотемы и головы");
    private final BooleanSetting ftHwBypass = new BooleanSetting("Обход FT/HW", false, "Для серверов с античитом");

    private final TimerUtil timer = new TimerUtil();
    private boolean bypassActive = false;
    private boolean awaitingSwap = false;
    private int pendingSlot = -1;
    private int pendingTargetSlot = -1;

    private boolean wheelOpen = false;
    private boolean cursorUnlocked = false;
    private List<WheelEntry> wheelEntries = new ArrayList<>();
    private int selectedSegment = -1;

    public SwapWheel() {
        addSettings(wheelBind, show, ftHwBypass);
    }

    @Override
    public void onEvent(Event event) {
        if (mc == null || mc.player == null || mc.world == null) return;

        if (event instanceof EventKey e) {
            if (mc.currentScreen != null) return;
            if (e.key == wheelBind.getKey()) {
                toggleWheel();
                e.isCancel();
            }
        }


        if (event instanceof EventMouse e) {
            if (!wheelOpen || mc.currentScreen != null) return;

            e.isCancel();

            if (e.getButton() == 0) { // ЛКМ
                if (selectedSegment >= 0 && selectedSegment < wheelEntries.size()) {
                    pickWheelSlot(selectedSegment);
                } else {
                    toggleWheel();
                }
            }
            if (e.getButton() == 1) {
                toggleWheel();
            }
        }


        if (event instanceof EventRender2D e) {
            if (!wheelOpen) return;
            if (mc.currentScreen != null) {
                wheelOpen = false;
                updateCursorLock(false);
                return;
            }
            renderWheel(e);
        }
    }

    @Override
    public void onDisable() {
        wheelOpen = false;
        updateCursorLock(false);
        bypassActive = false;
        awaitingSwap = false;
        super.onDisable();
    }

    private void toggleWheel() {
        wheelOpen = !wheelOpen;
        updateCursorLock(wheelOpen);
        if (wheelOpen) {
            wheelEntries = buildWheelEntries();
            selectedSegment = -1;
        }
    }

    private void updateCursorLock(boolean locked) {
        if (mc == null || mc.mouse == null) return;
        if (locked) {
            if (!cursorUnlocked) {
                mc.mouse.unlockCursor();
                cursorUnlocked = true;
            }
        } else {
            if (cursorUnlocked) {
                if (mc.currentScreen == null) {
                    mc.mouse.lockCursor();
                }
                cursorUnlocked = false;
            }
        }
    }

    private void pickWheelSlot(int index) {
        if (wheelEntries.isEmpty() || index >= wheelEntries.size()) {
            toggleWheel();
            return;
        }

        WheelEntry entry = wheelEntries.get(index);
        ItemSlot slot = findBestSlot(entry.predicate);
        if (slot == null) {
            toggleWheel();
            return;
        }

        moveToOffhand(slot);
        toggleWheel();
    }

    private void moveToOffhand(ItemSlot slot) {
        if (mc.player == null || mc.interactionManager == null || mc.player.currentScreenHandler == null) return;
        if (slot.idForServer == 45) return;

        int offhandSlot = 40;
        int fromSlot = slot.idForServer;


        int clickSlot = (fromSlot < 9) ? fromSlot + 36 : fromSlot;

        if (ftHwBypass.get()) {

            timer.reset();
            bypassActive = true;
            awaitingSwap = true;
            pendingSlot = clickSlot;
            pendingTargetSlot = offhandSlot;


            mc.options.forwardKey.setPressed(false);
            mc.options.backKey.setPressed(false);
            mc.options.leftKey.setPressed(false);
            mc.options.rightKey.setPressed(false);
            mc.options.sprintKey.setPressed(false);
        } else {

            mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, clickSlot, offhandSlot, SlotActionType.SWAP, mc.player);
        }
    }

    private void performSwap() {
        if (mc.player == null || mc.interactionManager == null || mc.player.currentScreenHandler == null) return;
        if (pendingSlot != -1 && pendingTargetSlot != -1) {
            mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, pendingSlot, pendingTargetSlot, SlotActionType.SWAP, mc.player);
            pendingSlot = -1;
            pendingTargetSlot = -1;
        }
    }

    private void resetBypass() {
        bypassActive = false;
        awaitingSwap = false;
        pendingSlot = -1;
        pendingTargetSlot = -1;


        if (mc.options != null) {
            updateKeyBinding(mc.options.forwardKey);
            updateKeyBinding(mc.options.backKey);
            updateKeyBinding(mc.options.leftKey);
            updateKeyBinding(mc.options.rightKey);
            updateKeyBinding(mc.options.sprintKey);
        }
    }

    private void updateKeyBinding(KeyBinding keyMapping) {
        if (keyMapping == null) return;
        keyMapping.setPressed(InputUtil.isKeyPressed(mc.getWindow().getHandle(), keyMapping.getDefaultKey().getCode()));
    }

    private void handleBypass() {
        if (!bypassActive) return;


        mc.options.forwardKey.setPressed(false);
        mc.options.backKey.setPressed(false);
        mc.options.leftKey.setPressed(false);
        mc.options.rightKey.setPressed(false);
        mc.options.sprintKey.setPressed(false);

        if (awaitingSwap && timer.hasTimeElapsed(90)) {
            awaitingSwap = false;
            performSwap();
        }

        if (timer.hasTimeElapsed(150)) {
            resetBypass();
        }
    }

    private ItemSlot findBestSlot(Predicate<ItemStack> predicate) {
        if (predicate == null) return null;

        List<ItemSlot> slots = new ArrayList<>();


        for (int i = 0; i <= 8; i++) {
            ItemStack stack = mc.player.getInventory().getStack(i);
            if (!stack.isEmpty() && predicate.test(stack)) {
                slots.add(new ItemSlot(i, stack));
            }
        }


        for (int i = 9; i <= 35; i++) {
            ItemStack stack = mc.player.getInventory().getStack(i);
            if (!stack.isEmpty() && predicate.test(stack)) {
                slots.add(new ItemSlot(i, stack));
            }
        }



        if (slots.isEmpty()) return null;


        slots.sort(Comparator.comparingInt(s -> {
            if (s.idForServer >= 0 && s.idForServer <= 8) return 0;
            return 1;
        }));

        return slots.get(0);
    }

    private List<WheelEntry> buildWheelEntries() {
        if (mc.player == null) return new ArrayList<>();

        List<WheelEntry> entries = new ArrayList<>();
        HashMap<String, WheelEntryAccum> accum = new HashMap<>();

        for (int i = 0; i <= 35; i++) {
            ItemStack stack = mc.player.getInventory().getStack(i);
            if (stack.isEmpty() || stack.getItem() == Items.AIR) continue;

            if ((show.is("Тотемы") || show.is("Тотемы и головы")) && stack.getItem() == Items.TOTEM_OF_UNDYING) {
                String key = "totem:" + getStackKey(stack);
                WheelEntryAccum acc = accum.computeIfAbsent(key, k ->
                        new WheelEntryAccum(stack.copyWithCount(1), 0, s -> s.getItem() == Items.TOTEM_OF_UNDYING && getStackKey(s).equals(getStackKey(stack))));
                acc.totalCount += stack.getCount();
            }

            if ((show.is("Головы") || show.is("Тотемы и головы")) && stack.getItem() == Items.PLAYER_HEAD) {
                String key = "head:" + getStackKey(stack);
                WheelEntryAccum acc = accum.computeIfAbsent(key, k ->
                        new WheelEntryAccum(stack.copyWithCount(1), 0, s -> s.getItem() == Items.PLAYER_HEAD && getStackKey(s).equals(getStackKey(stack))));
                acc.totalCount += stack.getCount();
            }
        }

        for (WheelEntryAccum acc : accum.values()) {
            entries.add(new WheelEntry(acc.displayStack, acc.totalCount, acc.predicate));
        }

        entries.sort((a, b) -> {
            if (a.displayStack.getItem() == Items.TOTEM_OF_UNDYING && b.displayStack.getItem() != Items.TOTEM_OF_UNDYING) return -1;
            if (a.displayStack.getItem() != Items.TOTEM_OF_UNDYING && b.displayStack.getItem() == Items.TOTEM_OF_UNDYING) return 1;
            return 0;
        });

        return entries;
    }

    private String getStackKey(ItemStack stack) {
        if (stack.getItem() == Items.TOTEM_OF_UNDYING) {
            if (stack.hasEnchantments()) return "enchanted";
            return "normal";
        }
        if (stack.getItem() == Items.PLAYER_HEAD) {
            try {
                var components = stack.getComponents();
                var profileComponent = components.get(net.minecraft.component.DataComponentTypes.PROFILE);
                if (profileComponent != null && profileComponent.name() != null) {
                    return profileComponent.name().get();
                }
            } catch (Exception ignored) {}
            return "player_head";
        }
        return stack.getItem().toString();
    }

    private void renderWheel(EventRender2D event) {

        if (bypassActive) {
            handleBypass();
        }

        int count = Math.max(3, wheelEntries.isEmpty() ? 4 : wheelEntries.size());
        float cx = mc.getWindow().getScaledWidth() / 2f;
        float cy = mc.getWindow().getScaledHeight() / 2f;
        float outerR = 92f + Math.max(0, count - 8) * 6f;
        float innerR = Math.max(26f, outerR - 38f);
        float mouseX = (float) (mc.mouse.getX() / mc.getWindow().getScaleFactor());
        float mouseY = (float) (mc.mouse.getY() / mc.getWindow().getScaleFactor());

        selectedSegment = getHoverIndex(mouseX, mouseY, cx, cy, innerR, outerR, count);
        if (selectedSegment >= count) selectedSegment = -1;

        RenderSystem.enableBlend();
        RenderSystem.disableDepthTest();
        RenderSystem.disableCull();
        RenderSystem.defaultBlendFunc();

        Matrix4f matrix = event.getDrawContext().getMatrices().peek().getPositionMatrix();
        Tessellator tessellator = RenderSystem.renderThreadTesselator();
        BufferBuilder buffer = tessellator.begin(VertexFormat.DrawMode.TRIANGLES, VertexFormats.POSITION_COLOR);

        drawBackgroundDarkening(event, cx, cy, outerR + 20);

        for (int i = 0; i < count; i++) {
            boolean isHover = i == selectedSegment;
            int r, g, b, a;
            if (isHover) {
                r = 255; g = 209; b = 47; a = 200;
            } else {
                r = 40; g = 40; b = 55; a = 180;
            }
            float start = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * (i / (double)count));
            float end = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * ((i + 1.0) / (double)count));
            drawRingSegment(buffer, matrix, cx, cy, innerR, outerR, start, end, r, g, b, a);
        }

        BufferRenderer.drawWithGlobalProgram(buffer.end());

        for (int i = 0; i < count && i < wheelEntries.size(); i++) {
            WheelEntry entry = wheelEntries.get(i);
            float start = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * (i / (double)count));
            float end = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * ((i + 1.0) / (double)count));
            float mid = (start + end) / 2f;
            float iconR = (innerR + outerR) / 2f;
            float ix = cx + (float)Math.cos(mid) * iconR;
            float iy = cy + (float)Math.sin(mid) * iconR;

            RenderUtil.drawRoundedRect(event.getDrawContext().getMatrices(), ix - 10, iy - 10, 20, 20, 4, new Color(0, 0, 0, 150).getRGB());
            event.getDrawContext().drawItem(entry.displayStack, (int)(ix - 8), (int)(iy - 8));
            if (entry.totalCount > 1) {
                String countStr = String.valueOf(entry.totalCount);
                event.getDrawContext().drawTextWithShadow(mc.textRenderer, countStr, (int)(ix + 5), (int)(iy + 3), 0xFFFFFF);
            }
        }

        drawCenterCircle(event, cx, cy, innerR - 8);

        RenderUtil.drawRoundedRect(event.getDrawContext().getMatrices(), cx - 16, cy - 16, 32, 32, 8, new Color(20, 20, 30, 220).getRGB());
        RenderUtil.drawRoundedBorder(event.getDrawContext().getMatrices(), cx - 16, cy - 16, 32, 32, 8, 2f, new Color(255, 209, 47, 200).getRGB());
        event.getDrawContext().drawItem(mc.player.getOffHandStack(), (int)cx - 8, (int)cy - 8);

        drawCrosshair(event, cx, cy);

        if (selectedSegment >= 0 && selectedSegment < wheelEntries.size()) {
            WheelEntry entry = wheelEntries.get(selectedSegment);
            String name = entry.displayStack.getName().getString();
            if (name.length() > 30) name = name.substring(0, 27) + "...";

            int textWidth = mc.textRenderer.getWidth(name);
            float tooltipX = mouseX + 12;
            float tooltipY = mouseY + 12;
            if (tooltipX + textWidth + 12 > mc.getWindow().getScaledWidth()) {
                tooltipX = mouseX - textWidth - 24;
            }
            if (tooltipY + 20 > mc.getWindow().getScaledHeight()) {
                tooltipY = mouseY - 20;
            }

            RenderUtil.drawRoundedRect(event.getDrawContext().getMatrices(), tooltipX, tooltipY, textWidth + 12, 18, 4, new Color(20, 20, 30, 220).getRGB());
            RenderUtil.drawRoundedBorder(event.getDrawContext().getMatrices(), tooltipX, tooltipY, textWidth + 12, 18, 4, 1f, new Color(255, 209, 47, 150).getRGB());
            event.getDrawContext().drawTextWithShadow(mc.textRenderer, name, (int)(tooltipX + 6), (int)(tooltipY + 5), 0xFFFFFF);
        }

        RenderSystem.enableCull();
        RenderSystem.enableDepthTest();
    }

    private void drawBackgroundDarkening(EventRender2D event, float cx, float cy, float radius) {
        MatrixStack matrices = event.getDrawContext().getMatrices();
        int screenWidth = mc.getWindow().getScaledWidth();
        int screenHeight = mc.getWindow().getScaledHeight();

        RenderUtil.drawRoundedRect(matrices, 0, 0, screenWidth, screenHeight, 0, new Color(0, 0, 0, 150).getRGB());
    }

    private void drawCenterCircle(EventRender2D event, float cx, float cy, float radius) {
        MatrixStack matrices = event.getDrawContext().getMatrices();
        RenderUtil.drawRoundedBorder(matrices, cx - radius, cy - radius, radius * 2, radius * 2, radius, 2f, new Color(255, 209, 47, 180).getRGB());
    }

    private void drawCrosshair(EventRender2D event, float cx, float cy) {
        MatrixStack matrices = event.getDrawContext().getMatrices();
        int size = 8;
        int thickness = 2;

        RenderUtil.drawRoundedRect(matrices, cx - size, cy - thickness / 2f, size * 2, thickness, 1, new Color(255, 255, 255, 200).getRGB());
        RenderUtil.drawRoundedRect(matrices, cx - thickness / 2f, cy - size, thickness, size * 2, 1, new Color(255, 255, 255, 200).getRGB());
        RenderUtil.drawRoundedRect(matrices, cx - 2, cy - 2, 4, 4, 2, new Color(255, 209, 47, 255).getRGB());
    }

    private void drawRingSegment(BufferBuilder buffer, Matrix4f matrix, float cx, float cy, float innerR, float outerR, float start, float end, int r, int g, int b, int a) {
        int steps = Math.max(16, (int)(48f * (Math.abs(end - start) / ((float)Math.PI * 2f))));
        float step = (end - start) / steps;
        for (int i = 0; i < steps; i++) {
            float a0 = start + step * i;
            float a1 = start + step * (i + 1);
            float x0o = cx + (float)Math.cos(a0) * outerR;
            float y0o = cy + (float)Math.sin(a0) * outerR;
            float x1o = cx + (float)Math.cos(a1) * outerR;
            float y1o = cy + (float)Math.sin(a1) * outerR;
            float x0i = cx + (float)Math.cos(a0) * innerR;
            float y0i = cy + (float)Math.sin(a0) * innerR;
            float x1i = cx + (float)Math.cos(a1) * innerR;
            float y1i = cy + (float)Math.sin(a1) * innerR;

            buffer.vertex(matrix, x0i, y0i, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x0o, y0o, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x1o, y1o, 0f).color(r, g, b, a);

            buffer.vertex(matrix, x0i, y0i, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x1o, y1o, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x1i, y1i, 0f).color(r, g, b, a);
        }
    }

    private int getHoverIndex(float mouseX, float mouseY, float cx, float cy, float innerR, float outerR, int count) {
        float dx = mouseX - cx;
        float dy = mouseY - cy;
        float dist = (float)Math.sqrt(dx * dx + dy * dy);
        if (dist < innerR || dist > outerR) return -1;
        double ang = Math.atan2(dy, dx);
        ang = ang + Math.PI / 2.0;
        if (ang < 0) ang += Math.PI * 2.0;
        int idx = (int)Math.floor(ang / (Math.PI * 2.0) * count);
        if (idx < 0 || idx >= count) return -1;
        return idx;
    }

    private static class WheelEntryAccum {
        final ItemStack displayStack;
        int totalCount;
        final Predicate<ItemStack> predicate;
        WheelEntryAccum(ItemStack displayStack, int totalCount, Predicate<ItemStack> predicate) {
            this.displayStack = displayStack;
            this.totalCount = totalCount;
            this.predicate = predicate;
        }
    }

    private static class WheelEntry {
        final ItemStack displayStack;
        final int totalCount;
        final Predicate<ItemStack> predicate;
        WheelEntry(ItemStack displayStack, int totalCount, Predicate<ItemStack> predicate) {
            this.displayStack = displayStack;
            this.totalCount = totalCount;
            this.predicate = predicate;
        }
    }

    private static class ItemSlot {
        final int idForServer;
        final ItemStack itemStack;
        ItemSlot(int idForServer, ItemStack itemStack) {
            this.idForServer = idForServer;
            this.itemStack = itemStack;
        }
    }
}
 

Вложения

  • photo_2026-04-05_18-04-30.jpg
    photo_2026-04-05_18-04-30.jpg
    37.4 KB · Просмотры: 247
Клесо свапа шаров таликов и тд сфер, фото прикреплю ниже вот код , жду del

Swap:
Expand Collapse Copy
package ru.levin.modules.player;

import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.render.*;
import net.minecraft.client.util.InputUtil;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.screen.slot.SlotActionType;
import org.joml.Matrix4f;
import ru.levin.events.Event;
import ru.levin.events.impl.input.EventKey;
import ru.levin.events.impl.input.EventMouse;
import ru.levin.events.impl.render.EventRender2D;
import ru.levin.modules.Function;
import ru.levin.modules.FunctionAnnotation;
import ru.levin.modules.Type;
import ru.levin.modules.setting.BindSetting;
import ru.levin.modules.setting.BooleanSetting;
import ru.levin.modules.setting.ModeSetting;
import ru.levin.util.player.InventoryUtil;
import ru.levin.util.player.TimerUtil;
import ru.levin.util.render.RenderUtil;

import java.awt.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;

@FunctionAnnotation(name = "SwapWheel", desc = "Круговое меню для быстрой смены предметов", type = Type.Render)
public class SwapWheel extends Function {

    private final BindSetting wheelBind = new BindSetting("Клавиша открытия", 0);
    private final ModeSetting show = new ModeSetting("Показывать", "Тотемы и головы", "Тотемы", "Головы", "Тотемы и головы");
    private final BooleanSetting ftHwBypass = new BooleanSetting("Обход FT/HW", false, "Для серверов с античитом");

    private final TimerUtil timer = new TimerUtil();
    private boolean bypassActive = false;
    private boolean awaitingSwap = false;
    private int pendingSlot = -1;
    private int pendingTargetSlot = -1;

    private boolean wheelOpen = false;
    private boolean cursorUnlocked = false;
    private List<WheelEntry> wheelEntries = new ArrayList<>();
    private int selectedSegment = -1;

    public SwapWheel() {
        addSettings(wheelBind, show, ftHwBypass);
    }

    @Override
    public void onEvent(Event event) {
        if (mc == null || mc.player == null || mc.world == null) return;

        if (event instanceof EventKey e) {
            if (mc.currentScreen != null) return;
            if (e.key == wheelBind.getKey()) {
                toggleWheel();
                e.isCancel();
            }
        }


        if (event instanceof EventMouse e) {
            if (!wheelOpen || mc.currentScreen != null) return;

            e.isCancel();

            if (e.getButton() == 0) { // ЛКМ
                if (selectedSegment >= 0 && selectedSegment < wheelEntries.size()) {
                    pickWheelSlot(selectedSegment);
                } else {
                    toggleWheel();
                }
            }
            if (e.getButton() == 1) {
                toggleWheel();
            }
        }


        if (event instanceof EventRender2D e) {
            if (!wheelOpen) return;
            if (mc.currentScreen != null) {
                wheelOpen = false;
                updateCursorLock(false);
                return;
            }
            renderWheel(e);
        }
    }

    @Override
    public void onDisable() {
        wheelOpen = false;
        updateCursorLock(false);
        bypassActive = false;
        awaitingSwap = false;
        super.onDisable();
    }

    private void toggleWheel() {
        wheelOpen = !wheelOpen;
        updateCursorLock(wheelOpen);
        if (wheelOpen) {
            wheelEntries = buildWheelEntries();
            selectedSegment = -1;
        }
    }

    private void updateCursorLock(boolean locked) {
        if (mc == null || mc.mouse == null) return;
        if (locked) {
            if (!cursorUnlocked) {
                mc.mouse.unlockCursor();
                cursorUnlocked = true;
            }
        } else {
            if (cursorUnlocked) {
                if (mc.currentScreen == null) {
                    mc.mouse.lockCursor();
                }
                cursorUnlocked = false;
            }
        }
    }

    private void pickWheelSlot(int index) {
        if (wheelEntries.isEmpty() || index >= wheelEntries.size()) {
            toggleWheel();
            return;
        }

        WheelEntry entry = wheelEntries.get(index);
        ItemSlot slot = findBestSlot(entry.predicate);
        if (slot == null) {
            toggleWheel();
            return;
        }

        moveToOffhand(slot);
        toggleWheel();
    }

    private void moveToOffhand(ItemSlot slot) {
        if (mc.player == null || mc.interactionManager == null || mc.player.currentScreenHandler == null) return;
        if (slot.idForServer == 45) return;

        int offhandSlot = 40;
        int fromSlot = slot.idForServer;


        int clickSlot = (fromSlot < 9) ? fromSlot + 36 : fromSlot;

        if (ftHwBypass.get()) {

            timer.reset();
            bypassActive = true;
            awaitingSwap = true;
            pendingSlot = clickSlot;
            pendingTargetSlot = offhandSlot;


            mc.options.forwardKey.setPressed(false);
            mc.options.backKey.setPressed(false);
            mc.options.leftKey.setPressed(false);
            mc.options.rightKey.setPressed(false);
            mc.options.sprintKey.setPressed(false);
        } else {

            mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, clickSlot, offhandSlot, SlotActionType.SWAP, mc.player);
        }
    }

    private void performSwap() {
        if (mc.player == null || mc.interactionManager == null || mc.player.currentScreenHandler == null) return;
        if (pendingSlot != -1 && pendingTargetSlot != -1) {
            mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, pendingSlot, pendingTargetSlot, SlotActionType.SWAP, mc.player);
            pendingSlot = -1;
            pendingTargetSlot = -1;
        }
    }

    private void resetBypass() {
        bypassActive = false;
        awaitingSwap = false;
        pendingSlot = -1;
        pendingTargetSlot = -1;


        if (mc.options != null) {
            updateKeyBinding(mc.options.forwardKey);
            updateKeyBinding(mc.options.backKey);
            updateKeyBinding(mc.options.leftKey);
            updateKeyBinding(mc.options.rightKey);
            updateKeyBinding(mc.options.sprintKey);
        }
    }

    private void updateKeyBinding(KeyBinding keyMapping) {
        if (keyMapping == null) return;
        keyMapping.setPressed(InputUtil.isKeyPressed(mc.getWindow().getHandle(), keyMapping.getDefaultKey().getCode()));
    }

    private void handleBypass() {
        if (!bypassActive) return;


        mc.options.forwardKey.setPressed(false);
        mc.options.backKey.setPressed(false);
        mc.options.leftKey.setPressed(false);
        mc.options.rightKey.setPressed(false);
        mc.options.sprintKey.setPressed(false);

        if (awaitingSwap && timer.hasTimeElapsed(90)) {
            awaitingSwap = false;
            performSwap();
        }

        if (timer.hasTimeElapsed(150)) {
            resetBypass();
        }
    }

    private ItemSlot findBestSlot(Predicate<ItemStack> predicate) {
        if (predicate == null) return null;

        List<ItemSlot> slots = new ArrayList<>();


        for (int i = 0; i <= 8; i++) {
            ItemStack stack = mc.player.getInventory().getStack(i);
            if (!stack.isEmpty() && predicate.test(stack)) {
                slots.add(new ItemSlot(i, stack));
            }
        }


        for (int i = 9; i <= 35; i++) {
            ItemStack stack = mc.player.getInventory().getStack(i);
            if (!stack.isEmpty() && predicate.test(stack)) {
                slots.add(new ItemSlot(i, stack));
            }
        }



        if (slots.isEmpty()) return null;


        slots.sort(Comparator.comparingInt(s -> {
            if (s.idForServer >= 0 && s.idForServer <= 8) return 0;
            return 1;
        }));

        return slots.get(0);
    }

    private List<WheelEntry> buildWheelEntries() {
        if (mc.player == null) return new ArrayList<>();

        List<WheelEntry> entries = new ArrayList<>();
        HashMap<String, WheelEntryAccum> accum = new HashMap<>();

        for (int i = 0; i <= 35; i++) {
            ItemStack stack = mc.player.getInventory().getStack(i);
            if (stack.isEmpty() || stack.getItem() == Items.AIR) continue;

            if ((show.is("Тотемы") || show.is("Тотемы и головы")) && stack.getItem() == Items.TOTEM_OF_UNDYING) {
                String key = "totem:" + getStackKey(stack);
                WheelEntryAccum acc = accum.computeIfAbsent(key, k ->
                        new WheelEntryAccum(stack.copyWithCount(1), 0, s -> s.getItem() == Items.TOTEM_OF_UNDYING && getStackKey(s).equals(getStackKey(stack))));
                acc.totalCount += stack.getCount();
            }

            if ((show.is("Головы") || show.is("Тотемы и головы")) && stack.getItem() == Items.PLAYER_HEAD) {
                String key = "head:" + getStackKey(stack);
                WheelEntryAccum acc = accum.computeIfAbsent(key, k ->
                        new WheelEntryAccum(stack.copyWithCount(1), 0, s -> s.getItem() == Items.PLAYER_HEAD && getStackKey(s).equals(getStackKey(stack))));
                acc.totalCount += stack.getCount();
            }
        }

        for (WheelEntryAccum acc : accum.values()) {
            entries.add(new WheelEntry(acc.displayStack, acc.totalCount, acc.predicate));
        }

        entries.sort((a, b) -> {
            if (a.displayStack.getItem() == Items.TOTEM_OF_UNDYING && b.displayStack.getItem() != Items.TOTEM_OF_UNDYING) return -1;
            if (a.displayStack.getItem() != Items.TOTEM_OF_UNDYING && b.displayStack.getItem() == Items.TOTEM_OF_UNDYING) return 1;
            return 0;
        });

        return entries;
    }

    private String getStackKey(ItemStack stack) {
        if (stack.getItem() == Items.TOTEM_OF_UNDYING) {
            if (stack.hasEnchantments()) return "enchanted";
            return "normal";
        }
        if (stack.getItem() == Items.PLAYER_HEAD) {
            try {
                var components = stack.getComponents();
                var profileComponent = components.get(net.minecraft.component.DataComponentTypes.PROFILE);
                if (profileComponent != null && profileComponent.name() != null) {
                    return profileComponent.name().get();
                }
            } catch (Exception ignored) {}
            return "player_head";
        }
        return stack.getItem().toString();
    }

    private void renderWheel(EventRender2D event) {

        if (bypassActive) {
            handleBypass();
        }

        int count = Math.max(3, wheelEntries.isEmpty() ? 4 : wheelEntries.size());
        float cx = mc.getWindow().getScaledWidth() / 2f;
        float cy = mc.getWindow().getScaledHeight() / 2f;
        float outerR = 92f + Math.max(0, count - 8) * 6f;
        float innerR = Math.max(26f, outerR - 38f);
        float mouseX = (float) (mc.mouse.getX() / mc.getWindow().getScaleFactor());
        float mouseY = (float) (mc.mouse.getY() / mc.getWindow().getScaleFactor());

        selectedSegment = getHoverIndex(mouseX, mouseY, cx, cy, innerR, outerR, count);
        if (selectedSegment >= count) selectedSegment = -1;

        RenderSystem.enableBlend();
        RenderSystem.disableDepthTest();
        RenderSystem.disableCull();
        RenderSystem.defaultBlendFunc();

        Matrix4f matrix = event.getDrawContext().getMatrices().peek().getPositionMatrix();
        Tessellator tessellator = RenderSystem.renderThreadTesselator();
        BufferBuilder buffer = tessellator.begin(VertexFormat.DrawMode.TRIANGLES, VertexFormats.POSITION_COLOR);

        drawBackgroundDarkening(event, cx, cy, outerR + 20);

        for (int i = 0; i < count; i++) {
            boolean isHover = i == selectedSegment;
            int r, g, b, a;
            if (isHover) {
                r = 255; g = 209; b = 47; a = 200;
            } else {
                r = 40; g = 40; b = 55; a = 180;
            }
            float start = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * (i / (double)count));
            float end = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * ((i + 1.0) / (double)count));
            drawRingSegment(buffer, matrix, cx, cy, innerR, outerR, start, end, r, g, b, a);
        }

        BufferRenderer.drawWithGlobalProgram(buffer.end());

        for (int i = 0; i < count && i < wheelEntries.size(); i++) {
            WheelEntry entry = wheelEntries.get(i);
            float start = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * (i / (double)count));
            float end = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * ((i + 1.0) / (double)count));
            float mid = (start + end) / 2f;
            float iconR = (innerR + outerR) / 2f;
            float ix = cx + (float)Math.cos(mid) * iconR;
            float iy = cy + (float)Math.sin(mid) * iconR;

            RenderUtil.drawRoundedRect(event.getDrawContext().getMatrices(), ix - 10, iy - 10, 20, 20, 4, new Color(0, 0, 0, 150).getRGB());
            event.getDrawContext().drawItem(entry.displayStack, (int)(ix - 8), (int)(iy - 8));
            if (entry.totalCount > 1) {
                String countStr = String.valueOf(entry.totalCount);
                event.getDrawContext().drawTextWithShadow(mc.textRenderer, countStr, (int)(ix + 5), (int)(iy + 3), 0xFFFFFF);
            }
        }

        drawCenterCircle(event, cx, cy, innerR - 8);

        RenderUtil.drawRoundedRect(event.getDrawContext().getMatrices(), cx - 16, cy - 16, 32, 32, 8, new Color(20, 20, 30, 220).getRGB());
        RenderUtil.drawRoundedBorder(event.getDrawContext().getMatrices(), cx - 16, cy - 16, 32, 32, 8, 2f, new Color(255, 209, 47, 200).getRGB());
        event.getDrawContext().drawItem(mc.player.getOffHandStack(), (int)cx - 8, (int)cy - 8);

        drawCrosshair(event, cx, cy);

        if (selectedSegment >= 0 && selectedSegment < wheelEntries.size()) {
            WheelEntry entry = wheelEntries.get(selectedSegment);
            String name = entry.displayStack.getName().getString();
            if (name.length() > 30) name = name.substring(0, 27) + "...";

            int textWidth = mc.textRenderer.getWidth(name);
            float tooltipX = mouseX + 12;
            float tooltipY = mouseY + 12;
            if (tooltipX + textWidth + 12 > mc.getWindow().getScaledWidth()) {
                tooltipX = mouseX - textWidth - 24;
            }
            if (tooltipY + 20 > mc.getWindow().getScaledHeight()) {
                tooltipY = mouseY - 20;
            }

            RenderUtil.drawRoundedRect(event.getDrawContext().getMatrices(), tooltipX, tooltipY, textWidth + 12, 18, 4, new Color(20, 20, 30, 220).getRGB());
            RenderUtil.drawRoundedBorder(event.getDrawContext().getMatrices(), tooltipX, tooltipY, textWidth + 12, 18, 4, 1f, new Color(255, 209, 47, 150).getRGB());
            event.getDrawContext().drawTextWithShadow(mc.textRenderer, name, (int)(tooltipX + 6), (int)(tooltipY + 5), 0xFFFFFF);
        }

        RenderSystem.enableCull();
        RenderSystem.enableDepthTest();
    }

    private void drawBackgroundDarkening(EventRender2D event, float cx, float cy, float radius) {
        MatrixStack matrices = event.getDrawContext().getMatrices();
        int screenWidth = mc.getWindow().getScaledWidth();
        int screenHeight = mc.getWindow().getScaledHeight();

        RenderUtil.drawRoundedRect(matrices, 0, 0, screenWidth, screenHeight, 0, new Color(0, 0, 0, 150).getRGB());
    }

    private void drawCenterCircle(EventRender2D event, float cx, float cy, float radius) {
        MatrixStack matrices = event.getDrawContext().getMatrices();
        RenderUtil.drawRoundedBorder(matrices, cx - radius, cy - radius, radius * 2, radius * 2, radius, 2f, new Color(255, 209, 47, 180).getRGB());
    }

    private void drawCrosshair(EventRender2D event, float cx, float cy) {
        MatrixStack matrices = event.getDrawContext().getMatrices();
        int size = 8;
        int thickness = 2;

        RenderUtil.drawRoundedRect(matrices, cx - size, cy - thickness / 2f, size * 2, thickness, 1, new Color(255, 255, 255, 200).getRGB());
        RenderUtil.drawRoundedRect(matrices, cx - thickness / 2f, cy - size, thickness, size * 2, 1, new Color(255, 255, 255, 200).getRGB());
        RenderUtil.drawRoundedRect(matrices, cx - 2, cy - 2, 4, 4, 2, new Color(255, 209, 47, 255).getRGB());
    }

    private void drawRingSegment(BufferBuilder buffer, Matrix4f matrix, float cx, float cy, float innerR, float outerR, float start, float end, int r, int g, int b, int a) {
        int steps = Math.max(16, (int)(48f * (Math.abs(end - start) / ((float)Math.PI * 2f))));
        float step = (end - start) / steps;
        for (int i = 0; i < steps; i++) {
            float a0 = start + step * i;
            float a1 = start + step * (i + 1);
            float x0o = cx + (float)Math.cos(a0) * outerR;
            float y0o = cy + (float)Math.sin(a0) * outerR;
            float x1o = cx + (float)Math.cos(a1) * outerR;
            float y1o = cy + (float)Math.sin(a1) * outerR;
            float x0i = cx + (float)Math.cos(a0) * innerR;
            float y0i = cy + (float)Math.sin(a0) * innerR;
            float x1i = cx + (float)Math.cos(a1) * innerR;
            float y1i = cy + (float)Math.sin(a1) * innerR;

            buffer.vertex(matrix, x0i, y0i, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x0o, y0o, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x1o, y1o, 0f).color(r, g, b, a);

            buffer.vertex(matrix, x0i, y0i, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x1o, y1o, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x1i, y1i, 0f).color(r, g, b, a);
        }
    }

    private int getHoverIndex(float mouseX, float mouseY, float cx, float cy, float innerR, float outerR, int count) {
        float dx = mouseX - cx;
        float dy = mouseY - cy;
        float dist = (float)Math.sqrt(dx * dx + dy * dy);
        if (dist < innerR || dist > outerR) return -1;
        double ang = Math.atan2(dy, dx);
        ang = ang + Math.PI / 2.0;
        if (ang < 0) ang += Math.PI * 2.0;
        int idx = (int)Math.floor(ang / (Math.PI * 2.0) * count);
        if (idx < 0 || idx >= count) return -1;
        return idx;
    }

    private static class WheelEntryAccum {
        final ItemStack displayStack;
        int totalCount;
        final Predicate<ItemStack> predicate;
        WheelEntryAccum(ItemStack displayStack, int totalCount, Predicate<ItemStack> predicate) {
            this.displayStack = displayStack;
            this.totalCount = totalCount;
            this.predicate = predicate;
        }
    }

    private static class WheelEntry {
        final ItemStack displayStack;
        final int totalCount;
        final Predicate<ItemStack> predicate;
        WheelEntry(ItemStack displayStack, int totalCount, Predicate<ItemStack> predicate) {
            this.displayStack = displayStack;
            this.totalCount = totalCount;
            this.predicate = predicate;
        }
    }

    private static class ItemSlot {
        final int idForServer;
        final ItemStack itemStack;
        ItemSlot(int idForServer, ItemStack itemStack) {
            this.idForServer = idForServer;
            this.itemStack = itemStack;
        }
    }
}
/del типу даже гпт не смог написать
 
Клесо свапа шаров таликов и тд сфер, фото прикреплю ниже вот код , жду del

Swap:
Expand Collapse Copy
package ru.levin.modules.player;

import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.render.*;
import net.minecraft.client.util.InputUtil;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.screen.slot.SlotActionType;
import org.joml.Matrix4f;
import ru.levin.events.Event;
import ru.levin.events.impl.input.EventKey;
import ru.levin.events.impl.input.EventMouse;
import ru.levin.events.impl.render.EventRender2D;
import ru.levin.modules.Function;
import ru.levin.modules.FunctionAnnotation;
import ru.levin.modules.Type;
import ru.levin.modules.setting.BindSetting;
import ru.levin.modules.setting.BooleanSetting;
import ru.levin.modules.setting.ModeSetting;
import ru.levin.util.player.InventoryUtil;
import ru.levin.util.player.TimerUtil;
import ru.levin.util.render.RenderUtil;

import java.awt.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;

@FunctionAnnotation(name = "SwapWheel", desc = "Круговое меню для быстрой смены предметов", type = Type.Render)
public class SwapWheel extends Function {

    private final BindSetting wheelBind = new BindSetting("Клавиша открытия", 0);
    private final ModeSetting show = new ModeSetting("Показывать", "Тотемы и головы", "Тотемы", "Головы", "Тотемы и головы");
    private final BooleanSetting ftHwBypass = new BooleanSetting("Обход FT/HW", false, "Для серверов с античитом");

    private final TimerUtil timer = new TimerUtil();
    private boolean bypassActive = false;
    private boolean awaitingSwap = false;
    private int pendingSlot = -1;
    private int pendingTargetSlot = -1;

    private boolean wheelOpen = false;
    private boolean cursorUnlocked = false;
    private List<WheelEntry> wheelEntries = new ArrayList<>();
    private int selectedSegment = -1;

    public SwapWheel() {
        addSettings(wheelBind, show, ftHwBypass);
    }

    @Override
    public void onEvent(Event event) {
        if (mc == null || mc.player == null || mc.world == null) return;

        if (event instanceof EventKey e) {
            if (mc.currentScreen != null) return;
            if (e.key == wheelBind.getKey()) {
                toggleWheel();
                e.isCancel();
            }
        }


        if (event instanceof EventMouse e) {
            if (!wheelOpen || mc.currentScreen != null) return;

            e.isCancel();

            if (e.getButton() == 0) { // ЛКМ
                if (selectedSegment >= 0 && selectedSegment < wheelEntries.size()) {
                    pickWheelSlot(selectedSegment);
                } else {
                    toggleWheel();
                }
            }
            if (e.getButton() == 1) {
                toggleWheel();
            }
        }


        if (event instanceof EventRender2D e) {
            if (!wheelOpen) return;
            if (mc.currentScreen != null) {
                wheelOpen = false;
                updateCursorLock(false);
                return;
            }
            renderWheel(e);
        }
    }

    @Override
    public void onDisable() {
        wheelOpen = false;
        updateCursorLock(false);
        bypassActive = false;
        awaitingSwap = false;
        super.onDisable();
    }

    private void toggleWheel() {
        wheelOpen = !wheelOpen;
        updateCursorLock(wheelOpen);
        if (wheelOpen) {
            wheelEntries = buildWheelEntries();
            selectedSegment = -1;
        }
    }

    private void updateCursorLock(boolean locked) {
        if (mc == null || mc.mouse == null) return;
        if (locked) {
            if (!cursorUnlocked) {
                mc.mouse.unlockCursor();
                cursorUnlocked = true;
            }
        } else {
            if (cursorUnlocked) {
                if (mc.currentScreen == null) {
                    mc.mouse.lockCursor();
                }
                cursorUnlocked = false;
            }
        }
    }

    private void pickWheelSlot(int index) {
        if (wheelEntries.isEmpty() || index >= wheelEntries.size()) {
            toggleWheel();
            return;
        }

        WheelEntry entry = wheelEntries.get(index);
        ItemSlot slot = findBestSlot(entry.predicate);
        if (slot == null) {
            toggleWheel();
            return;
        }

        moveToOffhand(slot);
        toggleWheel();
    }

    private void moveToOffhand(ItemSlot slot) {
        if (mc.player == null || mc.interactionManager == null || mc.player.currentScreenHandler == null) return;
        if (slot.idForServer == 45) return;

        int offhandSlot = 40;
        int fromSlot = slot.idForServer;


        int clickSlot = (fromSlot < 9) ? fromSlot + 36 : fromSlot;

        if (ftHwBypass.get()) {

            timer.reset();
            bypassActive = true;
            awaitingSwap = true;
            pendingSlot = clickSlot;
            pendingTargetSlot = offhandSlot;


            mc.options.forwardKey.setPressed(false);
            mc.options.backKey.setPressed(false);
            mc.options.leftKey.setPressed(false);
            mc.options.rightKey.setPressed(false);
            mc.options.sprintKey.setPressed(false);
        } else {

            mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, clickSlot, offhandSlot, SlotActionType.SWAP, mc.player);
        }
    }

    private void performSwap() {
        if (mc.player == null || mc.interactionManager == null || mc.player.currentScreenHandler == null) return;
        if (pendingSlot != -1 && pendingTargetSlot != -1) {
            mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, pendingSlot, pendingTargetSlot, SlotActionType.SWAP, mc.player);
            pendingSlot = -1;
            pendingTargetSlot = -1;
        }
    }

    private void resetBypass() {
        bypassActive = false;
        awaitingSwap = false;
        pendingSlot = -1;
        pendingTargetSlot = -1;


        if (mc.options != null) {
            updateKeyBinding(mc.options.forwardKey);
            updateKeyBinding(mc.options.backKey);
            updateKeyBinding(mc.options.leftKey);
            updateKeyBinding(mc.options.rightKey);
            updateKeyBinding(mc.options.sprintKey);
        }
    }

    private void updateKeyBinding(KeyBinding keyMapping) {
        if (keyMapping == null) return;
        keyMapping.setPressed(InputUtil.isKeyPressed(mc.getWindow().getHandle(), keyMapping.getDefaultKey().getCode()));
    }

    private void handleBypass() {
        if (!bypassActive) return;


        mc.options.forwardKey.setPressed(false);
        mc.options.backKey.setPressed(false);
        mc.options.leftKey.setPressed(false);
        mc.options.rightKey.setPressed(false);
        mc.options.sprintKey.setPressed(false);

        if (awaitingSwap && timer.hasTimeElapsed(90)) {
            awaitingSwap = false;
            performSwap();
        }

        if (timer.hasTimeElapsed(150)) {
            resetBypass();
        }
    }

    private ItemSlot findBestSlot(Predicate<ItemStack> predicate) {
        if (predicate == null) return null;

        List<ItemSlot> slots = new ArrayList<>();


        for (int i = 0; i <= 8; i++) {
            ItemStack stack = mc.player.getInventory().getStack(i);
            if (!stack.isEmpty() && predicate.test(stack)) {
                slots.add(new ItemSlot(i, stack));
            }
        }


        for (int i = 9; i <= 35; i++) {
            ItemStack stack = mc.player.getInventory().getStack(i);
            if (!stack.isEmpty() && predicate.test(stack)) {
                slots.add(new ItemSlot(i, stack));
            }
        }



        if (slots.isEmpty()) return null;


        slots.sort(Comparator.comparingInt(s -> {
            if (s.idForServer >= 0 && s.idForServer <= 8) return 0;
            return 1;
        }));

        return slots.get(0);
    }

    private List<WheelEntry> buildWheelEntries() {
        if (mc.player == null) return new ArrayList<>();

        List<WheelEntry> entries = new ArrayList<>();
        HashMap<String, WheelEntryAccum> accum = new HashMap<>();

        for (int i = 0; i <= 35; i++) {
            ItemStack stack = mc.player.getInventory().getStack(i);
            if (stack.isEmpty() || stack.getItem() == Items.AIR) continue;

            if ((show.is("Тотемы") || show.is("Тотемы и головы")) && stack.getItem() == Items.TOTEM_OF_UNDYING) {
                String key = "totem:" + getStackKey(stack);
                WheelEntryAccum acc = accum.computeIfAbsent(key, k ->
                        new WheelEntryAccum(stack.copyWithCount(1), 0, s -> s.getItem() == Items.TOTEM_OF_UNDYING && getStackKey(s).equals(getStackKey(stack))));
                acc.totalCount += stack.getCount();
            }

            if ((show.is("Головы") || show.is("Тотемы и головы")) && stack.getItem() == Items.PLAYER_HEAD) {
                String key = "head:" + getStackKey(stack);
                WheelEntryAccum acc = accum.computeIfAbsent(key, k ->
                        new WheelEntryAccum(stack.copyWithCount(1), 0, s -> s.getItem() == Items.PLAYER_HEAD && getStackKey(s).equals(getStackKey(stack))));
                acc.totalCount += stack.getCount();
            }
        }

        for (WheelEntryAccum acc : accum.values()) {
            entries.add(new WheelEntry(acc.displayStack, acc.totalCount, acc.predicate));
        }

        entries.sort((a, b) -> {
            if (a.displayStack.getItem() == Items.TOTEM_OF_UNDYING && b.displayStack.getItem() != Items.TOTEM_OF_UNDYING) return -1;
            if (a.displayStack.getItem() != Items.TOTEM_OF_UNDYING && b.displayStack.getItem() == Items.TOTEM_OF_UNDYING) return 1;
            return 0;
        });

        return entries;
    }

    private String getStackKey(ItemStack stack) {
        if (stack.getItem() == Items.TOTEM_OF_UNDYING) {
            if (stack.hasEnchantments()) return "enchanted";
            return "normal";
        }
        if (stack.getItem() == Items.PLAYER_HEAD) {
            try {
                var components = stack.getComponents();
                var profileComponent = components.get(net.minecraft.component.DataComponentTypes.PROFILE);
                if (profileComponent != null && profileComponent.name() != null) {
                    return profileComponent.name().get();
                }
            } catch (Exception ignored) {}
            return "player_head";
        }
        return stack.getItem().toString();
    }

    private void renderWheel(EventRender2D event) {

        if (bypassActive) {
            handleBypass();
        }

        int count = Math.max(3, wheelEntries.isEmpty() ? 4 : wheelEntries.size());
        float cx = mc.getWindow().getScaledWidth() / 2f;
        float cy = mc.getWindow().getScaledHeight() / 2f;
        float outerR = 92f + Math.max(0, count - 8) * 6f;
        float innerR = Math.max(26f, outerR - 38f);
        float mouseX = (float) (mc.mouse.getX() / mc.getWindow().getScaleFactor());
        float mouseY = (float) (mc.mouse.getY() / mc.getWindow().getScaleFactor());

        selectedSegment = getHoverIndex(mouseX, mouseY, cx, cy, innerR, outerR, count);
        if (selectedSegment >= count) selectedSegment = -1;

        RenderSystem.enableBlend();
        RenderSystem.disableDepthTest();
        RenderSystem.disableCull();
        RenderSystem.defaultBlendFunc();

        Matrix4f matrix = event.getDrawContext().getMatrices().peek().getPositionMatrix();
        Tessellator tessellator = RenderSystem.renderThreadTesselator();
        BufferBuilder buffer = tessellator.begin(VertexFormat.DrawMode.TRIANGLES, VertexFormats.POSITION_COLOR);

        drawBackgroundDarkening(event, cx, cy, outerR + 20);

        for (int i = 0; i < count; i++) {
            boolean isHover = i == selectedSegment;
            int r, g, b, a;
            if (isHover) {
                r = 255; g = 209; b = 47; a = 200;
            } else {
                r = 40; g = 40; b = 55; a = 180;
            }
            float start = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * (i / (double)count));
            float end = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * ((i + 1.0) / (double)count));
            drawRingSegment(buffer, matrix, cx, cy, innerR, outerR, start, end, r, g, b, a);
        }

        BufferRenderer.drawWithGlobalProgram(buffer.end());

        for (int i = 0; i < count && i < wheelEntries.size(); i++) {
            WheelEntry entry = wheelEntries.get(i);
            float start = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * (i / (double)count));
            float end = (float)(-Math.PI / 2.0 + 2.0 * Math.PI * ((i + 1.0) / (double)count));
            float mid = (start + end) / 2f;
            float iconR = (innerR + outerR) / 2f;
            float ix = cx + (float)Math.cos(mid) * iconR;
            float iy = cy + (float)Math.sin(mid) * iconR;

            RenderUtil.drawRoundedRect(event.getDrawContext().getMatrices(), ix - 10, iy - 10, 20, 20, 4, new Color(0, 0, 0, 150).getRGB());
            event.getDrawContext().drawItem(entry.displayStack, (int)(ix - 8), (int)(iy - 8));
            if (entry.totalCount > 1) {
                String countStr = String.valueOf(entry.totalCount);
                event.getDrawContext().drawTextWithShadow(mc.textRenderer, countStr, (int)(ix + 5), (int)(iy + 3), 0xFFFFFF);
            }
        }

        drawCenterCircle(event, cx, cy, innerR - 8);

        RenderUtil.drawRoundedRect(event.getDrawContext().getMatrices(), cx - 16, cy - 16, 32, 32, 8, new Color(20, 20, 30, 220).getRGB());
        RenderUtil.drawRoundedBorder(event.getDrawContext().getMatrices(), cx - 16, cy - 16, 32, 32, 8, 2f, new Color(255, 209, 47, 200).getRGB());
        event.getDrawContext().drawItem(mc.player.getOffHandStack(), (int)cx - 8, (int)cy - 8);

        drawCrosshair(event, cx, cy);

        if (selectedSegment >= 0 && selectedSegment < wheelEntries.size()) {
            WheelEntry entry = wheelEntries.get(selectedSegment);
            String name = entry.displayStack.getName().getString();
            if (name.length() > 30) name = name.substring(0, 27) + "...";

            int textWidth = mc.textRenderer.getWidth(name);
            float tooltipX = mouseX + 12;
            float tooltipY = mouseY + 12;
            if (tooltipX + textWidth + 12 > mc.getWindow().getScaledWidth()) {
                tooltipX = mouseX - textWidth - 24;
            }
            if (tooltipY + 20 > mc.getWindow().getScaledHeight()) {
                tooltipY = mouseY - 20;
            }

            RenderUtil.drawRoundedRect(event.getDrawContext().getMatrices(), tooltipX, tooltipY, textWidth + 12, 18, 4, new Color(20, 20, 30, 220).getRGB());
            RenderUtil.drawRoundedBorder(event.getDrawContext().getMatrices(), tooltipX, tooltipY, textWidth + 12, 18, 4, 1f, new Color(255, 209, 47, 150).getRGB());
            event.getDrawContext().drawTextWithShadow(mc.textRenderer, name, (int)(tooltipX + 6), (int)(tooltipY + 5), 0xFFFFFF);
        }

        RenderSystem.enableCull();
        RenderSystem.enableDepthTest();
    }

    private void drawBackgroundDarkening(EventRender2D event, float cx, float cy, float radius) {
        MatrixStack matrices = event.getDrawContext().getMatrices();
        int screenWidth = mc.getWindow().getScaledWidth();
        int screenHeight = mc.getWindow().getScaledHeight();

        RenderUtil.drawRoundedRect(matrices, 0, 0, screenWidth, screenHeight, 0, new Color(0, 0, 0, 150).getRGB());
    }

    private void drawCenterCircle(EventRender2D event, float cx, float cy, float radius) {
        MatrixStack matrices = event.getDrawContext().getMatrices();
        RenderUtil.drawRoundedBorder(matrices, cx - radius, cy - radius, radius * 2, radius * 2, radius, 2f, new Color(255, 209, 47, 180).getRGB());
    }

    private void drawCrosshair(EventRender2D event, float cx, float cy) {
        MatrixStack matrices = event.getDrawContext().getMatrices();
        int size = 8;
        int thickness = 2;

        RenderUtil.drawRoundedRect(matrices, cx - size, cy - thickness / 2f, size * 2, thickness, 1, new Color(255, 255, 255, 200).getRGB());
        RenderUtil.drawRoundedRect(matrices, cx - thickness / 2f, cy - size, thickness, size * 2, 1, new Color(255, 255, 255, 200).getRGB());
        RenderUtil.drawRoundedRect(matrices, cx - 2, cy - 2, 4, 4, 2, new Color(255, 209, 47, 255).getRGB());
    }

    private void drawRingSegment(BufferBuilder buffer, Matrix4f matrix, float cx, float cy, float innerR, float outerR, float start, float end, int r, int g, int b, int a) {
        int steps = Math.max(16, (int)(48f * (Math.abs(end - start) / ((float)Math.PI * 2f))));
        float step = (end - start) / steps;
        for (int i = 0; i < steps; i++) {
            float a0 = start + step * i;
            float a1 = start + step * (i + 1);
            float x0o = cx + (float)Math.cos(a0) * outerR;
            float y0o = cy + (float)Math.sin(a0) * outerR;
            float x1o = cx + (float)Math.cos(a1) * outerR;
            float y1o = cy + (float)Math.sin(a1) * outerR;
            float x0i = cx + (float)Math.cos(a0) * innerR;
            float y0i = cy + (float)Math.sin(a0) * innerR;
            float x1i = cx + (float)Math.cos(a1) * innerR;
            float y1i = cy + (float)Math.sin(a1) * innerR;

            buffer.vertex(matrix, x0i, y0i, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x0o, y0o, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x1o, y1o, 0f).color(r, g, b, a);

            buffer.vertex(matrix, x0i, y0i, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x1o, y1o, 0f).color(r, g, b, a);
            buffer.vertex(matrix, x1i, y1i, 0f).color(r, g, b, a);
        }
    }

    private int getHoverIndex(float mouseX, float mouseY, float cx, float cy, float innerR, float outerR, int count) {
        float dx = mouseX - cx;
        float dy = mouseY - cy;
        float dist = (float)Math.sqrt(dx * dx + dy * dy);
        if (dist < innerR || dist > outerR) return -1;
        double ang = Math.atan2(dy, dx);
        ang = ang + Math.PI / 2.0;
        if (ang < 0) ang += Math.PI * 2.0;
        int idx = (int)Math.floor(ang / (Math.PI * 2.0) * count);
        if (idx < 0 || idx >= count) return -1;
        return idx;
    }

    private static class WheelEntryAccum {
        final ItemStack displayStack;
        int totalCount;
        final Predicate<ItemStack> predicate;
        WheelEntryAccum(ItemStack displayStack, int totalCount, Predicate<ItemStack> predicate) {
            this.displayStack = displayStack;
            this.totalCount = totalCount;
            this.predicate = predicate;
        }
    }

    private static class WheelEntry {
        final ItemStack displayStack;
        final int totalCount;
        final Predicate<ItemStack> predicate;
        WheelEntry(ItemStack displayStack, int totalCount, Predicate<ItemStack> predicate) {
            this.displayStack = displayStack;
            this.totalCount = totalCount;
            this.predicate = predicate;
        }
    }

    private static class ItemSlot {
        final int idForServer;
        final ItemStack itemStack;
        ItemSlot(int idForServer, ItemStack itemStack) {
            this.idForServer = idForServer;
            this.itemStack = itemStack;
        }
    }
}
/del хуйня помойная
 
Назад
Сверху Снизу