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

+9
View File
@@ -0,0 +1,9 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf
# These are Windows script files and should use crlf
*.bat text eol=crlf
+30
View File
@@ -0,0 +1,30 @@
# Automatically build the project and run any configured tests for every push
# and submitted pull request. This can help catch issues that only occur on
# certain platforms or Java versions, and provides a first line of defence
# against bad commits.
name: build
on: [pull_request, push]
jobs:
build:
runs-on: ubuntu-24.04
steps:
- name: checkout repository
uses: actions/checkout@v4
- name: validate gradle wrapper
uses: gradle/actions/wrapper-validation@v4
- name: setup jdk
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'microsoft'
- name: make gradle wrapper executable
run: chmod +x ./gradlew
- name: build
run: ./gradlew build
- name: capture build artifacts
uses: actions/upload-artifact@v4
with:
name: Artifacts
path: build/libs/
+42
View File
@@ -0,0 +1,42 @@
# gradle
.gradle/
build/
out/
classes/
# eclipse
*.launch
# idea
.idea/
*.iml
*.ipr
*.iws
# vscode
.settings/
.vscode/
bin/
.classpath
.project
# macos
*.DS_Store
# fabric
run/
# java
hs_err_*.log
replay_*.log
*.hprof
*.jfr
todo.txt
+121
View File
@@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.
+78
View File
@@ -0,0 +1,78 @@
plugins {
id 'fabric-loom' version "${loom_version}"
id 'maven-publish'
}
version = project.mod_version
group = project.maven_group
base {
archivesName = project.archives_base_name
}
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": inputs.properties.version
}
}
tasks.withType(JavaCompile).configureEach {
it.options.release = 21
}
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
jar {
inputs.property "archivesName", project.base.archivesName
from("LICENSE") {
rename { "${it}_${inputs.properties.archivesName}"}
}
}
// configure the maven publication
publishing {
publications {
create("mavenJava", MavenPublication) {
artifactId = project.archives_base_name
from components.java
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}
+3
View File
@@ -0,0 +1,3 @@
meteor client
oyvey client (1.21 port by mioclient)
lennyboi (youtube)
+18
View File
@@ -0,0 +1,18 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
org.gradle.parallel=true
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.21.6
yarn_mappings=1.21.6+build.1
loader_version=0.17.2
loom_version=1.11-SNAPSHOT
# Mod Properties
mod_version=1.0.0
maven_group=me.retucio
archives_base_name=camtweaks
# Dependencies
fabric_version=0.128.2+1.21.6
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+251
View File
@@ -0,0 +1,251 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+94
View File
@@ -0,0 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+10
View File
@@ -0,0 +1,10 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
mavenCentral()
gradlePluginPortal()
}
}
@@ -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);
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

+33
View File
@@ -0,0 +1,33 @@
{
"required": true,
"package": "me.retucio.camtweaks.mixin",
"compatibilityLevel": "JAVA_21",
"mixins": [
"StringHelperMixin",
"accessor.StatusEffectInstanceAccessor"
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"requireAnnotations": true
},
"client": [
"CameraMixin",
"ChatHudLineMixin",
"ChatHudLineVisibleMixin",
"ChatHudMixin",
"ChatInputSuggestorMixin",
"ChatScreenMixin",
"ClientPlayNetworkHandlerMixin",
"GameOptionsMixin",
"GameRendererMixin",
"HeldItemRendererMixin",
"InGameHudMixin",
"KeyboardMixin",
"LightmapTextureManagerMixin",
"MessageHandlerMixin",
"MinecraftClientMixin",
"MouseMixin"
]
}
+34
View File
@@ -0,0 +1,34 @@
{
"schemaVersion": 1,
"id": "camtweaks",
"version": "${version}",
"name": "mod de putas de retucio",
"description": "cositas chulas pa la cámara",
"authors": [
"prepucio"
],
"contact": {
"homepage": "https://github.com/retuci0",
"sources": "https://github.com/retuci0/camera-tweaks"
},
"license": "CC0-1.0",
"icon": "assets/camtweaks/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"me.retucio.camtweaks.CameraTweaks"
]
},
"mixins": [
"camtweaks.mixins.json"
],
"depends": {
"fabricloader": ">=0.16.14",
"minecraft": "~1.21.6",
"java": ">=21",
"fabric-api": "*"
},
"suggests": {
"another-mod": "*"
}
}