diff --git a/cambios.md b/cambios.md index c386615..825a91f 100644 --- a/cambios.md +++ b/cambios.md @@ -1,4 +1,4 @@ -# v4.8 +# v4.9 ## novedades @@ -8,13 +8,15 @@ #### módulos nuevos: +- notificaciones: te avisa de ciertos eventos mediante notificaciones + ### arreglos / mejoras - `puntos de desconexión` ya renderiza la caja correctamente +- movido `aviso de baja durabilidad` a `notificaciones` ## problemas reconocidos - lo de siempre, además de: - `inventario plus` no funciona en creativo - - `RPC de Discord` no funciona e incluso puede provocar crashes - - las cajas `puntos de desconexión` están mal renderizadas \ No newline at end of file + - `RPC de Discord` no funciona e incluso puede provocar crashes \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 2fe11a1..165b88c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,6 +10,6 @@ discord_rpc_version=1.0.3 neutrino_version=1.1.0 jdk_version=25 -mod_version=4.8 +mod_version=4.9 maven_group=me.retucio archives_base_name=sputnik \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/Sputnik.java b/src/main/java/me/retucio/sputnik/Sputnik.java index b128626..c3005d7 100644 --- a/src/main/java/me/retucio/sputnik/Sputnik.java +++ b/src/main/java/me/retucio/sputnik/Sputnik.java @@ -24,6 +24,7 @@ import me.retucio.sputnik.ui.screen.UpdateScreen; import me.retucio.sputnik.ui.widgets.buttons.settings.BindButton; import me.retucio.sputnik.ui.widgets.buttons.SettingButton; import me.retucio.sputnik.ui.widgets.buttons.settings.TextButton; +import me.retucio.sputnik.ui.widgets.misc.NotificationWidget; import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel; import me.retucio.sputnik.ui.widgets.panels.SettingsPanel; import me.retucio.sputnik.ui.widgets.Button; diff --git a/src/main/java/me/retucio/sputnik/module/ModuleManager.java b/src/main/java/me/retucio/sputnik/module/ModuleManager.java index e3ed1fd..744d5db 100644 --- a/src/main/java/me/retucio/sputnik/module/ModuleManager.java +++ b/src/main/java/me/retucio/sputnik/module/ModuleManager.java @@ -6,6 +6,7 @@ import me.retucio.sputnik.module.modules.client.DiscordRPC; import me.retucio.sputnik.module.modules.client.Friends; import me.retucio.sputnik.module.modules.client.Hud; import me.retucio.sputnik.module.modules.camera.*; +import me.retucio.sputnik.module.modules.client.Notifications; import me.retucio.sputnik.module.modules.combat.*; import me.retucio.sputnik.module.modules.inventory.*; import me.retucio.sputnik.module.modules.misc.*; @@ -81,6 +82,7 @@ public class ModuleManager { add(new DiscordRPC()); add(new Friends()); add(new Hud()); + add(new Notifications()); } private void addCombat() { @@ -154,7 +156,6 @@ public class ModuleManager { add(new HandView()); add(new KeyPearl()); add(new NoFall()); - add(new WarnLowDurability()); } private void addRender() { diff --git a/src/main/java/me/retucio/sputnik/module/modules/client/Notifications.java b/src/main/java/me/retucio/sputnik/module/modules/client/Notifications.java new file mode 100644 index 0000000..dddc4fc --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/client/Notifications.java @@ -0,0 +1,219 @@ +package me.retucio.sputnik.module.modules.client; + +import me.retucio.sputnik.module.Category; +import me.retucio.sputnik.module.Module; +import me.retucio.sputnik.module.ModuleManager; +import me.retucio.sputnik.module.modules.misc.FakePlayer; +import me.retucio.sputnik.module.setting.SettingGroup; +import me.retucio.sputnik.module.setting.settings.*; +import me.retucio.sputnik.ui.hud.HudRenderer; +import me.retucio.sputnik.ui.widgets.misc.NotificationWidget; +import me.retucio.sputnik.util.Lists; + +import net.minecraft.ChatFormatting; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; + + +public class Notifications extends Module { + + private final SettingGroup sgSound = addSg(new SettingGroup("sonido", false)); + private final SettingGroup sgPlayer = addSg(new SettingGroup("jugador en rango", true)); + private final SettingGroup sgDurability = addSg(new SettingGroup("baja durabilidad", true)); + + + /* ajustes generales */ + + private final NumberSetting duration = sgGeneral.add(new NumberSetting( + "duración", + "duración de las notis en segundos", + 5, 0.1, 10, 0.1 + )); + + private final BooleanSetting popup = sgGeneral.add(new BooleanSetting( + "pop-up", + "mostrar pop-up de notificación", + true + )); + + + /* ajustes del volumen */ + + private final OptionSetting sound = sgSound.add(new OptionSetting<>( + "sonido", + "sonido a reproducir con las notis", + Lists.soundList, + SoundEvents.NOTE_BLOCK_BELL.value(), + Lists.soundNames + )); + + private final NumberSetting volume = sgSound.add(new NumberSetting( + "volumen", + "volumen del sonido", + 70, 0, 125, 1 + )); + + private final NumberSetting pitch = sgSound.add(new NumberSetting( + "frecuencia", + "altura del sonido", + 70, 0, 125, 1 + )); + + + /* cuando un jugador entra en rango */ + + private final BooleanSetting warnPlayerInRange = sgPlayer.add(new BooleanSetting( + "jugador en rango", + "notificar cuando un jugador entre en rango", + true + )); + + private final NumberSetting playerRange = sgPlayer.add(new NumberSetting( + "rango de jugadores", + "rango a tener en cuenta al notificar", + 12, + 1, + 32, + 1 + )).visibility(warnPlayerInRange::getValue); + + private final BooleanSetting ignoreFakePlayer = sgPlayer.add(new BooleanSetting( + "ignorar jugador falso", + "ignorar al muñeco de \"jugador falso\"", + false + )); + + + /* cuando una herramienta tiene la durabilidad baja */ + + private final BooleanSetting warnToolLowDurability = sgDurability.add(new BooleanSetting( + "baja durabilidad", + "notificar cuando la herramienta en uso tiene baja durabilidad", + true + )); + + private final ListSetting excludeItems = sgDurability.add(new ListSetting<>( + "items a excluir", + "no avisar de la baja durabilidad de estos", + Lists.itemList, + Lists.allFalse(Lists.itemList), + Lists.itemNames + )); + + private final NumberSetting limitPercentage = sgDurability.add(new NumberSetting( + "porcentaje", "porcentaje de durabilidad restante a la que se te avisa", + 5, 1, 100, 1 + )); + + + /* fields */ + + private final List notifiedPlayers = new ArrayList<>(); + private final List notifiedTools = new ArrayList<>(); + + private float prevPercentage = -1; + + + public Notifications() { + super( + "notificaciones", + "te notifica cuando sucedan ciertos eventos", + Category.CLIENT + ); + duration.onUpdate(v -> NotificationWidget.duration = (int) (v * 1000)); + } + + @Override + public void onDisable() { + notifiedPlayers.clear(); + notifiedTools.clear(); + super.onDisable(); + } + + @Override + public void onTick() { + if (mc.player == null || mc.level == null) return; + checkForPlayersInRange(); + checkForLowDurabilityTools(); + } + + // comprobar si algún jugador ha entrado en el rango + private void checkForPlayersInRange() { + if (!warnPlayerInRange.getValue()) return; + + for (Player player : mc.level.players()) { + if (player == mc.player) continue; + if (player == ModuleManager.INSTANCE.getModuleByClass(FakePlayer.class).getPlayer() + && ignoreFakePlayer.getValue()) continue; + + float d = player.distanceTo(mc.player); + if (d <= playerRange.getValue() * 16) { + if (!notifiedPlayers.contains(player)) { + notifiedPlayers.add(player); + String title = ChatFormatting.GREEN + player.getDisplayName().getString() + ChatFormatting.RESET + " ha entrado en rango!"; + String desc = player.getDisplayName().getString() + " está a " + ChatFormatting.GOLD + (int) d + ChatFormatting.RESET + " bloques de distancia."; + push(title, desc); + } + } else { + notifiedPlayers.remove(player); + } + } + } + + // comprobar si la herramienta en uso tiene la durabilidad por debajo del porcentaje seleccionado + private void checkForLowDurabilityTools() { + if (!warnToolLowDurability.getValue()) return; + + ItemStack stack = mc.player.getItemInHand(InteractionHand.MAIN_HAND); + if (notifiedTools.contains(stack) || excludeItems.isEnabled(stack.getItem())) return; + + float percentage = (1 - (float) stack.getDamageValue() / stack.getMaxDamage()) * 100; + + if (percentage <= limitPercentage.getValue() && percentage < prevPercentage) { + String customName = stack.getCustomName() == null ? "" : " \"" + stack.getCustomName().getString() + "\""; + String desc = ChatFormatting.AQUA + stack.getItemName().getString() + + ChatFormatting.GREEN + customName + ChatFormatting.RESET + + " a " + ChatFormatting.GOLD + ((int) percentage + "") + "%" + + ChatFormatting.RESET + " de durabilidad"; + push("baja durabilidad!", desc); + notifiedTools.add(stack); + } + + prevPercentage = percentage; + } + + + // enviar una notificación + private void push(String title, String desc) { + if (popup.getValue()) { + HudRenderer.pushNotification(title, desc); + } + if (sound.getValue() != SoundEvents.EMPTY) { + mc.level.playSound(mc.player, mc.player.blockPosition(), + sound.getValue(), + SoundSource.AMBIENT, + toExponential(volume), + toExponential(pitch) + ); + } + } + + // el sistema decibélico es un sistema logarítmico + private float toExponential(NumberSetting setting) { + double linear = setting.getValue(); + + double normalized = linear / (setting.getMax() - setting.getMax() / 4); + double exponential = Math.pow(normalized, 3); + + if (exponential < 0.01) exponential = 0; + return (float) exponential; + } +} diff --git a/src/main/java/me/retucio/sputnik/module/modules/player/WarnLowDurability.java b/src/main/java/me/retucio/sputnik/module/modules/player/WarnLowDurability.java deleted file mode 100644 index e29d65e..0000000 --- a/src/main/java/me/retucio/sputnik/module/modules/player/WarnLowDurability.java +++ /dev/null @@ -1,102 +0,0 @@ -package me.retucio.sputnik.module.modules.player; - -import me.retucio.sputnik.module.Category; -import me.retucio.sputnik.module.Module; -import me.retucio.sputnik.module.setting.SettingGroup; -import me.retucio.sputnik.module.setting.settings.BooleanSetting; -import me.retucio.sputnik.module.setting.settings.NumberSetting; -import me.retucio.sputnik.module.setting.settings.OptionSetting; -import me.retucio.sputnik.util.ChatUtil; -import me.retucio.sputnik.util.Lists; -import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.Component; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.item.ItemStack; - -import java.util.ArrayList; -import java.util.List; - - -public class WarnLowDurability extends Module { - - private final SettingGroup sgWarn = addSg(new SettingGroup("aviso", true)); - private final SettingGroup sgSound = addSg(new SettingGroup("sonido", true)); - - private final NumberSetting limitPercentage = sgGeneral.add(new NumberSetting("porcentaje", "porcentaje de durabilidad restante a la que se te avisa", - 5, 1, 100, 1)); - - private final BooleanSetting message = sgWarn.add(new BooleanSetting("enviar mensaje", "enviar un mensaje para alertar al usuario", true)); - private final BooleanSetting playSound = sgWarn.add(new BooleanSetting("reproducir sonido", "reproducir un sonido para alertar al usuario", true)); - - private final OptionSetting sound = sgSound.add(new OptionSetting<>("sonido", "qué sonido reproducir", - Lists.soundList, SoundEvents.NOTE_BLOCK_BELL.value(), Lists.soundNames)); - private final NumberSetting volume = sgSound.add(new NumberSetting("volumen", "volumen del sonido", 70, 0, 125, 1)); - private final NumberSetting pitch = sgSound.add(new NumberSetting("frecuencia", "altura del sonido", 70, 0, 125, 1)); - - private final List warned = new ArrayList<>(); - private float prevPercentage = -1; - - public WarnLowDurability() { - super("aviso de baja dur.", - "te avisa cuando la herramienta que sostengas sobrepase un límite de durabilidad", - Category.PLAYER); - - playSound.onUpdate(v -> { - sound.visibility(v); - volume.visibility(v); - pitch.visibility(v); - }); - } - - @Override - public void onTick() { - if (mc.player == null || mc.level == null) return; - ItemStack stack = mc.player.getItemInHand(InteractionHand.MAIN_HAND); - if (warned.contains(stack)) return; - - float percentage = (1 - (float) stack.getDamageValue() / stack.getMaxDamage()) * 100; - - if (percentage <= limitPercentage.getValue() && percentage < prevPercentage) { - if (playSound.getValue()) - mc.level.playSound(mc.player, mc.player.blockPosition(), - sound.getValue(), - SoundSource.AMBIENT, - toExponential(volume), - toExponential(pitch) - ); - - if (message.getValue()) { - String customName = stack.getCustomName() == null ? "" : " \"" + stack.getCustomName().getString() + "\""; - Component text = Component.literal(ChatFormatting.AQUA + stack.getItemName().getString() - + ChatFormatting.GREEN + customName + ChatFormatting.RESET - + " a " + ChatFormatting.GOLD + ((int) percentage + "") + "%" - + ChatFormatting.RESET + " de durabilidad"); - ChatUtil.warn(text); - } - - warned.add(stack); - } - - prevPercentage = percentage; - } - - @Override - public void onDisable() { - warned.clear(); - super.onDisable(); - } - - // el sistema decibélico es un sistema logarítmico - private float toExponential(NumberSetting setting) { - double linear = setting.getValue(); - - double normalized = linear / (setting.getMax() - setting.getMax() / 4); - double exponential = Math.pow(normalized, 3); - - if (exponential < 0.01) exponential = 0; - return (float) exponential; - } -} diff --git a/src/main/java/me/retucio/sputnik/ui/hud/HudRenderer.java b/src/main/java/me/retucio/sputnik/ui/hud/HudRenderer.java index 12a49f5..85636f1 100644 --- a/src/main/java/me/retucio/sputnik/ui/hud/HudRenderer.java +++ b/src/main/java/me/retucio/sputnik/ui/hud/HudRenderer.java @@ -4,6 +4,7 @@ import me.retucio.sputnik.config.ConfigManager; import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.modules.client.Hud; import me.retucio.sputnik.ui.hud.elements.*; +import me.retucio.sputnik.ui.widgets.misc.NotificationWidget; import net.minecraft.client.DeltaTracker; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; @@ -14,6 +15,7 @@ import java.awt.*; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Stack; public class HudRenderer { @@ -21,6 +23,7 @@ public class HudRenderer { private static final Minecraft mc = Minecraft.getInstance(); private static final List elements = new ArrayList<>(); + private static final Stack notifications = new Stack<>(); private static boolean initialized = false; public static void init() { @@ -101,6 +104,16 @@ public class HudRenderer { element.renderInGame(gui, dt.getGameTimeDeltaTicks(), hud); } } + + List toRemove = new ArrayList<>(); + for (NotificationWidget notification : notifications) { + notification.setIndex(notifications.indexOf(notification)); + notification.render(gui, 0, 0, 0); + if (System.currentTimeMillis() >= notification.popTime) { + toRemove.add(notification); + } + } + notifications.removeAll(toRemove); } private static boolean shouldSkipRendering() { @@ -116,6 +129,10 @@ public class HudRenderer { || mc.screen instanceof HudEditorScreen; } + public static void pushNotification(String title, String desc) { + notifications.push(new NotificationWidget(title, desc)); + } + public static List getElements() { return elements; } diff --git a/src/main/java/me/retucio/sputnik/ui/screen/ClickGui.java b/src/main/java/me/retucio/sputnik/ui/screen/ClickGui.java index cf54b1e..5037725 100644 --- a/src/main/java/me/retucio/sputnik/ui/screen/ClickGui.java +++ b/src/main/java/me/retucio/sputnik/ui/screen/ClickGui.java @@ -15,6 +15,7 @@ import me.retucio.sputnik.ui.widgets.buttons.FriendButton; import me.retucio.sputnik.ui.widgets.buttons.settings.ListButton; import me.retucio.sputnik.ui.widgets.buttons.ModuleButton; import me.retucio.sputnik.ui.widgets.buttons.SettingButton; +import me.retucio.sputnik.ui.widgets.misc.NotificationWidget; import me.retucio.sputnik.ui.widgets.panels.FriendsPanel; import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel; import me.retucio.sputnik.ui.widgets.panels.settings.ColorPickerPanel; diff --git a/src/main/java/me/retucio/sputnik/ui/widgets/misc/NotificationWidget.java b/src/main/java/me/retucio/sputnik/ui/widgets/misc/NotificationWidget.java new file mode 100644 index 0000000..e4b87b8 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/ui/widgets/misc/NotificationWidget.java @@ -0,0 +1,49 @@ +package me.retucio.sputnik.ui.widgets.misc; + +import me.retucio.sputnik.ui.widgets.Widget; +import me.retucio.sputnik.util.Colors; +import net.minecraft.client.gui.GuiGraphicsExtractor; + +public class NotificationWidget extends Widget { + + public static int duration = 5000; + private static final int PADDING = 4; + + private final String title, desc; + private int index; + + public final long pushTime; + public final long popTime; + + public NotificationWidget(String title, String desc) { + super(0, 0, + Math.max(mc.font.width(title), mc.font.width(desc)) + PADDING * 2, + mc.font.lineHeight * 2 + PADDING * 3); + + this.title = title; + this.desc = desc; + + this.x = mc.getWindow().getGuiScaledWidth() - w - 2; + this.pushTime = System.currentTimeMillis(); + this.popTime = pushTime + duration; + } + + @Override + public void render(GuiGraphicsExtractor gui, int mx, int my, float d) { + x = mc.getWindow().getGuiScaledWidth() - w - 2; + y = mc.getWindow().getGuiScaledHeight() - (index + 1) * (h + 2); + + gui.fill(x, y, x + w, y + h, Colors.panelBgColor.getRGB()); + + gui.text(mc.font, title, x + PADDING, y + PADDING, -1, true); + gui.text(mc.font, desc, x + PADDING, y + PADDING + mc.font.lineHeight + 2, 0xFFAAAAAA, true); + + float progress = (float) (popTime - System.currentTimeMillis()) / duration; + progress = Math.clamp(progress, 0f, 1f); + gui.fill(x, y + h - 2, x + (int) (progress * w), y + h, Colors.mainColor.getRGB()); + } + + public void setIndex(int index) { + this.index = index; + } +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/util/Lists.java b/src/main/java/me/retucio/sputnik/util/Lists.java index 94eef0a..fc96809 100644 --- a/src/main/java/me/retucio/sputnik/util/Lists.java +++ b/src/main/java/me/retucio/sputnik/util/Lists.java @@ -5,6 +5,7 @@ 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; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.Item; @@ -65,6 +66,7 @@ public class Lists { soundList.stream().map(sound -> Component.translatable( sound.location().toShortLanguageKey() ).getString()).toList()); + soundNames.replace(SoundEvents.EMPTY, "NINGUNO"); screenNames = getMapOfLists(screenList, screenList.stream().map(screen -> Component.translatable(