This commit is contained in:
retuci0 committed 2025-08-21 16:14:14 +02:00
commit cce703122b
90 files changed
+4654

No files matched your search

@@ -0,0 +1,114 @@
package me.retucio.camtweaks;
import me.retucio.camtweaks.command.CommandManager;
import me.retucio.camtweaks.command.commands.BindCommand;
import me.retucio.camtweaks.event.EventBus;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.ui.ClickGUI;
import me.retucio.camtweaks.ui.settings.*;
import net.fabricmc.api.ModInitializer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
public class CameraTweaks implements ModInitializer {
// cosas necesarias
public static final String MOD_ID = "camtweaks";
public static final CameraTweaks INSTANCE = new CameraTweaks();
public static final Logger LOGGER = LogManager.getLogger(CameraTweaks.class);
public static final EventBus EVENT_BUS = new EventBus();
public static final MinecraftClient mc = MinecraftClient.getInstance();
private Screen prevScreen;
// managers
public static ModuleManager moduleManager = new ModuleManager(EVENT_BUS);
public static CommandManager commandManager = new CommandManager();
@Override
public void onInitialize() {}
// se ejecuta cada vez que se presiona una tecla
public void onKeyPress(int key, int action) {
moduleManager.getEnabledModules().forEach(module -> module.onKey(key, action));
boolean anyFocused = isAnySettingButtonFocused();
if (action == GLFW.GLFW_PRESS) {
handleModuleToggle(key, anyFocused);
handleSettingButtonsKey(key, action);
handleClickGUIKey(key, anyFocused);
BindCommand.onKeyPress(key);
} else if (action == GLFW.GLFW_RELEASE && !anyFocused) {
handleModuleRelease(key);
}
}
// verifica si algún botón de ajustes está escuchando
private boolean isAnySettingButtonFocused() {
for (SettingsFrame sf : ClickGUI.INSTANCE.getSettingsFrames()) {
for (SettingButton sb : sf.getButtons()) {
if ((sb instanceof BindButton b && b.isFocused()) || (sb instanceof TextButton t && t.isFocused()))
return true;
}
}
return false;
}
// se ocupa de la lógica de encendido y apagado de los módulos
private void handleModuleToggle(int key, boolean anyFocused) {
if (mc.currentScreen != null && mc.currentScreen != ClickGUI.INSTANCE) return;
for (Module module : moduleManager.getModules()) {
if (key != module.getKey() || anyFocused) continue;
if (module.shouldToggleOnBindRelease() && !module.isEnabled())
module.setEnabled(true);
else if (!module.shouldToggleOnBindRelease())
module.toggle();
}
}
// se ocupa de hacer los botones de ajustes que lo necesiten escuchar teclas
private void handleSettingButtonsKey(int key, int action) {
for (SettingsFrame sf : ClickGUI.INSTANCE.getSettingsFrames()) {
for (SettingButton sb : sf.getButtons()) {
if (sb instanceof BindButton b) b.onKey(key, action);
else if (sb instanceof TextButton t) t.onKey(key, action);
}
}
}
// maneja la lógica de apertura de la interfaz
private void handleClickGUIKey(int key, boolean anyFocused) {
if (key != ClickGUISettingsFrame.guiSettings.getKey() || anyFocused) return;
if (mc.currentScreen != ClickGUI.INSTANCE) {
prevScreen = mc.currentScreen;
mc.setScreen(ClickGUI.INSTANCE);
} else {
mc.setScreen(prevScreen);
}
}
// se ocupa de apagar los módulos que tengan configurado hacerlo tras soltar su tecla
private void handleModuleRelease(int key) {
if (mc.currentScreen != null && mc.currentScreen != ClickGUI.INSTANCE) return;
for (Module module : moduleManager.getEnabledModules()) {
if (module.shouldToggleOnBindRelease() && key == module.getKey()) {
module.setEnabled(false);
}
}
}
// se ejecuta cada tick, es decir, 20 veces por segundo
public void onTick() {
if (mc.player == null) return;
moduleManager.getEnabledModules().forEach(Module::onTick);
}
}
@@ -0,0 +1,77 @@
package me.retucio.camtweaks.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandRegistryAccess;
import net.minecraft.command.CommandSource;
import net.minecraft.registry.BuiltinRegistries;
import net.minecraft.server.command.CommandManager;
import java.util.List;
import static me.retucio.camtweaks.CameraTweaks.commandManager;
public abstract class Command {
// utilizar los "registries" ya existentes de Brigadier para el autocompletado
protected static final CommandRegistryAccess REGISTRY_ACCESS = CommandManager.createRegistryAccess(BuiltinRegistries.createWrapperLookup());
protected static final int SUCCESS = com.mojang.brigadier.Command.SINGLE_SUCCESS;
protected static final MinecraftClient mc = MinecraftClient.getInstance();
private final String name, description;
private final List<String> aliases;
public Command(String name, String description, String... aliases) {
this.name = name;
this.description = description;
this.aliases = List.of(aliases);
}
// métodos "helper" para reducir el "boilerplate"
protected static <T> RequiredArgumentBuilder<CommandSource, T> argument(final String name, final ArgumentType<T> type) {
return RequiredArgumentBuilder.argument(name, type);
}
protected static LiteralArgumentBuilder<CommandSource> literal(final String name) {
return LiteralArgumentBuilder.literal(name);
}
// para registrar comandos
public final void registerTo(CommandDispatcher<CommandSource> dispatcher) {
register(dispatcher, name);
for (String alias : aliases) register(dispatcher, alias);
}
public void register(CommandDispatcher<CommandSource> dispatcher, String name) {
LiteralArgumentBuilder<CommandSource> builder = LiteralArgumentBuilder.literal(name);
build(builder);
dispatcher.register(builder);
}
public abstract void build(LiteralArgumentBuilder<CommandSource> builder);
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public List<String> getAliases() {
return aliases;
}
public String toString() {
return commandManager.getPrefix() + name;
}
public String toString(String... args) {
StringBuilder sb = new StringBuilder(toString());
for (String arg : args) sb.append(" ").append(arg);
return sb.toString();
}
}
@@ -0,0 +1,66 @@
package me.retucio.camtweaks.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.retucio.camtweaks.command.commands.*;
import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Comparator;
// registro de comandos
public class CommandManager {
private String prefix = "$";
public static final List<Command> commands = new ArrayList<>();
public static final CommandDispatcher<CommandSource> dispatcher = new CommandDispatcher<>();
private static final MinecraftClient mc = MinecraftClient.getInstance();
public CommandManager() {
registerCommands();
}
public static void registerCommands() {
addCommand(new BindCommand());
addCommand(new ToggleCommand());
addCommand(new SendCommand());
addCommand(new PrefixCommand());
commands.sort(Comparator.comparing(Command::getName));
}
public static void addCommand(Command command) {
commands.removeIf(existing -> existing.getName().equals(command.getName()));
command.registerTo(dispatcher);
commands.add(command);
}
public static void dispatch(String message) throws CommandSyntaxException {
dispatcher.execute(message, mc.getNetworkHandler().getCommandSource());
}
public static Command getCommandByName(String name) {
for (Command command : commands)
if (command.getName().equals(name))
return command;
return null;
}
public <T extends Command> T getCommandByClass(Class<T> clazz) {
for (Command command : commands)
if (clazz.isInstance(command))
return clazz.cast(command);
return null;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
}
@@ -0,0 +1,49 @@
package me.retucio.camtweaks.command.args;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.retucio.camtweaks.module.Module;
import net.minecraft.command.CommandSource;
import net.minecraft.text.Text;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
public class ModuleArgumentType implements ArgumentType<Module> {
public static final ModuleArgumentType INSTANCE = new ModuleArgumentType();
private static final DynamicCommandExceptionType unknownModuleE = new DynamicCommandExceptionType(
name -> Text.literal("módulo \"" + name + "\" no encontrado"));
private static final Collection<String> examples = moduleManager.getModules()
.stream().limit(3).map(Module::getName).toList();
@Override
public Module parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString();
Module module = moduleManager.getModuleByName(argument);
if (module == null) throw unknownModuleE.create(argument);
return module;
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return CommandSource.suggestMatching(moduleManager.getModules().stream().map(Module::getName), builder);
}
@Override
public Collection<String> getExamples() {
return examples;
}
public static Module get(CommandContext<CommandSource> ctx) {
return ctx.getArgument("módulo", Module.class);
}
}
@@ -0,0 +1,55 @@
package me.retucio.camtweaks.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command;
import me.retucio.camtweaks.command.args.ModuleArgumentType;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.util.ChatUtil;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandSource;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
public class BindCommand extends Command {
// The module currently waiting for a key press
private static Module listeningModule = null;
public BindCommand() {
super("bind", "asigna una tecla a un módulo", "keybind");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder
.then(argument("módulo", ModuleArgumentType.INSTANCE)
.executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
listeningModule = module;
ChatUtil.info("presiona una tecla para asignarla al módulo " + module.getName());
return SUCCESS;
})
);
}
public static void onKeyPress(int key) {
if (listeningModule != null) {
KeySetting bind = listeningModule.getBind();
if (bind != null)
bind.setKey(key);
ChatUtil.info(
Text.of("la tecla " + Formatting.AQUA + KeyUtil.getKeyName(key) + Formatting.RESET +
" ha sido asignada al módulo " + Formatting.GREEN + listeningModule.getName())
);
listeningModule = null;
}
}
}
@@ -0,0 +1,28 @@
package me.retucio.camtweaks.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command;
import me.retucio.camtweaks.ui.settings.ClickGUISettingsFrame;
import me.retucio.camtweaks.util.ChatUtil;
import net.minecraft.command.CommandSource;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
public class PrefixCommand extends Command {
public PrefixCommand() {
super("prefijo", "cambia el prefijo de los comandos", "prefix");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder
.then(argument("prefijo", StringArgumentType.word()).executes(ctx -> {
String prefix = ctx.getArgument("prefijo", String.class);
ClickGUISettingsFrame.guiSettings.commandPrefix.setValue(prefix);
ChatUtil.info(Text.literal("prefijo cambiado a " + Formatting.AQUA + prefix));
return SUCCESS;
}));
}
}
@@ -0,0 +1,26 @@
package me.retucio.camtweaks.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command;
import net.minecraft.command.CommandSource;
import net.minecraft.text.Text;
public class SendCommand extends Command {
public SendCommand() {
super("enviar", "manda un mensaje en el chat", "send");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder
.then(argument("mensaje", StringArgumentType.greedyString())
.executes(ctx -> {
if (mc.player == null) return 0;
mc.player.sendMessage(Text.of(ctx.getArgument("mensaje", String.class)), false);
return SUCCESS;
})
);
}
}
@@ -0,0 +1,58 @@
package me.retucio.camtweaks.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command;
import me.retucio.camtweaks.command.args.ModuleArgumentType;
import me.retucio.camtweaks.module.Module;
import net.minecraft.command.CommandSource;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
// porque usar el ratón es más lento que escribir, a menos que tengas toque con el teclado como yo
public class ToggleCommand extends Command {
public ToggleCommand() {
super("alternar", "enciende / apaga módulos", "toggle");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder
.then(literal("todos")
.then(literal("on")
.executes(ctx -> {
moduleManager.getModules().forEach(module -> {
if (!module.isEnabled()) module.toggle();
}); return SUCCESS;
})
)
.then(literal("off")
.executes(ctx -> {
moduleManager.getEnabledModules().forEach(Module::toggle);
return SUCCESS;
})
)
)
.then(argument("módulo", ModuleArgumentType.INSTANCE)
.executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
module.toggle();
return SUCCESS;
})
.then(literal("on")
.executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
module.setEnabled(true);
return SUCCESS;
})
)
.then(literal("off")
.executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
module.setEnabled(false);
return SUCCESS;
})
)
);
}
}
@@ -0,0 +1,14 @@
package me.retucio.camtweaks.event;
// evento base, cancelable
public class Event {
private boolean cancelled;
public boolean isCancelled() {
return cancelled;
}
public void cancel() {
cancelled = true;
}
}
@@ -0,0 +1,55 @@
package me.retucio.camtweaks.event;
import java.lang.reflect.Method;
import java.util.*;
// al llamar un evento, se guarda en el bus de eventos, de donde luego se puede detectar si ese evento ha sucedido
public class EventBus {
private final Map<Class<?>, List<ListenerMethod>> listeners = new HashMap<>();
private record ListenerMethod(Object instance, Method method) {}
public void register(Object listener) {
for (Method method : listener.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(Subscribe.class)) {
if (method.getParameterCount() != 1) continue; // must take 1 param
Class<?> eventType = method.getParameterTypes()[0];
method.setAccessible(true);
listeners.computeIfAbsent(eventType, k -> new ArrayList<>())
.add(new ListenerMethod(listener, method));
}
}
}
public void unregister(Object listener) {
for (List<ListenerMethod> list : listeners.values()) {
list.removeIf(lm -> lm.instance == listener);
}
}
public <T extends Event> T post(T event) {
List<ListenerMethod> list = listeners.get(event.getClass());
if (list != null) {
for (ListenerMethod lm : list) {
try {
lm.method.invoke(lm.instance, event);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return event;
}
public boolean isRegistered(Object listener) {
for (List<ListenerMethod> list : listeners.values()) {
for (ListenerMethod lm : list) {
if (lm.instance == listener) {
return true;
}
}
}
return false;
}
}
@@ -0,0 +1,8 @@
package me.retucio.camtweaks.event;
import java.lang.annotation.*;
// anotación para registar un "listener", que escucha cuando se llama un evento
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Subscribe {}
@@ -0,0 +1,22 @@
package me.retucio.camtweaks.event.events;
import net.minecraft.text.ClickEvent;
public class ClientClickEvent implements ClickEvent {
private final String value;
public ClientClickEvent(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public Action getAction() {
return Action.RUN_COMMAND;
}
}
@@ -0,0 +1,20 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
public class GetFOVEvent extends Event {
private float fov;
public GetFOVEvent(float fov) {
this.fov = fov;
}
public float getFov() {
return fov;
}
public void setFov(float fov) {
this.fov = fov;
}
}
@@ -0,0 +1,32 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Hand;
public class HeldItemRendererEvent extends Event {
private MatrixStack matrices;
private Hand hand;
public HeldItemRendererEvent(MatrixStack matrices, Hand hand) {
this.matrices = matrices;
this.hand = hand;
}
public MatrixStack getMatrices() {
return matrices;
}
public void setMatrices(MatrixStack matrices) {
this.matrices = matrices;
}
public Hand getHand() {
return hand;
}
public void setHand(Hand hand) {
this.hand = hand;
}
}
@@ -0,0 +1,27 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
// se genera en KeyboardMixin
public class KeyEvent extends Event {
private final int key, scancode, action;
public KeyEvent(int key, int scancode, int action) {
this.key = key;
this.scancode = scancode;
this.action = action;
}
public int getKey() {
return key;
}
public int getAction() {
return action;
}
public int getScancode() {
return scancode;
}
}
@@ -0,0 +1,22 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
// se genera en MouseMixin
public class MouseClickEvent extends Event {
private final int action, button;
public MouseClickEvent(int action, int button) {
this.action = action;
this.button = button;
}
public int getAction() {
return action;
}
public int getButton() {
return button;
}
}
@@ -0,0 +1,30 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
// se genera en MouseMixin
public class MouseScrollEvent extends Event {
private double horizontal, vertical;
public MouseScrollEvent(double horizontal, double vertical) {
this.horizontal = horizontal;
this.vertical = vertical;
}
public double getHorizontal() {
return horizontal;
}
public void setHorizontal(double horizontal) {
this.horizontal = horizontal;
}
public double getVertical() {
return vertical;
}
public void setVertical(double vertical) {
this.vertical = vertical;
}
}
@@ -0,0 +1,22 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
import net.minecraft.client.option.Perspective;
// se genera en GameOptionsMixin
public class PerspectiveChangeEvent extends Event {
private Perspective perspective;
public PerspectiveChangeEvent(Perspective perspective) {
this.perspective = perspective;
}
public Perspective getPerspective() {
return perspective;
}
public void setPerspective(Perspective perspective) {
this.perspective = perspective;
}
}
@@ -0,0 +1,43 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.text.Text;
public class ReceiveMessageEvent extends Event {
private Text text;
private MessageIndicator indicator;
private int id;
private boolean modified = false;
public ReceiveMessageEvent(Text text, MessageIndicator indicator, int id) {
this.text = text;
this.indicator = indicator;
this.id = id;
}
public Text getMessage() {
return text;
}
public MessageIndicator getIndicator() {
return indicator;
}
public void setMessage(Text message) {
this.text = message;
this.modified = true;
}
public void setIndicator(MessageIndicator indicator) {
this.indicator = indicator;
this.modified = true;
}
public boolean wasModified() {
return modified;
}
}
@@ -0,0 +1,32 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Hand;
public class RenderArmEvent extends Event {
private MatrixStack matrices;
private Hand hand;
public RenderArmEvent(MatrixStack matrices, Hand hand) {
this.matrices = matrices;
this.hand = hand;
}
public MatrixStack getMatrices() {
return matrices;
}
public void setMatrices(MatrixStack matrices) {
this.matrices = matrices;
}
public Hand getHand() {
return hand;
}
public void setHand(Hand hand) {
this.hand = hand;
}
}
@@ -0,0 +1,6 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
public class RenderEvent extends Event {
}
@@ -0,0 +1,20 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
public class SendMessageEvent extends Event {
private String message;
public SendMessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
@@ -0,0 +1,29 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.module.modules.Perspective;
import net.minecraft.client.render.Camera;
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.CallbackInfoReturnable;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(Camera.class)
public abstract class CameraMixin {
@ModifyVariable(method = "clipToSpace", at = @At("HEAD"), ordinal = 0, argsOnly = true)
private float modifyClipToSpace(float distance) {
Perspective perspective = moduleManager.getModuleByClass(Perspective.class);
return perspective.isEnabled() ? (float) perspective.getDistance() : distance;
}
@Inject(method = "clipToSpace", at = @At("HEAD"), cancellable = true)
private void onClipToSpace(float distance, CallbackInfoReturnable<Float> cir) {
if (moduleManager.getModuleByClass(Perspective.class).isEnabled()
&& moduleManager.getModuleByClass(Perspective.class).clip.isEnabled())
cir.setReturnValue(distance);
}
}
@@ -0,0 +1,38 @@
package me.retucio.camtweaks.mixin;
import com.mojang.authlib.GameProfile;
import me.retucio.camtweaks.util.interfaces.IChatHudLine;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@Mixin(ChatHudLine.class)
public class ChatHudLineMixin implements IChatHudLine {
@Shadow @Final
private Text content;
@Unique
private int id;
@Unique
private GameProfile sender;
@Override
public String smegma$getText() { return content.toString(); }
@Override
public int smegma$getId() { return id; }
@Override
public void smegma$setId(int id) { this.id = id; }
@Override
public GameProfile smegma$getSender() { return sender; }
@Override
public void smegma$setSender(GameProfile profile) { this.sender = profile; }
}
@@ -0,0 +1,55 @@
package me.retucio.camtweaks.mixin;
import com.mojang.authlib.GameProfile;
import me.retucio.camtweaks.util.interfaces.IChatHudLineVisible;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.text.OrderedText;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@Mixin(ChatHudLine.Visible.class)
public class ChatHudLineVisibleMixin implements IChatHudLineVisible {
@Shadow @Final
private OrderedText content;
@Unique
private int id;
@Unique
private GameProfile sender;
@Unique
private boolean startOfEntry;
@Override
public String smegma$getText() {
StringBuilder sb = new StringBuilder();
content.accept((index, style, codePoint) -> {
sb.appendCodePoint(codePoint);
return true;
});
return sb.toString();
}
@Override
public int smegma$getId() { return id; }
@Override
public void smegma$setId(int id) { this.id = id; }
@Override
public GameProfile smegma$getSender() { return sender; }
@Override
public void smegma$setSender(GameProfile profile) { this.sender = profile; }
@Override
public boolean smegma$isStartOfEntry() { return startOfEntry; }
@Override
public void smegma$setStartOfEntry(boolean start) { this.startOfEntry = start; }
}
@@ -0,0 +1,194 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.ModifyReceiver;
import com.llamalad7.mixinextras.sugar.Local;
import me.retucio.camtweaks.event.events.ReceiveMessageEvent;
import me.retucio.camtweaks.module.modules.BetterChat;
import me.retucio.camtweaks.util.interfaces.IChatHud;
import me.retucio.camtweaks.util.interfaces.IChatHudLine;
import me.retucio.camtweaks.util.interfaces.IChatHudLineVisible;
import me.retucio.camtweaks.util.interfaces.IMessageHandler;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.network.message.MessageSignatureData;
import net.minecraft.text.OrderedText;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
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 java.util.List;
import static me.retucio.camtweaks.CameraTweaks.*;
@Mixin(ChatHud.class)
public abstract class ChatHudMixin implements IChatHud {
@Unique
private boolean skipOnAddMessage;
@Unique
private int nextId;
@Unique @Final
private BetterChat betterChat = moduleManager.getModuleByClass(BetterChat.class);
@Shadow @Final
private List<ChatHudLine> messages;
@Shadow @Final
private List<ChatHudLine.Visible> visibleMessages;
@Shadow
public abstract void addMessage(Text message);
@Shadow
public abstract void addMessage(Text message, @Nullable MessageSignatureData signatureData, @Nullable MessageIndicator indicator);
// métodos relacionados a las interfaces IChatHud, IChatHudLine, IChadHudLineVisible y IMessageHandler
@Override
public void smegma$add(Text message, int id) {
nextId = id;
addMessage(message);
nextId = 0;
}
@Inject(method = "addVisibleMessage", at = @At(value = "INVOKE", target = "Ljava/util/List;add(ILjava/lang/Object;)V", shift = At.Shift.AFTER))
private void onAddMessageAfterNewChatHudLineVisible(ChatHudLine message, CallbackInfo ci) {
((IChatHudLine) (Object) visibleMessages.getFirst()).smegma$setId(nextId);
}
@Inject(method = "addMessage(Lnet/minecraft/client/gui/hud/ChatHudLine;)V", at = @At(value = "INVOKE", target = "Ljava/util/List;add(ILjava/lang/Object;)V", shift = At.Shift.AFTER))
private void onAddMessageAfterNewChatHudLine(ChatHudLine message, CallbackInfo ci) {
((IChatHudLine) (Object) messages.getFirst()).smegma$setId(nextId);
}
@SuppressWarnings("DataFlowIssue")
@ModifyExpressionValue(method = "addVisibleMessage", at = @At(value = "NEW", target = "(ILnet/minecraft/text/OrderedText;Lnet/minecraft/client/gui/hud/MessageIndicator;Z)Lnet/minecraft/client/gui/hud/ChatHudLine$Visible;"))
private ChatHudLine.Visible onAddMessage_modifyChatHudLineVisible(ChatHudLine.Visible line, @Local(ordinal = 1) int j) {
IMessageHandler handler = (IMessageHandler) mc.getMessageHandler();
if (handler == null) return line;
IChatHudLineVisible iLine = (IChatHudLineVisible) (Object) line;
iLine.smegma$setSender(handler.smegma$getSender());
iLine.smegma$setStartOfEntry(j == 0);
return line;
}
@ModifyExpressionValue(method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", at = @At(value = "NEW", target = "(ILnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)Lnet/minecraft/client/gui/hud/ChatHudLine;"))
private ChatHudLine onAddMessage_modifyChatHudLine(ChatHudLine line) {
IMessageHandler handler = (IMessageHandler) mc.getMessageHandler();
if (handler == null) return line;
((IChatHudLine) (Object) line).smegma$setSender(handler.smegma$getSender());
return line;
}
// modificar contenido del mensaje
@Inject(at = @At("HEAD"), method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", cancellable = true)
private void onAddMessage(Text message, MessageSignatureData signatureData, MessageIndicator indicator, CallbackInfo ci) {
if (skipOnAddMessage) return;
ReceiveMessageEvent event = EVENT_BUS.post(new ReceiveMessageEvent(message, indicator, nextId));
if (event.isCancelled()) {
ci.cancel();
} else {
visibleMessages.removeIf(msg -> ((IChatHudLine) (Object) msg).smegma$getId() == nextId && nextId != 0);
for (int i = messages.size() - 1; i > -1; i--) {
if (((IChatHudLine) (Object) messages.get(i)).smegma$getId() == nextId && nextId != 0) {
messages.remove(i);
betterChat.removeLine(i);
}
}
if (event.wasModified()) {
ci.cancel();
skipOnAddMessage = true;
addMessage(event.getMessage(), signatureData, event.getIndicator());
skipOnAddMessage = false;
}
}
}
// cabezas
@ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;ceil(F)I"))
private int onRender_modifyWidth(int width) {
return (betterChat.isEnabled() && betterChat.showHeads.isEnabled()) ? width + 10 : width;
}
@ModifyReceiver(method = "method_71991", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/OrderedText;III)V"))
private DrawContext onRender_beforeDrawTextWithShadow(DrawContext context, TextRenderer textRenderer, OrderedText text, int x, int y, int color, @Local(argsOnly = true) ChatHudLine.Visible line) {
betterChat.beforeDrawMessage(context, line, y, color);
return context;
}
@Inject(method = "method_71991", at = @At("TAIL"))
private void onRender_afterDrawTextWithShadow(int i, DrawContext context, float f, int j, int k, int l, ChatHudLine.Visible visible, int m, float g, CallbackInfo info) {
betterChat.afterDrawMessage(context);
}
// historial (registro) del chat
@ModifyExpressionValue(method = "addMessage(Lnet/minecraft/client/gui/hud/ChatHudLine;)V", at = @At(value = "CONSTANT", args = "intValue=100"))
private int maxLength(int size) {
if (!betterChat.isEnabled()) return size;
return size + betterChat.chatHistoryExtraLength.getIntValue();
}
@ModifyExpressionValue(method = "addVisibleMessage", at = @At(value = "CONSTANT", args = "intValue=100"))
private int maxLengthVisible(int size) {
if (!betterChat.isEnabled()) return size;
return size + betterChat.chatHistoryExtraLength.getIntValue();
}
@Inject(method = "addVisibleMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z"))
private void onBreakChatMessageLines(ChatHudLine message, CallbackInfo ci, @Local List<OrderedText> list) {
betterChat.lines.addFirst(list.size());
}
@Inject(method = "addMessage(Lnet/minecraft/client/gui/hud/ChatHudLine;)V", at = @At(value = "INVOKE", target = "Ljava/util/List;remove(I)Ljava/lang/Object;"))
private void onRemoveMessage(ChatHudLine message, CallbackInfo ci) {
int extra = betterChat.chatHistoryExtraLength.getIntValue();
int size = betterChat.lines.size();
while (size > 100 + extra) {
betterChat.lines.removeLast();
size--;
}
}
@Inject(method = "clear", at = @At("HEAD"))
private void onClear(boolean clearHistory, CallbackInfo ci) {
betterChat.lines.clear();
}
@Inject(method = "refresh", at = @At("HEAD"))
private void onRefresh(CallbackInfo ci) {
betterChat.lines.clear();
}
}
@@ -0,0 +1,63 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.brigadier.ParseResults;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.suggestion.Suggestions;
import me.retucio.camtweaks.command.CommandManager;
import net.minecraft.client.gui.screen.ChatInputSuggestor;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.command.CommandSource;
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 java.util.concurrent.CompletableFuture;
import static me.retucio.camtweaks.CameraTweaks.commandManager;
import static me.retucio.camtweaks.CameraTweaks.mc;
@Mixin(ChatInputSuggestor.class)
public abstract class ChatInputSuggestorMixin {
@Shadow
private ParseResults<CommandSource> parse;
@Shadow @Final
TextFieldWidget textField;
@Shadow
private ChatInputSuggestor.SuggestionWindow window;
@Shadow
boolean completingSuggestions;
@Shadow
private CompletableFuture<Suggestions> pendingSuggestions;
@Shadow protected abstract void showCommandSuggestions();
@Inject(method = "refresh", at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/StringReader;canRead()Z", remap = false), cancellable = true)
private void onRefresh(CallbackInfo ci, @Local StringReader reader) {
String prefix = commandManager.getPrefix();
int length = prefix.length();
if (reader.canRead(length) && reader.getString().startsWith(prefix, reader.getCursor())) {
reader.setCursor(reader.getCursor() + length);
if (parse == null) this.parse = CommandManager.dispatcher.parse(reader, mc.getNetworkHandler().getCommandSource());
int cursor = textField.getCursor();
if (cursor >= length && (window == null || !completingSuggestions)) {
pendingSuggestions = CommandManager.dispatcher.getCompletionSuggestions(parse, cursor);
pendingSuggestions.thenRun(() -> {
if (pendingSuggestions.isDone()) showCommandSuggestions();
});
}
ci.cancel();
}
}
}
@@ -0,0 +1,25 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.module.modules.BetterChat;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.widget.TextFieldWidget;
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 static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(value = ChatScreen.class, priority = 1001)
public class ChatScreenMixin {
@Shadow
protected TextFieldWidget chatField;
@Inject(method = "init", at = @At(value = "RETURN"))
private void onInit(CallbackInfo info) {
BetterChat betterChat = moduleManager.getModuleByClass(BetterChat.class);
if (betterChat.isEnabled() && betterChat.noCharLimit.isEnabled()) chatField.setMaxLength(Integer.MAX_VALUE);
}
}
@@ -0,0 +1,49 @@
package me.retucio.camtweaks.mixin;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.retucio.camtweaks.command.CommandManager;
import me.retucio.camtweaks.event.events.SendMessageEvent;
import me.retucio.camtweaks.util.ChatUtil;
import net.minecraft.client.network.ClientPlayNetworkHandler;
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 static me.retucio.camtweaks.CameraTweaks.*;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerMixin {
@Unique
private boolean ignoreChatMessage;
@Shadow
public abstract void sendChatMessage(String content);
@Inject(method = "sendChatMessage", at = @At("HEAD"), cancellable = true)
private void onSendMessage(String message, CallbackInfo ci) {
if (ignoreChatMessage) return;
if (!message.startsWith(commandManager.getPrefix())) {
SendMessageEvent event = EVENT_BUS.post(new SendMessageEvent(message));
if (!event.isCancelled()) {
ignoreChatMessage = true;
sendChatMessage(event.getMessage());
ignoreChatMessage = false;
}
ci.cancel();
return;
};
try {
CommandManager.dispatch(message.substring(commandManager.getPrefix().length()));
} catch (CommandSyntaxException e) {
ChatUtil.error(e.getMessage());
}
mc.inGameHud.getChatHud().addToMessageHistory(message);
ci.cancel();
}
}
@@ -0,0 +1,22 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.PerspectiveChangeEvent;
import net.minecraft.client.option.GameOptions;
import net.minecraft.client.option.Perspective;
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 static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
@Mixin(GameOptions.class)
public abstract class GameOptionsMixin {
@Inject(method = "setPerspective", at = @At("HEAD"), cancellable = true)
private void changePerspective(Perspective perspective, CallbackInfo ci) {
PerspectiveChangeEvent event = EVENT_BUS.post(new PerspectiveChangeEvent(perspective));
if (event.isCancelled()) ci.cancel();
}
}
@@ -0,0 +1,40 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.GetFOVEvent;
import me.retucio.camtweaks.event.events.RenderEvent;
import me.retucio.camtweaks.module.modules.Zoom;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.RenderTickCounter;
import org.joml.Matrix4f;
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 static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
@Mixin(GameRenderer.class)
public abstract class GameRendererMixin {
@Unique
Zoom zoom = CameraTweaks.moduleManager.getModuleByClass(Zoom.class);
@ModifyReturnValue(method = "getFov", at = @At("RETURN"))
private float modifyFov(float original) {
return EVENT_BUS.post(new GetFOVEvent(original)).getFov();
}
// @Inject(method = "render", at = @At("HEAD"))
// private void onRender(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci) {
// CameraTweaks.INSTANCE.EVENT_BUS.post(new RenderEvent());
// }
@Inject(method = "renderHand", at = @At("HEAD"), cancellable = true)
private void renderHand(float tickProgress, boolean sleeping, Matrix4f positionMatrix, CallbackInfo ci) {
if (zoom.isEnabled() && !zoom.showHands.isEnabled())
ci.cancel();
}
}
@@ -0,0 +1,49 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import me.retucio.camtweaks.event.events.HeldItemRendererEvent;
import me.retucio.camtweaks.event.events.RenderArmEvent;
import me.retucio.camtweaks.module.modules.HandView;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.item.HeldItemRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Arm;
import net.minecraft.util.Hand;
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 static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(HeldItemRenderer.class)
public abstract class HeldItemRendererMixin {
@Unique
HandView handView = moduleManager.getModuleByClass(HandView.class);
@Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemDisplayContext;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", shift = At.Shift.BEFORE))
private void onRenderItem(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack item, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) {
EVENT_BUS.post(new HeldItemRendererEvent(matrices, hand));
}
@ModifyReturnValue(method = "shouldSkipHandAnimationOnSwap", at = @At("RETURN"))
private boolean modifySkipSwapAnimation(boolean original) {
return original || (handView.isEnabled() && handView.skipSwapping.isEnabled());
}
@Inject(method = "applyEatOrDrinkTransformation", at = @At(value = "INVOKE", target = "Ljava/lang/Math;pow(DD)D", shift = At.Shift.BEFORE), cancellable = true)
private void cancelTransformations(MatrixStack matrices, float tickDelta, Arm arm, ItemStack stack, PlayerEntity player, CallbackInfo ci) {
if (handView.isEnabled() && handView.noFood.isEnabled()) ci.cancel();
}
@Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderArmHoldingItem(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IFFLnet/minecraft/util/Arm;)V"))
private void onRenderArm(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack item, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) {
EVENT_BUS.post(new RenderArmEvent(matrices, hand));
}
}
@@ -0,0 +1,28 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.module.modules.BetterChat;
import me.retucio.camtweaks.ui.HUD;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.render.RenderTickCounter;
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 static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(InGameHud.class)
public abstract class InGameHudMixin {
@Inject(method = "render", at = @At("RETURN"), cancellable = true)
private void renderHUD(DrawContext ctx, RenderTickCounter tc, CallbackInfo ci) {
HUD.render(ctx, tc);
}
@Inject(method = "clear", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;clear(Z)V"), cancellable = true)
private void onClear(CallbackInfo ci) {
BetterChat betterChat = moduleManager.getModuleByClass(BetterChat.class);
if (betterChat.isEnabled() && betterChat.logger.isEnabled()) ci.cancel();
}
}
@@ -0,0 +1,23 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.KeyEvent;
import net.minecraft.client.Keyboard;
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 static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
@Mixin(Keyboard.class)
public abstract class KeyboardMixin {
@Inject(method = "onKey", at = @At("HEAD"), cancellable = true)
private void onKeyPress(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) {
CameraTweaks.INSTANCE.onKeyPress(key, action);
KeyEvent event = EVENT_BUS.post(new KeyEvent(key, scancode, action));
if (event.isCancelled()) ci.cancel();
}
}
@@ -0,0 +1,37 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.GpuTexture;
import me.retucio.camtweaks.module.modules.Fullbright;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.util.math.ColorHelper;
import net.minecraft.util.profiler.Profiler;
import org.spongepowered.asm.mixin.Final;
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 static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(LightmapTextureManager.class)
public abstract class LightmapTextureManagerMixin {
@Unique
Fullbright fullbright = moduleManager.getModuleByClass(Fullbright.class);
@Shadow @Final
private GpuTexture glTexture;
@Inject(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;push(Ljava/lang/String;)V", shift = At.Shift.AFTER), cancellable = true)
private void update(float tickProgress, CallbackInfo ci, @Local Profiler profiler) {
if (fullbright.isEnabled() && fullbright.mode.is(Fullbright.Modes.GAMMA)) {
RenderSystem.getDevice().createCommandEncoder().clearColorTexture(glTexture, ColorHelper.getArgb(255, 255, 255, 255));
profiler.pop();
ci.cancel();
}
}
}
@@ -0,0 +1,37 @@
package me.retucio.camtweaks.mixin;
import com.mojang.authlib.GameProfile;
import me.retucio.camtweaks.util.interfaces.IMessageHandler;
import net.minecraft.client.network.message.MessageHandler;
import net.minecraft.network.message.MessageType;
import net.minecraft.network.message.SignedMessage;
import net.minecraft.text.Text;
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 java.time.Instant;
@Mixin(MessageHandler.class)
public abstract class MessageHandlerMixin implements IMessageHandler {
@Unique
private GameProfile sender;
@Inject(method = "processChatMessageInternal", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", shift = At.Shift.BEFORE))
private void onProcessChatMessageInternal_beforeAddMessage(MessageType.Parameters params, SignedMessage message, Text decorated, GameProfile sender, boolean onlyShowSecureChat, Instant receptionTimestamp, CallbackInfoReturnable<Boolean> info) {
this.sender = sender;
}
@Inject(method = "processChatMessageInternal", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", shift = At.Shift.AFTER))
private void onProcessChatMessageInternal_afterAddMessage(MessageType.Parameters params, SignedMessage message, Text decorated, GameProfile sender, boolean onlyShowSecureChat, Instant receptionTimestamp, CallbackInfoReturnable<Boolean> info) {
this.sender = null;
}
@Override
public GameProfile smegma$getSender() {
return sender;
}
}
@@ -0,0 +1,17 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import net.minecraft.client.MinecraftClient;
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;
@Mixin(MinecraftClient.class)
public abstract class MinecraftClientMixin {
@Inject(method = "tick", at = @At("HEAD"), cancellable = true)
private void onTick(CallbackInfo ci) {
CameraTweaks.INSTANCE.onTick();
}
}
@@ -0,0 +1,28 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.MouseClickEvent;
import me.retucio.camtweaks.event.events.MouseScrollEvent;
import net.minecraft.client.Mouse;
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 static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
@Mixin(Mouse.class)
public abstract class MouseMixin {
@Inject(method = "onMouseButton", at = @At("HEAD"), cancellable = true)
private void onMouseButton(long window, int button, int action, int mods, CallbackInfo ci) {
MouseClickEvent event = EVENT_BUS.post(new MouseClickEvent(action, button));
if (event.isCancelled()) ci.cancel();
}
@Inject(method = "onMouseScroll", at = @At("HEAD"), cancellable = true)
private void onMouseScroll(long window, double horizontal, double vertical, CallbackInfo ci) {
MouseScrollEvent event = EVENT_BUS.post(new MouseScrollEvent(horizontal, vertical));
if (event.isCancelled()) ci.cancel();
}
}
@@ -0,0 +1,18 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.module.modules.BetterChat;
import net.minecraft.util.StringHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(StringHelper.class)
public abstract class StringHelperMixin {
@ModifyArg(method = "truncateChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/StringHelper;truncate(Ljava/lang/String;IZ)Ljava/lang/String;"), index = 1)
private static int truncate(int maxLength) {
BetterChat betterChat = moduleManager.getModuleByClass(BetterChat.class);
return ((betterChat.isEnabled() && betterChat.noCharLimit.isEnabled()) ? Integer.MAX_VALUE : maxLength);
}
}
@@ -0,0 +1,12 @@
package me.retucio.camtweaks.mixin.accessor;
import net.minecraft.entity.effect.StatusEffectInstance;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(StatusEffectInstance.class)
public interface StatusEffectInstanceAccessor {
@Accessor("duration")
void setDuration(int duration);
}
@@ -0,0 +1,139 @@
package me.retucio.camtweaks.module;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.module.settings.EnumSetting;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.module.settings.Setting;
import me.retucio.camtweaks.util.ChatUtil;
import net.minecraft.client.MinecraftClient;
import org.lwjgl.glfw.GLFW;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
// clase base para los módulos
public class Module {
// atributos
private String name;
private String description;
private boolean enabled;
// dejar al usuario elegir si el módulo debería apagarse tras soltar su tecla asignada, o si la tecla debería alternar su estado
protected EnumSetting<KeyModes> keyMode = new EnumSetting<>("modo de tecla", "cómo interpretar la tecla configurada", KeyModes.class, KeyModes.TOGGLE);
protected KeySetting bind = new KeySetting("tecla", "tecla asignada al módulo, ESC para desactivar", GLFW.GLFW_KEY_UNKNOWN);
protected BooleanSetting notify = new BooleanSetting("notificar", "notificar en el chat al activar / desactivar", true);
private final List<Setting> settings = new ArrayList<>();
protected MinecraftClient mc = MinecraftClient.getInstance();
public Module(String name, String description) {
this.name = name;
this.description = description;
addSettings(bind, keyMode, notify);
}
// ajustes
public List<Setting> getSettings() {
return settings;
}
public void addSetting(Setting setting) {
settings.add(setting);
}
public void addSettings(Setting... settings) {
for (Setting setting : settings) addSetting(setting);
}
// encendido y apagado del módulo
public void toggle() {
enabled = !enabled;
if (enabled) onEnable();
else onDisable();
}
public void onEnable() {
if (notify.isEnabled()) ChatUtil.info(getName() + " fue activado");
for (Method method : getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(Subscribe.class)) {
CameraTweaks.EVENT_BUS.register(this);
break;
}
}
}
public void onDisable() {
if (notify.isEnabled()) ChatUtil.info(getName() + " fue desactivado");
if (CameraTweaks.EVENT_BUS.isRegistered(this))
CameraTweaks.EVENT_BUS.unregister(this);
}
// otros métodos
public void onTick() {}
public void onKey(int key, int action) {}
public boolean nullCheck() {
return mc.player == null;
}
public boolean fullNullCheck() {
return mc.player == null || mc.world == null;
}
// getters y setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
if (enabled) onEnable();
else onDisable();
}
public boolean shouldToggleOnBindRelease() {
return keyMode.is(KeyModes.HOLD);
}
public int getKey() {
return bind.getKey();
}
public void setKey(int key) {
bind.setKey(key);
bind.setDefaultKey(key);
}
public KeySetting getBind() {
return bind;
}
public enum KeyModes {
HOLD("mantener"),
TOGGLE("alternar");
private final String name;
KeyModes(String name) { this.name = name; }
@Override public String toString() { return name; }
}
}
@@ -0,0 +1,67 @@
package me.retucio.camtweaks.module;
import me.retucio.camtweaks.event.EventBus;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.module.modules.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
// donde se regisran los módulos, y los "listeners" de eventos en cada módulo que lo necesite
public class ModuleManager {
private final List<Module> modules = new ArrayList<>();
private final EventBus eventBus;
public ModuleManager(EventBus bus) {
this.eventBus = bus;
addModules();
}
private void addModules() {
modules.add(new Fullbright());
modules.add(new Zoom());
modules.add(new Perspective());
modules.add(new HandView());
modules.add(new BetterChat());
// registrar los "listeners" necesarios
for (Module module : getEnabledModules()) {
for (Method method : module.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(Subscribe.class)) {
eventBus.register(module);
break;
}
}
}
}
// para obtener módulos más fácilmente (por nombre, clase, o la lista completa)
public List<Module> getModules() {
return modules;
}
public List<Module> getEnabledModules() {
List<Module> enabledModules = new ArrayList<>();
for (Module module : modules)
if (module.isEnabled()) enabledModules.add(module);
return enabledModules;
}
public Module getModuleByName(String name) {
for (Module module : modules)
if (module.getName().equalsIgnoreCase(name)) return module;
return null;
}
public <T extends Module> T getModuleByClass(Class<T> clazz) {
for (Module module : modules)
if (clazz.isInstance(module))
return clazz.cast(module);
return null;
}
}
@@ -0,0 +1,214 @@
package me.retucio.camtweaks.module.modules;
import com.mojang.authlib.GameProfile;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.command.CommandManager;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.event.events.ClientClickEvent;
import me.retucio.camtweaks.event.events.ReceiveMessageEvent;
import me.retucio.camtweaks.event.events.SendMessageEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.util.ChatUtil;
import me.retucio.camtweaks.util.interfaces.IChatHudLine;
import me.retucio.camtweaks.util.interfaces.IChatHudLineVisible;
import me.retucio.camtweaks.util.interfaces.TextVisitor;
import net.minecraft.client.gl.RenderPipelines;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.PlayerSkinDrawer;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.text.*;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// continúa en ChatHudMixin, ChatScreenMixin, InGameHudMixin & StringHelperMixin
public class BetterChat extends Module {
// ajustes
public BooleanSetting timestamps = new BooleanSetting("sello de tiempo", "muestra a qué hora se ha enviado un mensaje", false);
public BooleanSetting timestampSecs = new BooleanSetting("mostrar segundos", "muestra segundos también en el sello de tiempo", false);
public BooleanSetting showHeads = new BooleanSetting("cabezas", "muestra la cabeza del jugador junto a su mensaje", true);
public BooleanSetting coordsProtection = new BooleanSetting("proteger coordenadas", "evitar enviar coordenadas por el chat", true);
public BooleanSetting keepHistory = new BooleanSetting("no borrar chat", "no borrar el chat tras desconectarse", true);
public BooleanSetting logger = new BooleanSetting("registro", "evita que se borre el chat de un server", true);
public BooleanSetting noCharLimit = new BooleanSetting("quitar límite de caracteres", "te deja escribir mensajes tan largos como desees", false);
public NumberSetting chatHistoryExtraLength = new NumberSetting("expandir chat", "cuántas líneas añadir al historial del chat",
0, 0, 1000, 1);
public BetterChat() {
super("chat plus", "mejoras para el chat");
addSettings(timestamps, timestampSecs, showHeads, coordsProtection, keepHistory, logger, noCharLimit, chatHistoryExtraLength);
updateDateFormat();
timestamps.onUpdate(b -> timestampSecs.setVisible(b));
timestampSecs.onUpdate(x -> updateDateFormat());
}
public final IntList lines = new IntArrayList();
private static final Pattern antiClearRegex = Pattern.compile("\\n(\\n|\\s)+\\n");
private static final Pattern usernameRegex = Pattern.compile("^(?:<[0-9]{2}:[0-9]{2}>\\s)?<(.*?)>.*");
private record CustomHeadEntry(String prefix, Identifier texture) {}
private static final List<CustomHeadEntry> CUSTOM_HEAD_ENTRIES = new ArrayList<>();
private static final Pattern TIMESTAMP_REGEX = Pattern.compile("^<\\d{1,2}:\\d{1,2}>");
private SimpleDateFormat dateFormat;
private void updateDateFormat() {
dateFormat = new SimpleDateFormat(timestampSecs.isEnabled() ? "HH:mm:ss" : "HH:mm");
}
private static final Pattern coordRegex = Pattern.compile("(?<x>-?\\d{3,}(?:\\.\\d*)?)(?:\\s+(?<y>-?\\d{1,3}(?:\\.\\d*)?))?\\s+(?<z>-?\\d{3,}(?:\\.\\d*)?)");
private boolean containsCoordinates(String message) {
return coordRegex.matcher(message).find();
}
@Subscribe
public void onReceiveMessage(ReceiveMessageEvent event) {
Text message = event.getMessage();
// registrar mensajes para evitar su eliminación
if (logger.isEnabled()) {
String messageString = message.getString();
if (antiClearRegex.matcher(messageString).find()) {
MutableText newMessage = Text.empty();
TextVisitor.visit(message, (text, style, string) -> {
Matcher antiClearMatcher = antiClearRegex.matcher(string);
if (antiClearMatcher.find())
newMessage.append(Text.literal(antiClearMatcher.replaceAll("\n\n")).setStyle(style));
else
newMessage.append(text.copyContentOnly().setStyle(style));
return Optional.empty();
}, Style.EMPTY);
message = newMessage;
}
}
// agregar sellos de tiempo a los mensajes
if (timestamps.isEnabled()) {
Text timestamp = Text.literal("<" + dateFormat.format(new Date()) + "> ").formatted(Formatting.GRAY);
message = Text.empty().append(timestamp).append(message);
}
// modificar el mensaje final
event.setMessage(message);
}
@Subscribe
private void onSendMessage(SendMessageEvent event) {
String message = event.getMessage();
// evitar mandar coordenadas por el chat
if (coordsProtection.isEnabled() && containsCoordinates(message)) {
ChatUtil.warn(Text.literal("cuidadito con las coordenadas chavalín").append(
getSendButton(message)));
event.cancel();
return;
}
event.setMessage(message);
}
static {
CUSTOM_HEAD_ENTRIES.add(new CustomHeadEntry("smegma", Identifier.of(CameraTweaks.MOD_ID, "icon_chat.png")));
}
public void beforeDrawMessage(DrawContext context, ChatHudLine.Visible line, int y, int color) {
if (!isEnabled() || !showHeads.isEnabled()) return;
// dibujar la cabeza al principio del mensaje
if (((IChatHudLineVisible) (Object) line).smegma$isStartOfEntry())
drawTexture(context, (IChatHudLine) (Object) line, y, color);
context.getMatrices().pushMatrix();
context.getMatrices().translate(10, 0);
}
public void afterDrawMessage(DrawContext context) {
// finalizar renderizado de la cabeza
if (!isEnabled() || !showHeads.isEnabled()) return;
context.getMatrices().popMatrix();
}
private void drawTexture(DrawContext context, IChatHudLine line, int y, int color) {
String text = line.smegma$getText().trim();
int startOffset = 0;
// buscar el sello del tiempo si lo hay para dibujar la cabeza correctamente
try {
Matcher m = TIMESTAMP_REGEX.matcher(text);
if (m.find()) startOffset = m.end() + 1;
} catch (IllegalStateException ignored) {}
// dibujar el icono del mod
for (CustomHeadEntry entry : CUSTOM_HEAD_ENTRIES) {
if (text.startsWith(entry.prefix(), startOffset)) {
context.drawTexture(RenderPipelines.GUI_TEXTURED, entry.texture(), 0, y, 0, 0, 8, 8, 64, 64, 64, 64, color);
return;
}
}
// obtener y dibujar la cabeza del jugador
GameProfile sender = getSender(line, text);
if (sender == null) return;
PlayerListEntry entry = mc.getNetworkHandler().getPlayerListEntry(sender.getId());
if (entry == null) return;
PlayerSkinDrawer.draw(context, entry.getSkinTextures(), 0, y, 8);
}
// obtener el jugador que envió un mensaje
private GameProfile getSender(IChatHudLine line, String text) {
GameProfile sender = line.smegma$getSender();
if (sender == null) {
Matcher usernameMatcher = usernameRegex.matcher(text);
if (usernameMatcher.matches()) {
String username = usernameMatcher.group(1);
PlayerListEntry entry = mc.getNetworkHandler().getPlayerListEntry(username);
if (entry != null) sender = entry.getProfile();
}
}
return sender;
}
// botón para enviar mensaje con coordenadas de todos modos
private MutableText getSendButton(String message) {
MutableText sendButton = Text.literal("[ME LA SUDA]");
MutableText hintBaseText = Text.literal("");
MutableText hintMsg = Text.literal("enviar de todos modos:");
hintMsg.setStyle(hintBaseText.getStyle().withFormatting(Formatting.GRAY));
hintBaseText.append(hintMsg);
hintBaseText.append(Text.literal("\n" + message));
sendButton.setStyle(sendButton.getStyle()
.withFormatting(Formatting.DARK_RED)
.withClickEvent(new ClientClickEvent(CommandManager.getCommandByName("send").toString(message)))
.withHoverEvent(new HoverEvent.ShowText(hintBaseText)));
return sendButton;
}
public void removeLine(int index) {
lines.removeInt(index);
}
}
@@ -0,0 +1,58 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.mixin.accessor.StatusEffectInstanceAccessor;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.EnumSetting;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.registry.Registries;
import org.lwjgl.glfw.GLFW;
// continúa en LightmapTextureManagerMixin
public class Fullbright extends Module {
public EnumSetting<Modes> mode = new EnumSetting<>("modo", "qué modo de iluminación emplear (usar poción con shaders)", Modes.class, Modes.GAMMA);
public Fullbright() {
super("brilli-brilli", "deshabilita la oscuridad");
setKey(GLFW.GLFW_KEY_B);
addSetting(mode);
mode.onUpdate(mode -> { if (mode != Modes.POTION) disableNightVision(); });
}
@Override
public void onDisable() {
disableNightVision();
}
@Override
public void onTick() {
if (mc.player == null || !mode.getValue().equals(Modes.POTION)) return;
if (mc.player.hasStatusEffect(Registries.STATUS_EFFECT.getEntry(StatusEffects.NIGHT_VISION.value()))) {
StatusEffectInstance instance = mc.player.getStatusEffect(Registries.STATUS_EFFECT.getEntry(StatusEffects.NIGHT_VISION.value()));
if (instance != null && instance.getDuration() < 69) ((StatusEffectInstanceAccessor) instance).setDuration(69);
} else {
mc.player.addStatusEffect(new StatusEffectInstance(Registries.STATUS_EFFECT.getEntry(StatusEffects.NIGHT_VISION.value()), 69, 0));
}
}
private void disableNightVision() {
if (mc.player == null) return;
if (mc.player.hasStatusEffect(Registries.STATUS_EFFECT.getEntry(StatusEffects.NIGHT_VISION.value())))
mc.player.removeStatusEffect(Registries.STATUS_EFFECT.getEntry(StatusEffects.NIGHT_VISION.value()));
}
public enum Modes {
GAMMA("gamma"),
POTION("poción");
private final String name;
Modes(String name) { this.name = name; }
@Override
public String toString() { return name; }
}
}
@@ -0,0 +1,49 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.module.settings.StringSetting;
import me.retucio.camtweaks.util.Colors;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
import static me.retucio.camtweaks.CameraTweaks.commandManager;
import static me.retucio.camtweaks.util.Colors.*;
public class GUI extends Module {
public NumberSetting red = new NumberSetting("rojo", "cantidad de rojo en el RGB del marco", 0, 0, 255, 1);
public NumberSetting green = new NumberSetting("verde", "cantidad de verde en el RGB del marco", 0, 0, 255, 1);
public NumberSetting blue = new NumberSetting("azul", "cantidad de azul en el RGB del marco", 255, 0, 255, 1);
public NumberSetting alpha = new NumberSetting("alpha", "opacidad del marco", 180, 0, 255, 1);
public NumberSetting scrollSens = new NumberSetting("sensibilidad del scroll", "qué tan sensible es la interfaz a la rueda del ratón",
5, 0, 15, 0.5);
public StringSetting commandPrefix = new StringSetting("prefijo", "prefijo de los comandos", commandManager.getPrefix(), 10);
public GUI() {
super("interfaz", "ajustes de la interfaz, y otros misceláneos");
addSettings(red, green, blue, alpha, scrollSens, commandPrefix);
setKey(GLFW.GLFW_KEY_RIGHT_SHIFT);
keyMode.setVisible(false);
notify.setVisible(false);
commandPrefix.onUpdate(prefix -> commandManager.setPrefix(prefix));
red.onUpdate(r -> {frameHeadRed = r.intValue(); updateFrameHeadColor();});
green.onUpdate(g -> {frameHeadGreen = g.intValue(); updateFrameHeadColor();});
blue.onUpdate(b -> {frameHeadBlue = b.intValue(); updateFrameHeadColor();});
alpha.onUpdate(a -> {frameHeadAlpha = a.intValue(); updateFrameHeadColor();});
}
private void updateFrameHeadColor() {
Colors.frameHeadColor = new Color(
frameHeadRed,
frameHeadGreen,
frameHeadBlue,
frameHeadAlpha
).getRGB();
}
}
@@ -0,0 +1,140 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.event.events.HeldItemRendererEvent;
import me.retucio.camtweaks.event.events.RenderArmEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.module.settings.NumberSetting;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Hand;
import net.minecraft.util.math.RotationAxis;
import org.joml.Vector3d;
import org.lwjgl.glfw.GLFW;
public class HandView extends Module {
// ajustes de animaciones
public BooleanSetting oldAnimations = new BooleanSetting("animaciones viejas", "como las de la 1.8, al pegar", false);
public BooleanSetting skipSwapping = new BooleanSetting("no swap", "se salta la animación de cambiar items de mano", false);
public BooleanSetting noFood = new BooleanSetting("no comer", "no renderiza la animación de comer", false);
// ajustes de mano principal
public NumberSetting scaleMainX = new NumberSetting("escala de la mano principal (X)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleMainY = new NumberSetting("escala de la mano principal (Y)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleMainZ = new NumberSetting("escala de la mano principal (Z)", ".",
1, 0.01, 5, 0.1);
public NumberSetting posMainX = new NumberSetting("posición de la mano principal (X)", ".",
0, -3, 3, 0.1);
public NumberSetting posMainY = new NumberSetting("posición de la mano principal (Y)", ".",
0, -3, 3, 0.1);
public NumberSetting posMainZ = new NumberSetting("posición de la mano principal (Z)", ".",
0, -3, 3, 0.1);
public NumberSetting rotMainX = new NumberSetting("rotación de la mano principal (X)", ".",
0, -180, 180, 1);
public NumberSetting rotMainY = new NumberSetting("rotación de la mano principal (Y)", ".",
0, -180, 180, 1);
public NumberSetting rotMainZ = new NumberSetting("rotación de la mano principal (Z)", ".",
0, -180, 180, 1);
// ajustes de mano secundaria
public NumberSetting scaleOffX = new NumberSetting("escala de la mano secundaria (X)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleOffY = new NumberSetting("escala de la mano secundaria (Y)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleOffZ = new NumberSetting("escala de la mano secundaria (Z)", ".",
1, 0.01, 5, 0.1);
public NumberSetting posOffX = new NumberSetting("posición de la mano secundaria (X)", ".",
0, -3, 3, 0.1);
public NumberSetting posOffY = new NumberSetting("posición de la mano secundaria (Y)", ".",
0, -3, 3, 0.1);
public NumberSetting posOffZ = new NumberSetting("posición de la mano secundaria (Z)", ".",
0, -3, 3, 0.1);
public NumberSetting rotOffX = new NumberSetting("rotación de la mano secundaria (X)", ".",
0, -180, 180, 1);
public NumberSetting rotOffY = new NumberSetting("rotación de la mano secundaria (Y)", ".",
0, -180, 180, 1);
public NumberSetting rotOffZ = new NumberSetting("rotación de la mano secundaria (Z)", ".",
0, -180, 180, 1);
// brazo
public NumberSetting scaleArmX = new NumberSetting("escala del brazo (X)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleArmY = new NumberSetting("escala del brazo (Y)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleArmZ = new NumberSetting("escala del brazo (Z)", ".",
1, 0.01, 5, 0.1);
public NumberSetting posArmX = new NumberSetting("posición del brazo (X)", ".",
0, -3, 3, 0.1);
public NumberSetting posArmY = new NumberSetting("posición del brazo (Y)", ".",
0, -3, 3, 0.1);
public NumberSetting posArmZ = new NumberSetting("posición del brazo (Z)", ".",
0, -3, 3, 0.1);
public NumberSetting rotArmX = new NumberSetting("rotación del brazo (X)", ".",
0, -180, 180, 1);
public NumberSetting rotArmY = new NumberSetting("rotación del brazo (Y)", ".",
0, -180, 180, 1);
public NumberSetting rotArmZ = new NumberSetting("rotación del brazo (Z)", ".",
0, -180, 180, 1);
public HandView() {
super("manos", "modifica la manera en la que se renderizan las manos");
addSettings(
oldAnimations, skipSwapping, noFood,
scaleMainX, scaleMainY, scaleMainZ,
posMainX, posMainY, posMainZ,
rotMainX, rotMainY, rotMainZ,
scaleOffX, scaleOffY, scaleOffZ,
posOffX, posOffY, posOffZ,
rotOffX, rotOffY, rotOffZ,
scaleArmX, scaleArmY, scaleArmZ,
posArmX, posArmY, posArmZ,
rotArmX, rotArmY, rotArmZ);
setKey(GLFW.GLFW_KEY_UNKNOWN);
}
@Subscribe
public void onHeldItemRender(HeldItemRendererEvent event) {
if (event.getHand() == Hand.MAIN_HAND) {
scale(event.getMatrices(), new Vector3d(scaleMainX.getValue(), scaleMainY.getValue(), scaleMainZ.getValue()));
translate(event.getMatrices(), new Vector3d(posMainX.getValue(), posMainY.getValue(), posMainZ.getValue()));
rotate(event.getMatrices(), new Vector3d(rotMainX.getValue(), rotMainY.getValue(), rotMainZ.getValue()));
}
else {
scale(event.getMatrices(), new Vector3d(scaleOffX.getValue(), scaleOffY.getValue(), scaleOffZ.getValue()));
translate(event.getMatrices(), new Vector3d(posOffX.getValue(), posOffY.getValue(), posOffZ.getValue()));
rotate(event.getMatrices(), new Vector3d(rotOffX.getValue(), rotOffY.getValue(), rotOffZ.getValue()));
}
}
@Subscribe
public void onRenderArm(RenderArmEvent event) {
scale(event.getMatrices(), new Vector3d(scaleArmX.getValue(), scaleArmY.getValue(), scaleArmZ.getValue()));
translate(event.getMatrices(), new Vector3d(posArmX.getValue(), posArmY.getValue(), posArmZ.getValue()));
rotate(event.getMatrices(), new Vector3d(rotArmX.getValue(), rotArmY.getValue(), rotArmZ.getValue()));
}
private void rotate(MatrixStack matrix, Vector3d rotation) {
matrix.multiply(RotationAxis.POSITIVE_X.rotationDegrees((float) rotation.x));
matrix.multiply(RotationAxis.POSITIVE_Y.rotationDegrees((float) rotation.y));
matrix.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((float) rotation.z));
}
private void scale(MatrixStack matrix, Vector3d scale) {
matrix.scale((float) scale.x, (float) scale.y, (float) scale.z);
}
private void translate(MatrixStack matrix, Vector3d translation) {
matrix.translate((float) translation.x, (float) translation.y, (float) translation.z);
}
}
@@ -0,0 +1,61 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.event.events.MouseScrollEvent;
import me.retucio.camtweaks.event.events.PerspectiveChangeEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.*;
import me.retucio.camtweaks.util.KeyUtil;
import org.lwjgl.glfw.GLFW;
// continúa en CameraMixin
public class Perspective extends Module {
private double distance;
// ajustes
public BooleanSetting clip = new BooleanSetting(
"clip", "atravesar bloques con la cámara", true);
public NumberSetting defaultDistance = new NumberSetting(
"distancia", "distancia a la que está la cámara del jugador por defecto",
4, 1, 20, 0.2);
public NumberSetting scrollSens = new NumberSetting(
"sensibilidad del scroll", "sensibilidad de la rueda del ratón (0 para desactivar)",
1, 0, 8, 0.1);
public KeySetting scrollKey = new KeySetting("tecla del scroll", "qué tecla mantener para usar la rueda del ratón", GLFW.GLFW_KEY_LEFT_ALT);
public Perspective() {
super("perspectiva", "añade cositas chulas al cambio de perspectiva");
addSettings(clip, defaultDistance, scrollSens, scrollKey);
setKey(GLFW.GLFW_KEY_UNKNOWN);
}
@Override
public void onEnable() {
distance = defaultDistance.getValue();
super.onEnable();
}
@Subscribe
public void onPerspectiveChange(PerspectiveChangeEvent event) {
distance = defaultDistance.getValue();
}
@Subscribe
public void onMouseScroll(MouseScrollEvent event) {
if (mc.options.getPerspective() == net.minecraft.client.option.Perspective.FIRST_PERSON
|| mc.currentScreen != null || scrollSens.getValue() <= 0) return;
if (KeyUtil.isKeyDown(scrollKey.getKey())) {
distance -= event.getVertical() / 4 * (scrollSens.getValue() * distance);
event.cancel();
}
}
public double getDistance() {
return distance;
}
}
@@ -0,0 +1,99 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.event.events.GetFOVEvent;
import me.retucio.camtweaks.event.events.MouseScrollEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.util.ChatUtil;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
// continúa en GameRendererMixin
public class Zoom extends Module {
public BooleanSetting showHands = new BooleanSetting("mostrar manos", "esconde o muestra las manos al hacer zoom", true);
public BooleanSetting showHUD = new BooleanSetting("mostrar HUD", "esconde o muestra los indicadores en pantalla", true);
public NumberSetting scrollSens = new NumberSetting("sensibilidad del scroll", "sensibilidad de la rueda del ratón (0 para desactivar)",
0.4, 0, 8, 0.1);
public KeySetting scrollKey = new KeySetting("tecla del scroll", "qué tecla mantener para usar la rueda del ratón", GLFW.GLFW_KEY_LEFT_CONTROL);
public NumberSetting defaultZoom = new NumberSetting("zoom", "cantidad de zoom",
6, 1, 10, 0.1);
public NumberSetting mouseSensMultiplier = new NumberSetting("sensibilidad", "multiplicador de la sensibilidad del ratón",
0.7, 0.01, 1, 0.05);
public BooleanSetting smoothCam = new BooleanSetting("cámara cinemática", "usa la cámara cinemática mientras hagas zoom", false);
private boolean prevSmoothCam;
private double prevMouseSens;
private double prevFov;
private boolean prevHUD;
private double value;
public Zoom() {
super("zoom", "lupa");
super.keyMode.setValue(KeyModes.HOLD);
super.keyMode.setDefaultValue(KeyModes.HOLD);
super.notify.setEnabled(false);
super.notify.setDefaultValue(false);
setKey(GLFW.GLFW_KEY_F);
addSettings(showHands, showHUD, scrollSens, scrollKey, defaultZoom, mouseSensMultiplier, smoothCam);
}
@Override
public void onEnable() {
CameraTweaks.EVENT_BUS.register(this);
prevSmoothCam = mc.options.smoothCameraEnabled;
prevMouseSens = mc.options.getMouseSensitivity().getValue();
prevFov = mc.options.getFov().getValue();
prevHUD = mc.options.hudHidden;
mc.options.hudHidden = !showHUD.isEnabled();
value = defaultZoom.getValue();
}
@Override
public void onDisable() {
CameraTweaks.EVENT_BUS.unregister(this);
mc.options.smoothCameraEnabled = prevSmoothCam;
mc.options.getMouseSensitivity().setValue(prevMouseSens);
mc.options.hudHidden = prevHUD;
mc.worldRenderer.scheduleTerrainUpdate();
}
@Override
public void onTick() {
mc.options.smoothCameraEnabled = smoothCam.isEnabled();
if (!smoothCam.isEnabled())
mc.options.getMouseSensitivity().setValue(prevMouseSens * mouseSensMultiplier.getValue());
}
@Subscribe
private void onMouseScroll(MouseScrollEvent event) {
if (scrollSens.getValue() > 0 && isEnabled() && KeyUtil.isKeyDown(scrollKey.getKey())) {
value += event.getVertical() * 0.25 * (scrollSens.getValue() * value);
if (value < 1) value = 1;
event.cancel();
}
}
@Subscribe
private void onGetFov(GetFOVEvent event) {
event.setFov((float) (event.getFov() / value));
if (prevFov != event.getFov()) mc.worldRenderer.scheduleTerrainUpdate();
prevFov = event.getFov();
}
}
@@ -0,0 +1,50 @@
package me.retucio.camtweaks.module.settings;
import java.util.function.Consumer;
// ajuste booleano, es decir, o se encuentra encencido o apagado (similar a un interruptor)
public class BooleanSetting extends Setting {
private boolean defaultValue;
private boolean enabled;
private Consumer<Boolean> updateListener;
public BooleanSetting(String name, String description, boolean defaultValue) {
super(name, description);
this.enabled = defaultValue;
this.defaultValue = defaultValue;
}
public void toggle() {
enabled = !enabled;
if (updateListener != null) updateListener.accept(enabled);
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
if (this.enabled != enabled) {
this.enabled = enabled;
if (updateListener != null) updateListener.accept(enabled);
}
}
public boolean getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(boolean value) {
defaultValue = value;
}
public void reset() {
setEnabled(defaultValue);
}
public void onUpdate(Consumer<Boolean> listener) {
this.updateListener = listener;
}
}
@@ -0,0 +1,87 @@
package me.retucio.camtweaks.module.settings;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
// ajuste que te deja elegir entre distintas opciones definidas, dentro de un Enum
public class EnumSetting<E extends Enum<E>> extends Setting {
private final Class<E> enumClass;
private final List<E> values;
private E defaultValue;
private E value;
private int index;
private Consumer<E> updateListener;
public EnumSetting(String name, String description, Class<E> enumClass, E defaultValue) {
super(name, description);
this.enumClass = enumClass;
this.values = Arrays.asList(enumClass.getEnumConstants());
this.defaultValue = defaultValue;
this.value = defaultValue;
this.index = values.indexOf(defaultValue);
}
public E getValue() {
return value;
}
public void setValue(E value) {
if (this.value != value) {
this.value = value;
this.index = values.indexOf(value);
if (updateListener != null) updateListener.accept(value);
}
}
public List<E> getValues() {
return values;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
if (this.index != index)
setValue(values.get(index));
}
public boolean is(E value) {
return this.value == value;
}
// ciclar entre los valores del enum
public void cycle() {
if (index < values.size() - 1)
setIndex(index + 1);
else
setIndex(0);
}
public void cycleBackwards() {
if (index > 0)
setIndex(index - 1);
else
setIndex(values.size() - 1);
}
public void setDefaultValue(E value) {
defaultValue = value;
}
public E getDefaultValue() {
return defaultValue;
}
public void reset() {
setValue(defaultValue);
}
public void onUpdate(Consumer<E> listener) {
this.updateListener = listener;
}
}
@@ -0,0 +1,49 @@
package me.retucio.camtweaks.module.settings;
import me.retucio.camtweaks.util.KeyUtil;
import java.util.function.Consumer;
public class KeySetting extends Setting {
private int key;
private int defaultKey;
private Consumer<Integer> updateListener;
public KeySetting(String name, String description, int defaultKey) {
super(name, description);
this.key = defaultKey;
this.defaultKey = defaultKey;
}
public int getKey() {
return key;
}
public void setKey(int key) {
if (this.key == key) return;
this.key = key;
if (updateListener != null) updateListener.accept(key);
}
public int getDefaultKey() {
return defaultKey;
}
public void setDefaultKey(int key) {
this.defaultKey = key;
}
public String getKeyName() {
return KeyUtil.getKeyName(key);
}
public void reset() {
setKey(defaultKey);
}
public void onUpdate(Consumer<Integer> listener) {
this.updateListener = listener;
}
}
@@ -0,0 +1,82 @@
package me.retucio.camtweaks.module.settings;
import java.util.function.Consumer;
// ajuste numérico, es decir, que eliges un valor númerico entre el mínimo y el máximo disponibles
public class NumberSetting extends Setting {
private double value;
private final double defaultValue;
private final double min;
private final double max;
private final double increment; // el "increment" es el salto que hay entre valores disponibles
private Consumer<Double> updateListener;
public NumberSetting(String name, String description, double defaultValue, double min, double max, double increment) {
super(name, description);
this.defaultValue = defaultValue;
this.value = defaultValue;
this.min = min;
this.max = max;
this.increment = increment;
}
// no permitir valores ajenos a los límites definidos
public static double clamp(double value, double min, double max) {
return Math.clamp(value, min, max);
}
// como decía, el valor crece o decrece por el "increment" definido
public void increment(boolean positive) {
if (positive) value += increment;
else value -= increment;
value = clamp(value, min, max);
if (updateListener != null) updateListener.accept(value);
}
public double getValue() {
return value;
}
public float getFloatValue() {
return (float) value; // en "float" por conveniencia
}
public int getIntValue() {
return (int) value; // en "int" (número entero) por conveniencia
}
public void setValue(double value) {
if (this.value == value) return;
double clamped = clamp(value, min, max);
this.value = Math.round(clamped / increment) * increment;
if (updateListener != null) updateListener.accept(this.value);
}
public double getDefaultValue() {
return defaultValue;
}
public void reset() {
setValue(defaultValue);
}
public double getMin() {
return min;
}
public double getMax() {
return max;
}
public double getIncrement() {
return increment;
}
public void onUpdate(Consumer<Double> listener) {
this.updateListener = listener;
}
}
@@ -0,0 +1,30 @@
package me.retucio.camtweaks.module.settings;
// base para los tipos de ajustes
public class Setting {
private final String name;
private final String description;
private boolean visible = true;
protected Setting(String name, String description) {
this.name = name;
this.description = description;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
}
@@ -0,0 +1,50 @@
package me.retucio.camtweaks.module.settings;
import java.util.Objects;
import java.util.function.Consumer;
public class StringSetting extends Setting {
private String value;
private String defaultValue;
private final int maxLength;
private Consumer<String> updateListener;
public StringSetting(String name, String description, String defaultValue, int maxLength) {
super(name, description);
this.value = defaultValue;
this.defaultValue = defaultValue;
this.maxLength = maxLength;
}
public String getValue() {
return value;
}
public void setValue(String value) {
if (this.value.equals(value)) return;
this.value = value.length() > maxLength ? value.substring(0, maxLength) : value;
if (updateListener != null) updateListener.accept(this.value);
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public void reset() {
setValue(defaultValue);
}
public int getMaxLength() {
return maxLength;
}
public void onUpdate(Consumer<String> listener) {
this.updateListener = listener;
}
}
@@ -0,0 +1,111 @@
package me.retucio.camtweaks.ui;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.event.events.MouseScrollEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.ui.settings.ClickGUISettingsFrame;
import me.retucio.camtweaks.ui.settings.SettingsFrame;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import java.util.ArrayList;
import java.util.List;
import static me.retucio.camtweaks.ui.settings.ClickGUISettingsFrame.guiSettings;
// interfaz gráfica, se abre con el shift derecho por defecto. aquí se encuentran los módulos y sus ajustes
public class ClickGUI extends Screen {
public static final ClickGUI INSTANCE = new ClickGUI();
private final ModuleFrame modulesFrame = new ModuleFrame(20, 30, 100, 20);
private final List<SettingsFrame> settingsFrames = new ArrayList<>();
private final ClickGUISettingsFrame guiSettingsFrame = new ClickGUISettingsFrame(200, 30, 100, 20);
protected ClickGUI() {
super(Text.of("interfaz"));
settingsFrames.add(guiSettingsFrame);
CameraTweaks.EVENT_BUS.register(this);
}
@Override
public void render(DrawContext ctx, int mouseX, int mouseY, float delta) {
// renderizar el marco de los módulos
modulesFrame.render(ctx, mouseX, mouseY, delta);
modulesFrame.updatePosition(mouseX, mouseY);
// renderizar el marco de los ajustes de cada módulo que lo tenga abierto. se abre haciendo clic derecho sobre el módulo
for (SettingsFrame sf : new ArrayList<>(settingsFrames)) {
sf.render(ctx, mouseX, mouseY, delta);
sf.updatePosition(mouseX, mouseY);
}
super.render(ctx, mouseX, mouseY, delta);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
// detectar clics sobre los marcos
modulesFrame.mouseClicked(mouseX, mouseY, button);
for (SettingsFrame sf : new ArrayList<>(settingsFrames))
sf.mouseClicked(mouseX, mouseY, button);
return super.mouseClicked(mouseX, mouseY, button);
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
// registrar cuándo se suelta el clic, en cada marco respectivamente
modulesFrame.mouseReleased(mouseX, mouseY, button);
for (SettingsFrame sf : new ArrayList<>(settingsFrames))
sf.mouseRelease(mouseX, mouseY, button);
return super.mouseReleased(mouseX, mouseY, button);
}
@Subscribe
public void onMouseScroll(MouseScrollEvent event) {
// subir y bajar la pantalla con la rueda del ratón
if (CameraTweaks.mc.currentScreen != this) return;
modulesFrame.y += (int) (event.getVertical() * guiSettings.scrollSens.getValue());
for (SettingsFrame sf : settingsFrames)
sf.y += (int) (event.getVertical() * guiSettings.scrollSens.getValue());
}
public void openSettingsFrame(Module module, int x, int y) {
// abrir un marco donde se encuentran los ajustes del módulo deseado
if (isSettingsFrameOpen(module)) return; // permitir un solo marco de ajustes por módulo
settingsFrames.add(new SettingsFrame(module, x + 120, y, 120, 18));
}
// cerrar el marco de ajustes
public void closeSettingsFrame(Module module) {
settingsFrames.removeIf(sf -> sf.module == module);
}
// verificar si un módulo tiene su marco de ajustes abierto
public boolean isSettingsFrameOpen(Module module) {
return settingsFrames.stream()
.anyMatch(sf -> sf.module.getName().equals(module.getName()));
}
// no pausar el juego cuando se abre la interfaz
@Override
public boolean shouldPause() {
return false;
}
public ModuleFrame getModulesFrame() {
return modulesFrame;
}
public ClickGUISettingsFrame getGuiSettingsFrame() {
return guiSettingsFrame;
}
public List<SettingsFrame> getSettingsFrames() {
return settingsFrames;
}
}
@@ -0,0 +1,13 @@
package me.retucio.camtweaks.ui;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.RenderTickCounter;
// en un principio esto iba a mostrar en pantalla los módulos activados, pero me parece innecesario. se queda aquí por si acaso
public class HUD {
private static final MinecraftClient mc = MinecraftClient.getInstance();
public static void render(DrawContext ctx, RenderTickCounter tc) {}
}
@@ -0,0 +1,68 @@
package me.retucio.camtweaks.ui;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.util.Colors;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import java.awt.*;
// clase para el botón para cada módulo
public class ModuleButton {
public Module module;
public ModuleFrame parent;
public int offset;
public final int height = 18;
public ModuleButton(Module module, ModuleFrame parent, int offset) {
this.module = module;
this.parent = parent;
this.offset = offset;
}
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
ctx.fill( // dibujar el contorno del botón
parent.x + 2, parent.y + offset + 3,
parent.x + parent.w - 2 , parent.y + height + offset,
determineColor(mouseX, mouseY));
ctx.drawTextWithShadow( // dibujar el nombre del módulo
parent.mc.textRenderer, module.getName(),
parent.x + 5, parent.y + offset + (height / 2) - (parent.mc.textRenderer.fontHeight / 2) + 2, -1);
// dibujar "tooltips" (cajas de texto) al pasar el puntero encima del botón, para mostrar su descripción
if (isHovered((int) mouseX, (int) mouseY)) {
Screen currentScreen = parent.mc.currentScreen;
if (currentScreen != null)
ctx.drawTooltip(Text.of(module.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) // clic izquierdo para activar / desactivar el módulo
module.toggle();
else if (button == 1) { // clic derecho para el marco de ajustes (también lo cierra si está abierto)
if (ClickGUI.INSTANCE.isSettingsFrameOpen(module))
ClickGUI.INSTANCE.closeSettingsFrame(module);
else
ClickGUI.INSTANCE.openSettingsFrame(module,
parent.x + parent.w, parent.y + offset);
}
}
}
// verifica si el puntero del ratón se encuentra sobre el botón del módulo
public boolean isHovered(int mouseX, int mouseY) {
return mouseX > parent.x && mouseX < parent.x + parent.w && mouseY > parent.y + offset && mouseY < parent.y + height + offset;
}
public int determineColor(double mouseX, double mouseY) {
// determina el color del botón, dependiendo de si está el puntero encima y si está activado
if (module.isEnabled())
return isHovered((int) mouseX, (int) mouseY) ? Colors.enabledHoveredModuleButtonColor : Colors.enabledModuleButtonColor;
return isHovered((int) mouseX, (int) mouseY) ? Colors.hoveredModuleButtonColor : Colors.moduleButtonColor;
}
}
@@ -0,0 +1,116 @@
package me.retucio.camtweaks.ui;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.EnumSetting;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.ui.settings.BindButton;
import me.retucio.camtweaks.ui.settings.CycleButton;
import me.retucio.camtweaks.ui.settings.SettingButton;
import me.retucio.camtweaks.ui.settings.SliderButton;
import me.retucio.camtweaks.util.Colors;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import java.util.ArrayList;
import java.util.List;
// marco para los módulos
public class ModuleFrame {
public int x, y, w, h, dragX, dragY;
public boolean dragging, extended;
public String title = "módulos -";
private final List<ModuleButton> moduleButtons = new ArrayList<>();
protected MinecraftClient mc = MinecraftClient.getInstance();
public ModuleFrame(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
dragging = false;
extended = true;
// determinar el saliente para cada botón de módulo
int offset = h;
for (Module module : CameraTweaks.moduleManager.getModules()) {
moduleButtons.add(new ModuleButton(module, this, offset));
updateWidth();
offset += h;
}
}
void updateWidth() {
// asegurarse de que todos los botones caben en el marco, haciendo que la anchura se ajuste al texto más largo
int maxWidth = mc.textRenderer.getWidth(title);
for (ModuleButton button : moduleButtons) {
String text = button.module.getName();
int textWidth = mc.textRenderer.getWidth(text);
maxWidth = Math.max(maxWidth, textWidth);
}
this.w = maxWidth + 22;
}
public void render(DrawContext ctx, int mouseX, int mouseY, float delta) {
updateWidth();
ctx.fill(x, y, x + w, y + h, Colors.frameHeadColor); // cabeza del marco
// título del marco
ctx.drawText(mc.textRenderer, title,
x + (w / 2) - (mc.textRenderer.getWidth(title) / 2),
y + ( h /2) - (mc.textRenderer.fontHeight / 2),
-1, false);
// dibujar sus módulos solo si está extendido
if (extended) {
int totalHeight = moduleButtons.size() * h + 3;
ctx.fill( // fondo para los botones
x, y + h + 1,
x + w, y + h + totalHeight,
Colors.frameBGColor);
// dibujar los botones para cada módulo
for (ModuleButton moduleButton : moduleButtons)
moduleButton.render(ctx, mouseX, mouseY, delta);
}
}
public void mouseClicked(double mouseX, double mouseY, int button) {
// registrar clics
if (isHovered(mouseX, mouseY)) {
if (button == 0) { // clic izquierdo para arrastrarlo
dragging = true;
dragX = (int) (mouseX - x);
dragY = (int) (mouseY - y);
} else if (button == 1) { // clic derecho para extenderlo
extended = !extended;
title = extended ? "módulos -" : "módulos +";
}
}
if (!extended) return; // solo dejar clicar en los módulos si el marco está extendido
for (ModuleButton moduleButton : moduleButtons)
moduleButton.mouseClicked(mouseX, mouseY, button);
}
// detectar cuándo se suelta el clic
public void mouseReleased(double mouseX, double mouseY, int button) {
if (button == 0 && dragging) dragging = false;
}
// verificar si el puntero del ratón se encuentra encima
public boolean isHovered(double mouseX, double mouseY) {
return mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h;
}
// actualizar la posición al arrastrar el marco
public void updatePosition(double mouseX, double mouseY) {
if (dragging) {
x = (int) (mouseX - dragX);
y = (int) (mouseY - dragY);
}
}
}
@@ -0,0 +1,74 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.util.Colors;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
// botón para configurar la tecla asignada a un módulo. comienza a escuchar al hacerle clic
public class BindButton extends SettingButton {
private final KeySetting setting;
private boolean listening = false;
public BindButton(KeySetting setting, SettingsFrame parent, int offset) {
super(setting, parent, offset);
this.setting = setting;
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
int x = parent.x + 4;
int y = parent.y + offset + 3;
int w = parent.w - 8;
int bgColor = isHovered((int) mouseX, (int) mouseY)
? Colors.hoveredCycleButtonColor
: Colors.cycleButtonColor;
ctx.fill(x, y, x + w, y + height, bgColor);
String label = setting.getName() + ": " + (listening ? "..." : setting.getKeyName());
ctx.drawTextWithShadow(parent.mc.textRenderer, label, x + 5, y + 3, -1);
if (isHovered((int) mouseX, (int) mouseY)) {
Screen currentScreen = parent.mc.currentScreen;
if (currentScreen != null)
ctx.drawTooltip(Text.of(setting.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) listening = !listening;
else if (button == 1 && KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT))
setting.reset();
} else {
listening = false;
}
}
public void onKey(int key, int action) {
if (listening && action == GLFW.GLFW_PRESS) {
if (key == GLFW.GLFW_KEY_ESCAPE)
setting.setKey(GLFW.GLFW_KEY_UNKNOWN);
else
setting.setKey(key);
listening = false;
}
}
public boolean isFocused() {
return listening;
}
public KeySetting getSetting() {
return setting;
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {}
}
@@ -0,0 +1,53 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.modules.GUI;
import me.retucio.camtweaks.util.Colors;
import net.minecraft.client.gui.DrawContext;
public class ClickGUISettingsFrame extends SettingsFrame {
public static final GUI guiSettings = new GUI();
private boolean extended = false;
private String title = "ajustes de la interfaz -";
public ClickGUISettingsFrame(int x, int y, int w, int h) {
super(guiSettings, x, y, w, h);
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered(mouseX, mouseY)) {
if (button == 0) {
dragging = true;
dragX = (int) (mouseX - x);
dragY = (int) (mouseY - y);
} else if (button == 1) {
extended = !extended;
title = extended ? "ajustes de la interfaz -" : "ajustes de la interfaz +";
}
}
if (!extended) return; // solo dejar clicar en los módulos si el marco está extendido
for (SettingButton settingButton : settingButtons)
settingButton.mouseClicked(mouseX, mouseY, button);
}
@Override
public void render(DrawContext ctx, int mouseX, int mouseY, float delta) {
updateWidth();
ctx.fill(x, y, x + w, y + h, Colors.frameHeadColor);
ctx.drawText(mc.textRenderer, title,
x + (w / 2) - (mc.textRenderer.getWidth(title) / 2),
y + (h / 2) - (mc.textRenderer.fontHeight / 2),
-1, false);
if (extended) {
int totalHeight = settingButtons.size() * h + 3;
ctx.fill(x, y + h + 1, x + w, y + h + totalHeight, Colors.frameBGColor);
for (SettingButton button : settingButtons)
button.render(ctx, mouseX, mouseY, delta);
}
}
}
@@ -0,0 +1,84 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.EnumSetting;
import me.retucio.camtweaks.util.Colors;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import org.lwjgl.glfw.GLFW;
import java.util.ArrayList;
import java.util.List;
// botón para los ajustes tipo EnumSetting (de modo)
public class CycleButton<E extends Enum<E>> extends SettingButton {
private final EnumSetting<E> setting;
public CycleButton(EnumSetting<E> setting, SettingsFrame parent, int offset) {
super(setting, parent, offset);
this.setting = setting;
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
int x = parent.x + 4;
int y = parent.y + offset + 3;
int w = parent.w - 8;
int bgColor = isHovered((int) mouseX, (int) mouseY)
? Colors.hoveredCycleButtonColor
: Colors.cycleButtonColor;
ctx.fill(x, y, x + w, y + height, bgColor); // fondo del botón
// texto del botón: nombre + valor de texto del enum
String label = setting.getName() + ": " + setting.getValue().toString();
ctx.drawTextWithShadow(parent.mc.textRenderer, label, x + 5, y + 3, -1);
// tooltip
if (isHovered((int) mouseX, (int) mouseY)) {
Screen currentScreen = parent.mc.currentScreen;
if (currentScreen != null)
// si shift está presionado mientras el puntero está encima, mostrar los modos disponibles
if (GLFW.glfwGetKey(parent.mc.getWindow().getHandle(), GLFW.GLFW_KEY_LEFT_SHIFT) != GLFW.GLFW_RELEASE) {
List<Text> lines = new ArrayList<>();
lines.add(Text.literal("modos disponibles:"));
for (Enum<?> val : setting.getValues()) {
if (val == setting.getValue())
lines.add(Text.literal("> " + val.toString() + " <").formatted(Formatting.GREEN)); // destacar modo actualmente seleccionado
else
lines.add(Text.literal(val.toString()));
}
ctx.drawTooltip(parent.mc.textRenderer, lines, (int) mouseX, (int) mouseY + 20);
// si shift no está siendo presionado, simplemente mostrar la descripción
} else {
ctx.drawTooltip(Text.of(setting.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) {
setting.cycle(); // clic izquierdo -> ciclar
} else if (button == 1) {
// shift + clic derecho -> restablecer
if (KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT))
setting.reset();
else
setting.cycleBackwards(); // simplemente clic derecho -> ciclar valores pero hacia atrás
}
}
}
public EnumSetting<E> getSetting() {
return setting;
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {}
}
@@ -0,0 +1,28 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.Setting;
import net.minecraft.client.gui.DrawContext;
// clase base para los botones de los ajustes
public abstract class SettingButton {
protected final Setting setting;
protected final SettingsFrame parent;
protected int offset;
protected final int height = 15;
public SettingButton(Setting setting, SettingsFrame parent, int offset) {
this.setting = setting;
this.parent = parent;
this.offset = offset;
}
public abstract void render(DrawContext ctx, double mouseX, double mouseY, float delta);
public abstract void mouseClicked(double mouseX, double mouseY, int button);
public abstract void mouseReleased(double mouseX, double mouseY, int button);
protected boolean isHovered(int mouseX, int mouseY) {
return mouseX > parent.x + 4 && mouseX < parent.x + parent.w - 4 &&
mouseY > parent.y + offset && mouseY < parent.y + offset + height;
}
}
@@ -0,0 +1,152 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.*;
import me.retucio.camtweaks.ui.ClickGUI;
import me.retucio.camtweaks.util.Colors;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
// marco para los botones de los ajustes de cada módulo
public class SettingsFrame {
private final String title;
public final Module module;
final List<SettingButton> settingButtons = new ArrayList<>();
public int x, y, w, h;
boolean dragging;
int dragX;
int dragY;
final MinecraftClient mc = MinecraftClient.getInstance();
public SettingsFrame(Module module, int x, int y, int w, int h) {
this.module = module;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
// añadir el respectivo tipo de botón de cada ajuste a la lista de botones
int offset = h;
for (Setting setting : module.getSettings()) {
if (!setting.isVisible()) continue;
switch (setting) {
case BooleanSetting b -> {
addButton(new ToggleButton(b, this, offset));
offset += 18;
} case NumberSetting n -> {
addButton(new SliderButton(n, this, offset));
offset += 18;
} case EnumSetting e -> {
addButton(new CycleButton<>(e, this, offset));
offset += 18;
} case KeySetting k -> {
addButton(new BindButton(k, this, offset));
offset += 18;
} case StringSetting s -> {
addButton(new TextButton(s, this, offset));
offset += 18;
} default -> {}
}
}
title = "ajustes de " + module.getName() + " -";
}
public void addButton(SettingButton button) {
settingButtons.add(button);
updateWidth(); // actualizar anchura del frame tras cada iteración
}
void updateWidth() {
// asegurarse de que todos los botones caben en el marco, haciendo que la anchura se ajuste al texto más largo
int maxWidth = mc.textRenderer.getWidth(title);
for (SettingButton button : settingButtons) {
String text = button.setting.getName();
switch (button) {
case SliderButton sliderButton -> text += ": " + sliderButton.df.format((sliderButton.getSetting()).getValue());
case CycleButton cycleButton -> text += ": " + cycleButton.getSetting().getValue();
case BindButton bindButton -> text += ": " + bindButton.getSetting().getKeyName();
case TextButton textButton -> text += ": " + textButton.getSetting().getValue();
default -> {}
}
int textWidth = mc.textRenderer.getWidth(text);
maxWidth = Math.max(maxWidth, textWidth);
}
this.w = maxWidth + 22;
}
public void render(DrawContext ctx, int mouseX, int mouseY, float delta) {
updateWidth();
ctx.fill(x, y, x + w, y + h, module.isEnabled()
? Colors.enabledToggleButtonColor.getRGB()
: Colors.disabledToggleButtonColor.getRGB());
ctx.drawText(mc.textRenderer, title,
x + (w / 2) - (mc.textRenderer.getWidth(title) / 2),
y + (h / 2) - (mc.textRenderer.fontHeight / 2),
-1, false);
// si no hay ajustes disponibles para un módulo, hacerle saber al usuario
if (settingButtons.isEmpty()) {
String msg = "no hay ajustes :(";
ctx.fill(x, y + h, x + w, y + 2 * h, Colors.frameBGColor);
ctx.drawText(mc.textRenderer, msg,
x + (w / 2) - (mc.textRenderer.getWidth(msg) / 2),
(int) (y + h * 1.25),
-1, false);
return;
}
int totalHeight = settingButtons.size() * h + 3;
ctx.fill(x, y + h + 1, x + w, y + h + totalHeight, Colors.frameBGColor);
for (SettingButton sb : settingButtons)
if (sb.setting.isVisible()) sb.render(ctx, mouseX, mouseY, delta);
}
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered(mouseX, mouseY)) {
if (button == 0) {
dragging = true;
dragX = (int) (mouseX - x);
dragY = (int) (mouseY - y);
} else if (button == 1) {
ClickGUI.INSTANCE.closeSettingsFrame(module);
}
}
for (SettingButton sb : settingButtons)
if (sb.setting.isVisible()) sb.mouseClicked(mouseX, mouseY, button);
}
public void mouseRelease(double mouseX, double mouseY, int button) {
if (button == 0) dragging = false;
for (SettingButton sb : settingButtons)
if (sb.setting.isVisible()) sb.mouseReleased(mouseX, mouseY, button);
}
public void updatePosition(double mouseX, double mouseY) {
if (dragging) {
x = (int) (mouseX - dragX);
y = (int) (mouseY - dragY);
}
}
boolean isHovered(double mouseX, double mouseY) {
return mouseX > x && mouseX < x + w &&
mouseY > y && mouseY < y + h;
}
public List<SettingButton> getButtons() {
return settingButtons;
}
}
@@ -0,0 +1,76 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.util.Colors;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
import java.text.DecimalFormat;
// slider (barrita) para los ajustes numéricos
public class SliderButton extends SettingButton {
private final NumberSetting setting;
private boolean dragging = false;
public final DecimalFormat df; // usar formato decimal para el valor del ajuste
public SliderButton(NumberSetting setting, SettingsFrame parent, int offset) {
super(setting, parent, offset);
this.setting = setting;
this.df = new DecimalFormat("#.##"); // solo mostrar dos décimales
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
int x = parent.x + 4;
int y = parent.y + offset + 3;
int w = parent.w - 8;
ctx.fill(x, y, x + w, y + height, Colors.moduleButtonColor);
// calcular cómo de llena tendría que estar el "slider"
double percent = (setting.getValue() - setting.getMin()) / (setting.getMax() - setting.getMin());
int filled = (int) (percent * w);
ctx.fill(x, y, x + filled, y + height, Colors.sliderFillingColor);
String label = setting.getName() + ": " + df.format(setting.getValue());
ctx.drawTextWithShadow(parent.mc.textRenderer, label, x + 5, y + 3, -1);
// lógica para el arrastre del valor
if (dragging) {
double newVal = setting.getMin() + ((mouseX - x) / (double) w) * (setting.getMax() - setting.getMin());
newVal = Math.max(setting.getMin(), Math.min(setting.getMax(), newVal));
newVal = Math.round(newVal / setting.getIncrement()) * setting.getIncrement();
setting.setValue((float) newVal);
}
// dibujar "tooltips" (cajas de texto) al pasar el puntero encima del botón, para mostrar su descripción
if (isHovered((int) mouseX, (int) mouseY)) {
Screen currentScreen = parent.mc.currentScreen;
if (currentScreen != null)
ctx.drawTooltip(Text.of(setting.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) dragging = true; // arrastrar con el clic izquierdo
else if (button == 1 && KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT)) // restablecer al valor por defecto con shift izquerdo + clic derecho
setting.reset();
}
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {
if (button == 0) dragging = false;
}
public NumberSetting getSetting() {
return setting;
}
}
@@ -0,0 +1,97 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.StringSetting;
import me.retucio.camtweaks.util.Colors;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
// botón para los ajustes que requieran introducir texto
public class TextButton extends SettingButton {
private boolean typing;
private final StringSetting setting;
private final StringBuilder buffer = new StringBuilder();
public TextButton(StringSetting setting, SettingsFrame parent, int offset) {
super(setting, parent, offset);
this.setting = setting;
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
int x = parent.x + 4;
int y = parent.y + offset + 3;
int w = parent.w - 8;
int bgColor = isHovered((int) mouseX, (int) mouseY)
? Colors.hoveredCycleButtonColor
: Colors.cycleButtonColor;
ctx.fill(x, y, x + w, y + height, bgColor);
String label = setting.getName() + ": " + (typing ? buffer.toString() + "_" : setting.getValue());
ctx.drawTextWithShadow(parent.mc.textRenderer, label, x + 5, y + 3, -1);
if (isHovered((int) mouseX, (int) mouseY)) {
if (parent.mc.currentScreen != null)
ctx.drawTooltip(Text.of(setting.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) {
typing = true;
buffer.setLength(0);
buffer.append(setting.getValue()); // precargar valor anterior
} else if (button == 1 && KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT)) {
typing = false;
setting.reset();
}
} else {
typing = false;
}
}
public void onKey(int key, int action) {
if (!typing || action != GLFW.GLFW_PRESS) return;
switch (key) { // casos para teclas especiales como enter, escape o borrar
case GLFW.GLFW_KEY_ENTER -> {
setting.setValue(buffer.toString());
typing = false;
} case GLFW.GLFW_KEY_BACKSPACE -> {
buffer.deleteCharAt(buffer.length() - 1);
} case GLFW.GLFW_KEY_ESCAPE -> {
typing = false;
} default -> {
String c = KeyUtil.getKeyName(key);
if (c.length() == 1) {
if (KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT) || KeyUtil.isKeyDown(GLFW.GLFW_KEY_RIGHT_SHIFT))
charTyped(c.toUpperCase().charAt(0));
else
charTyped(c.toLowerCase().charAt(0));
}
}
}
}
private void charTyped(char c) {
if (!typing) return;
buffer.append(c);
}
public boolean isFocused() {
return typing;
}
public StringSetting getSetting() {
return setting;
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {}
}
@@ -0,0 +1,63 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.util.Colors;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
// botón para los ajustes booleanos (funciona como un interruptor)
public class ToggleButton extends SettingButton {
private final BooleanSetting setting;
public ToggleButton(BooleanSetting setting, SettingsFrame parent, int offset) {
super(setting, parent, offset);
this.setting = setting;
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
int x = parent.x + 4;
int y = parent.y + offset + 3;
int w = parent.w - 8;
Color color = setting.isEnabled()
? Colors.enabledToggleButtonColor
: Colors.disabledToggleButtonColor;
if (isHovered((int) mouseX, (int) mouseY))
color = color.brighter(); // simplemente utilizar el método "built-in" de Java para hacer el botón más claro cuando el puntero se encuentra encima de él
ctx.fill(x, y, x + w, y + height, color.getRGB());
ctx.drawTextWithShadow(parent.mc.textRenderer,
setting.getName(), x + 5, y + 3, -1);
// dibujar "tooltips" (cajas de texto) al pasar el puntero encima del botón, para mostrar su descripción
if (isHovered((int) mouseX, (int) mouseY)) {
Screen currentScreen = parent.mc.currentScreen;
if (currentScreen != null)
ctx.drawTooltip(Text.of(setting.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) setting.toggle(); // clic izquierdo para alternar
else if (button == 1 && KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT)) // shift izquierdo + clic derecho para restablecer al valor por defecto
setting.reset();
}
}
public BooleanSetting getSetting() {
return setting;
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {}
}
@@ -0,0 +1,60 @@
package me.retucio.camtweaks.util;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
// cosas útiles relacionadas al chat
public class ChatUtil {
private static final MinecraftClient mc = MinecraftClient.getInstance();
public static void addMessage(String text) {
mc.inGameHud.getChatHud().addMessage(Text.of(text));
}
public static void addMessage(Text text) {
mc.inGameHud.getChatHud().addMessage(text);
}
public static void addMessageWithPrefix(String text) {
addMessageWithPrefix(Text.of(text));
}
public static void addMessageWithPrefix(Text text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.RESET).append(text));
}
public static void info(String text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.RESET + text));
}
public static void info(Text text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.RESET).append(text));
}
public static void warn(String text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.YELLOW + text));
}
public static void warn(Text text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.YELLOW).append(text));
}
public static void error(String text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.RED + text));
}
public static void error(Text text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.RED).append(text));
}
public static void sendServerMessage(String text) {
sendServerMessage(Text.of(text));
}
public static void sendServerMessage(Text text) {
if (mc.player == null) return;
mc.player.sendMessage(text, false);
}
}
@@ -0,0 +1,41 @@
package me.retucio.camtweaks.util;
import me.retucio.camtweaks.ui.ClickGUI;
import java.awt.*;
import static me.retucio.camtweaks.ui.settings.ClickGUISettingsFrame.guiSettings;
// interfaz para los colores, para no repetirme (regla DRY)
// si se ve especificado como color en algún lado "-1", significa blanco
public class Colors {
public static int frameHeadRed = guiSettings.red.getIntValue();
public static int frameHeadGreen = guiSettings.green.getIntValue();
public static int frameHeadBlue = guiSettings.blue.getIntValue();
public static int frameHeadAlpha = guiSettings.alpha.getIntValue();
public static int frameHeadColor = new Color(
frameHeadRed,
frameHeadGreen,
frameHeadBlue,
frameHeadAlpha
).getRGB();
public static int frameBGColor = new Color(60, 60, 60, 70).getRGB();
public static int enabledHoveredModuleButtonColor = new Color(20, 20, 255, 180).getRGB();
public static int enabledModuleButtonColor = new Color(0, 0, 255, 180).getRGB();
public static int hoveredModuleButtonColor = new Color(30, 30, 30, 100).getRGB();
public static int moduleButtonColor = new Color(40, 40, 40, 75).getRGB();
public static int hoveredCycleButtonColor = new Color(70, 70, 70, 180).getRGB();
public static int cycleButtonColor = new Color(50, 50, 50, 150).getRGB();
public static int sliderFillingColor = new Color(60, 120, 220, 180).getRGB();
// necesitan ser de tipo java.awt.Color para utilizar el método .brighter()
public static Color enabledToggleButtonColor = new Color(0, 180, 0, 180);
public static Color disabledToggleButtonColor = new Color(120, 30, 30, 150);
}
@@ -0,0 +1,45 @@
package me.retucio.camtweaks.util;
import me.retucio.camtweaks.CameraTweaks;
import org.lwjgl.glfw.GLFW;
// cosas útiles relacionadas a las teclas
public class KeyUtil {
public static boolean isKeyDown(int key) {
return GLFW.glfwGetKey(
CameraTweaks.mc.getWindow().getHandle(),
key) != GLFW.GLFW_RELEASE;
}
public static String getKeyName(int key) {
if (key == GLFW.GLFW_KEY_UNKNOWN) return "ninguna";
String name = GLFW.glfwGetKeyName(key, 0);
if (name != null) return name.toUpperCase();
// para teclas especiales
return switch (key) {
case GLFW.GLFW_KEY_SPACE -> "espacio";
case GLFW.GLFW_KEY_LEFT_SHIFT -> "shift izquierdo";
case GLFW.GLFW_KEY_RIGHT_SHIFT -> "shift derecho";
case GLFW.GLFW_KEY_LEFT_CONTROL -> "ctrl izquierdo";
case GLFW.GLFW_KEY_RIGHT_CONTROL -> "ctrl derecho";
case GLFW.GLFW_KEY_LEFT_ALT -> "alt izquierdo";
case GLFW.GLFW_KEY_RIGHT_ALT -> "alt gr";
case GLFW.GLFW_KEY_TAB -> "tab";
case GLFW.GLFW_KEY_ENTER -> "enter";
case GLFW.GLFW_KEY_BACKSPACE -> "borrar";
case GLFW.GLFW_KEY_DELETE -> "suprimir";
case GLFW.GLFW_KEY_UP -> "arriba";
case GLFW.GLFW_KEY_DOWN -> "abajo";
case GLFW.GLFW_KEY_LEFT -> "izquierda";
case GLFW.GLFW_KEY_RIGHT -> "derecha";
default -> { // F1–F25
if (key >= GLFW.GLFW_KEY_F1 && key <= GLFW.GLFW_KEY_F25)
yield "F" + (key - GLFW.GLFW_KEY_F1 + 1);
yield "tecla " + key;
}
};
}
}
@@ -0,0 +1,8 @@
package me.retucio.camtweaks.util.interfaces;
import net.minecraft.text.Text;
public interface IChatHud {
void smegma$add(Text message, int id);
}
@@ -0,0 +1,14 @@
package me.retucio.camtweaks.util.interfaces;
import com.mojang.authlib.GameProfile;
public interface IChatHudLine {
String smegma$getText();
int smegma$getId();
void smegma$setId(int id);
GameProfile smegma$getSender();
void smegma$setSender(GameProfile profile);
}
@@ -0,0 +1,7 @@
package me.retucio.camtweaks.util.interfaces;
public interface IChatHudLineVisible extends IChatHudLine {
boolean smegma$isStartOfEntry();
void smegma$setStartOfEntry(boolean start);
}
@@ -0,0 +1,8 @@
package me.retucio.camtweaks.util.interfaces;
import com.mojang.authlib.GameProfile;
public interface IMessageHandler {
GameProfile smegma$getSender();
}
@@ -0,0 +1,41 @@
package me.retucio.camtweaks.util.interfaces;
import net.minecraft.text.PlainTextContent;
import net.minecraft.text.StringVisitable;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
import java.util.ArrayDeque;
import java.util.Optional;
import java.util.Queue;
@FunctionalInterface
public interface TextVisitor<T> {
Optional<T> accept(Text text, Style style, String string);
static <T> Optional<T> visit(Text text, TextVisitor<T> visitor, Style baseStyle) {
Queue<Text> queue = collectSiblings(text);
return text.visit((style, string) -> visitor.accept(queue.remove(), style, string), baseStyle);
}
/**
* Collapses the tree of {@link Text} siblings into a one dimensional FIFO {@link Queue}. To match the behaviours of
* the {@link Text#visit(StringVisitable.Visitor)} and {@link Text#visit(StringVisitable.StyledVisitor, Style)}
* methods, texts with empty contents (created from {@link Text#empty()}) are ignored but their siblings are still
* processed.
* @param text the text
* @return the text and its siblings in the order they appear when rendered.
*/
static ArrayDeque<Text> collectSiblings(Text text) {
ArrayDeque<Text> queue = new ArrayDeque<>();
collectSiblings(text, queue);
return queue;
}
private static void collectSiblings(Text text, Queue<Text> queue) {
if (!(text.getContent() instanceof PlainTextContent ptc) || !ptc.string().isEmpty()) queue.add(text);
for (Text sibling : text.getSiblings()) {
collectSiblings(sibling, queue);
}
}
}