chore(history): squash 59 commit(s) from 2026-09-23
- refactor(hud/weather/render): Scoreboard 931→240 + Rain 903→159 + UiMeshGeometry 956→99 (8.5.2) - refactor(chams): Chams 985 → 129 + 4×≤195 (8.5.2) - refactor(module): Module 1031 → 139 + 4×≤134 (8.5.2) - refactor(tooltips): BetterTooltips 960 → 181 + 4×≤199 (8.5.2) - refactor(clickgui): ClickGuiPickerState 970→166 + ModulesMenuScreen 966→145 (8.5.2) - refactor(clickgui): CombatProtocolHeuristicsEditorState 932→79 + 4×≤192 (8.5.2) - refactor(hud): CompactHudStatModel 894 → 125 + 4×≤175 (8.5.2) - refactor(iris): ShaderPatchCompiler 843→66 + 4×≤174 (8.5.2) - refactor(tab): CustomTabList 834→136 + 4×≤166 (8.5.2) - refactor(relations): OnlineRelationPlayerPickerComponent 837→74 + 4×≤192 (8.5.2) - refactor(hits): HitEffect 770→120 + 4×≤176 (8.5.2) - refactor(predict): Predictions 815→150 + 4×≤195 (8.5.2) - refactor(config): ConfigProfilesComponent 799→125 + 4×≤195 (8.5.2) - refactor(sim): PlayerMovementSimulation 757→157 + 4×≤194 (8.5.2) - refactor(predict): ProjectilePuncher 747→104 + 3×≤134 (8.5.2) - refactor(crosshair): Crosshair 743→143 + 4×≤167 (8.5.2) - refactor(mediaplayer): MediaPlayer 743→174 + 4×≤196 (8.5.2) - refactor(hud): Itemizer 699→193 + Cooldowns 707→151 (8.5.2) - refactor(hud): Potions 617→176 + 4×≤166 (8.5.2) - refactor(hud): Admins 702→118 + 4×≤138 (8.5.2) - refactor(hud): Keybinds 611→157 + 4×≤160 (8.5.2) - refactor(hud): ModuleList 499→181 + 3×≤150 (8.5.2) - refactor(hud): Radar 392→161 + 2×≤124 (8.5.2) - refactor(hud): Fps 309→168 + 3×≤107 (8.5.2) - refactor(hud): Memory 304→151 + 3×≤144 (8.5.2) - refactor(hud): Tps 339→154 + 3×≤143 (8.5.2) - refactor(hud): Ping 309→145 + 3×≤125 (8.5.2) - refactor(hud): SpeedBps 334→156 + 3×≤142 (8.5.2) - refactor(hud): GameTime 314→150 + 3×≤127 (8.5.2) - refactor(hud): SystemTime 300→136 + 3×≤130 (8.5.2) - refactor(hud): Coordinates 337→174 + 2×≤172 (8.5.2) - refactor(hud): Armor 324→162 + 2×≤148 (8.5.2) - refactor(hud): Inventory 476→153 + 3×≤181 (8.5.2) - docs(todo): обновлён 8.5.2 — отмечены готовые гиганты (BetterButtons/InventorySwap/Svg/Темы/WorldParticles/Module/Chams и хвост) - docs(api): папка combatant-client-26.2/docs + addons.md (API можно ломать при рефакторе) - refactor(visuals): BlockHighlight 692→271 + 4×≤200 (8.5.2) - refactor(mixins): GameRendererMixin 830→344 (хуки) + 4 хелпера (8.5.2) - refactor(visuals): WorldParticles 821→260 + 6×≤200, режимы добиты (8.5.2) - docs(todo): 8.5.2 — WorldParticles/GameRendererMixin/BlockHighlight отмечены ГОТОВО - refactor(render): MeshBuilder 738→447 (фасад write-API) + 3 хелпера, FrameStats снесён (8.5.2) - docs(todo): Фаза 10 — дизайн платформы (аккаунты/конфиги/аватарки), backlog RPC-чата - docs(todo): Подсистема 3 — Аддоны 2.0 (скрипты/версии) + витрина аддонов + админка - docs(todo): Подсистема 2 — RPC-чат/друзья/виджеты-телеметрия + рейт-лимиты, доп. правки Подсистемы 3 - chore: переименование combatant-client-26.2 -> mod, добавлены backend/ и frontend/ - docs(backend): implementation plan for accounts-service (Подсистема 1, часть 1) - chore: пересоздать backend/frontend через cargo new и bun create vite + tailwind; убрать таблицу компонентов из TODO.md - docs(backend): edition 2024 в плане вместо 2021 - chore: обновить версии до реально актуальных (проверено компиляцией) - docs: правила платформы (лимит 250 строк, антипаттерны backend/frontend по итогам ресёрча) + frontend/ARCHITECTURE.md - docs: добавить правило ≤4 файла на папку в правила платформы (backend/frontend) - docs: commit messages in English from now on - chore: gitignore .superpowers/ scratch directory - docs: commit messages in English from now on (mod) - feat(backend): scaffold accounts-service with health check - chore(backend): remove target/ build artifacts from git, add gitignore - chore: track docs/ in git (was accidentally gitignored, never committed) - chore(backend): convert to Cargo workspace, plan full microservice layout - feat(backend): accounts/avatars/device_links schema + migration - feat(backend): argon2id password hashing
This commit is contained in:
parent
ec10395511
commit
9d08fa910a
2239 changed files with 35265 additions and 28110 deletions
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.chat;
|
||||
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.ChatComponent;
|
||||
import net.minecraft.client.gui.components.ChatComponent.DisplayMode;
|
||||
import net.minecraft.client.multiplayer.chat.GuiMessageSource;
|
||||
import net.minecraft.client.multiplayer.chat.GuiMessageTag;
|
||||
import net.minecraft.client.multiplayer.chat.GuiMessage;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MessageSignature;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.command.util.CommandOutput;
|
||||
import dev.loki.lovisual.features.gui.chat.model.BetterChatStoreManager;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
import dev.loki.lovisual.features.module.lifecycle.Modules;
|
||||
import dev.loki.lovisual.features.module.modules.misc.protect.MessageFilter;
|
||||
import dev.loki.lovisual.features.module.modules.misc.protect.NameProtect;
|
||||
import dev.loki.lovisual.features.module.modules.misc.protect.StreamerMode;
|
||||
import dev.loki.lovisual.Lifecycle;
|
||||
|
||||
@Mixin(ChatComponent.class)
|
||||
public class ChatComponentMixin {
|
||||
|
||||
@ModifyVariable(
|
||||
method = "addMessage(Lnet/minecraft/network/chat/Component;Lnet/minecraft/network/chat/MessageSignature;Lnet/minecraft/client/multiplayer/chat/GuiMessageSource;Lnet/minecraft/client/multiplayer/chat/GuiMessageTag;)V",
|
||||
at = @At("HEAD"),
|
||||
argsOnly = true
|
||||
)
|
||||
private Component lovisual$nameProtectChat(Component message) {
|
||||
if (!Lifecycle.canRunHud()) return message;
|
||||
Component protectedMessage = NameProtect.protect(message);
|
||||
return StreamerMode.protectComponent(protectedMessage);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "addMessage(Lnet/minecraft/network/chat/Component;Lnet/minecraft/network/chat/MessageSignature;Lnet/minecraft/client/multiplayer/chat/GuiMessageSource;Lnet/minecraft/client/multiplayer/chat/GuiMessageTag;)V",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void lovisual$filterChatFull(Component message,
|
||||
MessageSignature signatureData,
|
||||
GuiMessageSource source,
|
||||
GuiMessageTag indicator,
|
||||
CallbackInfo ci) {
|
||||
if (!Lifecycle.canRunHud()) return;
|
||||
MessageFilter messageFilter = Modules.get(MessageFilter.class);
|
||||
if (messageFilter != null && messageFilter.shouldHideChat(message)) {
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
if (!CommandOutput.isLoVisualMessage(message)) {
|
||||
BetterChatStoreManager.addMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "logChatMessage(Lnet/minecraft/client/multiplayer/chat/GuiMessage;)V", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$commands$suppressClientLog(GuiMessage message, CallbackInfo ci) {
|
||||
if (message != null && CommandOutput.isLoVisualMessage(message.content())) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "clearMessages(Z)V", at = @At("TAIL"))
|
||||
private void lovisual$betterChat$clear(boolean clearHistory, CallbackInfo ci) {
|
||||
if (!Lifecycle.canRunHud()) return;
|
||||
if (!BetterChat.isActive()) return;
|
||||
// Never wipe BetterChat store on vanilla clear; just persist to disk.
|
||||
BetterChatStoreManager.flushActive();
|
||||
}
|
||||
|
||||
@Inject(method = "extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;Lnet/minecraft/client/gui/Font;IIILnet/minecraft/client/gui/components/ChatComponent$DisplayMode;Z)V",
|
||||
at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$betterChat$hideVanilla(GuiGraphicsExtractor context,
|
||||
Font textRenderer,
|
||||
int currentTick,
|
||||
int mouseX,
|
||||
int mouseY,
|
||||
DisplayMode displayMode,
|
||||
boolean inChat,
|
||||
CallbackInfo ci) {
|
||||
if (!Lifecycle.canRunHud()) return;
|
||||
if (!BetterChat.isActive()) return;
|
||||
BetterChat cfg = BetterChat.get();
|
||||
if (cfg != null && cfg.hideVanilla()) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.chat;
|
||||
|
||||
import net.minecraft.client.gui.ActiveTextCollector;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.ChatComponent;
|
||||
import net.minecraft.client.gui.components.ChatComponent.DisplayMode;
|
||||
import net.minecraft.client.gui.components.CommandSuggestions;
|
||||
import net.minecraft.client.gui.screens.ChatScreen;
|
||||
import net.minecraft.client.input.KeyEvent;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import dev.loki.lovisual.features.command.core.CommandManager;
|
||||
import dev.loki.lovisual.features.gui.chat.render.BetterChatRenderer;
|
||||
import dev.loki.lovisual.features.gui.chat.messages.BetterChatTabHandler;
|
||||
import dev.loki.lovisual.features.gui.hud.draggable.impl.BetterChat;
|
||||
import dev.loki.lovisual.mixins.accessors.chat.ChatScreenSuggestorAccessor;
|
||||
import dev.loki.lovisual.Lifecycle;
|
||||
|
||||
@Mixin(ChatScreen.class)
|
||||
public class ChatScreenMixin {
|
||||
|
||||
@Inject(method = "extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$betterChat$render(GuiGraphicsExtractor context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
if (!Lifecycle.canRunHud()) return;
|
||||
if (BetterChat.isActive()) {
|
||||
BetterChat cfg = BetterChat.get();
|
||||
if (cfg != null && cfg.hideVanilla()) {
|
||||
// Stash suggestion context; BetterChatRenderer will draw it in correct Z-order.
|
||||
BetterChatRenderer.setSuggestionContext((ChatScreen) (Object) this, context, mouseX, mouseY);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "mouseClicked(Lnet/minecraft/client/input/MouseButtonEvent;Z)Z",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/components/ChatComponent;captureClickableText(Lnet/minecraft/client/gui/ActiveTextCollector;IILnet/minecraft/client/gui/components/ChatComponent$DisplayMode;)V"
|
||||
)
|
||||
)
|
||||
private void lovisual$betterChat$disableVanillaChatClickHitbox(ChatComponent chatHud,
|
||||
ActiveTextCollector textConsumer,
|
||||
int windowHeight,
|
||||
int currentTick,
|
||||
DisplayMode displayMode) {
|
||||
if (lovisual$betterChat$shouldHideVanillaChat()) {
|
||||
return;
|
||||
}
|
||||
chatHud.captureClickableText(textConsumer, windowHeight, currentTick, displayMode);
|
||||
}
|
||||
|
||||
@Inject(method = "mouseScrolled(DDDD)Z", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$betterChat$disableVanillaChatScroll(double mouseX,
|
||||
double mouseY,
|
||||
double horizontalAmount,
|
||||
double verticalAmount,
|
||||
CallbackInfoReturnable<Boolean> cir) {
|
||||
if (lovisual$betterChat$shouldHideVanillaChat()) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "keyPressed(Lnet/minecraft/client/input/KeyEvent;)Z", at = @At("HEAD"))
|
||||
private void lovisual$betterChat$allowEscape(KeyEvent input, CallbackInfoReturnable<Boolean> cir) {
|
||||
if (!Lifecycle.canRunHud()) return;
|
||||
if (!BetterChat.isActive()) return;
|
||||
if (!input.isEscape()) return;
|
||||
CommandSuggestions suggestor = ((ChatScreenSuggestorAccessor) this).getLoVisual$suggestor();
|
||||
if (suggestor != null) {
|
||||
suggestor.setAllowSuggestions(false);
|
||||
suggestor.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "handleChatInput(Ljava/lang/String;Z)V", at = @At("HEAD"), cancellable = true, require = 0)
|
||||
private void lovisual$commands$sendMessage(String message, boolean addToHistory, CallbackInfo ci) {
|
||||
if (!Lifecycle.isActive()) return;
|
||||
if (CommandManager.handle(message)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "keyPressed(Lnet/minecraft/client/input/KeyEvent;)Z", at = @At("HEAD"), cancellable = true, require = 0)
|
||||
private void lovisual$betterChat$tabCompleteCommands(KeyEvent input, CallbackInfoReturnable<Boolean> cir) {
|
||||
if (!Lifecycle.canRunHud()) return;
|
||||
if (!BetterChat.isActive()) return;
|
||||
if (BetterChatTabHandler.handleKey(input)) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean lovisual$betterChat$shouldHideVanillaChat() {
|
||||
if (!Lifecycle.canRunHud()) return false;
|
||||
if (!BetterChat.isActive()) return false;
|
||||
BetterChat cfg = BetterChat.get();
|
||||
return cfg != null && cfg.hideVanilla();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.chat;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.contextualbar.ContextualBar;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.bars.CustomBar;
|
||||
import dev.loki.lovisual.Lifecycle;
|
||||
|
||||
@Mixin(ContextualBar.class)
|
||||
public interface ContextualBarMixin {
|
||||
|
||||
@Inject(method = "extractExperienceLevel", at = @At("HEAD"), cancellable = true)
|
||||
private static void lovisual$drawExperienceLevel(GuiGraphicsExtractor context,
|
||||
Font textRenderer,
|
||||
int level,
|
||||
CallbackInfo ci) {
|
||||
if (!Lifecycle.canRunHud()) return;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null || mc.player == null) return;
|
||||
|
||||
CustomBar bar = CustomBar.get();
|
||||
if (bar == null) return;
|
||||
if (!bar.isHudBarEnabled() || !bar.isHotbarXpBarEnabled()) return;
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.core;
|
||||
|
||||
import dev.loki.lovisual.features.module.lifecycle.Modules;
|
||||
import dev.loki.lovisual.features.module.modules.misc.esp.ShitEsp;
|
||||
import dev.loki.lovisual.features.module.modules.misc.items.ItemScroller;
|
||||
import dev.loki.lovisual.features.module.modules.visuals.player.Animations;
|
||||
import dev.loki.lovisual.features.module.modules.visuals.items.ShulkerPreview;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.joml.Matrix3x2fStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(AbstractContainerScreen.class)
|
||||
public abstract class AbstractContainerScreenMixin {
|
||||
|
||||
@Shadow
|
||||
protected Slot hoveredSlot;
|
||||
|
||||
@Shadow
|
||||
protected AbstractContainerMenu menu;
|
||||
|
||||
@Shadow
|
||||
protected int leftPos;
|
||||
|
||||
@Shadow
|
||||
protected int topPos;
|
||||
|
||||
@Unique
|
||||
private boolean lovisual$animations$scaleApplied;
|
||||
|
||||
@Inject(method = "onClose", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$inventoryMove$deferClose(CallbackInfo ci) {
|
||||
// InventoryMove module removed; keep hook for compatibility.
|
||||
}
|
||||
|
||||
/**
|
||||
* Smooth open animation from the Animations module: the whole screen is drawn
|
||||
* with a scale between 0.9 and 1.0 around the screen center. The animated
|
||||
* value itself is advanced once per frame inside the module; this only reads
|
||||
* the settled value and wraps the extraction in a matching push/pop.
|
||||
*/
|
||||
@Inject(
|
||||
method = "extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V",
|
||||
at = @At("HEAD")
|
||||
)
|
||||
private void lovisual$animations$beginOpenScale(GuiGraphicsExtractor graphics,
|
||||
int mouseX,
|
||||
int mouseY,
|
||||
float partialTick,
|
||||
CallbackInfo ci) {
|
||||
lovisual$animations$scaleApplied = false;
|
||||
|
||||
Animations animations = Modules.get(Animations.class);
|
||||
if (animations == null) return;
|
||||
|
||||
float scale = animations.getInventoryScale();
|
||||
if (scale >= 0.999f) return;
|
||||
|
||||
Matrix3x2fStack pose = graphics.pose();
|
||||
float centerX = graphics.guiWidth() * 0.5f;
|
||||
float centerY = graphics.guiHeight() * 0.5f;
|
||||
|
||||
pose.pushMatrix();
|
||||
pose.translate(centerX, centerY);
|
||||
pose.scale(scale, scale);
|
||||
pose.translate(-centerX, -centerY);
|
||||
lovisual$animations$scaleApplied = true;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void lovisual$animations$endOpenScale(GuiGraphicsExtractor graphics,
|
||||
int mouseX,
|
||||
int mouseY,
|
||||
float partialTick,
|
||||
CallbackInfo ci) {
|
||||
if (!lovisual$animations$scaleApplied) return;
|
||||
lovisual$animations$scaleApplied = false;
|
||||
graphics.pose().popMatrix();
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private void lovisual$shulkerPreview$draw(GuiGraphicsExtractor graphics,
|
||||
int mouseX,
|
||||
int mouseY,
|
||||
float partialTick,
|
||||
CallbackInfo ci) {
|
||||
Slot slot = this.hoveredSlot;
|
||||
ItemStack stack = slot == null ? ItemStack.EMPTY : slot.getItem();
|
||||
if (stack.isEmpty()) return;
|
||||
ShulkerPreview.onScreenExtract(graphics, stack, mouseX, mouseY);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "mouseScrolled(DDDD)Z",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void lovisual$itemScroller$quickMove(double mouseX, double mouseY,
|
||||
double horizontal, double vertical,
|
||||
CallbackInfoReturnable<Boolean> cir) {
|
||||
ItemScroller scroller = Modules.get(ItemScroller.class);
|
||||
if (scroller == null || !scroller.isEnabled()) return;
|
||||
|
||||
Slot slot = this.hoveredSlot;
|
||||
ItemStack stack = slot == null ? null : slot.getItem();
|
||||
if (scroller.onMouseScrolled(vertical, slot, stack)) {
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("TAIL"))
|
||||
private void lovisual$shitEsp$highlight(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTick, CallbackInfo ci) {
|
||||
ShitEsp esp = Modules.get(ShitEsp.class);
|
||||
if (esp == null || !esp.isEnabled() || !esp.hasAnyGroup() || menu == null) return;
|
||||
for (Slot slot : menu.slots) {
|
||||
if (slot == null || !slot.hasItem()) continue;
|
||||
ItemStack stack = slot.getItem();
|
||||
Integer col = esp.colorFor(stack);
|
||||
if (col == null) continue;
|
||||
int x1 = leftPos + slot.x;
|
||||
int y1 = topPos + slot.y;
|
||||
int x2 = x1 + 16;
|
||||
int y2 = y1 + 16;
|
||||
graphics.fill(RenderPipelines.GUI, x1, y1, x2, y2, col);
|
||||
graphics.outline(x1, y1, x2 - x1, y2 - y1, col | 0xFF000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.core;
|
||||
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.AbstractSelectionList;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.buttons.BetterButtons;
|
||||
|
||||
@Mixin(AbstractSelectionList.class)
|
||||
public abstract class AbstractSelectionListMixin {
|
||||
|
||||
@Inject(method = "enableScissor(Lnet/minecraft/client/gui/GuiGraphicsExtractor;)V", at = @At("HEAD"))
|
||||
private void lovisual$pushScissor(GuiGraphicsExtractor context, CallbackInfo ci) {
|
||||
AbstractSelectionList<?> self = (AbstractSelectionList<?>) (Object) this;
|
||||
BetterButtons.pushScissorContext(self.getX(), self.getY(), self.getRight(), self.getBottom());
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "extractWidgetRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;disableScissor()V")
|
||||
)
|
||||
private void lovisual$popScissor(GuiGraphicsExtractor context, int mouseX, int mouseY, float deltaTicks, CallbackInfo ci) {
|
||||
BetterButtons.popScissorContext();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.core;
|
||||
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.AbstractSliderButton;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.screens.inventory.MerchantScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.buttons.BetterButtons;
|
||||
import dev.loki.lovisual.mixins.accessors.widget.SliderWidgetAccessor;
|
||||
|
||||
@Mixin(AbstractSliderButton.class)
|
||||
public abstract class AbstractSliderButtonMixin {
|
||||
|
||||
@Inject(method = "extractWidgetRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$renderWidget(GuiGraphicsExtractor ctx, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
BetterButtons buttons = BetterButtons.get();
|
||||
if (buttons == null || !buttons.useUiButtons()) return;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc != null && ClientScreen.current() instanceof MerchantScreen) return;
|
||||
AbstractSliderButton self = (AbstractSliderButton) (Object) this;
|
||||
AbstractWidget widget = self;
|
||||
|
||||
boolean hovered = widget.isMouseOver(mouseX, mouseY);
|
||||
boolean dragging = ((SliderWidgetAccessor) self).lovisual$isDragging();
|
||||
float hover = BetterButtons.updateHover(widget, hovered || dragging);
|
||||
|
||||
double value = ((SliderWidgetAccessor) self).lovisual$getValue();
|
||||
BetterButtons.enqueueSlider(widget, (float) value, dragging, hover);
|
||||
if (hovered) {
|
||||
BetterButtons.captureTooltip(widget, mouseX, mouseY);
|
||||
}
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.core;
|
||||
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import dev.loki.lovisual.mixininterface.entity.IRoundedHitbox;
|
||||
|
||||
@Mixin(AbstractWidget.class)
|
||||
public abstract class AbstractWidgetMixin implements IRoundedHitbox {
|
||||
|
||||
@Unique
|
||||
private float lovisual$roundedRadius = 0.0f;
|
||||
@Unique
|
||||
private boolean lovisual$roundedEnabled = false;
|
||||
|
||||
@Override
|
||||
public void lovisual$setRoundedHitbox(float radius) {
|
||||
lovisual$roundedRadius = Math.max(0.0f, radius);
|
||||
lovisual$roundedEnabled = lovisual$roundedRadius > 0.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lovisual$clearRoundedHitbox() {
|
||||
lovisual$roundedRadius = 0.0f;
|
||||
lovisual$roundedEnabled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lovisual$getRoundedHitbox() {
|
||||
return lovisual$roundedRadius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lovisual$useRoundedHitbox() {
|
||||
return lovisual$roundedEnabled;
|
||||
}
|
||||
|
||||
@Inject(method = "isMouseOver(DD)Z", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$isMouseOverRounded(double mouseX, double mouseY, CallbackInfoReturnable<Boolean> cir) {
|
||||
if (!lovisual$roundedEnabled) return;
|
||||
|
||||
AbstractWidget self = (AbstractWidget) (Object) this;
|
||||
float x = self.getX();
|
||||
float y = self.getY();
|
||||
float w = self.getWidth();
|
||||
float h = self.getHeight();
|
||||
|
||||
if (mouseX < x || mouseY < y || mouseX > x + w || mouseY > y + h) {
|
||||
cir.setReturnValue(false);
|
||||
return;
|
||||
}
|
||||
|
||||
float r = Math.min(lovisual$roundedRadius, Math.min(w, h) * 0.5f);
|
||||
if (r <= 0.0f) {
|
||||
cir.setReturnValue(true);
|
||||
return;
|
||||
}
|
||||
|
||||
float px = (float) (mouseX - x);
|
||||
float py = (float) (mouseY - y);
|
||||
|
||||
if ((px >= r && px <= w - r) || (py >= r && py <= h - r)) {
|
||||
cir.setReturnValue(true);
|
||||
return;
|
||||
}
|
||||
|
||||
float cx = (px < r) ? r : (w - r);
|
||||
float cy = (py < r) ? r : (h - r);
|
||||
float dx = px - cx;
|
||||
float dy = py - cy;
|
||||
cir.setReturnValue((dx * dx + dy * dy) <= (r * r));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.multiplayer;
|
||||
|
||||
import dev.loki.lovisual.features.theme.Theme;
|
||||
import dev.loki.lovisual.features.gui.hud.config.HudRenderUtil;
|
||||
import dev.loki.lovisual.features.theme.Themes;
|
||||
import dev.loki.lovisual.render.engine.animation.AnimationUtility;
|
||||
import dev.loki.lovisual.render.engine.core.context.ViewportContext;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
import dev.loki.lovisual.render.engine.svg.model.SvgRenderOptions;
|
||||
import dev.loki.lovisual.util.proxy.ProxyBackend;
|
||||
import dev.loki.lovisual.util.proxy.model.ProxyEntry;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen;
|
||||
import net.minecraft.util.Mth;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Screen.class)
|
||||
public abstract class JoinMultiplayerScreenMixin {
|
||||
@Unique
|
||||
private static final float LOVISUAL_PROXY_BUTTON_X = 8f;
|
||||
@Unique
|
||||
private static final float LOVISUAL_PROXY_BUTTON_Y = 8f;
|
||||
@Unique
|
||||
private static final float LOVISUAL_PROXY_BUTTON_W = 24f;
|
||||
@Unique
|
||||
private static final float LOVISUAL_PROXY_BUTTON_H = 24f;
|
||||
@Unique
|
||||
private float lovisual$proxyHover;
|
||||
@Unique
|
||||
private float lovisual$proxyPress;
|
||||
|
||||
@Inject(method = "extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("RETURN"))
|
||||
private void lovisual$renderProxyEntry(GuiGraphicsExtractor context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
Screen self = (Screen) (Object) this;
|
||||
if (!(self instanceof JoinMultiplayerScreen)) return;
|
||||
float dt = AnimationUtility.deltaTime();
|
||||
boolean hovered = lovisual$inside(mouseX, mouseY, LOVISUAL_PROXY_BUTTON_X, LOVISUAL_PROXY_BUTTON_Y,
|
||||
LOVISUAL_PROXY_BUTTON_W, LOVISUAL_PROXY_BUTTON_H);
|
||||
lovisual$proxyHover = lovisual$animate(lovisual$proxyHover, hovered ? 1f : 0f, dt, 11f);
|
||||
lovisual$proxyPress = lovisual$animate(lovisual$proxyPress, 0f, dt, 8f);
|
||||
|
||||
ViewportContext.beginScaled(context);
|
||||
Renderer2D.COLOR.begin();
|
||||
try {
|
||||
lovisual$drawProxyButton();
|
||||
} finally {
|
||||
Renderer2D.COLOR.render();
|
||||
ViewportContext.end(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void lovisual$drawProxyButton() {
|
||||
float hover = AnimationUtility.easeOutCubic(lovisual$proxyHover);
|
||||
float press = AnimationUtility.easeOutCubic(lovisual$proxyPress);
|
||||
Themes.Theme theme = Theme.theme();
|
||||
boolean enabled = ProxyBackend.isEnabled();
|
||||
ProxyEntry active = ProxyBackend.activeProxy();
|
||||
boolean configured = active.isConfigured();
|
||||
|
||||
float size = LOVISUAL_PROXY_BUTTON_W * (1.0f + hover * 0.035f - press * 0.025f);
|
||||
float x = LOVISUAL_PROXY_BUTTON_X + (LOVISUAL_PROXY_BUTTON_W - size) * 0.5f;
|
||||
float y = LOVISUAL_PROXY_BUTTON_Y + (LOVISUAL_PROXY_BUTTON_H - size) * 0.5f - hover * 0.35f;
|
||||
float radius = 5.0f;
|
||||
float centerX = x + size * 0.5f;
|
||||
float centerY = y + size * 0.5f;
|
||||
int accent = enabled && configured ? theme.accent() : 0x8A94A3;
|
||||
int baseTop = enabled && configured ? 0x222A20 : 0x1B2028;
|
||||
int baseBottom = enabled && configured ? 0x121812 : 0x10141B;
|
||||
int fillTop = HudRenderUtil.mixColor(withAlpha(baseTop, 226), withAlpha(accent, 96), (enabled && configured ? 0.18f : 0.06f) + hover * 0.12f);
|
||||
int fillBottom = HudRenderUtil.mixColor(withAlpha(baseBottom, 236), withAlpha(accent, 70), (enabled && configured ? 0.12f : 0.04f) + hover * 0.08f);
|
||||
int stroke = HudRenderUtil.mixColor(withAlpha(0x46505D, 180), withAlpha(accent, 218), (enabled && configured ? 0.38f : 0.10f) + hover * 0.35f);
|
||||
int iconColor = enabled && configured
|
||||
? HudRenderUtil.mixColor(withAlpha(0xFFFFFF, 245), withAlpha(theme.accent(), 255), 0.18f)
|
||||
: withAlpha(0xC5CCD6, 235);
|
||||
|
||||
Renderer2D.COLOR.roundedRectSoftShadow(x, y + 0.8f, size, size, radius,
|
||||
7.5f, 0.026f + hover * 0.012f, withAlpha(0x060810, 96));
|
||||
Renderer2D.COLOR.roundedRectGradientQuad(x, y, size, size, radius, 1f,
|
||||
fillTop, fillTop, fillBottom, fillBottom);
|
||||
Renderer2D.COLOR.roundedRectStrokeGradient(x + 0.5f, y + 0.5f, size - 1.0f, size - 1.0f,
|
||||
radius - 0.5f, 1f, 0.55f, HudRenderUtil.mixColor(stroke, 0xFFFFFFFF, 0.08f), stroke, 90f);
|
||||
Renderer2D.COLOR.svg("shield-user", centerX - 5.6f, centerY - 5.6f, 11.2f, 11.2f, SvgRenderOptions.overrideColor(iconColor));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static float lovisual$animate(float current, float target, float dt, float speed) {
|
||||
float next = AnimationUtility.approach(current, target, dt, speed);
|
||||
return AnimationUtility.snap(next, target, 0.001f);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static boolean lovisual$inside(float mx, float my, float x, float y, float w, float h) {
|
||||
return mx >= x && mx <= x + w && my >= y && my <= y + h;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static int withAlpha(int rgb, int alpha) {
|
||||
return (rgb & 0x00FFFFFF) | (Mth.clamp(alpha, 0, 255) << 24);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.multiplayer;
|
||||
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.module.lifecycle.Modules;
|
||||
import dev.loki.lovisual.features.module.modules.misc.vanilla.BetterMinecraft;
|
||||
|
||||
@Mixin(AbstractRecipeBookScreen.class)
|
||||
public abstract class RecipeBookScreenMixin {
|
||||
|
||||
@Final
|
||||
@Shadow
|
||||
private RecipeBookComponent<?> recipeBookComponent;
|
||||
|
||||
@Inject(method = "initButton", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$disableRecipeBook(CallbackInfo ci) {
|
||||
BetterMinecraft module = Modules.get(BetterMinecraft.class);
|
||||
if (module != null && module.isHideRecipeBookEnabled()) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.screens;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import dev.loki.lovisual.features.module.core.ModuleManager;
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.gui.clickgui.editor.ClickGuiEditorScreen;
|
||||
import dev.loki.lovisual.features.gui.clickgui.picker.screen.ClickGuiPickerScreen;
|
||||
import dev.loki.lovisual.features.gui.clickgui.render.ClickGuiRenderer;
|
||||
import dev.loki.lovisual.features.gui.clickgui.screen.ClickGuiScreen;
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.bars.CustomBar;
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.dynamicisland.DynamicIsland;
|
||||
import dev.loki.lovisual.features.module.modules.misc.ClickGui;
|
||||
import dev.loki.lovisual.render.engine.core.system.LoVisualRenderSystem;
|
||||
import dev.loki.lovisual.render.engine.core.frame.RenderPhase;
|
||||
import dev.loki.lovisual.render.engine.core.frame.RenderPhaseScope;
|
||||
import dev.loki.lovisual.render.engine.renderer.Renderer2D;
|
||||
|
||||
@Mixin(Gui.class)
|
||||
public abstract class GuiScreenMixin {
|
||||
|
||||
@Unique
|
||||
private static void lovisual$extractCustomBarHeads(GuiGraphicsExtractor drawContext, DeltaTracker tickCounter) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null || mc.player == null) return;
|
||||
if (ClientScreen.current() != null) return;
|
||||
|
||||
CustomBar bar = CustomBar.get();
|
||||
if (bar == null || !bar.isHudBarEnabled() || !bar.isHotbarXpBarEnabled()) return;
|
||||
if (CustomBar.shouldUseJumpBar(mc) || !CustomBar.shouldUseLocator(mc, bar)) return;
|
||||
|
||||
LoVisualRenderSystem.ensureFrameContext();
|
||||
try (RenderPhaseScope ignored = LoVisualRenderSystem.phase(RenderPhase.SCREEN_TOP, "2d:hud:custom_bar_heads")) {
|
||||
Renderer2D.withDeferredLayer(Renderer2D.Deferred2DLayer.AFTER_VANILLA_GUI, () -> {
|
||||
CustomBar.renderLocatorHeads(drawContext, tickCounter, mc);
|
||||
Renderer2D.COLOR.render();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "setScreen", at = @At("HEAD"))
|
||||
private void lovisual$closeClickGuiOnScreen(Screen screen, CallbackInfo ci) {
|
||||
if (!ModuleManager.isEnabled("clickgui")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ClickGuiRenderer.waitingForKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (screen instanceof ClickGuiScreen
|
||||
|| screen instanceof ClickGuiPickerScreen
|
||||
|| screen instanceof ClickGuiEditorScreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (screen != null) {
|
||||
ClickGui.setSuppressScreenClose(true);
|
||||
try {
|
||||
ModuleManager.setEnabled("clickgui", false);
|
||||
} finally {
|
||||
ClickGui.setSuppressScreenClose(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "extractRenderState",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;applyCursor(Lcom/mojang/blaze3d/platform/Window;)V"
|
||||
)
|
||||
)
|
||||
private void lovisual$extractTopLayer(DeltaTracker tickCounter, boolean renderLevel, boolean renderScreens, CallbackInfo ci,
|
||||
@Local(ordinal = 0) GuiGraphicsExtractor drawContext) {
|
||||
if (drawContext == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawContext.nextStratum();
|
||||
lovisual$extractCustomBarHeads(drawContext, tickCounter);
|
||||
DynamicIsland.renderScreenOverlay(drawContext, tickCounter.getGameTimeDeltaTicks());
|
||||
ClickGuiRenderer.renderTopLayer(drawContext, tickCounter.getGameTimeDeltaTicks());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.screens;
|
||||
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.Tooltip;
|
||||
import net.minecraft.client.gui.screens.PauseScreen;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.module.lifecycle.Modules;
|
||||
import dev.loki.lovisual.features.module.modules.combat.pvp.PvpCooldowns;
|
||||
|
||||
@Mixin(PauseScreen.class)
|
||||
public abstract class PauseScreenMixin {
|
||||
@Shadow
|
||||
@Nullable
|
||||
private Button disconnectButton;
|
||||
|
||||
@Unique
|
||||
private Button lovisual$lastExitButton;
|
||||
@Unique
|
||||
private boolean lovisual$disconnectBlocked;
|
||||
@Unique
|
||||
private boolean lovisual$previousExitActive = true;
|
||||
|
||||
@Inject(method = "init", at = @At("TAIL"))
|
||||
private void lovisual$blockDisconnectOnInit(CallbackInfo ci) {
|
||||
lovisual$syncDisconnectButton();
|
||||
}
|
||||
|
||||
@Inject(method = "extractRenderState", at = @At("HEAD"))
|
||||
private void lovisual$blockDisconnectOnRender(GuiGraphicsExtractor context, int mouseX, int mouseY, float deltaTicks, CallbackInfo ci) {
|
||||
lovisual$syncDisconnectButton();
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void lovisual$syncDisconnectButton() {
|
||||
Button button = disconnectButton;
|
||||
if (button == null) {
|
||||
lovisual$lastExitButton = null;
|
||||
lovisual$disconnectBlocked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (button != lovisual$lastExitButton) {
|
||||
lovisual$lastExitButton = button;
|
||||
lovisual$disconnectBlocked = false;
|
||||
lovisual$previousExitActive = button.active;
|
||||
}
|
||||
|
||||
PvpCooldowns cooldowns = Modules.get(PvpCooldowns.class);
|
||||
boolean shouldBlock = cooldowns != null && cooldowns.shouldBlockDisconnect();
|
||||
if (shouldBlock) {
|
||||
if (!lovisual$disconnectBlocked) {
|
||||
lovisual$previousExitActive = button.active;
|
||||
}
|
||||
lovisual$disconnectBlocked = true;
|
||||
button.active = false;
|
||||
button.setTooltip(Tooltip.create(cooldowns.getDisconnectBlockTooltip()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (lovisual$disconnectBlocked) {
|
||||
button.active = lovisual$previousExitActive;
|
||||
button.setTooltip(null);
|
||||
lovisual$disconnectBlocked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.screens;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import dev.loki.lovisual.config.i18n.MainConfig;
|
||||
import dev.loki.lovisual.features.command.core.CommandManager;
|
||||
import dev.loki.lovisual.features.command.util.CommandPrefix;
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.buttons.BetterButtons;
|
||||
import dev.loki.lovisual.features.module.lifecycle.Modules;
|
||||
import dev.loki.lovisual.features.module.modules.visuals.visibility.NoRender;
|
||||
import dev.loki.lovisual.render.engine.core.system.LoVisualRenderSystem;
|
||||
import dev.loki.lovisual.render.engine.core.frame.RenderPhase;
|
||||
import dev.loki.lovisual.render.engine.core.frame.RenderPhaseScope;
|
||||
import dev.loki.lovisual.render.engine.postprocess.render.MenuBackgroundRenderer;
|
||||
import dev.loki.lovisual.util.NarratorBlocker;
|
||||
|
||||
@Mixin(Screen.class)
|
||||
public abstract class ScreenMixin {
|
||||
@Unique
|
||||
private RenderPhaseScope lovisual$screenPhaseScope;
|
||||
|
||||
@Inject(method = "defaultHandleGameClickEvent", at = @At("HEAD"), cancellable = true)
|
||||
private static void lovisual$runClientChatCommand(ClickEvent event,
|
||||
Minecraft client,
|
||||
Screen screen,
|
||||
CallbackInfo ci) {
|
||||
if (!(event instanceof ClickEvent.RunCommand run)) return;
|
||||
String command = run.command();
|
||||
if (command == null || !CommandPrefix.isCommandLike(command)) return;
|
||||
if (CommandManager.handle(command)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "extractMenuBackgroundTexture", at = @At("HEAD"), cancellable = true)
|
||||
private static void lovisual$replaceMenuBackground(GuiGraphicsExtractor context,
|
||||
Identifier texture,
|
||||
int x,
|
||||
int y,
|
||||
float u,
|
||||
float v,
|
||||
int width,
|
||||
int height,
|
||||
CallbackInfo ci) {
|
||||
if (texture == null || context == null) return;
|
||||
|
||||
if (!Screen.MENU_BACKGROUND.equals(texture)) return;
|
||||
if (x != 0 || y != 0) return;
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null) return;
|
||||
if (width != context.guiWidth() || height != context.guiHeight()) return;
|
||||
|
||||
MainConfig cfg = MainConfig.get();
|
||||
if (cfg == null) return;
|
||||
String mode = cfg.getMenuBackgroundMode();
|
||||
if (mode == null || mode.equalsIgnoreCase("off")) return;
|
||||
|
||||
boolean aurora = mode.equalsIgnoreCase("aurora");
|
||||
MenuBackgroundRenderer.render(mc, aurora);
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "shouldRunNarration", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$disableNarratorUi(CallbackInfoReturnable<Boolean> cir) {
|
||||
if (!NarratorBlocker.isBlocked()) return;
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
|
||||
@Inject(method = "extractBackground(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$replaceScreenBackground(GuiGraphicsExtractor context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null || mc.level != null) return;
|
||||
MainConfig cfg = MainConfig.get();
|
||||
String mode = (cfg != null) ? cfg.getMenuBackgroundMode() : "off";
|
||||
if (mode == null || mode.equalsIgnoreCase("off")) return;
|
||||
|
||||
boolean aurora = mode.equalsIgnoreCase("aurora");
|
||||
MenuBackgroundRenderer.render(mc, aurora);
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "extractBackground(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$noRender$cancelDarkening(GuiGraphicsExtractor context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
NoRender module = Modules.get(NoRender.class);
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (module != null && mc != null && mc.level != null && module.screenDarkeningDisabled()) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "extractTransparentBackground(Lnet/minecraft/client/gui/GuiGraphicsExtractor;)V", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$noRender$cancelInGameBackground(GuiGraphicsExtractor context, CallbackInfo ci) {
|
||||
NoRender module = Modules.get(NoRender.class);
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (module != null && mc != null && mc.level != null && module.screenDarkeningDisabled()) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "extractPanorama", at = @At("HEAD"), cancellable = true, require = 0)
|
||||
private void lovisual$replaceTitlePanorama(GuiGraphicsExtractor context, float delta, CallbackInfo ci) {
|
||||
|
||||
Screen self = (Screen) (Object) this;
|
||||
if (!(self instanceof TitleScreen)) return;
|
||||
|
||||
MainConfig cfg = MainConfig.get();
|
||||
String mode = (cfg != null) ? cfg.getMenuBackgroundMode() : "off";
|
||||
if (mode == null || mode.equalsIgnoreCase("off")) return;
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null) return;
|
||||
|
||||
boolean aurora = mode.equalsIgnoreCase("aurora");
|
||||
MenuBackgroundRenderer.render(mc, aurora);
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "extractRenderStateWithTooltipAndSubtitles(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("HEAD"))
|
||||
private void lovisual$beginButtonQueue(GuiGraphicsExtractor ctx, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
BetterButtons.beginFrame(ctx);
|
||||
}
|
||||
|
||||
@Inject(method = "extractRenderStateWithTooltipAndSubtitles(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("HEAD"))
|
||||
private void lovisual$phaseScreenHead(GuiGraphicsExtractor ctx, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
Screen self = (Screen) (Object) this;
|
||||
String label = "2d:screen:" + self.getClass().getSimpleName();
|
||||
LoVisualRenderSystem.ensureFrameContext();
|
||||
lovisual$screenPhaseScope = LoVisualRenderSystem.phase(RenderPhase.SCREEN, label);
|
||||
}
|
||||
|
||||
@Inject(method = "extractRenderStateWithTooltipAndSubtitles(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("RETURN"))
|
||||
private void lovisual$phaseScreenTail(GuiGraphicsExtractor ctx, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
Screen self = (Screen) (Object) this;
|
||||
String label = "2d:screen:" + self.getClass().getSimpleName();
|
||||
if (lovisual$screenPhaseScope != null) {
|
||||
lovisual$screenPhaseScope.close();
|
||||
lovisual$screenPhaseScope = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.screens;
|
||||
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.gui.mainmenu.LoVisualMainMenuScreen;
|
||||
|
||||
@Mixin(TitleScreen.class)
|
||||
public abstract class TitleScreenMixin {
|
||||
|
||||
@Inject(method = "init", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$replaceTitleScreen(CallbackInfo ci) {
|
||||
|
||||
if (LoVisualMainMenuScreen.shouldUseVanillaTitleScreen()) return;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null) return;
|
||||
if (ClientScreen.current() instanceof LoVisualMainMenuScreen) return;
|
||||
ClientScreen.show(mc, new LoVisualMainMenuScreen());
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.widgets;
|
||||
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.screens.inventory.MerchantScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.buttons.BetterButtons;
|
||||
|
||||
@Mixin(Button.Plain.class)
|
||||
public abstract class ButtonWidgetTextMixin {
|
||||
|
||||
@Inject(method = "extractContents(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$drawIcon(GuiGraphicsExtractor ctx, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
BetterButtons buttons = BetterButtons.get();
|
||||
if (buttons == null || !buttons.useUiButtons()) return;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc != null && ClientScreen.current() instanceof MerchantScreen) return;
|
||||
AbstractWidget widget = (AbstractWidget) (Object) this;
|
||||
boolean hovered = widget.isMouseOver(mouseX, mouseY);
|
||||
float hover = BetterButtons.updateHover(widget, hovered, mouseX, mouseY);
|
||||
BetterButtons.enqueueButton(widget, widget.getMessage(), hover, widget.isActive(), widget.isFocused());
|
||||
if (hovered) {
|
||||
BetterButtons.captureTooltip(widget, mouseX, mouseY);
|
||||
}
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.widgets;
|
||||
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.components.CycleButton;
|
||||
import net.minecraft.client.gui.screens.inventory.MerchantScreen;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.buttons.BetterButtons;
|
||||
import dev.loki.lovisual.mixins.accessors.widget.CyclingButtonWidgetAccessor;
|
||||
|
||||
@Mixin(CycleButton.class)
|
||||
public abstract class CycleButtonMixin<T> {
|
||||
|
||||
@Inject(method = "extractContents(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$drawIcon(GuiGraphicsExtractor ctx, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
BetterButtons buttons = BetterButtons.get();
|
||||
if (buttons == null || !buttons.useUiButtons()) return;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc != null && ClientScreen.current() instanceof MerchantScreen) return;
|
||||
CycleButton<T> self = (CycleButton<T>) (Object) this;
|
||||
AbstractWidget widget = self;
|
||||
|
||||
boolean hovered = widget.isMouseOver(mouseX, mouseY);
|
||||
float hover = BetterButtons.updateHover(widget, hovered, mouseX, mouseY);
|
||||
|
||||
CyclingButtonWidgetAccessor<T> accessor = (CyclingButtonWidgetAccessor<T>) self;
|
||||
Identifier icon = accessor.lovisual$getIcon() != null
|
||||
? accessor.lovisual$getIcon().apply(self, self.getValue())
|
||||
: null;
|
||||
|
||||
boolean showLabel = accessor.lovisual$getLabelType() != CycleButton.DisplayState.HIDE;
|
||||
BetterButtons.enqueueCyclingButton(self, icon, showLabel, hover, widget.isActive(), widget.isFocused());
|
||||
if (hovered) {
|
||||
BetterButtons.captureTooltip(widget, mouseX, mouseY);
|
||||
}
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.widgets;
|
||||
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.components.SpriteIconButton;
|
||||
import net.minecraft.client.gui.screens.inventory.MerchantScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.buttons.BetterButtons;
|
||||
|
||||
@Mixin(SpriteIconButton.CenteredIcon.class)
|
||||
public abstract class SpriteIconButtonCenteredIconMixin {
|
||||
|
||||
@Inject(method = "extractContents(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$drawIcon(GuiGraphicsExtractor ctx, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
BetterButtons buttons = BetterButtons.get();
|
||||
if (buttons == null || !buttons.useUiButtons()) return;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc != null && ClientScreen.current() instanceof MerchantScreen) return;
|
||||
SpriteIconButton self = (SpriteIconButton) (Object) this;
|
||||
AbstractWidget widget = self;
|
||||
|
||||
boolean hovered = widget.isMouseOver(mouseX, mouseY);
|
||||
float hover = BetterButtons.updateHover(widget, hovered, mouseX, mouseY);
|
||||
|
||||
BetterButtons.enqueueTextIconButton(self, true, hover, widget.isActive(), widget.isFocused());
|
||||
if (hovered) {
|
||||
BetterButtons.captureTooltip(widget, mouseX, mouseY);
|
||||
}
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* This file is part of the LoVisual Client distribution.
|
||||
* Copyright (c) 2026 loki5512344.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0.
|
||||
*/
|
||||
|
||||
package dev.loki.lovisual.mixins.gui.widgets;
|
||||
|
||||
import dev.loki.lovisual.util.screen.ClientScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.components.SpriteIconButton;
|
||||
import net.minecraft.client.gui.screens.inventory.MerchantScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import dev.loki.lovisual.features.gui.hud.nondraggable.impl.buttons.BetterButtons;
|
||||
|
||||
@Mixin(SpriteIconButton.TextAndIcon.class)
|
||||
public abstract class SpriteIconButtonTextAndIconMixin {
|
||||
|
||||
@Inject(method = "extractContents(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$drawIcon(GuiGraphicsExtractor ctx, int mouseX, int mouseY, float delta, CallbackInfo ci) {
|
||||
BetterButtons buttons = BetterButtons.get();
|
||||
if (buttons == null || !buttons.useUiButtons()) return;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc != null && ClientScreen.current() instanceof MerchantScreen) return;
|
||||
SpriteIconButton self = (SpriteIconButton) (Object) this;
|
||||
AbstractWidget widget = self;
|
||||
|
||||
boolean hovered = widget.isMouseOver(mouseX, mouseY);
|
||||
float hover = BetterButtons.updateHover(widget, hovered, mouseX, mouseY);
|
||||
|
||||
BetterButtons.enqueueTextIconButton(self, false, hover, widget.isActive(), widget.isFocused());
|
||||
if (hovered) {
|
||||
BetterButtons.captureTooltip(widget, mouseX, mouseY);
|
||||
}
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue