feat: Add magic bullets

This commit is contained in:
VAC committed 2026-10-16 00:39:01 -03:00
1 parent f84ad4faeb
commit afb8235c08
10 files changed
+99 -3

No files matched your search

+1
View File
@@ -56,6 +56,7 @@ public static class Hotkeys
Toggle("Combat", "FreezeEnemies", "Freeze Enemies", () => Settings.FreezeEnemies, v => Settings.FreezeEnemies = v), Toggle("Combat", "FreezeEnemies", "Freeze Enemies", () => Settings.FreezeEnemies, v => Settings.FreezeEnemies = v),
Toggle("Combat", "WeakenEnemies", "Weaken Enemies", () => Settings.WeakenEnemies, v => Settings.WeakenEnemies = v), Toggle("Combat", "WeakenEnemies", "Weaken Enemies", () => Settings.WeakenEnemies, v => Settings.WeakenEnemies = v),
Toggle("Combat", "RapidAttack", "Rapid Attack", () => Settings.RapidAttack, v => Settings.RapidAttack = v), Toggle("Combat", "RapidAttack", "Rapid Attack", () => Settings.RapidAttack, v => Settings.RapidAttack = v),
Toggle("Combat", "MagicBullets", "Magic Bullets", () => Settings.MagicBullets, v => Settings.MagicBullets = v),
new("Combat", "GatherAll", "Gather All Enemies", CombatFeatures.GatherAll), new("Combat", "GatherAll", "Gather All Enemies", CombatFeatures.GatherAll),
new("Combat", "KillAll", "Kill All Enemies", CombatFeatures.KillAll), new("Combat", "KillAll", "Kill All Enemies", CombatFeatures.KillAll),
+1
View File
@@ -58,6 +58,7 @@ public static partial class Settings
[nameof(ShotPreview)] = "Combat.Aim.ShotPreview", [nameof(ShotPreview)] = "Combat.Aim.ShotPreview",
[nameof(AimFov)] = "Combat.Aim.CursorRadius", [nameof(AimFov)] = "Combat.Aim.CursorRadius",
[nameof(AimRange)] = "Combat.Aim.Range", [nameof(AimRange)] = "Combat.Aim.Range",
[nameof(MagicBullets)] = "Combat.MagicBullets",
[nameof(InstantBreak)] = "Items.InstantBreak", [nameof(InstantBreak)] = "Items.InstantBreak",
[nameof(InfItems)] = "Items.InfiniteItems", [nameof(InfItems)] = "Items.InfiniteItems",
+1 -1
View File
@@ -35,7 +35,7 @@ public static partial class Settings
public static bool KillAura, HitMagnet, FreezeEnemies, WeakenEnemies; public static bool KillAura, HitMagnet, FreezeEnemies, WeakenEnemies;
public static float KillAuraRange = 20f, HitMagnetRange = 30f; public static float KillAuraRange = 20f, HitMagnetRange = 30f;
public static int KillAuraInterval = 10; public static int KillAuraInterval = 10;
public static bool Aimbot, AimPrediction = true, AimDrawTarget = true, AutoAttack, AimExact = true, ShotPreview; public static bool Aimbot, AimPrediction = true, AimDrawTarget = true, AutoAttack, AimExact = true, ShotPreview, MagicBullets;
public static float AimFov = 250f, AimRange = 60f; public static float AimFov = 250f, AimRange = 60f;
public static bool InstantBreak, InfItems, ItemVacuum, AutoSell, BankButtons = true; public static bool InstantBreak, InfItems, ItemVacuum, AutoSell, BankButtons = true;
@@ -0,0 +1,69 @@
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using TerraSharp.Core;
using TerraSharp.Simulation;
namespace TerraSharp.Features.Combat;
public static class MagicBullets
{
const float MinSpeed = 8f;
const int GrappleStyle = 7, YoyoStyle = 99, FlailStyle = 15;
// Projectile type per slot while steered, 0 otherwise.
static readonly int[] Steered = new int[Main.maxProjectiles];
public static bool IsSteered(int slot) => Steered[slot] != 0 && Steered[slot] == Main.projectile[slot].type;
static bool Eligible(Projectile projectile) => projectile is { friendly: true, hostile: false, minion: false, sentry: false, bobber: false, npcProj: false, damage: > 0 } && projectile.aiStyle is not (GrappleStyle or YoyoStyle or FlailStyle) && !Main.projPet[projectile.type] && !ProjectileID.Sets.IsAWhip[projectile.type] && !Sandbox.Held(projectile);
// Projectile.AI postfix for the local player's projectiles.
public static void PostAI(Projectile projectile)
{
int slot = projectile.whoAmI;
NPC? target = Eligible(projectile) ? TargetFor(projectile) : null;
if (target is null)
{
if (IsSteered(slot) && ContentSamples.ProjectilesByType.TryGetValue(projectile.type, out Projectile? sample))
projectile.tileCollide = sample.tileCollide;
Steered[slot] = 0;
return;
}
Vector2 offset = target.Center - projectile.Center;
if (offset.LengthSquared() < 1f)
return;
projectile.velocity = Vector2.Normalize(offset) * Math.Max(projectile.velocity.Length(), MinSpeed);
projectile.tileCollide = false;
Steered[slot] = projectile.type;
}
static NPC? TargetFor(Projectile projectile)
{
if (Aimbot.Target is { } locked && Hostility.IsDamageable(locked))
return locked;
Player player = Main.player[projectile.owner];
float range = Settings.AimRange * 16f, rangeSq = range * range;
NPC? best = null;
float bestDistance = float.MaxValue;
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (!Hostility.IsDamageable(npc) || Vector2.DistanceSquared(npc.Center, player.Center) > rangeSq)
continue;
float distance = Vector2.DistanceSquared(npc.Center, projectile.Center);
if (distance < bestDistance)
{
bestDistance = distance;
best = npc;
}
}
return best;
}
}
+1
View File
@@ -21,6 +21,7 @@ internal sealed class HookRegistry : IDisposable
MainPatches.Install(hooks); MainPatches.Install(hooks);
PlayerPatches.Install(hooks); PlayerPatches.Install(hooks);
NpcPatches.Install(hooks); NpcPatches.Install(hooks);
ProjectilePatches.Install(hooks);
DamageNumberPatches.Install(hooks); DamageNumberPatches.Install(hooks);
VisionPatches.Install(hooks); VisionPatches.Install(hooks);
SpawnRatePatch.Install(hooks); SpawnRatePatch.Install(hooks);
@@ -0,0 +1,22 @@
using System;
using Terraria;
using TerraSharp.Core;
using TerraSharp.Features.Combat;
using TerraSharp.Simulation;
namespace TerraSharp.Patches;
// After the AI chose its velocity, before Projectile.Update moves it.
internal static class ProjectilePatches
{
delegate void Original(Projectile self);
internal static void Install(HookRegistry hooks) => hooks.Add(typeof(Projectile), nameof(Projectile.AI), (Action<Original, Projectile>)AI);
static void AI(Original original, Projectile self)
{
original(self);
if (Plugin.Active && Settings.MagicBullets && !Sandbox.Active && self.owner == Main.myPlayer)
Guard.Run("Projectile.AI postfix", MagicBullets.PostAI, self);
}
}
+1 -1
View File
@@ -133,7 +133,7 @@ public static class Ballistics
track.Age++; track.Age++;
// Tile hits, bounces and homing turns show up as sharp changes. // Tile hits, bounces and homing turns show up as sharp changes.
if (!track.Spoiled && track.Velocities.Count > 0 && (velocity - track.LastVelocity).Length() > Math.Max(4f, track.LastVelocity.Length() * 0.5f)) if (!track.Spoiled && (MagicBullets.IsSteered(i) || (track.Velocities.Count > 0 && (velocity - track.LastVelocity).Length() > Math.Max(4f, track.LastVelocity.Length() * 0.5f))))
track.Spoiled = true; track.Spoiled = true;
if (!track.Spoiled && track.Velocities.Count < MaxSamples) if (!track.Spoiled && track.Velocities.Count < MaxSamples)
track.Velocities.Add(velocity); track.Velocities.Add(velocity);
+1
View File
@@ -101,6 +101,7 @@ public sealed partial class MainWindow
Slider("Max range", ref Settings.AimRange, 5f, 150f, 60f, "{0:0} tiles"); Slider("Max range", ref Settings.AimRange, 5f, 150f, 60f, "{0:0} tiles");
Toggle("Show target", ref Settings.AimDrawTarget); Toggle("Show target", ref Settings.AimDrawTarget);
}); });
Toggle("Magic Bullets", ref Settings.MagicBullets);
Toggle("Shot Preview", ref Settings.ShotPreview); Toggle("Shot Preview", ref Settings.ShotPreview);
Group("Enemies"); Group("Enemies");
+1
View File
@@ -28,6 +28,7 @@ public sealed partial class MainWindow
["Prediction"] = "Leads moving enemies and accounts for projectile drop.", ["Prediction"] = "Leads moving enemies and accounts for projectile drop.",
["Exact check"] = "Checks and corrects aim before firing. Auto Attack skips known misses. Orange brackets mean an unconfirmed shot.", ["Exact check"] = "Checks and corrects aim before firing. Auto Attack skips known misses. Orange brackets mean an unconfirmed shot.",
["Shot Preview"] = "Shows your next shot's path, including spread and homing.", ["Shot Preview"] = "Shows your next shot's path, including spread and homing.",
["Magic Bullets"] = "Your projectiles home in on the aimbot target or the nearest enemy, through blocks.",
["Auto Attack"] = "Swings at enemies in reach and shoots at enemies your weapon can hit.", ["Auto Attack"] = "Swings at enemies in reach and shoots at enemies your weapon can hit.",
["Show target"] = "Shows the cursor radius, target brackets and aim point.", ["Show target"] = "Shows the cursor radius, target brackets and aim point.",
["Kill Aura"] = "Kills enemies in range and drops their loot.", ["Kill Aura"] = "Kills enemies in range and drops their loot.",
+1 -1
View File
@@ -32,7 +32,7 @@ public sealed partial class MainWindow : Window
[ [
new Page("Player", PlayerPage, () => Count(Settings.GodMode, Settings.InfHealth, Settings.InfMana, Settings.InfBreath, Settings.InfFlight, Settings.InstantRespawn, Settings.AutoHeal, Settings.AutoMana, Settings.GhostMode, Settings.DefenseBoost)), new Page("Player", PlayerPage, () => Count(Settings.GodMode, Settings.InfHealth, Settings.InfMana, Settings.InfBreath, Settings.InfFlight, Settings.InstantRespawn, Settings.AutoHeal, Settings.AutoMana, Settings.GhostMode, Settings.DefenseBoost)),
new Page("Movement", MovementPage, () => Count(Settings.Fly, Settings.SuperSpeed, Settings.HighJump, Settings.GravityFlip, Settings.NoFallDamage, Settings.LavaImmune, Settings.WaterWalk, Settings.NoKnockback, Settings.ExtendedReach)), new Page("Movement", MovementPage, () => Count(Settings.Fly, Settings.SuperSpeed, Settings.HighJump, Settings.GravityFlip, Settings.NoFallDamage, Settings.LavaImmune, Settings.WaterWalk, Settings.NoKnockback, Settings.ExtendedReach)),
new Page("Combat", CombatPage, () => Count(Settings.RapidAttack, Settings.AutoSwing, Settings.DamageBoost, Settings.NoManaCost, Settings.NoSpread, Settings.Aimbot, Settings.AutoAttack, Settings.KillAura, Settings.HitMagnet, Settings.FreezeEnemies, Settings.WeakenEnemies)), new Page("Combat", CombatPage, () => Count(Settings.RapidAttack, Settings.AutoSwing, Settings.DamageBoost, Settings.NoManaCost, Settings.NoSpread, Settings.Aimbot, Settings.AutoAttack, Settings.KillAura, Settings.HitMagnet, Settings.FreezeEnemies, Settings.WeakenEnemies, Settings.MagicBullets)),
new Page("Items", ItemsPage, () => Count(Settings.InstantBreak, Settings.InfItems, Settings.ItemVacuum, Settings.AutoSell, Settings.AutoFish, Settings.Sonar)), new Page("Items", ItemsPage, () => Count(Settings.InstantBreak, Settings.InfItems, Settings.ItemVacuum, Settings.AutoSell, Settings.AutoFish, Settings.Sonar)),
new Page("Visuals", VisualsPage, () => Count(Settings.Esp, Settings.Spelunker, Settings.Hunter, Settings.Shine, Settings.NightOwl, Settings.DangerSense, Settings.BiomeSight, Settings.FullBright, Settings.ExtendedZoom)), new Page("Visuals", VisualsPage, () => Count(Settings.Esp, Settings.Spelunker, Settings.Hunter, Settings.Shine, Settings.NightOwl, Settings.DangerSense, Settings.BiomeSight, Settings.FullBright, Settings.ExtendedZoom)),
new Page("World", WorldPage, () => Count(Settings.FreezeTime, Settings.SpawnRateBoost, Settings.MapTeleport)), new Page("World", WorldPage, () => Count(Settings.FreezeTime, Settings.SpawnRateBoost, Settings.MapTeleport)),