using Microsoft.Xna.Framework; using Terraria; using Terraria.ID; using Terraria.ModLoader; namespace SoulHarvest.Projectiles; public class DeathSickleProjectile : ModProjectile { private bool serverAuthorized; private Vector2 authoritativeCenter; private Vector2 authoritativeVelocity; internal void AuthorizeServerMotion() { if (Main.netMode == NetmodeID.MultiplayerClient) return; serverAuthorized = true; authoritativeCenter = Projectile.Center; authoritativeVelocity = Projectile.velocity; } public override void SetDefaults() { Projectile.friendly = true; Projectile.hostile = false; Projectile.width = 56; Projectile.height = 56; Projectile.tileCollide = false; Projectile.penetrate = -1; Projectile.ignoreWater = true; Projectile.timeLeft = 480; Projectile.light = 0.8f; Projectile.localNPCHitCooldown = 15; Projectile.usesLocalNPCImmunity = true; } public override bool ShouldUpdatePosition() => false; public override void AI() { if (Main.netMode == NetmodeID.Server) { if (!serverAuthorized) { Projectile.Kill(); return; } Projectile.Center = authoritativeCenter; Projectile.velocity = authoritativeVelocity; } Projectile.localAI[0]++; if (Projectile.localAI[0] >= 480f) { Projectile.Kill(); return; } Projectile.rotation += 0.48f; if (Main.netMode != NetmodeID.Server) { Dust blood = Dust.NewDustDirect(Projectile.position, Projectile.width, Projectile.height, DustID.Blood, Scale: 1.1f); blood.noGravity = true; blood.velocity *= 0.2f; if (Main.rand.NextBool(2)) { Dust soul = Dust.NewDustDirect(Projectile.position, Projectile.width, Projectile.height, DustID.AncientLight, Scale: 0.85f); soul.noGravity = true; soul.velocity *= 0.1f; } } NPC? target = FindTarget(700f); if (target is not null) { float speed = Projectile.velocity.Length(); float curve = MathHelper.ToRadians(18f) * (float)System.Math.Sin(Projectile.localAI[0] * 0.09f); Vector2 desiredVelocity = (target.Center - Projectile.Center).SafeNormalize(Vector2.UnitX).RotatedBy(curve) * speed; Projectile.velocity = Vector2.Lerp(Projectile.velocity, desiredVelocity, 0.09f); } Projectile.position += Projectile.velocity; if (Main.netMode == NetmodeID.Server) { authoritativeCenter = Projectile.Center; authoritativeVelocity = Projectile.velocity; } } private NPC? FindTarget(float maximumDistance) { NPC? target = null; foreach (NPC npc in Main.ActiveNPCs) { if (!npc.CanBeChasedBy(Projectile)) continue; float distance = Vector2.Distance(npc.Center, Projectile.Center); if (distance < maximumDistance) { maximumDistance = distance; target = npc; } } return target; } }