diff --git a/cambios.md b/cambios.md index caf3b6a..8aada33 100644 --- a/cambios.md +++ b/cambios.md @@ -1,4 +1,4 @@ -# v3.9 +# v4.0 ## novedades @@ -6,12 +6,12 @@ #### módulos nuevos: -- escaleras veloces: sube escaleras más rápido -- parkour: salta automáticamente -- confetti: cambia el color de las partículas del pop del tótem +- aura de cristales: hace cpvp por ti (muy mejorable, estoy en ello) ### arreglos / mejoras +- `$echest` ya funciona y tal + ## problemas reconocidos - lo de siempre \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index f1ace47..a9564b0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,6 +10,6 @@ fabric_api_version=0.140.2+1.21.11 neutrino_version=1.1.0 -mod_version=3.9 +mod_version=4.0 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 255fd4a..7511101 100644 --- a/src/main/java/me/retucio/sputnik/Sputnik.java +++ b/src/main/java/me/retucio/sputnik/Sputnik.java @@ -96,6 +96,8 @@ public class Sputnik implements ClientModInitializer { EVENT_BUS.subscribe(NetworkUtil.class); EVENT_BUS.subscribe(RenderUtil.class); + EVENT_BUS.subscribe(BlockIterator.class); + Lists.init(); Textures.init(); diff --git a/src/main/java/me/retucio/sputnik/module/ModuleManager.java b/src/main/java/me/retucio/sputnik/module/ModuleManager.java index e5f2895..88264e4 100644 --- a/src/main/java/me/retucio/sputnik/module/ModuleManager.java +++ b/src/main/java/me/retucio/sputnik/module/ModuleManager.java @@ -5,10 +5,7 @@ import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.module.modules.client.DiscordRPC; import me.retucio.sputnik.module.modules.client.HUD; import me.retucio.sputnik.module.modules.camera.*; -import me.retucio.sputnik.module.modules.combat.AttributeSwapper; -import me.retucio.sputnik.module.modules.combat.MaceKill; -import me.retucio.sputnik.module.modules.combat.ProjectileTrajectories; -import me.retucio.sputnik.module.modules.combat.SpearKill; +import me.retucio.sputnik.module.modules.combat.*; import me.retucio.sputnik.module.modules.inventory.*; import me.retucio.sputnik.module.modules.misc.*; import me.retucio.sputnik.module.modules.movement.*; @@ -40,6 +37,7 @@ public class ModuleManager { // añadir los módulos addCamera(); addClient(); + addCombat(); addInventory(); addMisc(); addMovement(); @@ -67,7 +65,6 @@ public class ModuleManager { - private void addCamera() { add(new Autism()); add(new Freecam()); @@ -83,6 +80,14 @@ public class ModuleManager { add(new HUD()); } + private void addCombat() { + add(new AttributeSwapper()); + add(new CrystalAura()); + add(new MaceKill()); + add(new ProjectileTrajectories()); + add(new SpearKill()); + } + private void addMisc() { add(new AntiInvis()); add(new AnvilFont()); @@ -138,15 +143,12 @@ public class ModuleManager { private void addPlayer() { add(new AirPlace()); add(new AntiHunger()); - add(new AttributeSwapper()); add(new AutoFish()); add(new Capes()); add(new FastUse()); add(new HandView()); add(new KeyPearl()); - add(new MaceKill()); add(new NoFall()); - add(new SpearKill()); add(new WarnLowDurability()); } @@ -175,7 +177,6 @@ public class ModuleManager { add(new ColoredSigns()); add(new LightOverlay()); add(new NoMiningInterruptions()); - add(new ProjectileTrajectories()); add(new Racist()); add(new StrongholdTriangulator()); add(new TimeChanger()); diff --git a/src/main/java/me/retucio/sputnik/module/modules/combat/CrystalAura.java b/src/main/java/me/retucio/sputnik/module/modules/combat/CrystalAura.java new file mode 100644 index 0000000..8c317f4 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/combat/CrystalAura.java @@ -0,0 +1,295 @@ +package me.retucio.sputnik.module.modules.combat; + +import com.github.retucio.neutrino.EventListener; +import me.retucio.sputnik.event.render.Render3DEvent; +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.ListSetting; +import me.retucio.sputnik.module.setting.settings.NumberSetting; +import me.retucio.sputnik.util.*; +import me.retucio.sputnik.util.render.RenderUtil; +import net.minecraft.block.Blocks; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.decoration.EndCrystalEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.Items; +import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.RaycastContext; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author retucio + */ +public class CrystalAura extends Module { + + private final SettingGroup sgPlace = addSg(new SettingGroup("colocar", false)); + private final SettingGroup sgBreak = addSg(new SettingGroup("reventar", false)); + private final SettingGroup sgRender = addSg(new SettingGroup("renderizado", false)); + + private final ListSetting> entities = sgGeneral.add(new ListSetting<>( + "entidades", "entidades a las que atacar", + Lists.entityList, Lists.allFalseExcept(Lists.entityList, EntityType.PLAYER), Lists.entityNames + )); + + private final NumberSetting targetRange = sgGeneral.add(new NumberSetting( + "rango", + "rango de detección de blancos", + 5, 0, 12, 0.1 + )); + + + // ajustes al colocar los cristales + + private final BooleanSetting placeCrystals = sgPlace.add(new BooleanSetting( + "colocar", "colocar los cristales", true + )); + + private final NumberSetting placeDelay = sgPlace.add(new NumberSetting( + "delay", "delay entre cristal y cristal", + 0, 0, 20, 1 + )).visibility(placeCrystals::getValue); + + private final NumberSetting placeRange = sgPlace.add(new NumberSetting( + "rango ", "rango máximo para colocar cristales", + 4.5, 0, 6, 0.1 + )).visibility(placeCrystals::getValue); + + private final BooleanSetting rotateToPlace = sgPlace.add(new BooleanSetting( + "rotar", "rotar para colocar cristales", false + )).visibility(placeCrystals::getValue); + + private final BooleanSetting autoSelect = sgPlace.add(new BooleanSetting( + "autoseleccionar", "seleccionar cristales automáticamente", true + )); + + + // ajustes al reventar los cristales + + private final BooleanSetting breakCrystals = sgBreak.add(new BooleanSetting( + "reventar", "reventar los cristales", true + )); + + private final NumberSetting breakDelay = sgBreak.add(new NumberSetting( + "delay ", "delay entre cristal y cristal", + 0, 0, 20, 1 + )).visibility(breakCrystals::getValue); + + private final BooleanSetting rotateToBreak = sgBreak.add(new BooleanSetting( + "rotar", "rotar para reventar cristales", false + )).visibility(breakCrystals::getValue); + + + // ajustes de renderizado + + private final BooleanSetting outlines = sgRender.add(new BooleanSetting( + "contorno", "renderizar contorno del bloque donde se colocan los cristales", true + )); + + private final ColorSetting outlineColor = sgRender.add(new ColorSetting( + "color del contorno", "color del contorno", + Colors.mainColor, false + )).visibility(outlines::getValue); + + private final NumberSetting outlineWidth = sgRender.add(new NumberSetting( + "grosor del contorno", "grosor del contorno", + 2, 0, 15, 0.1 + )).visibility(outlines::getValue); + + private final BooleanSetting fillings = sgRender.add(new BooleanSetting( + "relleno", "renderizar relleno del bloque donde se colocan los cristales", false + )); + + private final ColorSetting fillingColor = sgRender.add(new ColorSetting( + "color del relleno", "color del relleno", + Colors.withAlpha(Colors.mainColor, 70), false + )).visibility(fillings::getValue); + + + private final List targets = new ArrayList<>(); + private BlockPos bp = null; + + private int placeTimer = placeDelay.getIntValue(); + private int breakTimer = breakDelay.getIntValue(); + + + public CrystalAura() { + super("aura de cristales", + "coloca y revienta cristales del end por ti", Category.COMBAT); + } + + + @Override + public void onTick() { + if (mc.player == null || mc.interactionManager == null || mc.world == null || mc.getCameraEntity() == null) return; + + if (placeTimer > 0) placeTimer--; + if (breakTimer > 0) breakTimer--; + + findTargets(); + + if (!targets.isEmpty()) { + if (placeCrystals.getValue()) doPlace(); + if (breakCrystals.getValue()) doBreak(); + } else { + bp = null; + } + } + + // colocar cristal + private void doPlace() { + if (placeTimer > 0) return; + + if (!canPlaceCrystal()) { + bp = null; + return; + } + + // elegir como blanco primario la entidad más cercana + LivingEntity primaryTarget = null; + double closestTargetDist = Double.MAX_VALUE; + for (LivingEntity target : targets) { + double dist = mc.player.squaredDistanceTo(target); + if (dist < closestTargetDist) { + closestTargetDist = dist; + primaryTarget = target; + } + } + if (primaryTarget == null) { + bp = null; + return; + } + + final Vec3d targetPos = primaryTarget.getEntityPos(); + final BlockPos.Mutable[] bestPos = new BlockPos.Mutable[1]; + final double[] bestDist = { Double.MAX_VALUE }; + + BlockIterator.register((int) Math.ceil(placeRange.getValue()), (int) Math.ceil(placeRange.getValue()), (pos, state) -> { + if (!state.isOf(Blocks.OBSIDIAN) && !state.isOf(Blocks.BEDROCK)) return; + if (!mc.world.isAir(pos.add(0, 1, 0))) return; + + // cálculo de mejor distancia + double dist = targetPos.squaredDistanceTo(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); + if (dist < bestDist[0]) { + bestDist[0] = dist; + if (bestPos[0] == null) bestPos[0] = new BlockPos.Mutable(); + bestPos[0].set(pos); + } + }); + + BlockIterator.after(() -> { + if (bestPos[0] != null) { + BlockHitResult result = getBlockInfo(bestPos[0]); + placeCrystal(result); + bp = bestPos[0].toImmutable(); // guardar para el renderizado + } else { + bp = null; + } + }); + + placeTimer = placeDelay.getIntValue(); + } + + private void doBreak() { + if (breakTimer > 0) return; + for (Entity entity : mc.world.getEntities()) { + if (!(entity instanceof EndCrystalEntity) || entity.distanceTo(mc.player) > mc.player.getEntityInteractionRange()) continue; + if (rotateToBreak.getValue()) EntityUtil.lookAt(entity.getEyePos()); + mc.interactionManager.attackEntity(mc.player, entity); + } + breakTimer = breakDelay.getIntValue(); + } + + private void placeCrystal(BlockHitResult result) { + if (autoSelect.getValue()) { + int slot = InventoryUtil.findSlot(stack -> stack.isOf(Items.END_CRYSTAL)); + if (slot != InventoryUtil.OFFHAND_SLOT) { + if (!PlayerInventory.isValidHotbarIndex(slot)) { + InventoryUtil.swapWithHotbar(slot, mc.player.getInventory().getSelectedSlot()); + } else { + mc.player.getInventory().setSelectedSlot(slot); + } + } + } + + Hand hand; + if (mc.player.getMainHandStack().isOf(Items.END_CRYSTAL)) { + hand = Hand.MAIN_HAND; + } else if (mc.player.getOffHandStack().isOf(Items.END_CRYSTAL)) { + hand = Hand.OFF_HAND; + } else { + return; + } + + if (rotateToPlace.getValue()) EntityUtil.lookAt(result.getBlockPos().toCenterPos()); + mc.interactionManager.sendSequencedPacket(mc.world, (sequence) -> new PlayerInteractBlockC2SPacket(hand, result, sequence)); + } + + // encontrar blancos + private void findTargets() { + targets.clear(); + for (Entity e : mc.world.getEntities()) { + if (!(e instanceof LivingEntity entity)) continue; + if (!entity.isAlive() || entity.isInCreativeMode() || entity.equals(mc.player)) continue; + if (!entities.isEnabled(entity.getType())) continue; + if (entity.squaredDistanceTo(mc.player) > targetRange.getValue() * targetRange.getValue()) continue; + targets.add(entity); + } + } + + private BlockHitResult getBlockInfo(BlockPos pos) { + Vec3d start = new Vec3d(mc.player.getX(), mc.player.getY() + mc.player.getEyeHeight(mc.player.getPose()), mc.player.getZ()); + Vec3d end; + + for (Direction side : Direction.values()) { + end = new Vec3d( + pos.getX() + 0.5 + side.getVector().getX() * 0.5, + pos.getY() + 0.5 + side.getVector().getY() * 0.5, + pos.getZ() + 0.5 + side.getVector().getZ() * 0.5 + ); + + BlockHitResult result = mc.world.raycast(new RaycastContext( + start, end, RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, mc.player + )); + + if (result != null && result.getType() == HitResult.Type.BLOCK && result.getBlockPos().equals(pos)) { + return result; + } + } + + Direction side = pos.getY() > start.y ? Direction.DOWN : Direction.UP; + return new BlockHitResult(start, side, pos, false); + } + + private boolean canPlaceCrystal() { + if (autoSelect.getValue()) { + return InventoryUtil.findSlot(stack -> stack.isOf(Items.END_CRYSTAL)) != -1; + } else { + return InventoryUtil.hasInHotbar(Items.END_CRYSTAL); + } + } + + @EventListener + private void onRenderWorld(Render3DEvent event) { + if (bp != null) { + if (outlines.getValue()) { + RenderUtil.drawBlockOutline(event.getMatrices(), bp, outlineColor.getValue(), outlineWidth.getFloatValue(), false); + } + if (fillings.getValue()) { + RenderUtil.drawBlockFilled(event.getMatrices(), bp, fillingColor.getValue(), false); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/module/modules/player/AutoFish.java b/src/main/java/me/retucio/sputnik/module/modules/player/AutoFish.java index 70ef0e3..eafc628 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/player/AutoFish.java +++ b/src/main/java/me/retucio/sputnik/module/modules/player/AutoFish.java @@ -52,7 +52,7 @@ public class AutoFish extends Module { 0, 64, 1 - )); + )).visibility(onlyRecastIfLookingAtWater::getValue); private final BooleanSetting dontBreak = sgGeneral.add(new BooleanSetting( "evitar romper", @@ -71,8 +71,6 @@ public class AutoFish extends Module { onlyRecastIfLookingAtWater.visibility(v); recastRaycastDistance.visibility(v); }); - - onlyRecastIfLookingAtWater.onUpdate(v -> recastRaycastDistance.visibility(v)); } @Override diff --git a/src/main/java/me/retucio/sputnik/util/BlockIterator.java b/src/main/java/me/retucio/sputnik/util/BlockIterator.java new file mode 100644 index 0000000..445cd62 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/util/BlockIterator.java @@ -0,0 +1,102 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package me.retucio.sputnik.util; + +import com.github.retucio.neutrino.EventListener; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import me.retucio.sputnik.Sputnik; +import me.retucio.sputnik.event.TickEvent; +import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.math.BlockPos; + +import java.util.List; +import java.util.function.BiConsumer; + +// https://github.com/MeteorDevelopment/meteor-client/blob/master/src/main/java/meteordevelopment/meteorclient/utils/world/BlockIterator.java +public class BlockIterator { + + private static final Pool callbackPool = new Pool<>(Callback::new); + private static final List callbacks = new ReferenceArrayList<>(); + + private static final List afterCallbacks = new ReferenceArrayList<>(); + + private static final BlockPos.Mutable blockPos = new BlockPos.Mutable(); + private static int hRadius, vRadius; + + private static boolean disableCurrent; + + private BlockIterator() {} + + @EventListener() + private static void onTick(TickEvent.Pre event) { + MinecraftClient mc = MinecraftClient.getInstance(); + if (mc.player == null) return; + + int px = mc.player.getBlockX(); + int py = mc.player.getBlockY(); + int pz = mc.player.getBlockZ(); + + for (int x = px - hRadius; x <= px + hRadius; x++) { + for (int z = pz - hRadius; z <= pz + hRadius; z++) { + for (int y = Math.max(mc.world.getBottomY(), py - vRadius); y <= py + vRadius; y++) { + if (y > mc.world.getHeight()) break; + + blockPos.set(x, y, z); + BlockState blockState = mc.world.getBlockState(blockPos); + + int dx = Math.abs(x - px); + int dy = Math.abs(y - py); + int dz = Math.abs(z - pz); + + callbacks.removeIf(callback -> { + if (dx <= callback.hRadius && dy <= callback.vRadius && dz <= callback.hRadius) { + disableCurrent = false; + callback.function.accept(blockPos, blockState); + return disableCurrent; + } + return false; + }); + } + } + } + + hRadius = 0; + vRadius = 0; + + callbackPool.freeAll(callbacks); + callbacks.clear(); + + for (Runnable callback : afterCallbacks) callback.run(); + afterCallbacks.clear(); + } + + public static void register(int horizontalRadius, int verticalRadius, BiConsumer function) { + hRadius = Math.max(hRadius, horizontalRadius); + vRadius = Math.max(vRadius, verticalRadius); + + Callback callback = callbackPool.get(); + + callback.function = function; + callback.hRadius = horizontalRadius; + callback.vRadius = verticalRadius; + + callbacks.add(callback); + } + + public static void disableCurrent() { + disableCurrent = true; + } + + public static void after(Runnable callback) { + afterCallbacks.add(callback); + } + + private static class Callback { + public BiConsumer function; + public int hRadius, vRadius; + } +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/util/InventoryUtil.java b/src/main/java/me/retucio/sputnik/util/InventoryUtil.java index 740a40c..0fe57b2 100644 --- a/src/main/java/me/retucio/sputnik/util/InventoryUtil.java +++ b/src/main/java/me/retucio/sputnik/util/InventoryUtil.java @@ -1,5 +1,7 @@ package me.retucio.sputnik.util; +import com.github.retucio.neutrino.EventListener; +import com.google.common.eventbus.Subscribe; import me.retucio.sputnik.event.network.DisconnectEvent; import me.retucio.sputnik.event.interact.OpenScreenEvent; import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; @@ -332,6 +334,18 @@ public class InventoryUtil { return -1; } + public static boolean hasInHotbar(Item item) { + List stacks = findAll(stack -> stack.isOf(item)); + for (ItemStack stack : stacks) { + int slot = mc.player.getInventory().getSlotWithStack(stack); + if (slot < HOTBAR_END || slot == OFFHAND_SLOT) { + return true; + } + } + return false; + } + + @EventListener public static void onOpenScreen(OpenScreenEvent event) { if (mc.player == null || event.getScreen() == null @@ -343,6 +357,7 @@ public class InventoryUtil { echestInv = handler.getInventory(); } + @EventListener public void onDisconnect(DisconnectEvent event) { echestInv = null; } diff --git a/src/main/java/me/retucio/sputnik/util/Lists.java b/src/main/java/me/retucio/sputnik/util/Lists.java index 6edeadb..3a0b145 100644 --- a/src/main/java/me/retucio/sputnik/util/Lists.java +++ b/src/main/java/me/retucio/sputnik/util/Lists.java @@ -87,12 +87,26 @@ public class Lists { return map; } + @SafeVarargs + public static Map allTrueExcept(List options, T... exceptions) { + Map map = allFalse(options); + for (T exception : exceptions) map.replace(exception, false); + return map; + } + public static Map allFalse(List options) { Map map = new HashMap<>(); for (T option : options) map.put(option, false); return map; } + @SafeVarargs + public static Map allFalseExcept(List options, T... exceptions) { + Map map = allFalse(options); + for (T exception : exceptions) map.replace(exception, true); + return map; + } + public static Map getMapOfLists(List options, List names) { Map result = new HashMap<>(); diff --git a/src/main/java/me/retucio/sputnik/util/Pool.java b/src/main/java/me/retucio/sputnik/util/Pool.java new file mode 100644 index 0000000..fe3cf83 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/util/Pool.java @@ -0,0 +1,34 @@ +package me.retucio.sputnik.util; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Queue; +import java.util.function.Supplier; + +// https://github.com/MeteorDevelopment/meteor-client/blob/master/src/main/java/meteordevelopment/meteorclient/utils/misc/Pool.java +public final class Pool { + + private final Queue items = new ArrayDeque<>(); + private final Supplier producer; + + public Pool(Supplier producer) { + this.producer = producer; + } + + public synchronized T get() { + if (!items.isEmpty()) return items.poll(); + return producer.get(); + } + + public synchronized void free(T obj) { + items.offer(obj); + } + + public synchronized void freeAll(Iterable objects) { + if (objects instanceof Collection collection) { + items.addAll(collection); + } else { + objects.forEach(items::add); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/util/render/RenderUtil.java b/src/main/java/me/retucio/sputnik/util/render/RenderUtil.java index 9f6e3cd..b848ee8 100644 --- a/src/main/java/me/retucio/sputnik/util/render/RenderUtil.java +++ b/src/main/java/me/retucio/sputnik/util/render/RenderUtil.java @@ -94,6 +94,7 @@ public class RenderUtil { } public static void drawBlockFaceOutlines(MatrixStack matrices, BlockPos pos, Direction face, Color color, float lineWidth, boolean cull) { + if (lineWidth <= 0) return; Vec3d cameraPos = mc.gameRenderer.getCamera().getCameraPos(); float minX = (float) (pos.getX() - cameraPos.x); @@ -295,6 +296,7 @@ public class RenderUtil { // cajas public static void drawOutlineBox(MatrixStack matrices, Box box, Color color, float lineWidth, boolean cull) { + if (lineWidth <= 0) return; Vec3d cameraPos = mc.gameRenderer.getCamera().getCameraPos(); float minX = (float) (box.minX - cameraPos.x);