v5.6
This commit is contained in:
1 parent
81fc25c33d
commit
e0f8523809
29 files changed
+502
-181
No files matched your search
+14
-3
@@ -6,13 +6,24 @@
|
||||
|
||||
#### módulos nuevos:
|
||||
|
||||
#### comandos nuevos:
|
||||
- autoataque: mantén el botón de ataque pulsado para atacar automáticamente
|
||||
|
||||
- `$panorama`: construye un pack de texturas con una panorámica basada en tu posición actual.
|
||||
#### comandos nuevos:
|
||||
|
||||
### arreglos / mejoras
|
||||
|
||||
- arreglado ScreenshotMixin, es decir, que ya no crasheará al tomar capturas
|
||||
- arreglado el guardado al cerrar el juego: petaba antes de escribir nada, así que se perdía
|
||||
todo lo cambiado desde el último autoguardado y al volver a entrar salían los ajustes por defecto
|
||||
- el autoguardado cuenta desde el último guardado y no desde el último cambio, que tocando
|
||||
ajustes seguido no llegaba a guardar nunca
|
||||
- guardando dos veces seguidas ya no se descarta la segunda (era la más reciente)
|
||||
- las teclas de los módulos ya no se pisan entre ellas al cargar la config.
|
||||
- si algo falla al aplicar los ajustes ya no se queda el mod entero sin guardar
|
||||
- las listas (entidades, ítems, bloques...) se traducen al idioma del juego en vez de salir
|
||||
siempre en inglés, y se guardan por nombre interno para que no dependan del idioma
|
||||
- traducidos los títulos de los marcos, las descripciones de los módulos y los mensajes del
|
||||
arranque, que salían en español o directamente como la clave de traducción
|
||||
- añadidas las traducciones que faltaban del autoataque
|
||||
|
||||
## problemas reconocidos
|
||||
|
||||
|
||||
+1
-1
@@ -10,6 +10,6 @@ discord_rpc_version=1.0.3
|
||||
neutrino_version=1.1.0
|
||||
jdk_version=25
|
||||
|
||||
mod_version=5.5
|
||||
mod_version=5.6
|
||||
maven_group=me.retucio
|
||||
archives_base_name=sputnik
|
||||
@@ -135,11 +135,8 @@ public class Sputnik implements ClientModInitializer {
|
||||
|
||||
// se ejecuta cada tick, es decir, 20 veces por segundo
|
||||
public void onTick() {
|
||||
if (!settingsApplied
|
||||
&& ConfigManager.INSTANCE.getConfig() != null
|
||||
&& !ConfigManager.INSTANCE.hasLoaded()) {
|
||||
ConfigManager.INSTANCE.applyConfig();
|
||||
settingsApplied = true;
|
||||
if (!settingsApplied && !ConfigManager.INSTANCE.hasLoaded()) {
|
||||
settingsApplied = ConfigManager.INSTANCE.applyConfig();
|
||||
}
|
||||
|
||||
ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick);
|
||||
@@ -161,7 +158,7 @@ public class Sputnik implements ClientModInitializer {
|
||||
if (BindCommand.onKeyPress(key)) return;
|
||||
if (action == GLFW.GLFW_PRESS) {
|
||||
handleModuleToggle(key, anyFocused);
|
||||
handleClickGUIKey(key, anyFocused);
|
||||
handleClickGuiKey(key, anyFocused);
|
||||
handleHudEditorKey(key, anyFocused);
|
||||
}
|
||||
// solo reenviar teclas del teclado
|
||||
@@ -208,7 +205,7 @@ public class Sputnik implements ClientModInitializer {
|
||||
}
|
||||
|
||||
// maneja la lógica de apertura de la interfaz
|
||||
private void handleClickGUIKey(int key, boolean anyFocused) {
|
||||
private void handleClickGuiKey(int key, boolean anyFocused) {
|
||||
if (key != ClientSettingsPanel.clientSettings.getKey() || anyFocused || isOnTypingScreen())
|
||||
return;
|
||||
|
||||
@@ -221,7 +218,7 @@ public class Sputnik implements ClientModInitializer {
|
||||
}
|
||||
}
|
||||
|
||||
// manejar la lógica de apertura del editor de elementos del hud, con la misma lógica que handleClickGUIKey()
|
||||
// manejar la lógica de apertura del editor de elementos del hud, con la misma lógica que handleClickGuiKey()
|
||||
private void handleHudEditorKey(int key, boolean anyFocused) {
|
||||
Hud hud = ModuleManager.INSTANCE.getModuleByClass(Hud.class);
|
||||
if (key != hud.editorKey.getValue() || anyFocused || isOnTypingScreen()) return;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.retucio.sputnik.config;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
import me.retucio.sputnik.util.I18nUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -85,44 +85,59 @@ public class BinarySerializer {
|
||||
public static ClientConfig readConfig(File file)
|
||||
throws IOException {
|
||||
if (!file.exists() || file.length() == 0) {
|
||||
throw new IOException(Component.translatable("sputnik.config.error.empty").getString());
|
||||
throw new IOException(I18nUtil.fmt("sputnik.config.error.empty",
|
||||
"archivo de config. vacío o inexistente"));
|
||||
}
|
||||
|
||||
try (DataInputStream in = new DataInputStream(
|
||||
new BufferedInputStream(new FileInputStream(file)))) {
|
||||
|
||||
if (file.length() < 8) {
|
||||
throw new IOException(Component.translatable("sputnik.config.error.toosmall").getString());
|
||||
throw new IOException(I18nUtil.fmt("sputnik.config.error.toosmall",
|
||||
"archivo demasiado pequeño para ser válido"));
|
||||
}
|
||||
|
||||
// verificar cabezal
|
||||
int magic = in.readInt();
|
||||
if (magic != MAGIC_NUMBER) {
|
||||
throw new IOException(Component.translatable("sputnik.config.error.invalidformat", magic).getString());
|
||||
throw new IOException(I18nUtil.fmt("sputnik.config.error.invalidformat",
|
||||
"formato de archivo inválido: %s", magic));
|
||||
}
|
||||
|
||||
// todo: sustituir por lógica de migración en vez de simplemente no aceptarlo
|
||||
int version = in.readInt();
|
||||
if (version != VERSION) {
|
||||
throw new IOException(Component.translatable("sputnik.config.error.invalidversion", version).getString());
|
||||
throw new IOException(I18nUtil.fmt("sputnik.config.error.invalidversion",
|
||||
"versión inválida: %s", version));
|
||||
}
|
||||
|
||||
ClientConfig config = new ClientConfig();
|
||||
|
||||
// leer mapas
|
||||
config.moduleStates = readStringBooleanMap(in);
|
||||
config.settings = readSettingsMap(in);
|
||||
config.settingsPanels = readStringIntArrayMap(in);
|
||||
config.extendablePanels = readExtendablePanels(in);
|
||||
config.hudPositions = readStringIntArrayMap(in);
|
||||
config.hudVisibilities = readStringBooleanMap(in);
|
||||
config.hudImagePaths = readStringStringMap(in);
|
||||
config.friends = readStringStringMap(in);
|
||||
// leer mapas. se leen a locales y la ClientConfig se construye al final: su
|
||||
// constructor la suscribe al bus de eventos, así que crearla antes de terminar
|
||||
// de leer dejaba una config. huérfana escuchando si el archivo estaba corrupto
|
||||
Map<String, Boolean> moduleStates = readStringBooleanMap(in);
|
||||
Map<String, Object> settings = readSettingsMap(in);
|
||||
Map<String, int[]> settingsPanels = readStringIntArrayMap(in);
|
||||
Map<String, ClientConfig.PanelData> extendablePanels = readExtendablePanels(in);
|
||||
Map<String, int[]> hudPositions = readStringIntArrayMap(in);
|
||||
Map<String, Boolean> hudVisibilities = readStringBooleanMap(in);
|
||||
Map<String, String> hudImagePaths = readStringStringMap(in);
|
||||
Map<String, String> friends = readStringStringMap(in);
|
||||
|
||||
// leer pos. de la barra de búsqueda
|
||||
if (in.readBoolean()) {
|
||||
config.searchBarPosition = new int[]{in.readInt(), in.readInt()};
|
||||
}
|
||||
int[] searchBarPosition = in.readBoolean()
|
||||
? new int[]{in.readInt(), in.readInt()}
|
||||
: null;
|
||||
|
||||
ClientConfig config = new ClientConfig();
|
||||
config.moduleStates = moduleStates;
|
||||
config.settings = settings;
|
||||
config.settingsPanels = settingsPanels;
|
||||
config.extendablePanels = extendablePanels;
|
||||
config.hudPositions = hudPositions;
|
||||
config.hudVisibilities = hudVisibilities;
|
||||
config.hudImagePaths = hudImagePaths;
|
||||
config.friends = friends;
|
||||
if (searchBarPosition != null) config.searchBarPosition = searchBarPosition;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class ClientConfig {
|
||||
case StringSetting s: value = s.getValue(); break;
|
||||
case ListSetting l: value = l.getConfigValues(); break;
|
||||
case ColorSetting c: value = c.getConfigValue(); break;
|
||||
case OptionSetting o: value = o.getDisplayName(); break;
|
||||
case OptionSetting o: value = o.getConfigName(); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import me.retucio.sputnik.ui.screen.ClickGui;
|
||||
import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
|
||||
import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
|
||||
import me.retucio.sputnik.util.ChatUtil;
|
||||
import me.retucio.sputnik.util.I18nUtil;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
import java.io.File;
|
||||
@@ -30,7 +31,7 @@ import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
// se ocupa de guardar, cargar y aplicar ajustes
|
||||
@@ -42,8 +43,9 @@ public class ConfigManager {
|
||||
public static ConfigManager INSTANCE;
|
||||
|
||||
private boolean loaded = false;
|
||||
private boolean applying = false;
|
||||
private boolean dirty = false;
|
||||
private long lastDirtyTime = 0L;
|
||||
private long lastSaveTime = System.currentTimeMillis();
|
||||
private ClientConfig config = null;
|
||||
|
||||
private final ExecutorService saveExecutor = Executors.newSingleThreadExecutor(r -> {
|
||||
@@ -52,7 +54,10 @@ public class ConfigManager {
|
||||
return t;
|
||||
});
|
||||
|
||||
private final AtomicBoolean saveQueued = new AtomicBoolean(false);
|
||||
// instantánea pendiente de escribir. si llega otra antes de que el hilo de fondo escriba,
|
||||
// sustituye a la anterior en vez de descartarse (que era lo que pasaba con un simple
|
||||
// "ya hay uno en cola": se tiraba justo la más reciente y se perdían esos cambios)
|
||||
private final AtomicReference<byte[]> pending = new AtomicReference<>();
|
||||
|
||||
|
||||
// ------------------ MÉTODOS PRINCIPALES ------------------
|
||||
@@ -60,58 +65,81 @@ public class ConfigManager {
|
||||
// guardar configuraciones
|
||||
public void save() {
|
||||
if (!loaded) return;
|
||||
ensureConfig();
|
||||
|
||||
// serializar a bytes en el hilo principal (snapshot inmediato, evita race conditions)
|
||||
final byte[] snapshot;
|
||||
try {
|
||||
snapshot = BinarySerializer.toBytes(config);
|
||||
} catch (IOException e) {
|
||||
Sputnik.LOGGER.error(Component.translatable("sputnik.config.error.serialization").getString(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
// si ya hay un guardado en cola, no añadir otro
|
||||
if (!saveQueued.compareAndSet(false, true)) return;
|
||||
|
||||
saveExecutor.execute(() -> {
|
||||
try {
|
||||
BinarySerializer.writeBytes(snapshot, CONFIG_FILE);
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.saved").getString());
|
||||
} catch (IOException e) {
|
||||
Sputnik.LOGGER.error(Component.translatable("sputnik.config.error.saving").getString(), e);
|
||||
} finally {
|
||||
saveQueued.set(false);
|
||||
}
|
||||
});
|
||||
writeSnapshot(false);
|
||||
}
|
||||
|
||||
public void saveOnShutdown() {
|
||||
// esperar a que termine cualquier guardado en curso, luego guardar síncronamente
|
||||
// no aceptar más tareas y esperar a que termine el guardado en curso
|
||||
saveExecutor.shutdown();
|
||||
try {
|
||||
// dar hasta 5 segundos para que termine el guardado en curso
|
||||
if (!saveExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
Sputnik.LOGGER.warn(Component.translatable("sputnik.config.warn.thread").getString());
|
||||
Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.thread",
|
||||
"el hilo de guardado no terminó a tiempo"));
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
// guardado final sincrónico por si había cambios pendientes no guardados
|
||||
if (dirty) save();
|
||||
// el guardado final va en este mismo hilo: el executor ya está cerrado, así que
|
||||
// encolar la escritura lanzaba RejectedExecutionException y no se guardaba NADA al
|
||||
// cerrar el juego. o sea que se perdía todo lo hecho desde el último autoguardado
|
||||
if (!loaded) return;
|
||||
writeSnapshot(true);
|
||||
}
|
||||
|
||||
// serializa en el hilo actual (instantánea inmediata, evita race conditions) y escribe
|
||||
// en el de fondo, salvo que se pida síncrono (al cerrar)
|
||||
private void writeSnapshot(boolean sync) {
|
||||
ensureConfig();
|
||||
|
||||
final byte[] snapshot;
|
||||
try {
|
||||
snapshot = BinarySerializer.toBytes(config);
|
||||
} catch (IOException e) {
|
||||
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.serialization",
|
||||
"error al serializar ajustes"), e);
|
||||
return;
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
lastSaveTime = System.currentTimeMillis();
|
||||
|
||||
if (sync) {
|
||||
writeToDisk(snapshot);
|
||||
return;
|
||||
}
|
||||
|
||||
// si ya había una instantánea esperando, la tarea encolada recogerá ésta
|
||||
if (pending.getAndSet(snapshot) != null) return;
|
||||
|
||||
saveExecutor.execute(() -> {
|
||||
byte[] bytes = pending.getAndSet(null);
|
||||
if (bytes != null) writeToDisk(bytes);
|
||||
});
|
||||
}
|
||||
|
||||
private static void writeToDisk(byte[] bytes) {
|
||||
try {
|
||||
BinarySerializer.writeBytes(bytes, CONFIG_FILE);
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.saved", "ajustes guardados"));
|
||||
} catch (IOException e) {
|
||||
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.saving",
|
||||
"error al guardar ajustes"), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void load() {
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.loading").getString());
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.loading", "cargando ajustes..."));
|
||||
|
||||
if (CONFIG_FILE.exists()) {
|
||||
try {
|
||||
config = BinarySerializer.readConfig(CONFIG_FILE);
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.loaded").getString());
|
||||
setConfig(BinarySerializer.readConfig(CONFIG_FILE));
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.loaded", "ajustes cargados"));
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
Sputnik.LOGGER.error(Component.translatable("sputnik.config.error.loading").getString(), e);
|
||||
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.loading",
|
||||
"error cargando ajustes, intentando json"), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,35 +147,61 @@ public class ConfigManager {
|
||||
if (LEGACY_JSON_FILE.exists()) {
|
||||
try (FileReader reader = new FileReader(LEGACY_JSON_FILE)) {
|
||||
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
config = gson.fromJson(reader, ClientConfig.class);
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.json").getString());
|
||||
setConfig(gson.fromJson(reader, ClientConfig.class));
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.json",
|
||||
"ajustes json cargados (migración)"));
|
||||
|
||||
save();
|
||||
// aquí `loaded` todavía es false (se pone al aplicar), así que save() no
|
||||
// haría nada: la migración se escribe directamente
|
||||
writeSnapshot(true);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
Sputnik.LOGGER.error(Component.translatable("sputnik.config.error.json").getString(), e);
|
||||
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.json",
|
||||
"error cargando json, usando defaults"), e);
|
||||
}
|
||||
}
|
||||
|
||||
// crear nuevo config
|
||||
ensureConfig();
|
||||
save();
|
||||
writeSnapshot(true);
|
||||
}
|
||||
|
||||
// aplicar configuraciones cargadas
|
||||
public void applyConfig() {
|
||||
if (!shouldApply()) return;
|
||||
// aplicar configuraciones cargadas. devuelve false si todavía no se puede (falta algo por
|
||||
// construir), para que se reintente en el siguiente tick en vez de quedarse sin aplicar
|
||||
public boolean applyConfig() {
|
||||
if (!shouldApply()) return false;
|
||||
|
||||
applyModuleStates();
|
||||
applyModuleSettings();
|
||||
applySettingsPanels();
|
||||
applySearchBarPosition();
|
||||
applyClientSettings();
|
||||
applyExtendablePanels();
|
||||
applyFriends();
|
||||
applying = true;
|
||||
try {
|
||||
// los del mod primero: entre ellos está "teclas multimódulo", que decide si al
|
||||
// asignar una tecla se le quita a los demás módulos. aplicándolos los últimos,
|
||||
// las teclas de los módulos se cargaban con el valor por defecto de ese ajuste
|
||||
applyClientSettings();
|
||||
applyModuleStates();
|
||||
applyModuleSettings();
|
||||
applySettingsPanels();
|
||||
applySearchBarPosition();
|
||||
applyExtendablePanels();
|
||||
applyFriends();
|
||||
|
||||
loaded = true;
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.applied").getString());
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.applied", "ajustes aplicados"));
|
||||
} catch (Exception e) {
|
||||
// si algo peta a medio aplicar no se puede dejar `loaded` en false: save() no
|
||||
// haría nada en toda la sesión y se perdería la config. entera al cerrar
|
||||
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.applying",
|
||||
"error aplicando ajustes"), e);
|
||||
} finally {
|
||||
applying = false;
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// true mientras se está cargando la config. sobre los ajustes. lo mira KeySetting para no
|
||||
// tratar las teclas guardadas como si el usuario las acabara de asignar
|
||||
public boolean isApplying() {
|
||||
return applying;
|
||||
}
|
||||
|
||||
// obtener configuración actual
|
||||
@@ -213,7 +267,8 @@ public class ConfigManager {
|
||||
friend.setName(name);
|
||||
FriendManager.INSTANCE.add(friend);
|
||||
});
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.friends").getString());
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.friends",
|
||||
"amigos aplicados"));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@@ -233,8 +288,8 @@ public class ConfigManager {
|
||||
case ListSetting listSetting -> applyListSetting(listSetting, value);
|
||||
case ColorSetting colorSetting -> applyColorSetting(colorSetting, value);
|
||||
case OptionSetting optionSetting -> applyOptionSetting(optionSetting, value);
|
||||
default -> Sputnik.LOGGER.warn(Component.translatable("sputnik.config.warn.unknownsetting",
|
||||
setting.getClass().getSimpleName()).getString());
|
||||
default -> Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.unknownsetting",
|
||||
"watafac queseso: %s", setting.getClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,14 +299,16 @@ public class ConfigManager {
|
||||
module.setEnabled(config.moduleStates.get(module.getName()));
|
||||
}
|
||||
});
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.modulestates").getString());
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.modulestates",
|
||||
"estados de módulos aplicados"));
|
||||
}
|
||||
|
||||
private void applyModuleSettings() {
|
||||
ModuleManager.INSTANCE.getModules().forEach(module -> {
|
||||
module.getSettings().forEach(this::applySetting);
|
||||
});
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.modulesettings").getString());
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.modulesettings",
|
||||
"ajustes de módulos aplicados"));
|
||||
}
|
||||
|
||||
private void applySettingsPanels() {
|
||||
@@ -259,14 +316,16 @@ public class ConfigManager {
|
||||
Module module = ModuleManager.INSTANCE.getModuleByName(moduleName);
|
||||
|
||||
if (module == null) {
|
||||
Sputnik.LOGGER.warn(Component.translatable("sputnik.config.warn.nullmodule", moduleName).getString());
|
||||
Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.nullmodule",
|
||||
"\"null\" como módulo (\"%s\")", moduleName));
|
||||
return;
|
||||
}
|
||||
|
||||
ClickGui.INSTANCE.openSettingsPanel(module, position[0], position[1]);
|
||||
});
|
||||
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.settingspanels").getString());
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.settingspanels",
|
||||
"estados de marcos de ajustes aplicados"));
|
||||
}
|
||||
|
||||
private void applySearchBarPosition() {
|
||||
@@ -274,13 +333,15 @@ public class ConfigManager {
|
||||
ClickGui.INSTANCE.getSearchBar().setX(config.searchBarPosition[0]);
|
||||
ClickGui.INSTANCE.getSearchBar().setY(config.searchBarPosition[1]);
|
||||
}
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.searchbar").getString());
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.searchbar",
|
||||
"posición de la barra de búsqueda aplicada"));
|
||||
}
|
||||
|
||||
private void applyClientSettings() {
|
||||
ClientSettingsPanel.clientSettings.setEnabled(true);
|
||||
ClientSettingsPanel.clientSettings.getSettings().forEach(this::applySetting);
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.clientsettings").getString());
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.clientsettings",
|
||||
"ajustes del cliente aplicados"));
|
||||
}
|
||||
|
||||
private void applyExtendablePanels() {
|
||||
@@ -290,7 +351,8 @@ public class ConfigManager {
|
||||
applyExtendablePanel(ClickGui.INSTANCE.getClientSettingsPanel());
|
||||
applyExtendablePanel(ClickGui.INSTANCE.getFriendsPanel());
|
||||
|
||||
Sputnik.LOGGER.info(Component.translatable("sputnik.config.info.extendablepanels").getString());
|
||||
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.extendablepanels",
|
||||
"posiciones de marcos extendibles aplicadas"));
|
||||
ClickGui.INSTANCE.refreshListButtons();
|
||||
}
|
||||
|
||||
@@ -359,8 +421,14 @@ public class ConfigManager {
|
||||
Map<T, Boolean> convertedValues = new HashMap<>();
|
||||
|
||||
for (T option : setting.getOptions()) {
|
||||
String key = setting.getDisplayName(option);
|
||||
String key = setting.getConfigName(option);
|
||||
Object booleanValue = map.get(key);
|
||||
|
||||
// compatibilidad con configs. de antes, cuando la clave era el nombre ya
|
||||
// traducido (el de vanilla en inglés). sin esto la lista entera se quedaba
|
||||
// en false, o sea que al usuario se le borraba lo que tuviera marcado
|
||||
if (booleanValue == null) booleanValue = map.get(I18nUtil.vanillaName(key));
|
||||
|
||||
convertedValues.put(option, booleanValue instanceof Boolean && (Boolean) booleanValue);
|
||||
}
|
||||
|
||||
@@ -411,12 +479,24 @@ public class ConfigManager {
|
||||
return setting.getSg().getModule().getName() + ":" + setting.getTrueName();
|
||||
}
|
||||
|
||||
// ClickGui incluida: los marcos y la barra de búsqueda se aplican sobre ella, y se
|
||||
// construye en una tarea aparte. si todavía no está, mejor esperar al siguiente tick
|
||||
private boolean shouldApply() {
|
||||
return Sputnik.mc != null && ModuleManager.INSTANCE != null && config != null;
|
||||
return Sputnik.mc != null && ModuleManager.INSTANCE != null
|
||||
&& ClickGui.INSTANCE != null && config != null;
|
||||
}
|
||||
|
||||
private void ensureConfig() {
|
||||
if (config == null) config = new ClientConfig();
|
||||
if (config == null) setConfig(new ClientConfig());
|
||||
}
|
||||
|
||||
// ClientConfig se suscribe al bus en su constructor, así que al reemplazarla hay que
|
||||
// dar de baja a la anterior: si no, la vieja seguía escuchando y escribiendo en un
|
||||
// mapa que ya no era el que se guarda
|
||||
private void setConfig(ClientConfig newConfig) {
|
||||
if (config == newConfig) return;
|
||||
if (config != null) Sputnik.EVENT_BUS.unsubscribe(config);
|
||||
config = newConfig;
|
||||
}
|
||||
|
||||
public boolean hasLoaded() {
|
||||
@@ -425,7 +505,6 @@ public class ConfigManager {
|
||||
|
||||
public void markDirty() {
|
||||
this.dirty = true;
|
||||
this.lastDirtyTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean isDirty() {
|
||||
@@ -438,13 +517,14 @@ public class ConfigManager {
|
||||
ClientSettingsModule settings = ClientSettingsPanel.clientSettings;
|
||||
if (!settings.autosave.getValue()) return;
|
||||
|
||||
// desde el último guardado, no desde el último cambio: contando desde el cambio,
|
||||
// tocando ajustes cada menos del intervalo no se autoguardaba nunca
|
||||
long intervalMs = (long) (settings.autosaveInterval.getValue() * 1000);
|
||||
if (System.currentTimeMillis() - lastDirtyTime < intervalMs) return;
|
||||
if (System.currentTimeMillis() - lastSaveTime < intervalMs) return;
|
||||
|
||||
HudRenderer.INSTANCE.pushNotification(
|
||||
Component.translatable("sputnik.config.autosaving.title").getString(),
|
||||
Component.translatable("sputnik.config.autosaving.desc").getString());
|
||||
save();
|
||||
dirty = false;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import me.retucio.sputnik.event.render.Render3DEvent;
|
||||
import me.retucio.sputnik.event.render.RenderBlockOutlineEvent;
|
||||
import me.retucio.sputnik.module.ModuleManager;
|
||||
import me.retucio.sputnik.module.modules.camera.Freecam;
|
||||
import me.retucio.sputnik.util.render.SputnikBuffers;
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.SubmitNodeCollector;
|
||||
@@ -42,6 +43,7 @@ public abstract class LevelRendererMixin {
|
||||
|
||||
profiler.push(Sputnik.MOD_ID + "-3d");
|
||||
EVENT_BUS.post(new Render3DEvent(matrices, dt, cameraState));
|
||||
SputnikBuffers.flush(); // antes de que empiece el hud
|
||||
|
||||
profiler.pop();
|
||||
matrices.popPose();
|
||||
|
||||
@@ -83,10 +83,6 @@ public class Module {
|
||||
this.bind.setDefaultValue(key);
|
||||
|
||||
// puto marcos
|
||||
// la clave tiene que salir igual que en ConfigManager#getSettingKey, o sea con los
|
||||
// nombres internos: Module#getName ya lo es, pero en los ajustes el traducido es
|
||||
// getName() y el interno getTrueName(). si no cuadran no se encuentra la tecla
|
||||
// guardada y se le pisa al usuario con la de por defecto
|
||||
if (ConfigManager.INSTANCE.getConfig().settings
|
||||
.get(this.getName() + ":" + bind.getTrueName()) == null)
|
||||
this.bind.setValue(key);
|
||||
|
||||
@@ -89,6 +89,7 @@ public class ModuleManager {
|
||||
add(new AttributeSwapper());
|
||||
add(new ChestplateSwitch());
|
||||
add(new CrystalAura());
|
||||
add(new HoldAttack());
|
||||
add(new MaceKill());
|
||||
add(new ProjectileTrajectories());
|
||||
add(new SpearKill());
|
||||
|
||||
@@ -177,15 +177,14 @@ public class Freecam extends Module {
|
||||
super.onEnable();
|
||||
}
|
||||
|
||||
// allChanged() ya no existe; ahora hay que pasarle el estado a mano
|
||||
private static void invalidateChunks() {
|
||||
if (mc.level == null) return;
|
||||
mc.levelRenderer.invalidateCompiledGeometry(mc.level, mc.options, mc.gameRenderer.mainCamera(), mc.getBlockColors());
|
||||
mc.levelExtractor.allChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (reloadChunks.getValue()) mc.execute(Freecam::invalidateChunks);
|
||||
if (reloadChunks.getValue()) invalidateChunks();
|
||||
|
||||
mc.options.setCameraType(perspective);
|
||||
crouching = false;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package me.retucio.sputnik.module.modules.combat;
|
||||
|
||||
import me.retucio.sputnik.module.Category;
|
||||
import me.retucio.sputnik.module.Module;
|
||||
import me.retucio.sputnik.module.setting.settings.BooleanSetting;
|
||||
import me.retucio.sputnik.module.setting.settings.EnumSetting;
|
||||
import me.retucio.sputnik.module.setting.settings.NumberSetting;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
|
||||
|
||||
/**
|
||||
* @author retucio
|
||||
*/
|
||||
|
||||
public class HoldAttack extends Module {
|
||||
|
||||
private final EnumSetting<AttackMode> mode = sgGeneral.add(new EnumSetting<>(
|
||||
"modo de ataque",
|
||||
"cómo atacar",
|
||||
AttackMode.class,
|
||||
AttackMode.TIME
|
||||
));
|
||||
|
||||
private final NumberSetting timeCooldown = sgGeneral.add(new NumberSetting(
|
||||
"cooldown",
|
||||
"cómo de llena tiene que estar la barra de ataque antes de atacar",
|
||||
84.8f, 0.0f, 100.0f, 0.1f
|
||||
)).visibility(() -> mode.is(AttackMode.TIME));
|
||||
|
||||
private final NumberSetting spamCooldown = sgGeneral.add(new NumberSetting(
|
||||
"cooldown ",
|
||||
"cada cuánto atacar, en ticks",
|
||||
0, 0, 40, 1
|
||||
)).visibility(() -> mode.is(AttackMode.SPAM));
|
||||
|
||||
private final BooleanSetting attackInScreens = sgGeneral.add(new BooleanSetting(
|
||||
"atacar en pantallas",
|
||||
"atacar aun estando en una pantalla",
|
||||
false
|
||||
));
|
||||
|
||||
private int counter = 0;
|
||||
|
||||
public HoldAttack() {
|
||||
super("autoataque", "mantén pulsado el clic para atacar automáticamente", Category.COMBAT);
|
||||
mode.onUpdate(v -> counter = 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
counter = 0;
|
||||
super.onEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTick() {
|
||||
if (mc.player == null || mc.gameMode == null || mc.getCameraEntity() == null) return;
|
||||
|
||||
counter++;
|
||||
|
||||
if (mc.gui.screen() != null && !attackInScreens.getValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mc.options.keyAttack.isDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(mc.hitResult instanceof EntityHitResult ehr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode.is(AttackMode.TIME)) {
|
||||
if (mc.player.getAttackStrengthScale(0) < timeCooldown.getNormalizedValue()) {
|
||||
return;
|
||||
}
|
||||
} else if (mode.is(AttackMode.SPAM)) {
|
||||
if (counter < spamCooldown.getIntValue()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mc.gameMode.attack(mc.player, ehr.getEntity());
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
private enum AttackMode {
|
||||
SPAM("spamear"),
|
||||
TIME("timear");
|
||||
|
||||
private final String name;
|
||||
AttackMode(String name) { this.name = name; }
|
||||
@Override public String toString() { return name; }
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import me.retucio.sputnik.module.setting.settings.NumberSetting;
|
||||
import me.retucio.sputnik.util.Lists;
|
||||
import net.minecraft.core.particles.SimpleParticleType;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.List;
|
||||
@@ -26,9 +25,10 @@ public class Particles extends Module {
|
||||
.map(particleType -> (SimpleParticleType) particleType)
|
||||
.toList();
|
||||
|
||||
// claves de traducción, no texto traducido (ver Lists#init)
|
||||
Map<SimpleParticleType, String> particleNames = Lists.getMapOfLists(particleList,
|
||||
particleList.stream().map(particle -> Component.translatable(
|
||||
BuiltInRegistries.PARTICLE_TYPE.getKey(particle).toShortLanguageKey()).getString())
|
||||
particleList.stream().map(particle ->
|
||||
BuiltInRegistries.PARTICLE_TYPE.getKey(particle).toShortLanguageKey())
|
||||
.toList());
|
||||
|
||||
private final ListSetting<SimpleParticleType> particles = sgGeneral.add(new ListSetting<>(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.retucio.sputnik.module.setting.settings;
|
||||
|
||||
import me.retucio.sputnik.config.ConfigManager;
|
||||
import me.retucio.sputnik.module.Module;
|
||||
import me.retucio.sputnik.module.ModuleManager;
|
||||
import me.retucio.sputnik.module.setting.Setting;
|
||||
@@ -32,8 +33,17 @@ public class KeySetting extends Setting<Integer> {
|
||||
|
||||
// con "teclas multimódulo" puesto se permite repetir tecla, que es justo lo que
|
||||
// dice el ajuste. antes se le quitaba la tecla al otro módulo igualmente, así que
|
||||
// el ajuste sólo servía para las teclas de vanilla
|
||||
// el ajuste sólo servía para las teclas de vanilla.
|
||||
//
|
||||
// mientras se carga la config. no se toca nada: son las teclas que el usuario ya
|
||||
// tenía, y quitárselas aquí se las machacaba (y encima guardaba el -1).
|
||||
//
|
||||
// clientSettings puede ser null: el módulo de ajustes se construye en el
|
||||
// inicializador estático de ClientSettingsPanel, y su propia tecla por defecto pasa
|
||||
// por aquí antes de que el campo esté asignado
|
||||
if (ModuleManager.INSTANCE != null
|
||||
&& ClientSettingsPanel.clientSettings != null
|
||||
&& !ConfigManager.INSTANCE.isApplying()
|
||||
&& !ClientSettingsPanel.clientSettings.multipleKeybinds.getValue()) {
|
||||
for (Module module : ModuleManager.INSTANCE.getModules())
|
||||
if (module.getKey() == value && value != -1) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.retucio.sputnik.module.setting.settings;
|
||||
|
||||
import me.retucio.sputnik.module.setting.Setting;
|
||||
import me.retucio.sputnik.util.I18nUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -62,7 +63,7 @@ public class ListSetting<T> extends Setting<Map<T, Boolean>> {
|
||||
public Map<String, Boolean> getConfigValues() {
|
||||
Map<String, Boolean> configValues = new HashMap<>();
|
||||
for (T option : options) {
|
||||
configValues.put(getDisplayName(option), value.getOrDefault(option, false));
|
||||
configValues.put(getConfigName(option), value.getOrDefault(option, false));
|
||||
}
|
||||
return configValues;
|
||||
}
|
||||
@@ -103,10 +104,20 @@ public class ListSetting<T> extends Setting<Map<T, Boolean>> {
|
||||
return displayNames != null ? new HashMap<>(displayNames) : null;
|
||||
}
|
||||
|
||||
public String getDisplayName(T key) {
|
||||
// OJO: nombre interno de la opción. es la clave de la config. (ver
|
||||
// ConfigManager#applyListSetting) y para las listas del juego es la clave de
|
||||
// traducción ("entity.minecraft.zombie"), que no depende del idioma. no traducir
|
||||
public String getConfigName(T key) {
|
||||
if (displayNames != null && displayNames.containsKey(key)) {
|
||||
return displayNames.get(key);
|
||||
}
|
||||
return key != null ? key.toString() : "?";
|
||||
}
|
||||
|
||||
// nombre visible: se traduce aquí. antes se mostraba el nombre interno tal cual, o sea
|
||||
// los nombres de vanilla en inglés aunque el juego estuviera en otro idioma
|
||||
public String getDisplayName(T key) {
|
||||
String name = getConfigName(key);
|
||||
return I18nUtil.orElse(name, name);
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,10 @@ public class NumberSetting extends Setting<Double> {
|
||||
return value.byteValue();
|
||||
}
|
||||
|
||||
public double getNormalizedValue() {
|
||||
return (value - min ) / (max - min);
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
if (this.value == value || this.locked) return;
|
||||
double clamped = clamp(value, min, max);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.retucio.sputnik.module.setting.settings;
|
||||
|
||||
import me.retucio.sputnik.module.setting.Setting;
|
||||
import me.retucio.sputnik.util.I18nUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -26,14 +27,19 @@ public class OptionSetting<T> extends Setting<T> {
|
||||
if (options.contains(option)) super.setValue(option);
|
||||
}
|
||||
|
||||
// por nombre interno, no por el traducido: es lo que se guarda en la config.
|
||||
public void setValueByName(String name) {
|
||||
for (T opt : options) {
|
||||
if (displayNames != null && !displayNames.isEmpty()) {
|
||||
if (displayNames.get(opt).equals(name)) {
|
||||
setValue(opt);
|
||||
return;
|
||||
}
|
||||
} else if (opt.toString().equals(name)) {
|
||||
if (getConfigName(opt).equals(name)) {
|
||||
setValue(opt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// compatibilidad con configs. de antes, cuando se guardaba el nombre ya traducido
|
||||
// (el de vanilla en inglés). si no, la opción guardada se perdía y volvía a la de serie
|
||||
for (T opt : options) {
|
||||
if (I18nUtil.vanillaName(getConfigName(opt)).equals(name)) {
|
||||
setValue(opt);
|
||||
return;
|
||||
}
|
||||
@@ -82,9 +88,22 @@ public class OptionSetting<T> extends Setting<T> {
|
||||
return displayNames;
|
||||
}
|
||||
|
||||
// OJO: nombre interno de la opción, o sea la clave de la config. (ver ClientConfig
|
||||
// #onUpdateSetting). igual que en ListSetting, para las listas del juego es la clave
|
||||
// de traducción. no traducir
|
||||
public String getConfigName(T key) {
|
||||
if (displayNames != null && displayNames.containsKey(key)) return displayNames.get(key);
|
||||
return key != null ? key.toString() : "?";
|
||||
}
|
||||
|
||||
public String getConfigName() {
|
||||
return getConfigName(value);
|
||||
}
|
||||
|
||||
// nombre visible, traducido
|
||||
public String getDisplayName(T key) {
|
||||
if (displayNames != null) return displayNames.get(key);
|
||||
return key.toString();
|
||||
String name = getConfigName(key);
|
||||
return I18nUtil.orElse(name, name);
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
|
||||
@@ -4,11 +4,16 @@ import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
||||
public abstract class Panel<B extends Button> extends Widget {
|
||||
|
||||
protected String title;
|
||||
// el título se resuelve al pintarlo, no al construir el marco: los marcos se crean en
|
||||
// la inicialización del mod, cuando el idioma todavía no ha cargado, así que guardar el
|
||||
// texto ya traducido dejaba los títulos en español para siempre (y no cambiaban aunque
|
||||
// se cambiara el idioma del juego en marcha)
|
||||
private Supplier<String> title;
|
||||
|
||||
protected final List<B> buttons = new ArrayList<>();
|
||||
protected List<B> visibleButtons = new ArrayList<>();
|
||||
@@ -20,10 +25,22 @@ public abstract class Panel<B extends Button> extends Widget {
|
||||
protected boolean dragging;
|
||||
|
||||
public Panel(String title, int x, int y, int w, int h) {
|
||||
this(() -> title, x, y, w, h);
|
||||
}
|
||||
|
||||
public Panel(Supplier<String> title, int x, int y, int w, int h) {
|
||||
super(x, y, w, h);
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title.get();
|
||||
}
|
||||
|
||||
protected void setTitle(Supplier<String> title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public void drawTooltips(GuiGraphicsExtractor gui, int mouseX, int mouseY) {
|
||||
for (Button button : visibleButtons) button.drawTooltip(gui, mouseX, mouseY);
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ import me.retucio.sputnik.ui.widgets.Button;
|
||||
import me.retucio.sputnik.ui.widgets.Panel;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class CloseablePanel<B extends Button> extends Panel<B> {
|
||||
|
||||
protected static final int PADDING = 4;
|
||||
|
||||
public CloseablePanel(String title, int x, int y, int w, int h) {
|
||||
public CloseablePanel(Supplier<String> title, int x, int y, int w, int h) {
|
||||
super(title, x, y, w, h);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,12 @@ import me.retucio.sputnik.ui.widgets.Button;
|
||||
import me.retucio.sputnik.ui.widgets.Panel;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class ExtendablePanel<B extends Button> extends Panel<B> {
|
||||
protected boolean extended;
|
||||
|
||||
public ExtendablePanel(String title, int x, int y, int w, int h) {
|
||||
public ExtendablePanel(Supplier<String> title, int x, int y, int w, int h) {
|
||||
super(title, x, y, w, h);
|
||||
this.extended = true;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import me.retucio.sputnik.friend.FriendManager;
|
||||
import me.retucio.sputnik.ui.screen.ClickGui;
|
||||
import me.retucio.sputnik.ui.widgets.buttons.FriendButton;
|
||||
import me.retucio.sputnik.util.Colors;
|
||||
import me.retucio.sputnik.util.I18nUtil;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
@@ -20,7 +21,7 @@ public class FriendsPanel extends ExtendablePanel<FriendButton> {
|
||||
private int offset;
|
||||
|
||||
public FriendsPanel(int x, int y, int w, int h) {
|
||||
super("amigos", x, y, w, h);
|
||||
super(() -> I18nUtil.orElse("sputnik.ui.friends.title", "amigos"), x, y, w, h);
|
||||
dragging = false;
|
||||
|
||||
Sputnik.EVENT_BUS.subscribe(this);
|
||||
@@ -34,7 +35,7 @@ public class FriendsPanel extends ExtendablePanel<FriendButton> {
|
||||
|
||||
@Override
|
||||
protected void updateWidth() {
|
||||
int maxWidth = mc.font.width(title) + mc.font.width(" +");
|
||||
int maxWidth = mc.font.width(getTitle()) + mc.font.width(" +");
|
||||
for (FriendButton button : buttons) {
|
||||
int textWidth = mc.font.width(button.getFriend().getName());
|
||||
maxWidth = Math.max(maxWidth, textWidth);
|
||||
@@ -47,7 +48,7 @@ public class FriendsPanel extends ExtendablePanel<FriendButton> {
|
||||
updateWidth();
|
||||
gui.fill(x, renderY, x + w, renderY + h, Colors.mainColor.getRGB());
|
||||
|
||||
gui.text(mc.font, Component.literal(title),
|
||||
gui.text(mc.font, Component.literal(getTitle()),
|
||||
x + 8,
|
||||
renderY + (h / 2) - (mc.font.lineHeight / 2),
|
||||
-1, true);
|
||||
|
||||
@@ -21,7 +21,7 @@ public class ModulePanel extends ExtendablePanel<ModuleButton> {
|
||||
public final Category category;
|
||||
|
||||
public ModulePanel(Category category, int x, int y, int w, int h) {
|
||||
super(category.getDisplayName(), x, y, w, h);
|
||||
super(category::getDisplayName, x, y, w, h);
|
||||
this.category = category;
|
||||
dragging = false;
|
||||
|
||||
@@ -35,7 +35,7 @@ public class ModulePanel extends ExtendablePanel<ModuleButton> {
|
||||
|
||||
@Override
|
||||
protected void updateWidth() {
|
||||
int maxWidth = mc.font.width(title);
|
||||
int maxWidth = mc.font.width(getTitle());
|
||||
for (ModuleButton button : buttons) {
|
||||
int textWidth = mc.font.width(button.getModule().getDisplayName());
|
||||
maxWidth = Math.max(maxWidth, textWidth);
|
||||
@@ -48,7 +48,7 @@ public class ModulePanel extends ExtendablePanel<ModuleButton> {
|
||||
updateWidth();
|
||||
gui.fill(x, renderY, x + w, renderY + h, Colors.mainColor.getRGB());
|
||||
|
||||
gui.text(mc.font, Component.literal(title),
|
||||
gui.text(mc.font, Component.literal(getTitle()),
|
||||
x + 8,
|
||||
renderY + (h / 2) - (mc.font.lineHeight / 2),
|
||||
-1, true);
|
||||
|
||||
@@ -27,7 +27,8 @@ public class SettingsPanel extends CloseablePanel<SettingButton<? extends Settin
|
||||
public boolean drawHeader = true;
|
||||
|
||||
public SettingsPanel(Module module, int x, int y, int w, int h) {
|
||||
super(I18nUtil.orElse("sputnik.ui.settingsof", "ajustes de " + module.getDisplayName(), module.getDisplayName()), x, y, w, h);
|
||||
super(() -> I18nUtil.orElse("sputnik.ui.settingsof",
|
||||
"ajustes de " + module.getDisplayName(), module.getDisplayName()), x, y, w, h);
|
||||
|
||||
this.module = module;
|
||||
this.settingGroups = module.getSgs();
|
||||
@@ -59,7 +60,7 @@ public class SettingsPanel extends CloseablePanel<SettingButton<? extends Settin
|
||||
: -1;
|
||||
|
||||
gui.fill(x, renderY, x + w, renderY + h, headerColor);
|
||||
gui.text(mc.font, title, titleX, titleY, -1, true);
|
||||
gui.text(mc.font, getTitle(), titleX, titleY, -1, true);
|
||||
drawCloseButton(gui, mouseX, mouseY, closeButtonColor);
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ public class SettingsPanel extends CloseablePanel<SettingButton<? extends Settin
|
||||
.toList();
|
||||
|
||||
// descripción
|
||||
List<String> descLines = wrapDescription(module.getDescription(), w - 2 * PADDING);
|
||||
List<String> descLines = wrapDescription(module.getDisplayDescription(), w - 2 * PADDING);
|
||||
int lineSpacing = 2;
|
||||
|
||||
int descHeight = descLines.size() * mc.font.lineHeight + (descLines.size() - 1) * lineSpacing;
|
||||
@@ -212,7 +213,7 @@ public class SettingsPanel extends CloseablePanel<SettingButton<? extends Settin
|
||||
@Override
|
||||
public 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.font.width(title);
|
||||
int maxWidth = mc.font.width(getTitle());
|
||||
for (SettingButton<?> button : buttons) {
|
||||
String text = button.getSetting().getName();
|
||||
|
||||
@@ -246,7 +247,7 @@ public class SettingsPanel extends CloseablePanel<SettingButton<? extends Settin
|
||||
|
||||
public int getGroupHeaderY(int groupIndex) {
|
||||
int currentY = renderY + h + PADDING;
|
||||
List<String> descLines = wrapDescription(module.getDescription(), w - 2 * PADDING);
|
||||
List<String> descLines = wrapDescription(module.getDisplayDescription(), w - 2 * PADDING);
|
||||
int descHeight = descLines.size() * mc.font.lineHeight + (descLines.size() - 1) * 2;
|
||||
currentY += descHeight + 3 * PADDING; // descBoxBottom + padding
|
||||
|
||||
|
||||
+3
-2
@@ -6,6 +6,7 @@ import me.retucio.sputnik.module.modules.client.ClientSettingsModule;
|
||||
import me.retucio.sputnik.ui.screen.ClickGui;
|
||||
import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
|
||||
import me.retucio.sputnik.util.Colors;
|
||||
import me.retucio.sputnik.util.I18nUtil;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
|
||||
public class ClientSettingsPanel extends SettingsPanel {
|
||||
@@ -15,7 +16,7 @@ public class ClientSettingsPanel extends SettingsPanel {
|
||||
|
||||
public ClientSettingsPanel(int x, int y, int w, int h) {
|
||||
super(clientSettings, x, y, w, h);
|
||||
this.title = "ajustes del mod";
|
||||
setTitle(() -> I18nUtil.orElse("sputnik.ui.clientsettings.title", "ajustes del mod"));
|
||||
this.drawHeader = false; // disable the original header
|
||||
}
|
||||
|
||||
@@ -25,7 +26,7 @@ public class ClientSettingsPanel extends SettingsPanel {
|
||||
|
||||
// Draw our own custom header (no close button, with +/-
|
||||
gui.fill(x, renderY, x + w, renderY + h, Colors.mainColor.getRGB());
|
||||
gui.text(mc.font, title,
|
||||
gui.text(mc.font, getTitle(),
|
||||
x + 8,
|
||||
renderY + (h / 2) - (mc.font.lineHeight / 2),
|
||||
-1, true);
|
||||
|
||||
@@ -47,6 +47,21 @@ public class I18nUtil {
|
||||
return Language.getInstance().has(key) ? I18n.get(key, args) : fallback;
|
||||
}
|
||||
|
||||
// igual que orElse pero rellenando también los %s del texto de reserva.
|
||||
// para lo que se registra durante el arranque (cargar la config., comprobar la versión...):
|
||||
// ahí el idioma todavía no está cargado y Component#translatable escupía la clave pelada
|
||||
public static String fmt(String key, String fallback, Object... args) {
|
||||
if (Language.getInstance().has(key)) return I18n.get(key, args);
|
||||
return args.length == 0 ? fallback : String.format(fallback, args);
|
||||
}
|
||||
|
||||
// texto que daba el en_us de vanilla para una clave. sólo para leer configs. viejas:
|
||||
// las listas guardaban el nombre ya traducido y Language, durante el arranque, es
|
||||
// siempre el en_us de vanilla, así que lo guardado eran los nombres en inglés
|
||||
public static String vanillaName(String key) {
|
||||
return Language.DEFAULT_INSTANCE.getOrDefault(key, key);
|
||||
}
|
||||
|
||||
// nombre visible de una constante de enum. los EnumSetting se guardan por índice
|
||||
// (ver ConfigManager#applyEnumSetting), así que su toString() es sólo texto y se traduce.
|
||||
// la clave se deriva de la clase que la declara, así que dos enums con el mismo nombre
|
||||
|
||||
@@ -3,7 +3,6 @@ package me.retucio.sputnik.util;
|
||||
|
||||
import net.minecraft.core.particles.ParticleType;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
@@ -40,38 +39,36 @@ public class Lists {
|
||||
public static final List<Color> colorList = new ArrayList<>();
|
||||
public static final List<String> fontList = new ArrayList<>();
|
||||
|
||||
// intentar traducir nombres y fallar miserablemente
|
||||
// estos mapas guardan la CLAVE de traducción, no el texto ya traducido: son los nombres
|
||||
// internos de las opciones y por tanto claves de la config. (ver ListSetting#getConfigName).
|
||||
// antes se resolvía aquí con Component#translatable, pero esto corre en la inicialización
|
||||
// del mod, cuando Language todavía es el en_us de vanilla: salían los nombres en inglés
|
||||
// pasara lo que pasara. ahora se traducen al mostrarlos (ListSetting#getDisplayName)
|
||||
public static void init() {
|
||||
entityNames = getMapOfLists(entityList,
|
||||
entityList.stream().map(entity -> Component.translatable(
|
||||
entity.getDescriptionId()
|
||||
).getString()).toList());
|
||||
entityList.stream().map(EntityType::getDescriptionId).toList());
|
||||
|
||||
particleNames = getMapOfLists(particleList,
|
||||
particleList.stream().map(particle -> Component.translatable(
|
||||
particleList.stream().map(particle ->
|
||||
BuiltInRegistries.PARTICLE_TYPE.getKey(particle).toLanguageKey()
|
||||
).getString()).toList());
|
||||
).toList());
|
||||
|
||||
itemNames = getMapOfLists(itemList,
|
||||
itemList.stream().map(item -> Component.translatable(
|
||||
item.getDescriptionId()
|
||||
).getString()).toList());
|
||||
itemList.stream().map(Item::getDescriptionId).toList());
|
||||
|
||||
blockNames = getMapOfLists(blockList,
|
||||
blockList.stream().map(block -> Component.translatable(
|
||||
block.getDescriptionId()
|
||||
).getString()).toList());
|
||||
blockList.stream().map(Block::getDescriptionId).toList());
|
||||
|
||||
soundNames = getMapOfLists(soundList,
|
||||
soundList.stream().map(sound -> Component.translatable(
|
||||
soundList.stream().map(sound ->
|
||||
sound.location().toShortLanguageKey()
|
||||
).getString()).toList());
|
||||
).toList());
|
||||
soundNames.replace(SoundEvents.EMPTY, "NINGUNO");
|
||||
|
||||
screenNames = getMapOfLists(screenList,
|
||||
screenList.stream().map(screen -> Component.translatable(
|
||||
screenList.stream().map(screen ->
|
||||
BuiltInRegistries.MENU.getKey(screen).toLanguageKey()
|
||||
).getString()).toList());
|
||||
).toList());
|
||||
|
||||
colorList.addAll(Arrays.asList(
|
||||
RED, ORANGE, YELLOW, LIME, GREEN, CYAN,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package me.retucio.sputnik.util.render;
|
||||
|
||||
import com.mojang.blaze3d.PrimitiveTopology;
|
||||
import com.mojang.blaze3d.pipeline.DepthStencilState;
|
||||
import com.mojang.blaze3d.pipeline.RenderPipeline;
|
||||
import com.mojang.blaze3d.platform.CompareOp;
|
||||
import com.mojang.blaze3d.systems.GpuDevice;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
@@ -28,12 +30,15 @@ public class Pipelines {
|
||||
|
||||
static List<RenderPipeline> PIPELINES = new ArrayList<>();
|
||||
|
||||
// 26.2 usa depth invertido (near = 1), así que el LEQUAL de siempre es GREATER_THAN_OR_EQUAL
|
||||
private static final DepthStencilState NO_DEPTH = new DepthStencilState(CompareOp.ALWAYS_PASS, false);
|
||||
private static final DepthStencilState DEPTH = new DepthStencilState(CompareOp.GREATER_THAN_OR_EQUAL, false);
|
||||
|
||||
static final RenderPipeline LINES_PIPELINE = add(RenderPipeline.builder(RenderPipelines.LINES_SNIPPET)
|
||||
.withLocation(Identifier.fromNamespaceAndPath(Sputnik.MOD_ID, "pipeline/lines"))
|
||||
.withVertexBinding(0, DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH)
|
||||
.withPrimitiveTopology(PrimitiveTopology.LINES)
|
||||
// .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST)
|
||||
// .withDepthWrite(false)
|
||||
.withDepthStencilState(NO_DEPTH)
|
||||
.withCull(false)
|
||||
.build());
|
||||
|
||||
@@ -41,8 +46,7 @@ public class Pipelines {
|
||||
.withLocation(Identifier.fromNamespaceAndPath(Sputnik.MOD_ID, "pipeline/lines_cull"))
|
||||
.withVertexBinding(0, DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH)
|
||||
.withPrimitiveTopology(PrimitiveTopology.LINES)
|
||||
// .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST)
|
||||
// .withDepthWrite(false)
|
||||
.withDepthStencilState(DEPTH)
|
||||
.withCull(true)
|
||||
.build());
|
||||
|
||||
@@ -50,9 +54,7 @@ public class Pipelines {
|
||||
.withLocation(Identifier.fromNamespaceAndPath(Sputnik.MOD_ID, "pipeline/quads"))
|
||||
.withVertexBinding(0, DefaultVertexFormat.POSITION_COLOR)
|
||||
.withPrimitiveTopology(PrimitiveTopology.QUADS)
|
||||
// .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST)
|
||||
// .withBlend(BlendFunction.TRANSLUCENT)
|
||||
// .withDepthWrite(false)
|
||||
.withDepthStencilState(NO_DEPTH)
|
||||
.withCull(false)
|
||||
.build());
|
||||
|
||||
@@ -60,9 +62,7 @@ public class Pipelines {
|
||||
.withLocation(Identifier.fromNamespaceAndPath(Sputnik.MOD_ID, "pipeline/quads_cull"))
|
||||
.withVertexBinding(0, DefaultVertexFormat.POSITION_COLOR)
|
||||
.withPrimitiveTopology(PrimitiveTopology.QUADS)
|
||||
// .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST)
|
||||
// .withBlend(BlendFunction.TRANSLUCENT)
|
||||
// .withDepthWrite(false)
|
||||
.withDepthStencilState(DEPTH)
|
||||
.withCull(false)
|
||||
.build());
|
||||
|
||||
@@ -76,9 +76,7 @@ public class Pipelines {
|
||||
// siguen usando uniforms con nombre (u_Texture, u_ProjectionMatrix) y seguramente
|
||||
// haya que reescribirlos para que casen con las convenciones nuevas
|
||||
.withBindGroupLayout(BindGroupLayouts.SAMPLER0)
|
||||
// .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST)
|
||||
// .withDepthWrite(false)
|
||||
// .withBlend(BlendFunction.TRANSLUCENT)
|
||||
.withDepthStencilState(NO_DEPTH)
|
||||
.withCull(false)
|
||||
.build());
|
||||
|
||||
|
||||
@@ -5,24 +5,32 @@ import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import me.retucio.sputnik.Sputnik;
|
||||
import net.minecraft.client.renderer.StagedVertexBuffer;
|
||||
import net.minecraft.client.renderer.rendertype.PreparedRenderType;
|
||||
import net.minecraft.client.renderer.rendertype.RenderType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/** emula el antiguo Tesselator + RenderType.draw(MeshData), que 26.2 se cargó
|
||||
* <p>
|
||||
* ahora los vértices se escriben en un StagedVertexBuffer: se pide un draw, se
|
||||
* escriben los vértices, se sube todo a la gpu y se dibuja. el ciclo es
|
||||
* appendDraw -> upload -> drawFromBuffer -> endDraw; upload() peta si se llama
|
||||
* dos veces sin endDraw() de por medio.
|
||||
* el ciclo del StagedVertexBuffer es appendDraw -> upload -> drawFromBuffer -> endDraw,
|
||||
* y hay que respetarlo entero: los vértices no llegan al Draw hasta que upload() cierra
|
||||
* el BufferBuilder, y appendDraw() peta después de un upload(). de ahí que draw() sólo
|
||||
* encole y sea flush() quien suba y dibuje todo de una.
|
||||
* <p>
|
||||
* subimos y dibujamos en cada llamada (en vez de agrupar draws) para mantener el
|
||||
* mismo orden de dibujado que antes; es menos eficiente pero se comporta igual.
|
||||
* un upload() por caja tampoco vale: cada uno se pilla un buffer de 256 KB del pool que
|
||||
* no vuelve hasta el final del frame.
|
||||
*/
|
||||
public class SputnikBuffers {
|
||||
|
||||
private static StagedVertexBuffer buffer;
|
||||
private static StagedVertexBuffer.Draw current;
|
||||
|
||||
private static final List<QueuedDraw> queue = new ArrayList<>();
|
||||
|
||||
private record QueuedDraw(PreparedRenderType type, StagedVertexBuffer.Draw draw) {}
|
||||
|
||||
// lazy: al cargar la clase todavía puede no haber device
|
||||
private static StagedVertexBuffer buffer() {
|
||||
if (buffer == null) {
|
||||
@@ -32,8 +40,6 @@ public class SputnikBuffers {
|
||||
}
|
||||
|
||||
public static VertexConsumer begin(VertexFormat format, PrimitiveTopology topology) {
|
||||
if (current != null) buffer().endDraw(); // por si quedó un draw a medias
|
||||
|
||||
current = buffer().appendDraw(format, topology);
|
||||
return buffer().getVertexBuilder(current);
|
||||
}
|
||||
@@ -41,20 +47,33 @@ public class SputnikBuffers {
|
||||
public static void draw(RenderType type) {
|
||||
if (current == null) return;
|
||||
|
||||
if (!current.isEmpty()) {
|
||||
buffer().upload();
|
||||
type.prepare().drawFromBuffer(buffer().getExecuteInfo(current));
|
||||
queue.add(new QueuedDraw(type.prepare(), current));
|
||||
current = null;
|
||||
}
|
||||
|
||||
/** sube y dibuja lo encolado; hay que llamarlo antes de que el juego pinte el hud */
|
||||
public static void flush() {
|
||||
if (buffer == null) return;
|
||||
|
||||
current = null;
|
||||
if (queue.isEmpty()) return;
|
||||
|
||||
buffer.upload();
|
||||
|
||||
for (QueuedDraw queued : queue) {
|
||||
StagedVertexBuffer.ExecuteInfo info = buffer.getExecuteInfo(queued.draw());
|
||||
if (info != null) queued.type().drawFromBuffer(info);
|
||||
}
|
||||
|
||||
buffer().endDraw();
|
||||
current = null;
|
||||
queue.clear();
|
||||
buffer.endDraw();
|
||||
}
|
||||
|
||||
// devuelve los buffers al pool; una vez por frame
|
||||
public static void endFrame() {
|
||||
if (buffer == null) return;
|
||||
|
||||
current = null;
|
||||
flush();
|
||||
buffer.endFrame();
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,7 @@
|
||||
"sputnik.commands.wait.desc": "waits a given amount of time before running another command",
|
||||
"sputnik.config.autosaving.desc": "saving settings...",
|
||||
"sputnik.config.autosaving.title": "autosaving...",
|
||||
"sputnik.config.error.applying": "error applying settings",
|
||||
"sputnik.config.error.empty": "config file empty or nonexistent",
|
||||
"sputnik.config.error.invalidformat": "invalid file format: %s",
|
||||
"sputnik.config.error.invalidversion": "invalid version: %s",
|
||||
@@ -105,6 +106,8 @@
|
||||
"sputnik.enum.module.modules.client.hud.dynosaurs.velocirraptor": "velociraptor",
|
||||
"sputnik.enum.module.modules.client.hud.timeformat.twelve_hour": "12h",
|
||||
"sputnik.enum.module.modules.client.hud.timeformat.twenty_four_hour": "24h",
|
||||
"sputnik.enum.module.modules.combat.holdattack.attackmode.spam": "spam",
|
||||
"sputnik.enum.module.modules.combat.holdattack.attackmode.time": "time",
|
||||
"sputnik.enum.module.modules.combat.projectiletrajectories.trajectorystyle.dashed": "dashed",
|
||||
"sputnik.enum.module.modules.combat.projectiletrajectories.trajectorystyle.dotted": "points",
|
||||
"sputnik.enum.module.modules.combat.projectiletrajectories.trajectorystyle.solid": "solid",
|
||||
@@ -327,6 +330,8 @@
|
||||
"sputnik.module.auradecristales.name": "crystal aura",
|
||||
"sputnik.module.autismo.desc": "hehe",
|
||||
"sputnik.module.autismo.name": "autism",
|
||||
"sputnik.module.autoataque.desc": "hold the attack button down to attack automatically",
|
||||
"sputnik.module.autoataque.name": "autoattack",
|
||||
"sputnik.module.autocartel.desc": "lets you place signs with the same text without typing it out one by one",
|
||||
"sputnik.module.autocartel.name": "auto sign",
|
||||
"sputnik.module.autofish.rodbreaking": "the rod is about to break",
|
||||
@@ -646,6 +651,14 @@
|
||||
"sputnik.setting.autismo.general.direccion.name": "direction",
|
||||
"sputnik.setting.autismo.general.intervalo.desc": "rotation interval",
|
||||
"sputnik.setting.autismo.general.intervalo.name": "interval",
|
||||
"sputnik.setting.autoataque.general.atacarenpantallas.desc": "attack even while in a screen",
|
||||
"sputnik.setting.autoataque.general.atacarenpantallas.name": "attack in screens",
|
||||
"sputnik.setting.autoataque.general.cooldown.desc": "how full the attack bar has to be before attacking",
|
||||
"sputnik.setting.autoataque.general.cooldown.name": "cooldown",
|
||||
"sputnik.setting.autoataque.general.cooldown1.desc": "how often to attack, in ticks",
|
||||
"sputnik.setting.autoataque.general.cooldown1.name": "cooldown",
|
||||
"sputnik.setting.autoataque.general.mododeataque.desc": "how to attack",
|
||||
"sputnik.setting.autoataque.general.mododeataque.name": "attack mode",
|
||||
"sputnik.setting.autoherramienta.general.cambiardevuelta.desc": "switch back to the original tool",
|
||||
"sputnik.setting.autoherramienta.general.cambiardevuelta.name": "switch back",
|
||||
"sputnik.setting.autoherramienta.general.delay.desc": "delay when switching tools",
|
||||
@@ -1434,8 +1447,10 @@
|
||||
"sputnik.setting.zoom.visual.mostrarmanos.name": "show hands",
|
||||
"sputnik.ui.availablemodes": "available modes:",
|
||||
"sputnik.ui.clickgui.title": "gui",
|
||||
"sputnik.ui.clientsettings.title": "mod settings",
|
||||
"sputnik.ui.friend.manage": "manage %s",
|
||||
"sputnik.ui.friend.remove": "remove friend",
|
||||
"sputnik.ui.friends.title": "friends",
|
||||
"sputnik.ui.list.tooltip": "%s (%s of %s)",
|
||||
"sputnik.ui.preview.echest": "echest",
|
||||
"sputnik.ui.preview.shulker": "shulker preview",
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"sputnik.commands.wait.desc": "espera un tiempo determinado antes de ejecutar otro comando",
|
||||
"sputnik.config.autosaving.desc": "guardando configuración...",
|
||||
"sputnik.config.autosaving.title": "autoguardando...",
|
||||
"sputnik.config.error.applying": "error aplicando ajustes",
|
||||
"sputnik.config.error.empty": "archivo de config. vacío o inexistente",
|
||||
"sputnik.config.error.invalidformat": "formato de archivo inválido: %s",
|
||||
"sputnik.config.error.invalidversion": "versión inválida: %s",
|
||||
@@ -105,6 +106,8 @@
|
||||
"sputnik.enum.module.modules.client.hud.dynosaurs.velocirraptor": "velocirráptor",
|
||||
"sputnik.enum.module.modules.client.hud.timeformat.twelve_hour": "12h",
|
||||
"sputnik.enum.module.modules.client.hud.timeformat.twenty_four_hour": "24h",
|
||||
"sputnik.enum.module.modules.combat.holdattack.attackmode.spam": "spamear",
|
||||
"sputnik.enum.module.modules.combat.holdattack.attackmode.time": "timear",
|
||||
"sputnik.enum.module.modules.combat.projectiletrajectories.trajectorystyle.dashed": "discontinua",
|
||||
"sputnik.enum.module.modules.combat.projectiletrajectories.trajectorystyle.dotted": "puntos",
|
||||
"sputnik.enum.module.modules.combat.projectiletrajectories.trajectorystyle.solid": "continua",
|
||||
@@ -327,6 +330,8 @@
|
||||
"sputnik.module.auradecristales.name": "aura de cristales",
|
||||
"sputnik.module.autismo.desc": "jeje",
|
||||
"sputnik.module.autismo.name": "autismo",
|
||||
"sputnik.module.autoataque.desc": "mantén pulsado el clic para atacar automáticamente",
|
||||
"sputnik.module.autoataque.name": "autoataque",
|
||||
"sputnik.module.autocartel.desc": "te permite colocar carteles con el mismo texto sin tener que escribirlo uno por uno",
|
||||
"sputnik.module.autocartel.name": "autocartel",
|
||||
"sputnik.module.autofish.rodbreaking": "caña a punto de romperse",
|
||||
@@ -646,6 +651,14 @@
|
||||
"sputnik.setting.autismo.general.direccion.name": "dirección",
|
||||
"sputnik.setting.autismo.general.intervalo.desc": "intervalo de rotación",
|
||||
"sputnik.setting.autismo.general.intervalo.name": "intervalo",
|
||||
"sputnik.setting.autoataque.general.atacarenpantallas.desc": "atacar aun estando en una pantalla",
|
||||
"sputnik.setting.autoataque.general.atacarenpantallas.name": "atacar en pantallas",
|
||||
"sputnik.setting.autoataque.general.cooldown.desc": "cómo de llena tiene que estar la barra de ataque antes de atacar",
|
||||
"sputnik.setting.autoataque.general.cooldown.name": "cooldown",
|
||||
"sputnik.setting.autoataque.general.cooldown1.desc": "cada cuánto atacar, en ticks",
|
||||
"sputnik.setting.autoataque.general.cooldown1.name": "cooldown",
|
||||
"sputnik.setting.autoataque.general.mododeataque.desc": "cómo atacar",
|
||||
"sputnik.setting.autoataque.general.mododeataque.name": "modo de ataque",
|
||||
"sputnik.setting.autoherramienta.general.cambiardevuelta.desc": "cambiar de vuelta a la herramienta inicial",
|
||||
"sputnik.setting.autoherramienta.general.cambiardevuelta.name": "cambiar de vuelta",
|
||||
"sputnik.setting.autoherramienta.general.delay.desc": "delay al cambiar de herramienta",
|
||||
@@ -1434,8 +1447,10 @@
|
||||
"sputnik.setting.zoom.visual.mostrarmanos.name": "mostrar manos",
|
||||
"sputnik.ui.availablemodes": "modos disponibles:",
|
||||
"sputnik.ui.clickgui.title": "interfaz",
|
||||
"sputnik.ui.clientsettings.title": "ajustes del mod",
|
||||
"sputnik.ui.friend.manage": "gestiona a %s",
|
||||
"sputnik.ui.friend.remove": "eliminar amigo",
|
||||
"sputnik.ui.friends.title": "amigos",
|
||||
"sputnik.ui.list.tooltip": "%s (%s de %s)",
|
||||
"sputnik.ui.preview.echest": "echest",
|
||||
"sputnik.ui.preview.shulker": "previsualización del shulker",
|
||||
|
||||
Reference in new issue
Block a user