diff --git a/build.gradle b/build.gradle index ac3b42f..96ca9dd 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,6 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}" - } loom { diff --git a/cambios.md b/cambios.md index f3b72fd..1f7b9ca 100644 --- a/cambios.md +++ b/cambios.md @@ -1,15 +1,28 @@ -# v2.5 +# v2.6 ## novedades -- grupos de ajustes, para mayor orden, y arreglo de algún que otro bug - ### adiciones +- layouts de teclado (en_us y es_es) (fuck azerty) +- comando `$config` para abrir el marco de ajustes de un módulo +- elemento del hud que muestra la rotación de la cámara del jugador + #### módulos nuevos: +- fuentes: cambia la tipografía que usa todo el juego a una custom +- escalones: actúa con la altura deseada como si se estuviesen subiendo escalones +- escalones inversos: escalones, pabajo +- partículas: genera particulas artificiales + ### arreglos / mejoras +- ahora puedes cambiar el grosor de las líneas del contorno de los bloques +- arreglada la compatibilidad con meteor client +- optimizado y mejorado superposición de luz y resaltado de bloques +- ahora, en los marcos de seleccion única, en el tooltip de cada opción dice "seleccionar" en lugar de "incluir" +- ahora precoz también tiene una opción de reducir el cooldown al minar bloques + ## problemas reconocidos - muchos, pero repórtalos en la página de problemas si quieres para que queden bien registrados \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index d84facd..0bba6b7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,6 +8,6 @@ loom_version=1.13-SNAPSHOT fabric_api_version=0.140.2+1.21.11 -mod_version=2.5 +mod_version=2.6 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 48f0ab8..6de179b 100644 --- a/src/main/java/me/retucio/sputnik/Sputnik.java +++ b/src/main/java/me/retucio/sputnik/Sputnik.java @@ -29,6 +29,7 @@ import me.retucio.sputnik.ui.widgets.frames.SettingsFrame; import me.retucio.sputnik.ui.widgets.Button; import me.retucio.sputnik.util.*; +import me.retucio.sputnik.util.KeyUtil; import me.retucio.sputnik.util.render.DrawUtil; import me.retucio.sputnik.util.render.RenderUtil; @@ -69,6 +70,11 @@ public class Sputnik implements ClientModInitializer { private Screen prevScreen; public static boolean settingsApplied = false; + static { + // puta vida + System.setProperty("java.awt.headless", "false"); + } + @Override public void onInitializeClient() { mc = MinecraftClient.getInstance(); diff --git a/src/main/java/me/retucio/sputnik/command/CommandManager.java b/src/main/java/me/retucio/sputnik/command/CommandManager.java index 2dd47e3..94a7cfd 100644 --- a/src/main/java/me/retucio/sputnik/command/CommandManager.java +++ b/src/main/java/me/retucio/sputnik/command/CommandManager.java @@ -32,6 +32,7 @@ public class CommandManager { public static void registerCommands() { addCommand(new BindCommand()); + addCommand(new ConfigCommand()); addCommand(new CopyScreenshotCommand()); addCommand(new EnderChestCommand()); addCommand(new GarbageCleanerCommand()); diff --git a/src/main/java/me/retucio/sputnik/command/args/ModuleArgumentType.java b/src/main/java/me/retucio/sputnik/command/args/ModuleArgumentType.java index 6e45bca..a22213b 100644 --- a/src/main/java/me/retucio/sputnik/command/args/ModuleArgumentType.java +++ b/src/main/java/me/retucio/sputnik/command/args/ModuleArgumentType.java @@ -9,6 +9,7 @@ import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.ModuleManager; +import me.retucio.sputnik.util.MiscUtil; import net.minecraft.command.CommandSource; import net.minecraft.text.Text; @@ -19,6 +20,7 @@ import java.util.concurrent.CompletableFuture; public class ModuleArgumentType implements ArgumentType { public static final ModuleArgumentType INSTANCE = new ModuleArgumentType(); + private static final DynamicCommandExceptionType UNKNOWN_MODULE = new DynamicCommandExceptionType( name -> Text.literal("módulo \"" + name + "\" no encontrado")); @@ -35,7 +37,8 @@ public class ModuleArgumentType implements ArgumentType { @Override public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { - return CommandSource.suggestMatching(ModuleManager.INSTANCE.getModules().stream().map(module -> module.getName().replace(" ", "_")), builder); + return CommandSource.suggestMatching(ModuleManager.INSTANCE.getModules() + .stream().map(module -> MiscUtil.removeAccentMarks(module.getName().replace(" ", "_"))), builder); } @Override diff --git a/src/main/java/me/retucio/sputnik/command/commands/ConfigCommand.java b/src/main/java/me/retucio/sputnik/command/commands/ConfigCommand.java new file mode 100644 index 0000000..a370f0b --- /dev/null +++ b/src/main/java/me/retucio/sputnik/command/commands/ConfigCommand.java @@ -0,0 +1,67 @@ +package me.retucio.sputnik.command.commands; + +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import me.retucio.sputnik.command.Command; +import me.retucio.sputnik.command.args.ModuleArgumentType; +import me.retucio.sputnik.module.setting.Setting; +import me.retucio.sputnik.ui.screen.ClickGUI; +import me.retucio.sputnik.ui.widgets.frames.SettingsFrame; +import net.minecraft.command.CommandSource; +import me.retucio.sputnik.module.Module; + +public class ConfigCommand extends Command { + + public ConfigCommand() { + super("config", "abre el marco de ajustes de un módulo", "ajustes", "settings"); + } + + @Override + public void build(LiteralArgumentBuilder builder) { + builder + .then(argument("módulo", ModuleArgumentType.INSTANCE) + .executes(ctx -> { + mc.execute(() -> openSf(ctx)); + return SUCCESS; + }) + .then(argument("x", IntegerArgumentType.integer()) + .then(argument("y", IntegerArgumentType.integer()) + .executes(ctx -> { + mc.execute(() -> openSfWithPos(ctx, + ctx.getArgument("x", Integer.class), + ctx.getArgument("y", Integer.class))); + return SUCCESS; + }) + ) + ) + ); + } + + + private void openSf(CommandContext ctx) { + Module module = ctx.getArgument("módulo", Module.class); + mc.setScreen(ClickGUI.INSTANCE); + + if (ClickGUI.INSTANCE.isSettingsFrameOpen(module)) return; + + SettingsFrame sf = ClickGUI.INSTANCE.getSfOfModule(module); + int x = mc.getWindow().getScaledWidth() / 2 - sf.getW() / 2; + int y = 67; + + ClickGUI.INSTANCE.openSettingsFrame(module, x, y); + } + + private void openSfWithPos(CommandContext ctx, int x, int y) { + Module module = ctx.getArgument("módulo", Module.class); + mc.setScreen(ClickGUI.INSTANCE); + + if (ClickGUI.INSTANCE.isSettingsFrameOpen(module)) { + SettingsFrame sf = ClickGUI.INSTANCE.getSfOfModule(module); + sf.setX(x); + sf.setY(y); + } + + ClickGUI.INSTANCE.openSettingsFrame(module, x, y); + } +} diff --git a/src/main/java/me/retucio/sputnik/config/ConfigManager.java b/src/main/java/me/retucio/sputnik/config/ConfigManager.java index 8580546..f61a1ab 100644 --- a/src/main/java/me/retucio/sputnik/config/ConfigManager.java +++ b/src/main/java/me/retucio/sputnik/config/ConfigManager.java @@ -189,8 +189,15 @@ public class ConfigManager { private static void applySettingsFrames() { config.settingsFrames.forEach((moduleName, position) -> { Module module = ModuleManager.INSTANCE.getModuleByName(moduleName); + + if (module == null) { + Sputnik.LOGGER.warn("\"null\" como módulo (\"{}\")", moduleName); + return; + } + ClickGUI.INSTANCE.openSettingsFrame(module, position[0], position[1]); }); + Sputnik.LOGGER.info("estados de marcos de ajustes aplicados"); } diff --git a/src/main/java/me/retucio/sputnik/mixin/BossBarHudMixin.java b/src/main/java/me/retucio/sputnik/mixin/BossBarHudMixin.java index 9eb94b7..4781b97 100644 --- a/src/main/java/me/retucio/sputnik/mixin/BossBarHudMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/BossBarHudMixin.java @@ -15,7 +15,8 @@ import java.util.Iterator; import static me.retucio.sputnik.Sputnik.EVENT_BUS; -@Mixin(BossBarHud.class) + +@Mixin(value = BossBarHud.class) public abstract class BossBarHudMixin { @ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Ljava/util/Collection;iterator()Ljava/util/Iterator;")) @@ -30,7 +31,8 @@ public abstract class BossBarHudMixin { return event.getName(); } - @ModifyConstant(method = "render", constant = @Constant(intValue = 9, ordinal = 1)) + // require = 0 para meteor + @ModifyConstant(method = "render", constant = @Constant(intValue = 9, ordinal = 1), require = 0) public int modifySpacingConstant(int constant) { RenderBossbarEvent.BossSpacing event = EVENT_BUS.post(new RenderBossbarEvent.BossSpacing(constant)); return event.getSpacing(); diff --git a/src/main/java/me/retucio/sputnik/mixin/ClientPlayerInteractionManagerMixin.java b/src/main/java/me/retucio/sputnik/mixin/ClientPlayerInteractionManagerMixin.java index 03d38c6..ed02805 100644 --- a/src/main/java/me/retucio/sputnik/mixin/ClientPlayerInteractionManagerMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/ClientPlayerInteractionManagerMixin.java @@ -6,6 +6,7 @@ import me.retucio.sputnik.event.events.InteractEntityEvent; import me.retucio.sputnik.event.events.PlaceBlockEvent; import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.modules.camera.Freecam; +import me.retucio.sputnik.module.modules.player.FastUse; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayerEntity; @@ -16,6 +17,8 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -31,8 +34,8 @@ import static me.retucio.sputnik.Sputnik.mc; @Mixin(ClientPlayerInteractionManager.class) public abstract class ClientPlayerInteractionManagerMixin { - @Shadow @Final - private MinecraftClient client; + @Shadow + private int blockBreakingCooldown; @Unique Freecam freecam; @@ -69,4 +72,13 @@ public abstract class ClientPlayerInteractionManagerMixin { AttackEntityEvent event = EVENT_BUS.post(new AttackEntityEvent(target)); if (event.isCancelled()) ci.cancel(); } + + + + @Inject(method = "updateBlockBreakingProgress", at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;blockBreakingCooldown:I")) + private void modifyBlockBreakingCooldown(BlockPos pos, Direction direction, CallbackInfoReturnable cir) { + FastUse fastUse = ModuleManager.INSTANCE.getModuleByClass(FastUse.class); + if (!fastUse.isEnabled() || !fastUse.mining.isEnabled()) return; + blockBreakingCooldown = fastUse.miningCooldown.getIntValue(); + } } diff --git a/src/main/java/me/retucio/sputnik/mixin/FontManagerMixin.java b/src/main/java/me/retucio/sputnik/mixin/FontManagerMixin.java new file mode 100644 index 0000000..5a329c6 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/mixin/FontManagerMixin.java @@ -0,0 +1,38 @@ +package me.retucio.sputnik.mixin; + +import me.retucio.sputnik.Sputnik; + +import me.retucio.sputnik.module.ModuleManager; +import me.retucio.sputnik.module.modules.render.Fonts; +import net.minecraft.client.font.FontManager; +import net.minecraft.client.font.FontStorage; +import net.minecraft.text.StyleSpriteSource; +import net.minecraft.util.Identifier; + +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.CallbackInfoReturnable; + + +@Mixin(FontManager.class) +public abstract class FontManagerMixin { + + @Shadow + public abstract FontStorage getStorageInternal(Identifier id); + + @Inject(method = "getStorageInternal", at = @At("HEAD"), cancellable = true) + private void onGetStorageInternal(Identifier id, CallbackInfoReturnable cir) { + Fonts fonts = ModuleManager.INSTANCE.getModuleByClass(Fonts.class); + if (!fonts.isEnabled()) return; + + if (id.equals(StyleSpriteSource.Font.DEFAULT.id())) { + cir.setReturnValue(getStorageInternal( + Identifier.of( + Sputnik.MOD_ID, + fonts.font.getValue()) + )); + } + } +} diff --git a/src/main/java/me/retucio/sputnik/mixin/LivingEntityMixin.java b/src/main/java/me/retucio/sputnik/mixin/LivingEntityMixin.java index d7478ed..af0585b 100644 --- a/src/main/java/me/retucio/sputnik/mixin/LivingEntityMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/LivingEntityMixin.java @@ -2,6 +2,7 @@ package me.retucio.sputnik.mixin; import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.modules.player.ElytraBounce; +import me.retucio.sputnik.module.modules.player.Step; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.entity.LivingEntity; import net.minecraft.sound.SoundCategory; @@ -12,6 +13,7 @@ import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import static me.retucio.sputnik.Sputnik.mc; @@ -26,7 +28,7 @@ public abstract class LivingEntityMixin { @SuppressWarnings("ConstantConditions") @Inject(method = "tickMovement", at = @At("TAIL")) - public void recastIfLanded(CallbackInfo ci) { + private void recastIfLanded(CallbackInfo ci) { ElytraBounce bounce = ModuleManager.INSTANCE.getModuleByClass(ElytraBounce.class); if (!((Object) this instanceof ClientPlayerEntity) @@ -47,4 +49,11 @@ public abstract class LivingEntityMixin { prevElytra = elytra; } + + + @Inject(method = "getStepHeight", at = @At("RETURN"), cancellable = true) + private void step(CallbackInfoReturnable cir) { + Step step = ModuleManager.INSTANCE.getModuleByClass(Step.class); + if (step.isEnabled()) cir.setReturnValue(step.height.getFloatValue()); + } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/mixin/MinecraftClientMixin.java b/src/main/java/me/retucio/sputnik/mixin/MinecraftClientMixin.java index 88bdb80..bb0f71c 100644 --- a/src/main/java/me/retucio/sputnik/mixin/MinecraftClientMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/MinecraftClientMixin.java @@ -14,9 +14,9 @@ import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.world.ClientWorld; import net.minecraft.item.ItemStack; import net.minecraft.util.Hand; +import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.*; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.*; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -27,6 +27,12 @@ public abstract class MinecraftClientMixin { @Shadow private int itemUseCooldown; + @Shadow + public int attackCooldown; + + + // eventos + @Inject(method = "tick", at = @At("HEAD")) private void onTickPre(CallbackInfo ci) { Sputnik.INSTANCE.onTick(); @@ -49,9 +55,10 @@ public abstract class MinecraftClientMixin { if (event.isCancelled()) ci.cancel(); } - @Inject(method = "isTelemetryEnabledByApi", at = @At("RETURN"), cancellable = true) - private void disableMicropenisTelemetryShi(CallbackInfoReturnable cir) { - cir.setReturnValue(false); + @Inject(method = "joinWorld", at = @At("HEAD"), cancellable = true) + private void onJoinWorld(ClientWorld world, CallbackInfo ci) { + JoinWorldEvent event = Sputnik.EVENT_BUS.post(new JoinWorldEvent(world)); + if (event.isCancelled()) ci.cancel(); } @Inject(method = "doItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getStackInHand(Lnet/minecraft/util/Hand;)Lnet/minecraft/item/ItemStack;"), cancellable = true) @@ -61,16 +68,21 @@ public abstract class MinecraftClientMixin { if (event.isCancelled()) ci.cancel(); } + + // telemetría + + @Inject(method = "isTelemetryEnabledByApi", at = @At("RETURN"), cancellable = true) + private void disableMicropenisTelemetryShi(CallbackInfoReturnable cir) { + cir.setReturnValue(false); + } + + + // precoz + @Inject(method = "doItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isItemEnabled(Lnet/minecraft/resource/featuretoggle/FeatureSet;)Z")) private void modifyItemUseCooldown(CallbackInfo ci, @Local ItemStack stack) { FastUse fastUse = ModuleManager.INSTANCE.getModuleByClass(FastUse.class); if (!fastUse.isEnabled()) return; itemUseCooldown = fastUse.getCooldown(stack); } - - @Inject(method = "joinWorld", at = @At("HEAD"), cancellable = true) - private void onJoinWorld(ClientWorld world, CallbackInfo ci) { - JoinWorldEvent event = Sputnik.EVENT_BUS.post(new JoinWorldEvent(world)); - if (event.isCancelled()) ci.cancel(); - } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/mixin/WorldRendererMixin.java b/src/main/java/me/retucio/sputnik/mixin/WorldRendererMixin.java index 7a3f4aa..d781cec 100644 --- a/src/main/java/me/retucio/sputnik/mixin/WorldRendererMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/WorldRendererMixin.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; import com.mojang.blaze3d.buffers.GpuBufferSlice; import me.retucio.sputnik.Sputnik; @@ -18,13 +19,12 @@ import net.minecraft.util.profiler.Profiler; import org.joml.Matrix4f; import org.joml.Vector4f; 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.ModifyArg; -import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.*; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.lang.annotation.Target; + import static me.retucio.sputnik.Sputnik.EVENT_BUS; import static me.retucio.sputnik.Sputnik.mc; @@ -66,11 +66,18 @@ public abstract class WorldRendererMixin { if (!noRender.blindnessEffect.isEnabled() || !noRender.darknessEffect.isEnabled()) cir.setReturnValue(null); } - @Redirect(method = "renderTargetBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/ColorHelper;toAlpha(I)I")) - private int modifyBlockOutlineColor(int alpha) { - BlockOutline outline = ModuleManager.INSTANCE.getModuleByClass(BlockOutline.class); - if (!outline.isEnabled()) return ColorHelper.toAlpha(alpha); + @ModifyArg(method = "renderTargetBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;drawBlockOutline(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;DDDLnet/minecraft/client/render/state/OutlineRenderState;IF)V")) + private int modifyBlockOutlineColor(int original) { + BlockOutline outline = ModuleManager.INSTANCE.getModuleByClass(BlockOutline.class); + if (!outline.isEnabled()) return original; return outline.color.getRGB(); } + + @ModifyArg(method = "renderTargetBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;drawBlockOutline(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;DDDLnet/minecraft/client/render/state/OutlineRenderState;IF)V")) + private float modifyBlockOutlineLineWidth(float original) { + BlockOutline outline = ModuleManager.INSTANCE.getModuleByClass(BlockOutline.class); + if (!outline.isEnabled()) return original; + return outline.lineWidth.getFloatValue(); + } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/module/ModuleManager.java b/src/main/java/me/retucio/sputnik/module/ModuleManager.java index 38bd586..97624f3 100644 --- a/src/main/java/me/retucio/sputnik/module/ModuleManager.java +++ b/src/main/java/me/retucio/sputnik/module/ModuleManager.java @@ -10,6 +10,7 @@ import me.retucio.sputnik.module.modules.player.*; import me.retucio.sputnik.module.modules.render.*; import me.retucio.sputnik.module.modules.render.CritsPlus; import me.retucio.sputnik.module.modules.world.*; +import me.retucio.sputnik.util.MiscUtil; import java.lang.reflect.Method; import java.util.ArrayList; @@ -47,6 +48,7 @@ public class ModuleManager { modules.add(new ElytraBounce()); modules.add(new FakePlayer()); modules.add(new FastUse()); + modules.add(new Fonts()); modules.add(new Freecam()); modules.add(new Freelook()); modules.add(new Fullbright()); @@ -62,15 +64,18 @@ public class ModuleManager { modules.add(new NoRender()); modules.add(new Offhand()); modules.add(new PacketDelay()); + modules.add(new Particles()); modules.add(new PerspectivePlus()); modules.add(new PortalGUI()); modules.add(new Racist()); modules.add(new Reconnect()); + modules.add(new ReverseStep()); modules.add(new Rotations()); modules.add(new RPackBypass()); modules.add(new SafeWalk()); modules.add(new ScreenshotPlus()); modules.add(new ShulkerPeek()); + modules.add(new Step()); modules.add(new TimeChanger()); modules.add(new UIMove()); modules.add(new WarnLowDurability()); @@ -105,7 +110,8 @@ public class ModuleManager { public Module getModuleByName(String name) { for (Module module : modules) - if (module.getName().equalsIgnoreCase(name)) return module; + if (MiscUtil.removeAccentMarks(module.getName()).equalsIgnoreCase(name)) + return module; return null; } diff --git a/src/main/java/me/retucio/sputnik/module/modules/misc/BossbarStack.java b/src/main/java/me/retucio/sputnik/module/modules/misc/BossbarStack.java index bf2bb64..4525b75 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/misc/BossbarStack.java +++ b/src/main/java/me/retucio/sputnik/module/modules/misc/BossbarStack.java @@ -4,21 +4,41 @@ import me.retucio.sputnik.event.events.RenderBossbarEvent; import me.retucio.sputnik.event.SubscribeEvent; 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.ColorSetting; +import me.retucio.sputnik.module.setting.settings.EnumSetting; import me.retucio.sputnik.module.setting.settings.NumberSetting; import net.minecraft.client.gui.hud.ClientBossBar; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import java.awt.*; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; public class BossbarStack extends Module { - public BooleanSetting stackBars = sgGeneral.add(new BooleanSetting("apilar barras", "apilar las bossbars", true)); - public BooleanSetting hideNames = sgGeneral.add(new BooleanSetting("esconder nombres", "no renderiza el nombre de los bosses", false)); - public NumberSetting spaceReduction = sgGeneral.add(new NumberSetting("reducción de espacio", "cuánto reducir el espacio entre bossbars", - 0, 0, 10, 0.1)); + public BooleanSetting stackBars = sgGeneral.add(new BooleanSetting( + "apilar barras", "apilar las bossbars", + true + )); + + public BooleanSetting hideNames = sgGeneral.add(new BooleanSetting( + "esconder nombres", "no renderiza el nombre de los bosses", + false + )); + + public NumberSetting spaceReduction = sgGeneral.add(new NumberSetting( + "reducción de espacio", + "cuánto reducir el espacio entre bossbars", + 0, + 0, + 10, + 0.1 + )); + private final Map bossBarMap = new WeakHashMap<>(); diff --git a/src/main/java/me/retucio/sputnik/module/modules/player/FastUse.java b/src/main/java/me/retucio/sputnik/module/modules/player/FastUse.java index 9eff3b5..437f78f 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/player/FastUse.java +++ b/src/main/java/me/retucio/sputnik/module/modules/player/FastUse.java @@ -6,19 +6,60 @@ import me.retucio.sputnik.module.setting.settings.BooleanSetting; import me.retucio.sputnik.module.setting.settings.NumberSetting; import net.minecraft.item.BlockItem; import net.minecraft.item.ItemStack; +import org.lwjgl.glfw.GLFW; public class FastUse extends Module { - public BooleanSetting items = sgGeneral.add(new BooleanSetting("items", "usar items precozmente", true)); - public NumberSetting itemCooldown = sgGeneral.add(new NumberSetting("cooldown de items", "cooldown para usar items en ticks", 0, 0, 4, 1)); + public BooleanSetting items = sgGeneral.add(new BooleanSetting( + "items", + "usar items precozmente", + true + )); + + public NumberSetting itemCooldown = sgGeneral.add(new NumberSetting( + "cooldown de items", + "cooldown para usar items en ticks", + 0, + 0, + 4, + 1 + )); + + + public BooleanSetting blocks = sgGeneral.add(new BooleanSetting( + "bloques", + "colocar bloques precozmente", + true + )); + + public NumberSetting blockCooldown = sgGeneral.add(new NumberSetting( + "cooldown de bloques", + "cooldown para colocar bloques en ticks", + 0, + 0, + 4, + 1 + )); + + + public BooleanSetting mining = sgGeneral.add(new BooleanSetting( + "minar", + "minar precozmente", + true + )); + + public NumberSetting miningCooldown = sgGeneral.add(new NumberSetting( + "cooldown al minar", + "cooldown entre rotura de bloques en ticks", + 5, 0, 20, 1 + )); - public BooleanSetting blocks = sgGeneral.add(new BooleanSetting("bloques", "colocar bloques precozmente", true)); - public NumberSetting blockCooldown = sgGeneral.add(new NumberSetting("cooldown de bloques", "cooldown para colocar bloques en ticks", 0, 0, 4, 1)); public FastUse() { super("precoz", "hace cosas como usar items o colocar bloques más rápido", - Category.PLAYER); + Category.PLAYER, + GLFW.GLFW_KEY_F6); items.onUpdate(v -> itemCooldown.setVisible(v)); blocks.onUpdate(v -> blockCooldown.setVisible(v)); diff --git a/src/main/java/me/retucio/sputnik/module/modules/player/ReverseStep.java b/src/main/java/me/retucio/sputnik/module/modules/player/ReverseStep.java new file mode 100644 index 0000000..4b17046 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/player/ReverseStep.java @@ -0,0 +1,85 @@ +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.settings.BooleanSetting; +import me.retucio.sputnik.module.setting.settings.NumberSetting; +import me.retucio.sputnik.util.ChatUtil; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.RaycastContext; + +public class ReverseStep extends Module { + + public NumberSetting height = sgGeneral.add(new NumberSetting( + "altura", "altura máxima a bajar", + 1, 0, 20, 0.1 + )); + + public NumberSetting velocity = sgGeneral.add(new NumberSetting( + "velocidad", "velocidad a la que caer en bps", + 1, 0, 10, 0.1 + )); + + public BooleanSetting jumping = sgGeneral.add(new BooleanSetting( + "saltar", "permitir saltar", + true + )); + + public BooleanSetting disableInWater = sgGeneral.add(new BooleanSetting( + "desactivar en agua", ".", + true + )); + + public ReverseStep() { + super("escalones inversos", + "escalones pero pabajo", + Category.PLAYER + ); + } + + @Override + public void onTick() { + if (mc.player == null || mc.world == null) return; + + if (!mc.player.isOnGround()) { + if (jumping.isEnabled()) return; + } + + if ((mc.player.isTouchingWater() || mc.player.isInLava())) { + if (disableInWater.isEnabled()) return; + } + + double dropHeight = getHeight(); + if (dropHeight > 0 && dropHeight <= height.getValue()) { + mc.player.addVelocity(0, -height.getValue() + mc.player.getVelocity().getY(), 0); + } + } + + private double getHeight() { + Vec3d start = new Vec3d(mc.player.getX(), mc.player.getY() - 0.1, mc.player.getZ()); + Vec3d end = new Vec3d(mc.player.getX(), mc.player.getY() - height.getValue() - 1, mc.player.getZ()); + + RaycastContext context = new RaycastContext( + start, end, + RaycastContext.ShapeType.COLLIDER, + RaycastContext.FluidHandling.ANY, + mc.player + ); + + HitResult hit = mc.world.raycast(context); + + if (hit != null && hit.getType() == HitResult.Type.BLOCK) { + BlockPos hitPos = new BlockPos( + (int) hit.getPos().x, + (int) hit.getPos().y, + (int) hit.getPos().z + ); + + return mc.player.getY() - (hitPos.getY()); + } + + return -1; + } +} diff --git a/src/main/java/me/retucio/sputnik/module/modules/player/Step.java b/src/main/java/me/retucio/sputnik/module/modules/player/Step.java new file mode 100644 index 0000000..6361ab3 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/player/Step.java @@ -0,0 +1,17 @@ +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.settings.NumberSetting; + +public class Step extends Module { + + public NumberSetting height = sgGeneral.add(new NumberSetting( + "altura", "altura máxima a subir", + 1, 0, 3, 0.1 + )); + + public Step() { + super("escalones", "hace que el bloque de subida de escalón sea la distancia deseada", Category.PLAYER); + } +} diff --git a/src/main/java/me/retucio/sputnik/module/modules/render/BlockESP.java b/src/main/java/me/retucio/sputnik/module/modules/render/BlockESP.java index 352f103..d560162 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/render/BlockESP.java +++ b/src/main/java/me/retucio/sputnik/module/modules/render/BlockESP.java @@ -14,30 +14,96 @@ import me.retucio.sputnik.util.Lists; import me.retucio.sputnik.util.render.RenderUtil; import net.minecraft.block.Block; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3i; +import net.minecraft.util.math.Vec3d; import net.minecraft.util.shape.VoxelShape; import java.awt.*; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class BlockESP extends Module { + SettingGroup sgSearch = addSg(new SettingGroup("búsqueda", true)); SettingGroup sgOulines = addSg(new SettingGroup("contorno", true)); SettingGroup sgFilling = addSg(new SettingGroup("relleno", true)); - public ListSetting blocks = sgGeneral.add(new ListSetting<>("bloques", "bloques a resaltar", - Lists.blockList, Lists.allFalse(Lists.blockList), Lists.blockNames)); - public NumberSetting radius = sgGeneral.add(new NumberSetting("radio", "radio de búsqueda de bloques", - 24, 2, 128, 1)); - public BooleanSetting outlines = sgOulines.add(new BooleanSetting("contorno", "renderizar contorno de bloques", true)); - public ColorSetting outlineColor = sgOulines.add(new ColorSetting("color del contorno", "color del contorno", Colors.CELESTE, false)); - public NumberSetting lineWidth = sgOulines.add(new NumberSetting("grosor del contorno", "grosor de las líneas del contorno", 1, 1, 5, 0.2)); + // bloques - public BooleanSetting fillings = sgFilling.add(new BooleanSetting("relleno", "renderizar relleno de bloques", true)); - public ColorSetting fillingColor = sgFilling.add(new ColorSetting("color del relleno", "color del relleno", new Color(57, 177, 215, 67), false)); + public ListSetting blocks = sgGeneral.add(new ListSetting<>( + "bloques", "bloques a resaltar", + Lists.blockList, + Lists.allFalse(Lists.blockList), + Lists.blockNames + )); + + public NumberSetting maxBlocks = sgGeneral.add(new NumberSetting( + "máx. bloques", "número máximo de bloques a resaltar", + 500, 1, 2000, 1 + )); + + + // búsqueda + + public NumberSetting radius = sgSearch.add(new NumberSetting( + "radio", "radio de búsqueda de bloques", + 24, 2, 128, 1 + )); + + public NumberSetting searchInterval = sgSearch.add(new NumberSetting( + "intervalo", "intervalo de búsqueda en ticks", + 10, 1, 40, 1 + )); + + public BooleanSetting asyncSearch = sgSearch.add(new BooleanSetting( + "búsqueda asíncrona", "busca los bloques en un hilo separado (optimización)", + true + )); + + public NumberSetting distanceToMove = sgSearch.add(new NumberSetting( + "distancia a moverse", "distancia a moverse antes de actualizar los bloques", + 3, 0, 10, 0.1 + )); + + + // contorno + + public BooleanSetting outlines = sgOulines.add(new BooleanSetting( + "contorno", "renderizar contorno de bloques", + true + )); + + public ColorSetting outlineColor = sgOulines.add(new ColorSetting( + "color del contorno", "color del contorno", + Colors.CELESTE, false + )); + + public NumberSetting lineWidth = sgOulines.add(new NumberSetting( + "grosor del contorno", "grosor de las líneas del contorno", + 1, 1, 5, 0.2 + )); + + + // relleno + + public BooleanSetting fillings = sgFilling.add(new BooleanSetting( + "relleno", "renderizar relleno de bloques", + true + )); + + public ColorSetting fillingColor = sgFilling.add(new ColorSetting( + "color del relleno", "color del relleno", + new Color(57, 177, 215, 67), false + )); + + // caché + private final Map cachedBlocks = new ConcurrentHashMap<>(); + private final Set enabledBlocks = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private BlockPos lastPlayerPos = null; + private int lastRadius = -1; + private int searchTimer = 0; + private boolean isSearching = false; public BlockESP() { @@ -45,61 +111,166 @@ public class BlockESP extends Module { "te muestra la posición de cierto tipo de bloque renderizando una caja con su forma", Category.RENDER); + blocks.onUpdate(v -> + updateEnabledBlocks() + ); + outlines.onUpdate(v -> { outlineColor.setVisible(v); lineWidth.setVisible(v); }); - fillings.onUpdate(v -> fillingColor.setVisible(v)); + fillings.onUpdate(v -> + fillingColor.setVisible(v) + ); + } + + @Override + public void onEnable() { + cachedBlocks.clear(); + enabledBlocks.clear(); + updateEnabledBlocks(); + searchTimer = 0; + + super.onEnable(); + } + + @Override + public void onDisable() { + cachedBlocks.clear(); + enabledBlocks.clear(); + + super.onDisable(); } @SubscribeEvent public void onRenderWorld(Render3DEvent event) { if (mc.player == null || mc.world == null) return; - for (Block block : blocks.getOptions()) { - if (blocks.isEnabled(block)) { - List blocksToHighlight = getNearbyBlocks(block); - for (BlockPos pos : blocksToHighlight) { - if (outlines.isEnabled()) { - RenderUtil.drawVoxelShapeOutline( - event.getMatrices(), - getVoxelShape(pos), - pos, - outlineColor.getColor(), - lineWidth.getFloatValue(), - false); - } - if (fillings.isEnabled()) { - RenderUtil.drawVoxelShapeFilled( - event.getMatrices(), - getVoxelShape(pos), - pos, - fillingColor.getColor(), - false); + // aplicar el intervalo + if (searchTimer++ >= searchInterval.getIntValue()) { + searchTimer = 0; + searchBlocks(); + } + + Vec3d cameraPos = mc.gameRenderer.getCamera().getCameraPos(); + + int rendered = 0; + Iterator> iterator = cachedBlocks.entrySet().iterator(); + + while (iterator.hasNext() && rendered < maxBlocks.getIntValue()) { + Map.Entry entry = iterator.next(); + BlockPos pos = entry.getKey(); + VoxelShape shape = entry.getValue(); + + if (!isValidBlock(pos)) { + iterator.remove(); + continue; + } + + // validación de distancia + double distance = Math.sqrt(pos.getSquaredDistance(cameraPos)); + if (distance > radius.getIntValue()) { + iterator.remove(); + continue; + } + + // renderizado + if (outlines.isEnabled()) { + Color color = outlineColor.getColor(); + RenderUtil.drawVoxelShapeOutline( + event.getMatrices(), + shape, + pos, + color, + lineWidth.getFloatValue(), + false); + } + + if (fillings.isEnabled()) { + Color color = fillingColor.getColor(); + RenderUtil.drawVoxelShapeFilled( + event.getMatrices(), + shape, + pos, + color, + false); + } + + rendered++; + } + } + + private void updateEnabledBlocks() { + enabledBlocks.clear(); + enabledBlocks.addAll(blocks.getEnabledOptions()); + } + + private void searchBlocks() { + if (mc.player == null || mc.world == null || enabledBlocks.isEmpty()) return; + + BlockPos playerPos = mc.player.getBlockPos(); + int currentRadius = radius.getIntValue(); + + // si el jugador no se ha movido mucho, omitir la búsqueda + if (lastPlayerPos != null && lastRadius == currentRadius && + playerPos.getSquaredDistance(lastPlayerPos) < (distanceToMove.getValue() * distanceToMove.getIntValue())) { + return; + } + + lastPlayerPos = playerPos; + lastRadius = currentRadius; + + // multithreading + if (asyncSearch.isEnabled()) { + if (isSearching) return; + + isSearching = true; + new Thread(() -> { + try { + performBlockSearch(playerPos, currentRadius); + } finally { + isSearching = false; + } + }, "sputnik-blockESP-search").start(); + } else { + performBlockSearch(playerPos, currentRadius); + } + } + + // buscar los bloques + private void performBlockSearch(BlockPos center, int radius) { + Map newBlocks = new HashMap<>(); + int radiusSq = radius * radius; + + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + for (int z = -radius; z <= radius; z++) { + if (x*x + y*y + z*z > radiusSq) continue; + + BlockPos pos = center.add(x, y, z); + Block block = mc.world.getBlockState(pos).getBlock(); + + if (enabledBlocks.contains(block)) { + VoxelShape shape = mc.world.getBlockState(pos).getOutlineShape(mc.world, pos); + if (!shape.isEmpty()) { + newBlocks.put(pos.toImmutable(), shape); + } } } } } + + // actualizar caché + cachedBlocks.keySet().retainAll(newBlocks.keySet()); + cachedBlocks.putAll(newBlocks); } - private List getNearbyBlocks(Block block) { - List blockPosList = new ArrayList<>(); - - for (int i = -radius.getIntValue(); i < radius.getIntValue(); i++) { - for (int j = -radius.getIntValue(); j < radius.getIntValue(); j++) { - for (int k = -radius.getIntValue(); k < radius.getIntValue(); k++) { - BlockPos pos = mc.player.getBlockPos().add(new Vec3i(i, j, k)); - if (mc.world.getBlockState(pos).getBlock() == block) { - blockPosList.add(pos); - } - } - } - } - return blockPosList; - } - - private VoxelShape getVoxelShape(BlockPos pos) { - return mc.world.getBlockState(pos).getOutlineShape(mc.world, pos); + // verificar que el bloque sea válido + @SuppressWarnings("deprecation") + private boolean isValidBlock(BlockPos pos) { + if (mc.world == null || !mc.world.isChunkLoaded(pos)) return false; + Block block = mc.world.getBlockState(pos).getBlock(); + return enabledBlocks.contains(block); } } diff --git a/src/main/java/me/retucio/sputnik/module/modules/render/BlockOutline.java b/src/main/java/me/retucio/sputnik/module/modules/render/BlockOutline.java index e927e9c..91f8d76 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/render/BlockOutline.java +++ b/src/main/java/me/retucio/sputnik/module/modules/render/BlockOutline.java @@ -3,6 +3,8 @@ package me.retucio.sputnik.module.modules.render; import me.retucio.sputnik.module.Category; import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.setting.settings.ColorSetting; +import me.retucio.sputnik.module.setting.settings.NumberSetting; +import me.retucio.sputnik.util.Colors; import java.awt.*; @@ -12,8 +14,21 @@ import java.awt.*; public class BlockOutline extends Module { - public ColorSetting color = sgGeneral.add(new ColorSetting("color", "color del contorno de los bloques", - new Color(0, 0, 0, 102), true)); + public ColorSetting color = sgGeneral.add(new ColorSetting( + "color", + "color del contorno de los bloques", + Colors.mainColor, + true) + ); + + public NumberSetting lineWidth = sgGeneral.add(new NumberSetting( + "grosor", + "grosor de las líneas", + 0.5, + 0.1, + 10, + 0.1 + )); public BlockOutline() { super("contorno", diff --git a/src/main/java/me/retucio/sputnik/module/modules/render/CritsPlus.java b/src/main/java/me/retucio/sputnik/module/modules/render/CritsPlus.java index 0f6e547..80c67fb 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/render/CritsPlus.java +++ b/src/main/java/me/retucio/sputnik/module/modules/render/CritsPlus.java @@ -6,6 +6,7 @@ import me.retucio.sputnik.module.setting.SettingGroup; import me.retucio.sputnik.module.setting.settings.BooleanSetting; import me.retucio.sputnik.module.setting.settings.ColorSetting; import me.retucio.sputnik.module.setting.settings.NumberSetting; +import me.retucio.sputnik.util.Colors; import java.awt.*; @@ -19,7 +20,7 @@ public class CritsPlus extends Module { SettingGroup sgAppearance = addSg(new SettingGroup("aspecto", true)); SettingGroup sgBehaviour = addSg(new SettingGroup("funcionamiento", true)); - public ColorSetting color = sgAppearance.add(new ColorSetting("color", "color de las partículas", new Color(0, 0, 255, 255), false)); + public ColorSetting color = sgAppearance.add(new ColorSetting("color", "color de las partículas", Colors.mainColor, false)); public NumberSetting scale = sgAppearance.add(new NumberSetting("escala", "tamaño", 1, 0, 2, 0.05)); public NumberSetting multiplier = sgAppearance.add(new NumberSetting("múltiplo", "número por el que multiplicar la cantidad de partículas generadas", 1, 0, 10, 0.1)); diff --git a/src/main/java/me/retucio/sputnik/module/modules/render/Fonts.java b/src/main/java/me/retucio/sputnik/module/modules/render/Fonts.java new file mode 100644 index 0000000..4ff15f3 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/render/Fonts.java @@ -0,0 +1,45 @@ +package me.retucio.sputnik.module.modules.render; + +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.OptionSetting; +import me.retucio.sputnik.util.Lists; + + +public class Fonts extends Module { + + public OptionSetting font = sgGeneral.add(new OptionSetting<>( + "fuente", "fuente a emplear", + Lists.fontList, "ubuntu") + ); + + public BooleanSetting reload = sgGeneral.add(new BooleanSetting( + "recargar", "recargar automáticamente", + false + )); + + public Fonts() { + super("fuentes", + "modifica el tipo de letra utilizado al renderizar texto", + Category.RENDER); + + font.onUpdate(v -> reload()); + } + + @Override + public void onEnable() { + reload(); + } + + @Override + public void onDisable() { + reload(); + } + + private void reload() { + if (mc.getResourceManager() != null && reload.isEnabled()) { + mc.reloadResources(); + } + } +} diff --git a/src/main/java/me/retucio/sputnik/module/modules/render/GlintPlus.java b/src/main/java/me/retucio/sputnik/module/modules/render/GlintPlus.java index fb6a917..5294b28 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/render/GlintPlus.java +++ b/src/main/java/me/retucio/sputnik/module/modules/render/GlintPlus.java @@ -4,6 +4,7 @@ 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 me.retucio.sputnik.util.render.GlintRenderLayer; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayers; @@ -17,9 +18,24 @@ import net.minecraft.util.DyeColor; public class GlintPlus extends Module { - public BooleanSetting items = sgGeneral.add(new BooleanSetting("items", "modificar el destello de encantamiento de los items", true)); - public BooleanSetting armor = sgGeneral.add(new BooleanSetting("armadura", "modificar el destello de encantamiento de la armadura", true)); - public EnumSetting glintColor = sgGeneral.add(new EnumSetting<>("colores", "color del glint", GlintColors.class, GlintColors.PURPLE)); + public BooleanSetting items = sgGeneral.add(new BooleanSetting( + "items", + "modificar el destello de encantamiento de los items", + true + )); + + public BooleanSetting armor = sgGeneral.add(new BooleanSetting( + "armadura", + "modificar el destello de encantamiento de la armadura", + true + )); + + public EnumSetting glintColor = sgGeneral.add(new EnumSetting<>( + "colores", + "color del glint", + GlintColors.class, + GlintColors.PURPLE + )); public GlintPlus() { super("destello de enchants.", diff --git a/src/main/java/me/retucio/sputnik/module/modules/render/Particles.java b/src/main/java/me/retucio/sputnik/module/modules/render/Particles.java new file mode 100644 index 0000000..31bfa33 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/render/Particles.java @@ -0,0 +1,189 @@ +package me.retucio.sputnik.module.modules.render; + +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.ListSetting; +import me.retucio.sputnik.module.setting.settings.NumberSetting; +import me.retucio.sputnik.util.Lists; +import net.minecraft.particle.SimpleParticleType; +import net.minecraft.registry.Registries; +import net.minecraft.text.Text; +import net.minecraft.util.math.Vec3d; + +import java.util.List; +import java.util.Map; +import java.util.Random; + +public class Particles extends Module { + + SettingGroup sgDispersion = addSg(new SettingGroup("dispersión", true)); + SettingGroup sgOffset = addSg(new SettingGroup("desplazamiento", true)); + + List particleList = Lists.particleList.stream() + .filter(particleType -> particleType instanceof SimpleParticleType) + .map(particleType -> (SimpleParticleType) particleType) + .toList(); + + Map particleNames = Lists.getMapOfLists(particleList, + particleList.stream().map(particle -> Text.translatable( + Registries.PARTICLE_TYPE.getId(particle).toShortTranslationKey()).getString() + ).toList()); + + public ListSetting particles = sgGeneral.add(new ListSetting<>( + "partículas", + "partículas a generar", + particleList, + Lists.allFalse(particleList), + particleNames + )); + + public NumberSetting spawnInterval = sgGeneral.add(new NumberSetting( + "intervalo", + "intervalo entre generaciones (en ticks)", + 4, 1, 100, 1 + )); + + public NumberSetting particleCount = sgGeneral.add(new NumberSetting( + "cantidad", + "partículas por generación", + 10, 1, 100, 1 + )); + + + public NumberSetting velocity = sgDispersion.add(new NumberSetting( + "velocidad", + "multiplicador de la velocidad de dispersión", + 0.2, 0, 2, 0.05 + )); + + public NumberSetting spreadX = sgDispersion.add(new NumberSetting( + "dispersión X", + "dispersión horizontal de posición", + 0.5, 0, 5, 0.1 + )); + + public NumberSetting spreadY = sgDispersion.add(new NumberSetting( + "dispersión Y", + "dispersión vertical de posición", + 0.5, 0, 5, 0.1 + )); + + public NumberSetting spreadZ = sgDispersion.add(new NumberSetting( + "dispersión Z", + "dispersión de profundidad de posición", + 0.5, 0, 5, 0.1 + )); + + public NumberSetting velocityRandomness = sgDispersion.add(new NumberSetting( + "aleatoriedad velocidad", + "variación aleatoria en la velocidad", + 0.3, 0, 1, 0.05 + )); + + public NumberSetting positionRandomness = sgDispersion.add(new NumberSetting( + "aleatoriedad posición", + "variación aleatoria en la posición", + 0.2, 0, 1, 0.05 + )); + + + public NumberSetting offsetX = sgOffset.add(new NumberSetting( + "desplazamiento pos. X", + "desplazamiento en el eje X respecto al jugador", + 0, -1, 1, 0.01 + )); + + public NumberSetting offsetY = sgOffset.add(new NumberSetting( + "desplazamiento pos. Y", + "desplazamiento en el eje Y respecto al jugador", + 0, -1, 1, 0.01 + )); + + public NumberSetting offsetZ = sgOffset.add(new NumberSetting( + "desplazamiento pos. Z", + "desplazamiento en el eje Z respecto al jugador", + -0.8, -1, 1, 0.01 + )); + + + private final Random random = new Random(); + private int tickCounter = 0; + + + public Particles() { + super("partículas", "emite particulas artificiales", Category.RENDER); + } + + + @Override + public void onEnable() { + tickCounter = 0; + super.onEnable(); + } + + @Override + public void onTick() { + if (mc.player == null || mc.world == null) return; + + tickCounter++; + if (tickCounter < spawnInterval.getIntValue()) { + return; + } + tickCounter = 0; + + List enabledParticles = particles.getEnabledOptions(); + if (enabledParticles.isEmpty()) return; + + Vec3d basePos = new Vec3d( + mc.player.getX(), + mc.player.getY() + mc.player.getEyeHeight(mc.player.getPose()), + mc.player.getZ() + ); + + for (int i = 0; i < particleCount.getIntValue(); i++) { + SimpleParticleType particle = enabledParticles.get( + random.nextInt(enabledParticles.size()) + ); + + Vec3d pos = getPos(basePos); + Vec3d vel = getVel(); + + double yaw = Math.toRadians(mc.player.getYaw()); + double offX = offsetX.getValue() * Math.cos(yaw) - offsetZ.getValue() * Math.sin(yaw); + double offZ = offsetX.getValue() * Math.sin(yaw) + offsetZ.getValue() * Math.cos(yaw); + + mc.world.addParticleClient( + particle, + pos.x + offX, + pos.y + offsetY.getValue(), + pos.z + offZ, + vel.x, vel.y, vel.z + ); + } + } + + private Vec3d getPos(Vec3d basePos) { + double randX = (random.nextDouble() - 0.5) * 2 * positionRandomness.getValue(); + double randY = (random.nextDouble() - 0.5) * 2 * positionRandomness.getValue(); + double randZ = (random.nextDouble() - 0.5) * 2 * positionRandomness.getValue(); + + return new Vec3d( + basePos.x + (random.nextDouble() - 0.5) * spreadX.getValue() + randX, + basePos.y + (random.nextDouble() - 0.5) * spreadY.getValue() + randY, + basePos.z + (random.nextDouble() - 0.5) * spreadZ.getValue() + randZ + ); + } + + private Vec3d getVel() { + double randX = (random.nextDouble() - 0.5) * 2 * velocityRandomness.getValue(); + double randY = (random.nextDouble() - 0.5) * 2 * velocityRandomness.getValue(); + double randZ = (random.nextDouble() - 0.5) * 2 * velocityRandomness.getValue(); + + return new Vec3d( + (random.nextDouble() - 0.5) * velocity.getValue() + randX, + (random.nextDouble() - 0.5) * velocity.getValue() + randY, + (random.nextDouble() - 0.5) * velocity.getValue() + randZ + ); + } +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/module/modules/world/LightOverlay.java b/src/main/java/me/retucio/sputnik/module/modules/world/LightOverlay.java index c95f527..8419b02 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/world/LightOverlay.java +++ b/src/main/java/me/retucio/sputnik/module/modules/world/LightOverlay.java @@ -10,140 +10,350 @@ import me.retucio.sputnik.module.setting.settings.ColorSetting; import me.retucio.sputnik.module.setting.settings.NumberSetting; import me.retucio.sputnik.util.render.RenderUtil; import net.minecraft.block.*; -import net.minecraft.block.Blocks; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.Heightmap; import net.minecraft.world.LightType; import java.awt.*; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class LightOverlay extends Module { SettingGroup sgDetection = addSg(new SettingGroup("detección", true)); SettingGroup sgColors = addSg(new SettingGroup("colores", true)); + SettingGroup sgOptimization = addSg(new SettingGroup("optimización", true)); - public NumberSetting radius = sgDetection.add(new NumberSetting("radio", "radio a tener en cuenta al renderizar superposición", + // detección + public NumberSetting radius = sgDetection.add(new NumberSetting( + "radio", + "radio a tener en cuenta al renderizar superposición", 16, 5, 128, 1)); - public NumberSetting yRadius = sgDetection.add(new NumberSetting("rango vertical", "distancia vertical a tener en cuenta", + + public NumberSetting yRadius = sgDetection.add(new NumberSetting( + "rango vertical", + "distancia vertical a tener en cuenta", 4, 1, 16, 1)); - public NumberSetting interval = sgDetection.add(new NumberSetting("intervalo", "cada cuántos ticks comprobar el nivel de luz", + public BooleanSetting onWater = sgDetection.add(new BooleanSetting( + "en agua", + "mostrar también bloques cubiertos en agua", true)); + + public BooleanSetting dontCullWater = sgDetection.add(new BooleanSetting( + "evitar culling en agua", + "mostrar superposición a través de bloques para poder verla desde fuera del agua", false)); + + + // optimización, porque iba como la mierda + public NumberSetting updateInterval = sgOptimization.add(new NumberSetting( + "intervalo de búsqueda", + "cada cuántos ticks actualizar la búsqueda de bloques", 10, 1, 80, 1)); - public BooleanSetting onWater = sgDetection.add(new BooleanSetting("en agua", "mostrar también bloques cubiertos en agua", true)); - public BooleanSetting dontCullWater = sgDetection.add(new BooleanSetting("evitar culling en agua", "mostrar superposición a través de bloques para poder verla desde fuera del agua", false)); + public NumberSetting movementThreshold = sgOptimization.add(new NumberSetting( + "umbral de movimiento", + "distancia que debe moverse el jugador antes de forzar una actualización", + 3.0, 0.5, 10.0, 0.5)); - public ColorSetting lightColor = sgColors.add(new ColorSetting("color de la luz", "color de los bloques con luz", + public BooleanSetting asyncSearch = sgOptimization.add(new BooleanSetting( + "búsqueda asíncrona", + "buscar bloques en un hilo separado (reduce lag)", + true)); + + public BooleanSetting incrementalUpdates = sgOptimization.add(new BooleanSetting( + "actualizaciones incrementales", + "actualizar solo los bloques nuevos / eliminados en lugar de recalcular todo", + true)); + + // colores + public ColorSetting lightColor = sgColors.add(new ColorSetting( + "color de la luz", + "color de los bloques con luz", new Color(0, 255, 0, 67), false)); - public ColorSetting darknessColor = sgColors.add(new ColorSetting("color de oscuridad", "color de bloques con 0 luz", + + public ColorSetting darknessColor = sgColors.add(new ColorSetting( + "color de oscuridad", + "color de bloques con 0 luz", new Color(255, 0, 0, 67), false)); - List blocks = new ArrayList<>(); + // caché y estado + private final Set cachedBlocks = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final Set blocksToRender = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private BlockPos lastPlayerPos = null; + private int lastRadius = -1; + private int lastYRadius = -1; + private int updateTimer = 0; + private boolean isSearching = false; + + // para actualizaciones incrementales + private int incrementalLayer = 0; + private boolean fullRescanNeeded = false; public LightOverlay() { super("superposición de luz", "te muestra el nivel de luz en bloques, para prevenir mob spawns", Category.WORLD); + + onWater.onUpdate(v -> dontCullWater.setVisible(v)); + } + + @Override + public void onEnable() { + cachedBlocks.clear(); + blocksToRender.clear(); + lastPlayerPos = null; + lastRadius = -1; + lastYRadius = -1; + updateTimer = 0; + incrementalLayer = 0; + fullRescanNeeded = true; + + super.onEnable(); + } + + @Override + public void onDisable() { + cachedBlocks.clear(); + blocksToRender.clear(); + + super.onDisable(); } @SubscribeEvent + @SuppressWarnings("deprecation") public void onRenderWorld(Render3DEvent event) { if (mc.player == null || mc.world == null) return; - if (mc.player.age % interval.getIntValue() == 0) { - blocks = findExposedBlocks( - radius.getIntValue(), - yRadius.getIntValue() - ); + + // actualización periódica + if (updateTimer++ >= updateInterval.getIntValue()) { + updateTimer = 0; + updateBlocks(); } - for (BlockPos block : blocks) { - if (mc.world.getBlockState(block).isAir() || !canSpawnOn(block)) continue; - if (!mc.world.getBlockState(block).isOpaque()) continue; + // renderizado + Vec3d cameraPos = mc.gameRenderer.getCamera().getCameraPos(); + int radiusValue = radius.getIntValue(); + double radiusSq = radiusValue * radiusValue; + + // actualizar lista de renderizado basado en distancia + blocksToRender.clear(); + for (BlockPos pos : cachedBlocks) { + double distanceSq = pos.getSquaredDistance(cameraPos); + if (distanceSq <= radiusSq) { + blocksToRender.add(pos); + } + } + + // renderizar bloques visibles + for (BlockPos block : blocksToRender) { + if (!mc.world.isChunkLoaded(block)) continue; + + BlockState state = mc.world.getBlockState(block); + BlockState aboveState = mc.world.getBlockState(block.up()); + + if (!isValidSpawnSurface(block, state, aboveState)) continue; + + // nivel de luz int light = mc.world.getLightLevel(LightType.BLOCK, block.up()); - Color color = light == 0 ? darknessColor.getColor() - : new Color( - (lightColor.getR() / 15) * light, - (lightColor.getG() / 15) * light, - (lightColor.getB() / 15) * light, - lightColor.getA()); + // color + Color color; + if (light == 0) { + color = darknessColor.getColor(); + } else { + float ratio = light / 15.0f; + color = new Color( + (int)(lightColor.getR() * ratio), + (int)(lightColor.getG() * ratio), + (int)(lightColor.getB() * ratio), + lightColor.getA() + ); + } - boolean shouldCull = !(onWater.isEnabled() && mc.world.getBlockState(block.up()).isOf(Blocks.WATER) && dontCullWater.isEnabled()); + // renderizar + boolean shouldCull = !(onWater.isEnabled() && aboveState.isOf(Blocks.WATER) && dontCullWater.isEnabled()); RenderUtil.drawBlockFaceFilled(event.getMatrices(), block, Direction.UP, color, 0.001f, shouldCull); } } - private List findExposedBlocks(int radius, int yRadius) { - List results = new ArrayList<>(); + private void updateBlocks() { + if (mc.player == null || mc.world == null) return; - BlockPos pos = mc.player.getBlockPos(); + BlockPos playerPos = mc.player.getBlockPos(); + int currentRadius = radius.getIntValue(); + int currentYRadius = yRadius.getIntValue(); - int minX = pos.getX() - radius; - int maxX = pos.getX() + radius; - int minY = Math.max(mc.world.getDimension().minY(), pos.getY() - yRadius); - int maxY = Math.min(mc.world.getDimension().height() - 1, pos.getY() + yRadius); - int minZ = pos.getZ() - radius; - int maxZ = pos.getZ() + radius; + // verificar si hace falta actualizar + boolean needsUpdate = fullRescanNeeded || + lastRadius != currentRadius || + lastYRadius != currentYRadius || + (lastPlayerPos != null && + playerPos.getSquaredDistance(lastPlayerPos) > (movementThreshold.getValue() * movementThreshold.getValue())); - maxY = Math.min(maxY, mc.world.getDimension().height() - 2); + if (!needsUpdate && incrementalUpdates.isEnabled() && incrementalLayer >= 0) { + // actualización incremental de una sola capa + performIncrementalUpdate(playerPos, currentRadius, currentYRadius); + return; + } + + if (!needsUpdate) { + return; + } + + lastPlayerPos = playerPos; + lastRadius = currentRadius; + lastYRadius = currentYRadius; + fullRescanNeeded = false; + incrementalLayer = 0; + + // búsqueda + if (asyncSearch.isEnabled()) { + if (isSearching) return; + + isSearching = true; + new Thread(() -> { + try { + performFullBlockSearch(playerPos, currentRadius, currentYRadius); + } finally { + isSearching = false; + } + }, "sputnik-lightoverlay-search").start(); + } else { + performFullBlockSearch(playerPos, currentRadius, currentYRadius); + } + } + + @SuppressWarnings("deprecation") + private void performFullBlockSearch(BlockPos center, int radius, int yRadius) { + Set newBlocks = new HashSet<>(); + int radiusSq = radius * radius; + + int minY = Math.max(mc.world.getBottomY(), center.getY() - yRadius); + int maxY = Math.min(mc.world.getTopY(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, mc.player.getBlockPos()) - 1, center.getY() + yRadius); + + for (int x = -radius; x <= radius; x++) { + for (int z = -radius; z <= radius; z++) { + if (x*x + z*z > radiusSq) continue; - for (int x = minX; x <= maxX; x++) { - for (int z = minZ; z <= maxZ; z++) { for (int y = minY; y <= maxY; y++) { - BlockPos current = new BlockPos(x, y, z); + BlockPos pos = center.add(x, y - center.getY(), z); - if (!mc.world.isAir(current) && canSpawnOn(current)) - results.add(current); + // nuh uh si el chunk no está cargado + if (!mc.world.isChunkLoaded(pos)) continue; + + BlockState state = mc.world.getBlockState(pos); + BlockState aboveState = mc.world.getBlockState(pos.up()); + + if (isValidSpawnSurface(pos, state, aboveState)) { + newBlocks.add(pos.toImmutable()); + } } } } - return results; + // actualizar caché + cachedBlocks.clear(); + cachedBlocks.addAll(newBlocks); } - private boolean canSpawnOn(BlockPos pos) { - BlockState aboveState = mc.world.getBlockState(pos.up()); + @SuppressWarnings("deprecation") + private void performIncrementalUpdate(BlockPos center, int radius, int yRadius) { + if (incrementalLayer > radius * 2) { + incrementalLayer = -radius; + } + + int currentX = center.getX() - radius + incrementalLayer; + int radiusSq = radius * radius; + int minY = Math.max(mc.world.getBottomY(), center.getY() - yRadius); + int maxY = Math.min(mc.world.getTopY(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, mc.player.getBlockPos()) - 1, center.getY() + yRadius); + + // buscar en una columna específica (x fijo, variar z) + for (int z = -radius; z <= radius; z++) { + int xDist = incrementalLayer; + if (xDist*xDist + z*z > radiusSq) continue; + + for (int y = minY; y <= maxY; y++) { + BlockPos pos = new BlockPos(currentX, y, center.getZ() + z); + + if (!mc.world.isChunkLoaded(pos)) continue; + + BlockState state = mc.world.getBlockState(pos); + BlockState aboveState = mc.world.getBlockState(pos.up()); + + boolean isValid = isValidSpawnSurface(pos, state, aboveState); + + if (isValid) { + cachedBlocks.add(pos.toImmutable()); + } else { + cachedBlocks.remove(pos); + } + } + } + + incrementalLayer++; + + // cada 10 actualizaciones incrementales, hacer una limpieza de bloques lejanos + if (incrementalLayer % 10 == 0) { + cleanupDistantBlocks(center, radius); + } + } + + private void cleanupDistantBlocks(BlockPos center, int radius) { + int radiusSq = radius * radius * 2; + Iterator iterator = cachedBlocks.iterator(); + + while (iterator.hasNext()) { + BlockPos pos = iterator.next(); + int dx = pos.getX() - center.getX(); + int dz = pos.getZ() - center.getZ(); + + if (dx * dx + dz * dz > radiusSq) { + iterator.remove(); + } + } + } + + private boolean isValidSpawnSurface(BlockPos pos, BlockState state, BlockState aboveState) { + if (state.isAir() || !state.isOpaque() || !state.isFullCube(mc.world, pos)) { + return false; + } + Block aboveBlock = aboveState.getBlock(); if (aboveState.isAir()) return true; if (aboveBlock == Blocks.WATER && onWater.isEnabled()) return true; - if (aboveBlock instanceof PlantBlock) return true; - if (!aboveState.isOpaque() && !aboveState.isFullCube(mc.world, pos.up())) { - - if (aboveBlock instanceof SlabBlock - || aboveBlock instanceof StairsBlock - || aboveBlock instanceof ButtonBlock - || aboveBlock instanceof CarpetBlock - || aboveBlock instanceof PressurePlateBlock - || aboveBlock instanceof PaneBlock - || aboveBlock instanceof TrapdoorBlock - || aboveBlock instanceof FenceBlock - || aboveBlock instanceof WallBlock - || aboveBlock instanceof AnvilBlock - || aboveBlock instanceof BedBlock) { - return false; - } - - if (aboveBlock == Blocks.CHEST || - aboveBlock == Blocks.ENDER_CHEST || - aboveBlock == Blocks.BARREL || - aboveBlock == Blocks.TRAPPED_CHEST) { - return false; - } - - return aboveBlock != Blocks.ENCHANTING_TABLE - && aboveBlock != Blocks.GRINDSTONE - && aboveBlock != Blocks.STONECUTTER - && aboveBlock != Blocks.LOOM - && aboveBlock != Blocks.COMPOSTER - && aboveBlock != Blocks.CAKE; + if (aboveBlock instanceof SlabBlock || + aboveBlock instanceof StairsBlock || + aboveBlock instanceof ButtonBlock || + aboveBlock instanceof CarpetBlock || + aboveBlock instanceof PressurePlateBlock || + aboveBlock instanceof PaneBlock || + aboveBlock instanceof TrapdoorBlock || + aboveBlock instanceof FenceBlock || + aboveBlock instanceof WallBlock || + aboveBlock instanceof AnvilBlock || + aboveBlock instanceof BedBlock) { + return false; } - return true; + if (aboveBlock == Blocks.CHEST || + aboveBlock == Blocks.ENDER_CHEST || + aboveBlock == Blocks.BARREL || + aboveBlock == Blocks.TRAPPED_CHEST || + aboveBlock == Blocks.ENCHANTING_TABLE || + aboveBlock == Blocks.GRINDSTONE || + aboveBlock == Blocks.STONECUTTER || + aboveBlock == Blocks.LOOM || + aboveBlock == Blocks.COMPOSTER || + aboveBlock == Blocks.CAKE) { + return false; + } + + return !aboveState.isOpaque() || !aboveState.isFullCube(mc.world, pos.up()); } -} +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/module/setting/settings/ListSetting.java b/src/main/java/me/retucio/sputnik/module/setting/settings/ListSetting.java index 54db86f..dbf24c3 100644 --- a/src/main/java/me/retucio/sputnik/module/setting/settings/ListSetting.java +++ b/src/main/java/me/retucio/sputnik/module/setting/settings/ListSetting.java @@ -105,6 +105,16 @@ public class ListSetting extends Setting { return options; } + public List getEnabledOptions() { + List enabledOptions = new ArrayList<>(); + for (T option : options) { + if (isEnabled(option)) { + enabledOptions.add(option); + } + } + return enabledOptions; + } + public Map getDisplayNames() { return displayNames; } 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 cf493e4..3aa5fe9 100644 --- a/src/main/java/me/retucio/sputnik/ui/hud/HudRenderer.java +++ b/src/main/java/me/retucio/sputnik/ui/hud/HudRenderer.java @@ -36,6 +36,7 @@ public class HudRenderer { addElement(new CustomTextElement(), positions, visibilities); addElement(new TimeElement(), positions, visibilities); addElement(new PingElement(), positions, visibilities); + addElement(new RotationElement(), positions, visibilities); addElement(new DynoElement(), positions, visibilities); addElement(new EchestElement(), positions, visibilities); diff --git a/src/main/java/me/retucio/sputnik/ui/hud/elements/RotationElement.java b/src/main/java/me/retucio/sputnik/ui/hud/elements/RotationElement.java new file mode 100644 index 0000000..2d2b30a --- /dev/null +++ b/src/main/java/me/retucio/sputnik/ui/hud/elements/RotationElement.java @@ -0,0 +1,43 @@ +package me.retucio.sputnik.ui.hud.elements; + +import me.retucio.sputnik.module.ModuleManager; +import me.retucio.sputnik.module.modules.client.HUD; +import me.retucio.sputnik.ui.hud.TextHudElement; +import net.minecraft.text.Text; + +import java.util.List; + +public class RotationElement extends TextHudElement { + + public RotationElement() { + super("rotation", mc.getWindow().getScaledWidth() - mc.textRenderer.getWidth("180º 180º (N)"), mc.getWindow().getScaledHeight() - mc.textRenderer.fontHeight); + } + + @Override + public String getText(float delta, HUD hud) { + if (mc.player == null) return getPreviewText(); + return String.format("%.2f", mc.player.getYaw()) + "º " + String.format("%.2f", mc.player.getPitch()) + "º (" + getDirection() + ")"; + } + + @Override + public String getPreviewText() { + if (mc.player == null) + return "69º 69º (W)"; + return getText(mc.getRenderTickCounter().getDynamicDeltaTicks(), ModuleManager.INSTANCE.getModuleByClass(HUD.class)); + } + + @Override + public List getTooltip() { + return List.of(); + } + + private String getDirection() { + return switch (mc.player.getHorizontalFacing()) { + case NORTH -> "N"; + case SOUTH -> "S"; + case EAST -> "E"; + case WEST -> "O"; + default -> "?"; + }; + } +} 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 168e3cd..9c49e62 100644 --- a/src/main/java/me/retucio/sputnik/ui/screen/ClickGUI.java +++ b/src/main/java/me/retucio/sputnik/ui/screen/ClickGUI.java @@ -35,6 +35,7 @@ import org.lwjgl.glfw.GLFW; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import static me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame.guiSettings; @@ -248,15 +249,21 @@ public class ClickGUI extends Screen { } } + public SettingsFrame getSfOfModule(Module module) { + Optional frame = settingsFrames.stream() + .filter(sf -> sf.getModule().equals(module)) + .findFirst(); + + return frame.orElseGet(() -> + new SettingsFrame(module, 0, 0, 100, 20) + ); + } + // abrir un marco donde se encuentran los ajustes del módulo deseado public void openSettingsFrame(Module module, int x, int y) { - for (SettingsFrame frame : settingsFrames) { - if (frame.getModule() == module) { - frame.setX(x); - frame.setY(y); - return; - } - } + // asegurarse de que no se sale de la pantalla + x = Math.clamp(x, 0, mc.getWindow().getScaledWidth() - 80); + y = Math.clamp(y, 0, mc.getWindow().getScaledHeight() - 120); SettingsFrame frame = new SettingsFrame(module, x, y, 100, 20); settingsFrames.add(frame); @@ -289,8 +296,8 @@ public class ClickGUI extends Screen { // verificar si un módulo tiene su marco de ajustes abierto public boolean isSettingsFrameOpen(Module module) { - return settingsFrames.stream() - .anyMatch(sf -> sf.getModule().getName().equals(module.getName())); + return settingsFrames.stream().anyMatch( + sf -> sf.getModule().equals(module)); } public boolean isColorPickerFrameOpen(ColorSetting setting) { diff --git a/src/main/java/me/retucio/sputnik/ui/widgets/buttons/ModuleButton.java b/src/main/java/me/retucio/sputnik/ui/widgets/buttons/ModuleButton.java index 055bfd0..e5c751e 100644 --- a/src/main/java/me/retucio/sputnik/ui/widgets/buttons/ModuleButton.java +++ b/src/main/java/me/retucio/sputnik/ui/widgets/buttons/ModuleButton.java @@ -43,7 +43,7 @@ public class ModuleButton extends Button { @Override public void mouseClicked(int mouseX, int mouseY, int button) { - if (isHovered((int) mouseX, (int) mouseY) && ClickGUI.INSTANCE.trySelect(this)) { + if (isHovered(mouseX, mouseY) && ClickGUI.INSTANCE.trySelect(this)) { 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) @@ -56,12 +56,6 @@ public class ModuleButton extends Button { } } - @Override - public void mouseDragged(int mouseX, int mouseY) {} - - @Override - public void onKey(int key, int action) {} - // verifica si el puntero del ratón se encuentra sobre el botón del módulo @Override public boolean isHovered(int mouseX, int mouseY) { diff --git a/src/main/java/me/retucio/sputnik/ui/widgets/buttons/settings/ChooseButton.java b/src/main/java/me/retucio/sputnik/ui/widgets/buttons/settings/ChooseButton.java index ec8c0ae..3634b52 100644 --- a/src/main/java/me/retucio/sputnik/ui/widgets/buttons/settings/ChooseButton.java +++ b/src/main/java/me/retucio/sputnik/ui/widgets/buttons/settings/ChooseButton.java @@ -81,7 +81,7 @@ public class ChooseButton extends SettingButton> { String displayName = setting.getDisplayName(option); BooleanSetting b = new BooleanSetting( displayName, - "incluir " + displayName, + "seleccionar " + displayName, setting.is(option) ); diff --git a/src/main/java/me/retucio/sputnik/ui/widgets/buttons/settings/ListButton.java b/src/main/java/me/retucio/sputnik/ui/widgets/buttons/settings/ListButton.java index 7382b0f..420928f 100644 --- a/src/main/java/me/retucio/sputnik/ui/widgets/buttons/settings/ListButton.java +++ b/src/main/java/me/retucio/sputnik/ui/widgets/buttons/settings/ListButton.java @@ -7,6 +7,7 @@ import me.retucio.sputnik.module.setting.settings.ListSetting; import me.retucio.sputnik.ui.widgets.buttons.SettingButton; import me.retucio.sputnik.ui.screen.ClickGUI; import me.retucio.sputnik.ui.widgets.frames.SettingsFrame; +import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.Colors; import me.retucio.sputnik.util.KeyUtil; import net.minecraft.client.gui.DrawContext; diff --git a/src/main/java/me/retucio/sputnik/util/KeyUtil.java b/src/main/java/me/retucio/sputnik/util/KeyUtil.java index 6f4bde4..86a7b65 100644 --- a/src/main/java/me/retucio/sputnik/util/KeyUtil.java +++ b/src/main/java/me/retucio/sputnik/util/KeyUtil.java @@ -5,6 +5,11 @@ import net.minecraft.client.input.KeyInput; import net.minecraft.client.option.KeyBinding; import org.lwjgl.glfw.GLFW; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.im.InputContext; +import java.util.Locale; + import static me.retucio.sputnik.Sputnik.mc; // cosas útiles relacionadas a las teclas @@ -28,6 +33,11 @@ public class KeyUtil { || isKeyDown(GLFW.GLFW_KEY_RIGHT_SHIFT); } + + public static boolean isCapsLockOn() throws HeadlessException { + return Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK); + } + public static void pressKey(int key, int action) { pressKey(key, action, 0, 0); } @@ -36,7 +46,12 @@ public class KeyUtil { mc.keyboard.onKey( mc.getWindow().getHandle(), action, - new KeyInput(key, scancode, modifiers)); + new KeyInput( + key, + scancode, + modifiers + ) + ); } public static String getKeyName(int key) { @@ -72,31 +87,93 @@ public class KeyUtil { }; } - // quizás debería de añadir layouts... + public static String shiftKey(String c) { - return switch (c) { - case "`" -> "~"; - case "1" -> "!"; - case "2" -> "@"; - case "3" -> "#"; - case "4" -> "$"; - case "5" -> "%"; - case "6" -> "^"; - case "7" -> "&"; - case "8" -> "*"; - case "9" -> "("; - case "0" -> ")"; - case "-" -> "_"; - case "=" -> "+"; - case "[" -> "{"; - case "]" -> "}"; - case "\\" -> "|"; - case ";" -> ":"; - case "'" -> "\""; - case "," -> "<"; - case "." -> ">"; - case "/" -> "?"; - default -> c.toUpperCase(); - }; + return shiftKey(c, getLayout()); + } + + public static String shiftKey(String c, KeyboardLayout layout) { + String uppercase = isCapsLockOn() ? c.toLowerCase() : c.toUpperCase(); + + switch (layout) { + case EN_US -> { + return switch (c) { + case "`" -> "~"; + case "1" -> "!"; + case "2" -> "@"; + case "3" -> "#"; + case "4" -> "$"; + case "5" -> "%"; + case "6" -> "^"; + case "7" -> "&"; + case "8" -> "*"; + case "9" -> "("; + case "0" -> ")"; + case "-" -> "_"; + case "=" -> "+"; + case "[" -> "{"; + case "]" -> "}"; + case "\\" -> "|"; + case ";" -> ":"; + case "'" -> "\""; + case "," -> "<"; + case "." -> ">"; + case "/" -> "?"; + default -> uppercase; + }; + } + + case ES_ES -> { + return switch (c) { + case "º" -> "ª"; + case "1" -> "!"; + case "2" -> "\""; + case "3" -> "·"; + case "4" -> "$"; + case "5" -> "%"; + case "6" -> "&"; + case "7" -> "/"; + case "8" -> "("; + case "9" -> ")"; + case "0" -> "="; + case "'" -> "?"; + case "¡" -> "¿"; + case "`" -> "^"; + case "+" -> "*"; + case "´" -> "¨"; + case "," -> ";"; + case "." -> ":"; + case "-" -> "_"; + case "<" -> ">"; + default -> uppercase; + }; + } + } + + return uppercase; + } + + + + public static KeyboardLayout getLayout() { + Locale locale = InputContext.getInstance().getLocale(); + if (locale == null) locale = Locale.getDefault(); + + String country = locale.getCountry(); + String lang = locale.getLanguage(); + + if (country.equals("US") && lang.equals("en")) { + return KeyboardLayout.EN_US; + } else if (country.equals("ES") && lang.equals("es")) { + return KeyboardLayout.ES_ES; + } + + // fallback + return KeyboardLayout.EN_US; + } + + public enum KeyboardLayout { + EN_US, + ES_ES; } } diff --git a/src/main/java/me/retucio/sputnik/util/Lists.java b/src/main/java/me/retucio/sputnik/util/Lists.java index 8576bd2..6edeadb 100644 --- a/src/main/java/me/retucio/sputnik/util/Lists.java +++ b/src/main/java/me/retucio/sputnik/util/Lists.java @@ -36,6 +36,7 @@ public class Lists { public static Map, String> screenNames; public static final List colorList = new ArrayList<>(); + public static final List fontList = new ArrayList<>(); // intentar traducir nombres y fallar miserablemente public static void init() { @@ -68,7 +69,16 @@ public class Lists { ).toList()); colorList.addAll(Arrays.asList( - RED, ORANGE, YELLOW, LIME, GREEN, CYAN, CELESTE, BLUE, PURPLE, MAGENTA, PINK, LAVENDER, WHITE, SILVER, GRAY, BLACK, BROWN)); + RED, ORANGE, YELLOW, LIME, GREEN, CYAN, + CELESTE, BLUE, PURPLE, MAGENTA, PINK, + LAVENDER, WHITE, SILVER, GRAY, BLACK, BROWN + )); + + fontList.addAll(List.of( + "bahnschrift", "calibri", "cantarell", "comic-sans-ms", + "deja-vu-sans-mono", "new-times-roman", "roboto", "segoe-ui", + "ubuntu" + )); } public static Map allTrue(List options) { diff --git a/src/main/java/me/retucio/sputnik/util/render/Layers.java b/src/main/java/me/retucio/sputnik/util/render/Layers.java index 07c5e39..a7a3079 100644 --- a/src/main/java/me/retucio/sputnik/util/render/Layers.java +++ b/src/main/java/me/retucio/sputnik/util/render/Layers.java @@ -14,6 +14,8 @@ public class Layers { private static final RenderLayer QUADS; private static final RenderLayer QUADS_CULL; + private static final RenderLayer TEXT; + public static RenderLayer lines() { return LINES; @@ -31,12 +33,18 @@ public class Layers { return QUADS_CULL; } + public static RenderLayer text() { + return TEXT; + } + static { LINES = RenderLayer.of(Sputnik.MOD_ID + "_lines", RenderSetup.builder(LINES_PIPELINE).build()); LINES_CULL = RenderLayer.of(Sputnik.MOD_ID + "_lines_cull", RenderSetup.builder(LINES_CULL_PIPELINE).build()); QUADS = RenderLayer.of(Sputnik.MOD_ID + "_quads", RenderSetup.builder(QUADS_PIPELINE).build()); - QUADS_CULL = RenderLayer.of(Sputnik.MOD_ID + "quads_cull", RenderSetup.builder(QUADS_CULL_PIPELINE).build()); + QUADS_CULL = RenderLayer.of(Sputnik.MOD_ID + "_quads_cull", RenderSetup.builder(QUADS_CULL_PIPELINE).build()); + + TEXT = RenderLayer.of(Sputnik.MOD_ID + "_text", RenderSetup.builder(TEXT_PIPELINE).build()); } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/util/render/Pipelines.java b/src/main/java/me/retucio/sputnik/util/render/Pipelines.java index a63bcf5..4bb4b3e 100644 --- a/src/main/java/me/retucio/sputnik/util/render/Pipelines.java +++ b/src/main/java/me/retucio/sputnik/util/render/Pipelines.java @@ -6,12 +6,18 @@ import com.mojang.blaze3d.platform.DepthTestFunction; import com.mojang.blaze3d.vertex.VertexFormat; import me.retucio.sputnik.Sputnik; import net.minecraft.client.gl.RenderPipelines; +import net.minecraft.client.gl.UniformType; import net.minecraft.client.render.VertexFormats; import net.minecraft.util.Identifier; public class Pipelines { + static final RenderPipeline.Snippet MESH_UNIFORMS = RenderPipeline.builder() + .withUniform("MeshData",UniformType.UNIFORM_BUFFER) + .buildSnippet(); + + static final RenderPipeline LINES_PIPELINE = RenderPipeline.builder(RenderPipelines.RENDERTYPE_LINES_SNIPPET) .withLocation(Identifier.of(Sputnik.MOD_ID, "pipeline/lines")) .withVertexFormat(VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH, VertexFormat.DrawMode.LINES) @@ -45,4 +51,16 @@ public class Pipelines { .withDepthWrite(false) .withCull(false) .build(); + + public static final RenderPipeline TEXT_PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET) + .withLocation(Identifier.of(Sputnik.MOD_ID, "pipeline/text")) + .withVertexFormat(VertexFormats.POSITION_TEXTURE_COLOR, VertexFormat.DrawMode.TRIANGLES) + .withVertexShader(Identifier.of(Sputnik.MOD_ID, "shaders/text.vert")) + .withFragmentShader(Identifier.of(Sputnik.MOD_ID,"shaders/text.frag")) + .withSampler("u_Texture") + .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) + .withDepthWrite(false) + .withBlend(BlendFunction.TRANSLUCENT) + .withCull(false) + .build(); } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/util/render/Texture.java b/src/main/java/me/retucio/sputnik/util/render/Texture.java new file mode 100644 index 0000000..0d03f24 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/util/render/Texture.java @@ -0,0 +1,89 @@ +package me.retucio.sputnik.util.render; + +import com.mojang.blaze3d.platform.TextureUtil; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.AddressMode; +import com.mojang.blaze3d.textures.FilterMode; +import com.mojang.blaze3d.textures.TextureFormat; +import net.minecraft.client.texture.AbstractTexture; +import net.minecraft.client.texture.NativeImage; +import org.jetbrains.annotations.NotNull; +import org.lwjgl.BufferUtils; +import org.lwjgl.stb.STBImage; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +public class Texture extends AbstractTexture { + public Texture(int width, int height, TextureFormat format, FilterMode min, FilterMode mag) { + glTexture = RenderSystem.getDevice().createTexture("", 15, format, width, height, 1, 1); + sampler = RenderSystem.getSamplerCache().get(AddressMode.REPEAT, AddressMode.REPEAT, min, mag, false); + + glTextureView = RenderSystem.getDevice().createTextureView(glTexture); + } + + public int getWidth() { + return getGlTexture().getWidth(0); + } + + public int getHeight() { + return getGlTexture().getHeight(0); + } + + public void upload(byte[] bytes) { + upload(BufferUtils.createByteBuffer(bytes.length).put(bytes)); + } + + public void upload(ByteBuffer buffer) { + NativeImage image = getImage(); + + buffer.rewind(); + MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), image.imageId(), buffer.remaining()); + + RenderSystem.getDevice().createCommandEncoder().writeToTexture(glTexture, image); + + image.close(); + } + + private @NotNull NativeImage getImage() { + NativeImage.Format imageFormat = switch (glTexture.getFormat()) { + case RGBA8 -> NativeImage.Format.RGBA; + case RED8 -> NativeImage.Format.LUMINANCE; + default -> throw new IllegalArgumentException(); + }; + + // Workaround for writeToTexture(IntBuffer) overload comparing width * height to the size of the int buffer. + // And since we are working with pixels which are only one byte in size, the sizes don't match + return new NativeImage(imageFormat, getWidth(), getHeight(), false); + } + + public static Texture readResource(String path, boolean flipY, FilterMode filter) { + try (var in = Texture.class.getResourceAsStream(path)) { + if (in == null) return null; + + var data = TextureUtil.readResource(in).rewind(); + + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer width = stack.mallocInt(1); + IntBuffer height = stack.mallocInt(1); + IntBuffer comp = stack.mallocInt(1); + + STBImage.stbi_set_flip_vertically_on_load(flipY); + ByteBuffer image = STBImage.stbi_load_from_memory(data, width, height, comp, 4); + + var texture = new Texture(width.get(0), height.get(0), TextureFormat.RGBA8, filter, filter); + texture.upload(image); + + STBImage.stbi_image_free(image); + STBImage.stbi_set_flip_vertically_on_load(false); + + return texture; + } + } catch (IOException e) { + return null; + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/font/bahnschrift.json b/src/main/resources/assets/sputnik/font/bahnschrift.json new file mode 100644 index 0000000..f932f91 --- /dev/null +++ b/src/main/resources/assets/sputnik/font/bahnschrift.json @@ -0,0 +1,12 @@ +{ + "providers": [ + { + "type": "ttf", + "file": "sputnik:bahnschrift.ttf", + "size": 10.0, + "oversample": 2.0, + "shift": [0.0, 0.0], + "range": "0x0000-0x00FF" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/font/bahnschrift.ttf b/src/main/resources/assets/sputnik/font/bahnschrift.ttf new file mode 100644 index 0000000..8332c6c Binary files /dev/null and b/src/main/resources/assets/sputnik/font/bahnschrift.ttf differ diff --git a/src/main/resources/assets/sputnik/font/calibri.json b/src/main/resources/assets/sputnik/font/calibri.json new file mode 100644 index 0000000..9d08f29 --- /dev/null +++ b/src/main/resources/assets/sputnik/font/calibri.json @@ -0,0 +1,12 @@ +{ + "providers": [ + { + "type": "ttf", + "file": "sputnik:calibri.ttf", + "size": 10.0, + "oversample": 2.0, + "shift": [0.0, 0.0], + "range": "0x0000-0x00FF" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/font/calibri.ttf b/src/main/resources/assets/sputnik/font/calibri.ttf new file mode 100644 index 0000000..072384a Binary files /dev/null and b/src/main/resources/assets/sputnik/font/calibri.ttf differ diff --git a/src/main/resources/assets/sputnik/font/cantarell.json b/src/main/resources/assets/sputnik/font/cantarell.json new file mode 100644 index 0000000..b175f83 --- /dev/null +++ b/src/main/resources/assets/sputnik/font/cantarell.json @@ -0,0 +1,12 @@ +{ + "providers": [ + { + "type": "ttf", + "file": "sputnik:cantarell.ttf", + "size": 10.0, + "oversample": 2.0, + "shift": [0.0, 0.0], + "range": "0x0000-0x00FF" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/font/cantarell.ttf b/src/main/resources/assets/sputnik/font/cantarell.ttf new file mode 100644 index 0000000..8a63054 Binary files /dev/null and b/src/main/resources/assets/sputnik/font/cantarell.ttf differ diff --git a/src/main/resources/assets/sputnik/font/comic-sans-ms.json b/src/main/resources/assets/sputnik/font/comic-sans-ms.json new file mode 100644 index 0000000..eb048e9 --- /dev/null +++ b/src/main/resources/assets/sputnik/font/comic-sans-ms.json @@ -0,0 +1,12 @@ +{ + "providers": [ + { + "type": "ttf", + "file": "sputnik:comic-sans-ms.ttf", + "size": 10.0, + "oversample": 2.0, + "shift": [0.0, 0.0], + "range": "0x0000-0x00FF" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/font/comic-sans-ms.ttf b/src/main/resources/assets/sputnik/font/comic-sans-ms.ttf new file mode 100644 index 0000000..8237d1f Binary files /dev/null and b/src/main/resources/assets/sputnik/font/comic-sans-ms.ttf differ diff --git a/src/main/resources/assets/sputnik/font/deja-vu-sans-mono.json b/src/main/resources/assets/sputnik/font/deja-vu-sans-mono.json new file mode 100644 index 0000000..1113489 --- /dev/null +++ b/src/main/resources/assets/sputnik/font/deja-vu-sans-mono.json @@ -0,0 +1,12 @@ +{ + "providers": [ + { + "type": "ttf", + "file": "sputnik:deja-vu-sans-mono.ttf", + "size": 10.0, + "oversample": 2.0, + "shift": [0.0, 0.0], + "range": "0x0000-0x00FF" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/font/deja-vu-sans-mono.ttf b/src/main/resources/assets/sputnik/font/deja-vu-sans-mono.ttf new file mode 100644 index 0000000..8b7bb2a Binary files /dev/null and b/src/main/resources/assets/sputnik/font/deja-vu-sans-mono.ttf differ diff --git a/src/main/resources/assets/sputnik/font/new-times-roman.json b/src/main/resources/assets/sputnik/font/new-times-roman.json new file mode 100644 index 0000000..ea68e52 --- /dev/null +++ b/src/main/resources/assets/sputnik/font/new-times-roman.json @@ -0,0 +1,12 @@ +{ + "providers": [ + { + "type": "ttf", + "file": "sputnik:new-times-roman.ttf", + "size": 10.0, + "oversample": 2.0, + "shift": [0.0, 0.0], + "range": "0x0000-0x00FF" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/font/new-times-roman.ttf b/src/main/resources/assets/sputnik/font/new-times-roman.ttf new file mode 100644 index 0000000..51261a0 Binary files /dev/null and b/src/main/resources/assets/sputnik/font/new-times-roman.ttf differ diff --git a/src/main/resources/assets/sputnik/font/roboto.json b/src/main/resources/assets/sputnik/font/roboto.json new file mode 100644 index 0000000..0d2eb38 --- /dev/null +++ b/src/main/resources/assets/sputnik/font/roboto.json @@ -0,0 +1,12 @@ +{ + "providers": [ + { + "type": "ttf", + "file": "sputnik:roboto.ttf", + "size": 10.0, + "oversample": 2.0, + "shift": [0.0, 0.0], + "range": "0x0000-0x00FF" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/font/roboto.ttf b/src/main/resources/assets/sputnik/font/roboto.ttf new file mode 100644 index 0000000..d51221a Binary files /dev/null and b/src/main/resources/assets/sputnik/font/roboto.ttf differ diff --git a/src/main/resources/assets/sputnik/font/segoe-ui.json b/src/main/resources/assets/sputnik/font/segoe-ui.json new file mode 100644 index 0000000..92b7ef8 --- /dev/null +++ b/src/main/resources/assets/sputnik/font/segoe-ui.json @@ -0,0 +1,12 @@ +{ + "providers": [ + { + "type": "ttf", + "file": "sputnik:segoe-ui.ttf", + "size": 10.0, + "oversample": 2.0, + "shift": [0.0, 0.0], + "range": "0x0000-0x00FF" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/font/segoe-ui.ttf b/src/main/resources/assets/sputnik/font/segoe-ui.ttf new file mode 100644 index 0000000..46b3b99 Binary files /dev/null and b/src/main/resources/assets/sputnik/font/segoe-ui.ttf differ diff --git a/src/main/resources/assets/sputnik/font/ubuntu.json b/src/main/resources/assets/sputnik/font/ubuntu.json new file mode 100644 index 0000000..52a1bd3 --- /dev/null +++ b/src/main/resources/assets/sputnik/font/ubuntu.json @@ -0,0 +1,12 @@ +{ + "providers": [ + { + "type": "ttf", + "file": "sputnik:ubuntu.ttf", + "size": 10.0, + "oversample": 2.0, + "shift": [0.0, 0.0], + "range": "0x0000-0x00FF" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/font/ubuntu.ttf b/src/main/resources/assets/sputnik/font/ubuntu.ttf new file mode 100644 index 0000000..b5505e6 Binary files /dev/null and b/src/main/resources/assets/sputnik/font/ubuntu.ttf differ diff --git a/src/main/resources/assets/sputnik/shaders/text.frag b/src/main/resources/assets/sputnik/shaders/text.frag new file mode 100644 index 0000000..c03ad87 --- /dev/null +++ b/src/main/resources/assets/sputnik/shaders/text.frag @@ -0,0 +1,14 @@ +#version 150 +in vec2 vTexCoord; +in vec4 vColor; + +out vec4 fragColor; + +uniform sampler2D u_Texture; + +void main() { + float distance = texture(u_Texture, vTexCoord).r; + float alpha = smoothstep(0.4, 0.6, distance); + + fragColor = vec4(vColor.rgb, vColor.a * alpha); +} \ No newline at end of file diff --git a/src/main/resources/assets/sputnik/shaders/text.vert b/src/main/resources/assets/sputnik/shaders/text.vert new file mode 100644 index 0000000..c82557a --- /dev/null +++ b/src/main/resources/assets/sputnik/shaders/text.vert @@ -0,0 +1,15 @@ +#version 150 +in vec3 aPos; +in vec2 aTexCoord; +in vec4 aColor; + +out vec2 vTexCoord; +out vec4 vColor; + +uniform mat4 u_ProjectionMatrix; + +void main() { + gl_Position = u_ProjectionMatrix * vec4(aPos, 1.0); + vTexCoord = aTexCoord; + vColor = aColor; +} \ No newline at end of file diff --git a/src/main/resources/sputnik.mixins.json b/src/main/resources/sputnik.mixins.json index a52f689..d14dda6 100644 --- a/src/main/resources/sputnik.mixins.json +++ b/src/main/resources/sputnik.mixins.json @@ -52,6 +52,7 @@ "EmitterParticleMixin", "EquipmentRendererMixin", "FogRendererMixin", + "FontManagerMixin", "GameOptionsMixin", "GameRendererMixin", "HeldItemRendererMixin",