using Microsoft.Xna.Framework; using Terraria; using Terraria.ID; using Terraria.ModLoader; namespace SoulHarvest.Projectiles; public class BloodCrescentProjectile : ModProjectile { private bool serverAuthorized; private Vector2 authoritativeCenter; private Vector2 authoritativeVelocity; private readonly float[] authoritativeAI = new float[3]; internal void AuthorizeServerMotion() { if (Main.netMode == NetmodeID.MultiplayerClient) return; serverAuthorized = true; authoritativeCenter = Projectile.Center; authoritativeVelocity = Projectile.velocity; for (int index = 0; index < authoritativeAI.Length; index++) authoritativeAI[index] = float.IsFinite(Projectile.ai[index]) ? Projectile.ai[index] : 0f; } public override void SetStaticDefaults() { ProjectileID.Sets.TrailCacheLength[Type] = 12; ProjectileID.Sets.TrailingMode[Type] = 2; } public override void SetDefaults() { Projectile.width = 34; Projectile.height = 34; Projectile.friendly = true; Projectile.tileCollide = false; Projectile.ignoreWater = true; Projectile.penetrate = 4; Projectile.timeLeft = 210; Projectile.DamageType = DamageClass.Melee; } 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; for (int index = 0; index < authoritativeAI.Length; index++) Projectile.ai[index] = authoritativeAI[index]; } Projectile.localAI[0]++; float timer = Projectile.localAI[0]; if (timer >= 210f) { Projectile.Kill(); return; } Player owner = Main.player[Projectile.owner]; if (!owner.active || owner.dead) { Projectile.Kill(); return; } float outboundTime = Projectile.ai[0] > 0f ? 52f : 34f; if (timer < outboundTime) Projectile.velocity = Projectile.velocity.RotatedBy(0.008f * System.Math.Sign(Projectile.ai[2] == 0f ? 1f : Projectile.ai[2])); else { Vector2 toOwner = owner.Center - Projectile.Center; float returnSpeed = 13f + Projectile.ai[1] * 5f; Projectile.velocity = Vector2.Lerp(Projectile.velocity, toOwner.SafeNormalize(Vector2.UnitY) * returnSpeed, 0.11f); if (timer > outboundTime + 12f && toOwner.LengthSquared() < 24f * 24f) Projectile.Kill(); } Projectile.rotation += 0.34f * System.Math.Sign(Projectile.velocity.X); if (Main.netMode != NetmodeID.Server) { Lighting.AddLight(Projectile.Center, new Vector3(0.55f, 0.03f, 0.08f)); if (Main.rand.NextBool(2)) { Dust dust = Dust.NewDustPerfect(Projectile.Center, DustID.Blood, -Projectile.velocity * 0.1f, 70, new Color(240, 35, 65), 0.85f + Projectile.ai[1] * 0.25f); dust.noGravity = true; } } if (!Projectile.active) return; Projectile.position += Projectile.velocity; if (Main.netMode == NetmodeID.Server) { authoritativeCenter = Projectile.Center; authoritativeVelocity = Projectile.velocity; } } public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone) { Projectile.velocity = Projectile.velocity.RotatedByRandom(0.35f) * 0.92f; if (Main.netMode == NetmodeID.Server) { authoritativeCenter = Projectile.Center; authoritativeVelocity = Projectile.velocity; } } public override bool PreDraw(ref Color lightColor) => ReaperProjectileHelper.DrawTrail(Projectile, new Color(245, 25, 70), scaleMultiplier: 1f + Projectile.ai[1] * 0.18f, copies: 10); }