diff --git a/Sources/Everglow.Function/Templates/Weapons/HandholdProjectile.cs b/Sources/Everglow.Function/Templates/Weapons/HandheldProjectile.cs similarity index 64% rename from Sources/Everglow.Function/Templates/Weapons/HandholdProjectile.cs rename to Sources/Everglow.Function/Templates/Weapons/HandheldProjectile.cs index a969a25f91..4ce1f9f684 100644 --- a/Sources/Everglow.Function/Templates/Weapons/HandholdProjectile.cs +++ b/Sources/Everglow.Function/Templates/Weapons/HandheldProjectile.cs @@ -1,11 +1,12 @@ +using Everglow.Commons.Utilities; using Terraria.DataStructures; namespace Everglow.Commons.Templates.Weapons; /// -/// Handhold projectile. +/// Handheld projectile. /// -public abstract class HandholdProjectile : ModProjectile +public abstract class HandheldProjectile : ModProjectile { /// /// The rotation of texture. @@ -35,11 +36,54 @@ public abstract class HandholdProjectile : ModProjectile public float DepartLength = 50; public Vector2 ArmRootPos = Vector2.zeroVector; + /// + /// World-space cursor of the owner. Main.MouseWorld already accounts for local gravity. + /// + protected Vector2 AimPosition + { + get + { + Player player = Main.player[Projectile.owner]; + player.ListenMouseWorld(); + return Projectile.owner == Main.myPlayer ? Main.MouseWorld : player.MouseWorld(); + } + } + + protected Vector2 GravityDrawOffset => DrawOffset * new Vector2(1, Main.player[Projectile.owner].gravDir); + + private bool rotationInitialized; + public override void OnSpawn(IEntitySource source) { - base.OnSpawn(source); + InitializeRotation(); + } + + private void InitializeRotation() + { + rotationInitialized = true; + // Spawn velocity is synchronized by Terraria, unlike the observer's cursor. + Projectile.rotation = Projectile.velocity.SafeNormalize(Vector2.UnitX * Main.player[Projectile.owner].direction).ToRotation() + MathHelper.PiOver4; } + public override bool PreAI() + { + Player player = Main.player[Projectile.owner]; + if (!player.active || player.dead) + { + Projectile.Kill(); + return false; + } + // Received projectiles do not run OnSpawn. + if (!rotationInitialized) + { + InitializeRotation(); + } + player.ListenMouseWorld(); + return true; + } + + public override bool ShouldUpdatePosition() => false; + public override void SetDefaults() { Projectile.width = 32; @@ -69,13 +113,13 @@ public virtual void HeldProjectileAI() { Player player = Main.player[Projectile.owner]; player.heldProj = Projectile.whoAmI; - ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2); + ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2 * player.gravDir); player.SetCompositeArmFront(true, Player.CompositeArmStretchAmount.Full, (Projectile.rotation - MathF.PI * 0.25f) * player.gravDir - MathF.PI * 0.5f); - Vector2 mouseToPlayer = Main.MouseWorld - ArmRootPos; - mouseToPlayer = Vector2.Normalize(mouseToPlayer); + Vector2 mouseToPlayer = AimPosition - ArmRootPos; + mouseToPlayer = mouseToPlayer.SafeNormalize(Vector2.UnitX * player.direction); if (player.controlUseItem) { - float addRot = -MathF.Asin(Vector3.Cross(new Vector3(mouseToPlayer, 0), new Vector3(new Vector2(0, 1).RotatedBy(Projectile.rotation - Math.PI * 0.75), 0)).Z); + float addRot = MathHelper.WrapAngle(mouseToPlayer.ToRotation() + MathHelper.PiOver4 - Projectile.rotation); addRot *= LerpFactorOfRotation; if (Math.Abs(addRot) > MaxRotationSpeed) { @@ -102,7 +146,7 @@ public virtual void HeldProjectileAI() public virtual void RemoveExtraSameProjectiles(Player owner) { - if (owner.ownedProjectileCounts[Projectile.type] > 1) + if (Projectile.owner == Main.myPlayer && owner.ownedProjectileCounts[Projectile.type] > 1) { int count = owner.ownedProjectileCounts[Projectile.type]; for (int a = Main.projectile.Length - 1; a >= 0; a--) @@ -137,12 +181,12 @@ public virtual void DrawBaseTexture(Color lightColor) Player player = Main.player[Projectile.owner]; var texMain = (Texture2D)ModContent.Request(Texture); SpriteEffects se = SpriteEffects.None; - if (player.direction == -1) + if (player.direction * player.gravDir == -1) { se = SpriteEffects.FlipVertically; } - float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction; - Main.spriteBatch.Draw(texMain, Projectile.Center - Main.screenPosition + DrawOffset, null, lightColor, rot, texMain.Size() / 2f, 1f, se, 0); + float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction * player.gravDir; + Main.spriteBatch.Draw(texMain, Projectile.Center - Main.screenPosition + GravityDrawOffset, null, lightColor, rot, texMain.Size() / 2f, 1f, se, 0); } } diff --git a/Sources/Everglow.UnitTests/Function/HandheldProjectileTest.cs b/Sources/Everglow.UnitTests/Function/HandheldProjectileTest.cs new file mode 100644 index 0000000000..9f599fed61 --- /dev/null +++ b/Sources/Everglow.UnitTests/Function/HandheldProjectileTest.cs @@ -0,0 +1,172 @@ +using System.Reflection; +using Everglow.Commons; +using Everglow.Commons.Templates.Weapons; +using Everglow.Yggdrasil.YggdrasilTown.Projectiles.Magic; +using Microsoft.Xna.Framework; +using Terraria; +using Terraria.ModLoader; + +namespace Everglow.UnitTests.Function; + +[TestClass] +[DoNotParallelize] +public class HandheldProjectileTest +{ + private Player[] originalPlayers = null!; + private int originalMyPlayer; + private Vector2 originalScreenPosition; + private int originalMouseX; + private int originalMouseY; + private EverglowPlayer? originalModPlayer; + private Player owner = null!; + private EverglowPlayer modPlayer = null!; + private TestProjectile held = null!; + + private static readonly PropertyInfo InstanceProperty = typeof(ContentInstance).GetProperty("Instance")!; + private static readonly FieldInfo MouseWorldField = typeof(EverglowPlayer).GetField("mouseWorld", BindingFlags.Instance | BindingFlags.NonPublic)!; + + [TestInitialize] + public void Initialize() + { + Program.SavePath = string.Empty; + originalPlayers = Main.player; + originalMyPlayer = Main.myPlayer; + originalScreenPosition = Main.screenPosition; + originalMouseX = Main.mouseX; + originalMouseY = Main.mouseY; + originalModPlayer = ContentInstance.Instance; + + owner = new Player { whoAmI = 1, active = true, controlUseItem = true, itemTimeMax = 30 }; + owner.Center = new Vector2(100, 100); + Main.player = new Player[Main.maxPlayers + 1]; + Main.player[0] = new Player { whoAmI = 0 }; + Main.player[1] = owner; + Main.myPlayer = 0; + Main.screenPosition = Vector2.Zero; + Main.mouseX = 300; + Main.mouseY = 100; + + modPlayer = (EverglowPlayer)new EverglowPlayer().NewInstance(owner); + InstanceProperty.SetValue(null, modPlayer); + typeof(Player).GetField("modPlayers", BindingFlags.Instance | BindingFlags.NonPublic)!.SetValue(owner, new ModPlayer[] { modPlayer }); + held = (TestProjectile)new TestProjectile().NewInstance(new Projectile { owner = 1, active = true }); + held.SetDefaults(); + held.Projectile.rotation = MathHelper.PiOver4; + } + + [TestCleanup] + public void Cleanup() + { + Main.player = originalPlayers; + Main.myPlayer = originalMyPlayer; + Main.screenPosition = originalScreenPosition; + Main.mouseX = originalMouseX; + Main.mouseY = originalMouseY; + InstanceProperty.SetValue(null, originalModPlayer); + } + + [TestMethod] + public void RemoteOwner_AimsAtSynchronizedMouseInsteadOfLocalMouse() + { + MouseWorldField.SetValue(modPlayer, new Vector2(96, 298)); + held.HeldProjectileAI(); + Assert.AreEqual(96f, held.Projectile.Center.X, 0.001f); + Assert.AreEqual(148f, held.Projectile.Center.Y, 0.001f); + } + + [TestMethod] + public void InvertedGravity_MirrorsArmRootOffset() + { + owner.gravDir = -1; + MouseWorldField.SetValue(modPlayer, new Vector2(296, 102)); + held.HeldProjectileAI(); + Assert.AreEqual(new Vector2(96, 102), held.ArmRootPos); + } + + [TestMethod] + public void AimAtArmRoot_DoesNotProduceNonFinitePosition() + { + Main.myPlayer = 1; + Main.mouseX = 96; + Main.mouseY = 98; + MouseWorldField.SetValue(modPlayer, new Vector2(96, 98)); + held.HeldProjectileAI(); + Assert.IsTrue(float.IsFinite(held.Projectile.rotation)); + Assert.IsTrue(float.IsFinite(held.Projectile.Center.X)); + Assert.IsTrue(float.IsFinite(held.Projectile.Center.Y)); + } + + [TestMethod] + public void SpawnDirection_UsesNetworkedVelocityInsteadOfObserverMouse() + { + held.Projectile.velocity = new Vector2(0, -12); + held.OnSpawn(new Terraria.DataStructures.EntitySource_Misc("Test")); + Assert.AreEqual(-MathHelper.PiOver4, held.Projectile.rotation, 0.001f); + } + + [TestMethod] + public void LocalOwner_RequestsMouseSynchronizationBeforeDerivedAI() + { + Main.myPlayer = 1; + Assert.IsTrue(held.PreAI()); + var listenField = typeof(EverglowPlayer).GetField("listenMouseWorld", BindingFlags.Instance | BindingFlags.NonPublic)!; + Assert.IsTrue((bool)listenField.GetValue(modPlayer)!); + } + + [TestMethod] + public void OppositeAim_TurnsToTheOtherSide() + { + MouseWorldField.SetValue(modPlayer, new Vector2(-104, 98)); + held.HeldProjectileAI(); + Assert.AreEqual(46f, held.Projectile.Center.X, 0.001f); + Assert.AreEqual(98f, held.Projectile.Center.Y, 0.001f); + Assert.AreEqual(-1, owner.direction); + } + + [TestMethod] + public void RemoteOwnerGravity_IsIndependentOfObserverGravity() + { + Main.player[0].gravDir = -1; + MouseWorldField.SetValue(modPlayer, new Vector2(96, 298)); + held.HeldProjectileAI(); + Assert.AreEqual(148f, held.Projectile.Center.Y, 0.001f); + } + + [TestMethod] + public void InvertedGravity_MirrorsDrawOffset() + { + owner.gravDir = -1; + held.DrawOffset = new Vector2(-6, -8); + Assert.AreEqual(new Vector2(-6, 8), held.DrawOffsetInWorld); + } + + [TestMethod] + public void CrystalBeforeParentSync_WaitsWithoutDereferencingMissingParent() + { + var crystal = (YggdrasilAmberLaser_crystal)new YggdrasilAmberLaser_crystal().NewInstance(new Projectile { owner = 1, timeLeft = 900 }); + crystal.AI(); + Assert.IsFalse(crystal.EndAI); + } + + [TestMethod] + public void ReceivedProjectile_InitializesDirectionWithoutOnSpawn() + { + held.Projectile.velocity = new Vector2(0, -12); + held.PreAI(); + Assert.AreEqual(-MathHelper.PiOver4, held.Projectile.rotation, 0.001f); + } + + [TestMethod] + public void ReceivedCrystal_InitializesTrackWithoutOnSpawn() + { + var projectile = new Projectile { owner = 1, timeLeft = 900, Center = new Vector2(120, 140) }; + var crystal = (YggdrasilAmberLaser_crystal)new YggdrasilAmberLaser_crystal().NewInstance(projectile); + crystal.AI(); + Assert.AreEqual(new Vector2(120, 140), crystal.CrystalTrack[0]); + } + + private sealed class TestProjectile : HandheldProjectile + { + public Vector2 DrawOffsetInWorld => GravityDrawOffset; + } +} diff --git a/Sources/Modules/Example/Test/HandholdProj.cs b/Sources/Modules/Example/Test/HandholdProj.cs index 48dc26e939..77f2c730c4 100644 --- a/Sources/Modules/Example/Test/HandholdProj.cs +++ b/Sources/Modules/Example/Test/HandholdProj.cs @@ -2,7 +2,7 @@ namespace Everglow.Example.Test; -public class HandholdProj : HandholdProjectile +public class HandholdProj : HandheldProjectile { public override string LocalizationCategory => Everglow.Commons.Utilities.LocalizationUtils.Categories.MiscsProjectiles; } diff --git a/Sources/Modules/Example/Test/HandholdProj2.cs b/Sources/Modules/Example/Test/HandholdProj2.cs index ac6085102f..0a9e09470a 100644 --- a/Sources/Modules/Example/Test/HandholdProj2.cs +++ b/Sources/Modules/Example/Test/HandholdProj2.cs @@ -2,13 +2,12 @@ namespace Everglow.Example.Test; -public class HandholdProj2 : HandholdProjectile +public class HandholdProj2 : HandheldProjectile { public override string LocalizationCategory => Everglow.Commons.Utilities.LocalizationUtils.Categories.MiscsProjectiles; public override void SetDef() { TextureRotation = 1.204f; - base.SetDef(); } } diff --git a/Sources/Modules/Myth/LanternMoon/Items/Weapons/GildingRevolver.cs b/Sources/Modules/Myth/LanternMoon/Items/Weapons/GildingRevolver.cs index aab54dea20..b83a95697c 100644 --- a/Sources/Modules/Myth/LanternMoon/Items/Weapons/GildingRevolver.cs +++ b/Sources/Modules/Myth/LanternMoon/Items/Weapons/GildingRevolver.cs @@ -35,6 +35,11 @@ public override void SetDefaults() public override void HoldItem(Player player) { + if (player.whoAmI != Main.myPlayer) + { + return; + } + player.ListenMouseWorld(); if (player.ownedProjectileCounts[ModContent.ProjectileType()] <= 0) { Projectile.NewProjectile(Item.GetSource_FromAI(), player.Center, Vector2.zeroVector, ModContent.ProjectileType(), 0, 0, player.whoAmI); diff --git a/Sources/Modules/Myth/LanternMoon/Items/Weapons/KeroseneLanternFlameThrower.cs b/Sources/Modules/Myth/LanternMoon/Items/Weapons/KeroseneLanternFlameThrower.cs index 92cb07a5af..2a621ebdb4 100644 --- a/Sources/Modules/Myth/LanternMoon/Items/Weapons/KeroseneLanternFlameThrower.cs +++ b/Sources/Modules/Myth/LanternMoon/Items/Weapons/KeroseneLanternFlameThrower.cs @@ -40,6 +40,11 @@ public override void SetDefaults() public override void HoldItem(Player player) { + if (player.whoAmI != Main.myPlayer) + { + return; + } + player.ListenMouseWorld(); if (UIBar is not null && UIBar.Active) { return; diff --git a/Sources/Modules/Myth/LanternMoon/Projectiles/Weapons/GildingRevolver_Hold.cs b/Sources/Modules/Myth/LanternMoon/Projectiles/Weapons/GildingRevolver_Hold.cs index 8c800b77d4..fac67ad62b 100644 --- a/Sources/Modules/Myth/LanternMoon/Projectiles/Weapons/GildingRevolver_Hold.cs +++ b/Sources/Modules/Myth/LanternMoon/Projectiles/Weapons/GildingRevolver_Hold.cs @@ -1,11 +1,10 @@ using Everglow.Commons.Templates.Weapons; using Everglow.Myth.LanternMoon.Items.Weapons; using Terraria.Audio; -using Terraria.DataStructures; namespace Everglow.Myth.LanternMoon.Projectiles.Weapons; -public class GildingRevolver_Hold : HandholdProjectile +public class GildingRevolver_Hold : HandheldProjectile { public override string LocalizationCategory => Everglow.Commons.Utilities.LocalizationUtils.Categories.RangedProjectiles; @@ -41,17 +40,13 @@ public override void SetDef() ProjectileID.Sets.HeldProjDoesNotUsePlayerGfxOffY[Type] = true; } - public override void OnSpawn(IEntitySource source) - { - } - public override void AI() { if (ReloadCooling <= 0) { if (NormalBulletTimer < 120) { - NormalBulletTimer += 6 * (5f / Main.LocalPlayer.itemTimeMax); + NormalBulletTimer += 6 * (5f / Math.Max(Main.player[Projectile.owner].itemTimeMax, 1)); } if (NormalBulletTimer > 120) @@ -87,7 +82,7 @@ public override void AI() { if (proj is not null && proj.active && proj.type == ModContent.ProjectileType() && proj.owner == Projectile.owner) { - if ((Main.MouseWorld - proj.Center).Length() < 70) + if ((AimPosition - proj.Center).Length() < 70) { MouseInLanternZone = true; break; @@ -102,14 +97,14 @@ public override void HeldProjectileAI() Player player = Main.player[Projectile.owner]; player.heldProj = Projectile.whoAmI; int dir = 1; - if (Main.MouseWorld.X < player.MountedCenter.X) + if (AimPosition.X < player.MountedCenter.X) { dir = -1; } - ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2); - Vector2 mouseToPlayer = Main.MouseWorld - ArmRootPos; - mouseToPlayer = Vector2.Normalize(mouseToPlayer); + ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2 * player.gravDir); + Vector2 mouseToPlayer = AimPosition - ArmRootPos; + mouseToPlayer = mouseToPlayer.SafeNormalize(Vector2.UnitX * player.direction); Projectile.rotation = mouseToPlayer.ToRotation() + MathHelper.PiOver4; Projectile.Center = player.MountedCenter + mouseToPlayer * 12; Item item = player.HeldItem; @@ -158,6 +153,10 @@ public override void HeldProjectileAI() private void Shoot() { + if (Projectile.owner != Main.myPlayer) + { + return; + } Player player = Main.player[Projectile.owner]; var item = player.HeldItem; if (item is not null @@ -166,48 +165,50 @@ private void Shoot() && gildingRevolver.ShootType >= 0) { Vector2 vel = (Projectile.rotation - MathHelper.PiOver4).ToRotationVector2().RotatedByRandom(0.02f) * item.shootSpeed; - Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), Projectile.Center + vel.NormalizeSafe() * -2 + DrawOffset, vel, gildingRevolver.ShootType, item.damage, item.knockBack, Projectile.owner); + Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), Projectile.Center + vel.NormalizeSafe() * -2 + GravityDrawOffset, vel, gildingRevolver.ShootType, item.damage, item.knockBack, Projectile.owner); UsedBulletsCount++; if (NormalBulletsCount == 6 || NormalBulletsCount == 3) { vel = (Projectile.rotation - MathHelper.PiOver4).ToRotationVector2().RotatedByRandom(0.02f) * item.shootSpeed / 4f; - Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), Projectile.Center + vel.NormalizeSafe() * -2 + DrawOffset, vel, ModContent.ProjectileType(), item.damage, item.knockBack, Projectile.owner); + Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), Projectile.Center + vel.NormalizeSafe() * -2 + GravityDrawOffset, vel, ModContent.ProjectileType(), item.damage, item.knockBack, Projectile.owner); } } } private void ShootPhantom() { - SoundEngine.PlaySound(SoundID.Item101); + SoundEngine.PlaySound(SoundID.Item101, Projectile.Center); + if (Projectile.owner != Main.myPlayer) + { + return; + } Player player = Main.player[Projectile.owner]; Item item = player.HeldItem; if (item is null) { return; } - Vector2 vel = (Main.MouseWorld - Projectile.Center).NormalizeSafe().RotatedByRandom(0.03f) * item.shootSpeed * 1.5f; - Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), Projectile.Center + vel.NormalizeSafe() * -2 + DrawOffset, vel, ModContent.ProjectileType(), (int)(item.damage * 0.7f), item.knockBack, Projectile.owner); + Vector2 vel = (AimPosition - Projectile.Center).NormalizeSafe().RotatedByRandom(0.03f) * item.shootSpeed * 1.5f; + Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), Projectile.Center + vel.NormalizeSafe() * -2 + GravityDrawOffset, vel, ModContent.ProjectileType(), (int)(item.damage * 0.7f), item.knockBack, Projectile.owner); } private void Shoot_RightClick() { + if (Projectile.owner != Main.myPlayer) + { + return; + } Player player = Main.player[Projectile.owner]; Item item = player.HeldItem; if (item is null) { return; } - Vector2 vel = (Main.MouseWorld - Projectile.Center).NormalizeSafe() * item.shootSpeed * 1.5f; - Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), Projectile.Center + vel.NormalizeSafe() * -2 + DrawOffset, vel, ModContent.ProjectileType(), item.damage, item.knockBack, Projectile.owner); + Vector2 vel = (AimPosition - Projectile.Center).NormalizeSafe() * item.shootSpeed * 1.5f; + Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), Projectile.Center + vel.NormalizeSafe() * -2 + GravityDrawOffset, vel, ModContent.ProjectileType(), item.damage, item.knockBack, Projectile.owner); UsedBulletsCount++; } - public override bool PreDraw(ref Color lightColor) - { - DrawBaseTexture(lightColor); - return false; - } - public override void DrawBaseTexture(Color lightColor) { Player player = Main.player[Projectile.owner]; @@ -220,12 +221,12 @@ public override void DrawBaseTexture(Color lightColor) { var texMain = ModAsset.GildingRevolver_Hold.Value; SpriteEffects se = SpriteEffects.None; - if (player.direction == -1) + if (player.direction * player.gravDir == -1) { se = SpriteEffects.FlipVertically; } - float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction; - Vector2 drawCenter = Projectile.Center - Main.screenPosition + DrawOffset; + float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction * player.gravDir; + Vector2 drawCenter = Projectile.Center - Main.screenPosition + GravityDrawOffset; Main.spriteBatch.Draw(texMain, drawCenter, null, lightColor, rot, texMain.Size() * 0.5f, 1f, se, 0); } diff --git a/Sources/Modules/Myth/LanternMoon/Projectiles/Weapons/KeroseneLanternFlameThrower_Hold.cs b/Sources/Modules/Myth/LanternMoon/Projectiles/Weapons/KeroseneLanternFlameThrower_Hold.cs index 64aa7a5dfa..8fa0342400 100644 --- a/Sources/Modules/Myth/LanternMoon/Projectiles/Weapons/KeroseneLanternFlameThrower_Hold.cs +++ b/Sources/Modules/Myth/LanternMoon/Projectiles/Weapons/KeroseneLanternFlameThrower_Hold.cs @@ -2,17 +2,21 @@ using Everglow.Commons.Graphics; using Everglow.Commons.Templates.Weapons; using Everglow.Myth.LanternMoon.Items.Weapons; +using ReLogic.Utilities; using Terraria.Audio; -using Terraria.DataStructures; namespace Everglow.Myth.LanternMoon.Projectiles.Weapons; -public class KeroseneLanternFlameThrower_Hold : HandholdProjectile, IWarpProjectile_warpStyle2 +public class KeroseneLanternFlameThrower_Hold : HandheldProjectile, IWarpProjectile_warpStyle2 { public override string LocalizationCategory => Everglow.Commons.Utilities.LocalizationUtils.Categories.RangedProjectiles; public int Timer = 0; + private SlotId beginSoundSlot = SlotId.Invalid; + private SlotId cycleSoundSlot = SlotId.Invalid; + private bool playedEndSound; + public struct SubProj { public Vector2 Pos; @@ -33,17 +37,16 @@ public override void SetDef() DrawOffset = new Vector2(-6, -8); } - public override void OnSpawn(IEntitySource source) - { - SoundEngine.PlaySound(new SoundStyle(ModAsset.Flamethrower_begin_Mod), Projectile.Center); - } - public override void AI() { + if (Timer == 0) + { + beginSoundSlot = SoundEngine.PlaySound(new SoundStyle(ModAsset.Flamethrower_begin_Mod), Projectile.Center); + } Timer++; float shootSpeed = GetShootSpeed(); Vector2 vel = (Projectile.rotation - MathHelper.PiOver4).ToRotationVector2() * shootSpeed; - Vector2 pos = Projectile.Center + vel.NormalizeSafe() * (32 - shootSpeed) + DrawOffset; + Vector2 pos = Projectile.Center + vel.NormalizeSafe() * (32 - shootSpeed) + GravityDrawOffset; var sub = new SubProj { Pos = pos, @@ -77,17 +80,18 @@ public override void HeldProjectileAI() if (player.wet) { Projectile.Kill(); + return; } player.heldProj = Projectile.whoAmI; - ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2); + ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2 * player.gravDir); player.SetCompositeArmFront(true, Player.CompositeArmStretchAmount.Full, (Projectile.rotation - MathF.PI * 0.25f) * player.gravDir - MathF.PI * 0.5f); int dir = 1; - if (Main.MouseWorld.X < player.MountedCenter.X) + if (AimPosition.X < player.MountedCenter.X) { dir = -1; } - Vector2 mouseToPlayer = Main.MouseWorld - ArmRootPos; + Vector2 mouseToPlayer = AimPosition - ArmRootPos; mouseToPlayer = mouseToPlayer.NormalizeSafe(); float powerRate = 1; if (player.HeldItem is not null && player.HeldItem.type == ModContent.ItemType()) @@ -97,9 +101,10 @@ public override void HeldProjectileAI() } if (player.controlUseItem) { + playedEndSound = false; if (Timer % (int)(45 * 2.5f / (powerRate + 2)) == (int)(15 * 2.5f / (powerRate + 2))) { - SoundEngine.PlaySound(new SoundStyle(ModAsset.Flamethrower_cycle_Mod).WithPitchOffset(powerRate), Projectile.Center); + cycleSoundSlot = SoundEngine.PlaySound(new SoundStyle(ModAsset.Flamethrower_cycle_Mod).WithPitchOffset(powerRate), Projectile.Center); } float addRot = -MathF.Asin(Vector3.Cross(new Vector3(mouseToPlayer, 0), new Vector3(new Vector2(0, 1).RotatedBy(Projectile.rotation - Math.PI * 0.75), 0)).Z); addRot *= LerpFactorOfRotation; @@ -111,16 +116,24 @@ public override void HeldProjectileAI() Projectile.Center = ArmRootPos + new Vector2(0, 1).RotatedBy(Projectile.rotation - Math.PI * 0.75) * DepartLength; Projectile.timeLeft = player.itemTimeMax; float shootSpeed = GetShootSpeed(); - if (Math.Abs(addRot) > 0.02f - || Timer % 2 == 1) + if (Projectile.owner == Main.myPlayer && (Math.Abs(addRot) > 0.02f + || Timer % 2 == 1)) { Vector2 vel = (Projectile.rotation - MathHelper.PiOver4).ToRotationVector2() * shootSpeed; - Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), Projectile.Center + vel.NormalizeSafe() * 60 + DrawOffset, vel, ModContent.ProjectileType(), Projectile.damage, Projectile.knockBack, Projectile.owner); + Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), Projectile.Center + vel.NormalizeSafe() * 60 + GravityDrawOffset, vel, ModContent.ProjectileType(), Projectile.damage, Projectile.knockBack, Projectile.owner); } } - else + else if (!playedEndSound) { - SoundEngine.StopTrackedSounds(); + playedEndSound = true; + if (SoundEngine.TryGetActiveSound(beginSoundSlot, out var beginSound)) + { + beginSound.Stop(); + } + if (SoundEngine.TryGetActiveSound(cycleSoundSlot, out var cycleSound)) + { + cycleSound.Stop(); + } SoundEngine.PlaySound(new SoundStyle(ModAsset.Flamethrower_end_Mod).WithPitchOffset(Math.Clamp(powerRate, -1, 0)), Projectile.Center); } player.direction = dir; @@ -138,25 +151,18 @@ public float GetShootSpeed() return 12f; } - public override bool PreDraw(ref Color lightColor) - { - DrawBaseTexture(lightColor); - - return false; - } - public override void DrawBaseTexture(Color lightColor) { Player player = Main.player[Projectile.owner]; var texMain = ModAsset.KeroseneLanternFlameThrower_Hold.Value; SpriteEffects se = SpriteEffects.None; - if (player.direction == -1) + if (player.direction * player.gravDir == -1) { se = SpriteEffects.FlipVertically; } - float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction; - Vector2 drawCenter = Projectile.Center - Main.screenPosition + DrawOffset; + float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction * player.gravDir; + Vector2 drawCenter = Projectile.Center - Main.screenPosition + GravityDrawOffset; var bars0 = new List(); var bars1 = new List(); @@ -235,24 +241,16 @@ public void CreateTrailVertex(List bars0, List bars1, List= 0) + float width = 1f; + if (i >= ShootProjPos.Count - 5) { - width = 1f; - if (i >= ShootProjPos.Count - 5) - { - width = 1 - MathF.Cos((ShootProjPos.Count - i) / 5f * MathHelper.Pi); - width *= 0.5f; - } + width = 1 - MathF.Cos((ShootProjPos.Count - i) / 5f * MathHelper.Pi); + width *= 0.5f; } // timeValue, animate the trail. float timeValue = Timer * 0.05f; - Vector2 drawPos = Projectile.Center; - if (i >= 0) - { - drawPos = ShootProjPos[i].Pos; - } + Vector2 drawPos = ShootProjPos[i].Pos; Color drawColor = GetTrailColor(i, style); drawPos += offset; bars0.Add(drawPos + new Vector2(0, 1).RotatedBy(MathHelper.TwoPi * 2f / 3f) * trailWidth, drawColor, ModifyWarpTexCoordinate(factor, timeValue, 0, width)); diff --git a/Sources/Modules/Myth/TheTusk/Projectiles/Weapon/ToothBow.cs b/Sources/Modules/Myth/TheTusk/Projectiles/Weapon/ToothBow.cs index b47ce3936c..83b29cc9c5 100644 --- a/Sources/Modules/Myth/TheTusk/Projectiles/Weapon/ToothBow.cs +++ b/Sources/Modules/Myth/TheTusk/Projectiles/Weapon/ToothBow.cs @@ -3,7 +3,7 @@ namespace Everglow.Myth.TheTusk.Projectiles.Weapon; -public class ToothBow : HandholdProjectile +public class ToothBow : HandheldProjectile { public override string LocalizationCategory => Everglow.Commons.Utilities.LocalizationUtils.Categories.RangedProjectiles; @@ -16,22 +16,17 @@ public override void SetDef() DepartLength = 20; } - public override void AI() - { - base.AI(); - } - public override void HeldProjectileAI() { Player player = Main.player[Projectile.owner]; player.heldProj = Projectile.whoAmI; int dir = 1; - if (Main.MouseWorld.X < player.MountedCenter.X) + if (AimPosition.X < player.MountedCenter.X) { dir = -1; } - ArmRootPos = player.MountedCenter + new Vector2(-4 * dir, -2); + ArmRootPos = player.MountedCenter + new Vector2(-4 * dir, -2 * player.gravDir); float backValue = (player.itemTimeMax - player.itemTime) / (float)player.itemTimeMax; Player.CompositeArmStretchAmount compositeArmStretchAmount = Player.CompositeArmStretchAmount.Full; if (backValue < 0.5f) @@ -68,8 +63,8 @@ public override void HeldProjectileAI() player.SetCompositeArmFront(true, compositeArmStretchAmount, (Projectile.rotation - MathF.PI * 0.25f) * player.gravDir + MathF.PI * 0.5f); } player.SetCompositeArmBack(true, Player.CompositeArmStretchAmount.Full, (Projectile.rotation - MathF.PI * 0.25f) * player.gravDir - MathF.PI * 0.5f); - Vector2 mouseToPlayer = Main.MouseWorld - ArmRootPos; - mouseToPlayer = Vector2.Normalize(mouseToPlayer); + Vector2 mouseToPlayer = AimPosition - ArmRootPos; + mouseToPlayer = mouseToPlayer.SafeNormalize(Vector2.UnitX * player.direction); if (player.controlUseItem) { float addRot = -MathF.Asin(Vector3.Cross(new Vector3(mouseToPlayer, 0), new Vector3(new Vector2(0, 1).RotatedBy(Projectile.rotation - Math.PI * 0.75), 0)).Z); @@ -86,37 +81,34 @@ public override void HeldProjectileAI() else { SoundEngine.PlaySound(SoundID.Item5, Projectile.Center); - Projectile.NewProjectileDirect(Terraria.Entity.InheritSource(Projectile), Projectile.Center, new Vector2(0, 1).RotatedBy(Projectile.rotation - Math.PI * 0.75) * 30f, ModContent.ProjectileType(), Projectile.damage, Projectile.knockBack, player.whoAmI); + if (Projectile.owner == Main.myPlayer) + { + Projectile.NewProjectileDirect(Terraria.Entity.InheritSource(Projectile), Projectile.Center, new Vector2(0, 1).RotatedBy(Projectile.rotation - Math.PI * 0.75) * 30f, ModContent.ProjectileType(), Projectile.damage, Projectile.knockBack, player.whoAmI); + } Projectile.Kill(); + return; } player.direction = dir; } - public override bool PreDraw(ref Color lightColor) - { - DrawBaseTexture(lightColor); - - return false; - } - public override void DrawBaseTexture(Color lightColor) { Player player = Main.player[Projectile.owner]; var texMain = ModAsset.ToothBow_bow.Value; var texArrow = ModAsset.ToothBow_BloodArrow.Value; SpriteEffects se = SpriteEffects.None; - if (player.direction == -1) + if (player.direction * player.gravDir == -1) { se = SpriteEffects.FlipVertically; } - float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction; + float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction * player.gravDir; float backValue = (player.itemTimeMax - player.itemTime) / (float)player.itemTimeMax * 20; - Vector2 drawCenter = Projectile.Center - Main.screenPosition + DrawOffset; + Vector2 drawCenter = Projectile.Center - Main.screenPosition + GravityDrawOffset; Main.spriteBatch.Draw(texArrow, drawCenter + new Vector2(5 - backValue, 0).RotatedBy(rot), null, lightColor, rot, texArrow.Size() / 2f, 0.75f, se, 0); Main.spriteBatch.Draw(texMain, drawCenter, null, lightColor, rot, texMain.Size() / 2f, 1f, se, 0); - if (Main.MouseWorld.X < player.MountedCenter.X) + if (AimPosition.X < player.MountedCenter.X) { player.direction = -1; } diff --git a/Sources/Modules/Yggdrasil/KelpCurtain/Items/Weapons/Legacies/AncientSyringe.cs b/Sources/Modules/Yggdrasil/KelpCurtain/Items/Weapons/Legacies/AncientSyringe.cs index 878ab16538..90e9b0588d 100644 --- a/Sources/Modules/Yggdrasil/KelpCurtain/Items/Weapons/Legacies/AncientSyringe.cs +++ b/Sources/Modules/Yggdrasil/KelpCurtain/Items/Weapons/Legacies/AncientSyringe.cs @@ -32,10 +32,7 @@ public override bool Shoot(Player player, EntitySource_ItemUse_WithAmmo source, { if (Main.myPlayer == player.whoAmI) { - var p0 = Projectile.NewProjectileDirect(player.GetSource_ItemUse(Item), player.Center, Vector2.Zero, type, player.GetWeaponDamage(Item), Item.knockBack, player.whoAmI); - Vector2 mouseToPlayer = Main.MouseWorld - player.MountedCenter; - mouseToPlayer = Vector2.Normalize(mouseToPlayer); - p0.rotation = mouseToPlayer.ToRotation() + MathHelper.PiOver4; + Projectile.NewProjectileDirect(source, player.Center, velocity, type, damage, knockback, player.whoAmI); } } return false; diff --git a/Sources/Modules/Yggdrasil/KelpCurtain/Projectiles/Legacies/AncientSyringe_proj.cs b/Sources/Modules/Yggdrasil/KelpCurtain/Projectiles/Legacies/AncientSyringe_proj.cs index adc32994ce..48879fd369 100644 --- a/Sources/Modules/Yggdrasil/KelpCurtain/Projectiles/Legacies/AncientSyringe_proj.cs +++ b/Sources/Modules/Yggdrasil/KelpCurtain/Projectiles/Legacies/AncientSyringe_proj.cs @@ -6,7 +6,7 @@ namespace Everglow.Yggdrasil.KelpCurtain.Projectiles.Legacies; -public class AncientSyringe_proj : HandholdProjectile +public class AncientSyringe_proj : HandheldProjectile { public override string LocalizationCategory => LocalizationUtils.Categories.MagicProjectiles; @@ -15,11 +15,11 @@ public override void SetDef() DepartLength = 110; Projectile.usesLocalNPCImmunity = true; Projectile.localNPCHitCooldown = 20; - base.SetDef(); } public override void OnSpawn(IEntitySource source) { + base.OnSpawn(source); Player player = Main.player[Projectile.owner]; int timeMax = (int)(player.itemTimeMax / player.meleeSpeed); player.itemTime = timeMax; @@ -27,6 +27,11 @@ public override void OnSpawn(IEntitySource source) public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone) { + target.AddBuff(ModContent.BuffType(), 480); + if (Main.dedServ) + { + return; + } int times = 10; for (int x = 0; x < times; x++) { @@ -60,13 +65,6 @@ public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone) }; Ins.VFXManager.Add(blood); } - target.AddBuff(ModContent.BuffType(), 480); - base.OnHitNPC(target, hit, damageDone); - } - - public override void AI() - { - base.AI(); } public override void HeldProjectileAI() @@ -75,12 +73,13 @@ public override void HeldProjectileAI() if (player.HeldItem.type != ModContent.ItemType()) { Projectile.Kill(); + return; } player.heldProj = Projectile.whoAmI; - ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2); + ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2 * player.gravDir); - Vector2 mouseToPlayer = Main.MouseWorld - ArmRootPos; - mouseToPlayer = Vector2.Normalize(mouseToPlayer); + Vector2 mouseToPlayer = AimPosition - ArmRootPos; + mouseToPlayer = mouseToPlayer.SafeNormalize(Vector2.UnitX * player.direction); int timeMax = (int)(player.itemTimeMax / player.meleeSpeed); if (Projectile.localNPCHitCooldown > timeMax - 1) { @@ -120,6 +119,7 @@ public override void HeldProjectileAI() if (!player.controlUseItem) { Projectile.Kill(); + return; } if (Projectile.Center.X < ArmRootPos.X) { @@ -136,13 +136,13 @@ public override bool PreDraw(ref Color lightColor) Player player = Main.player[Projectile.owner]; var texMain = (Texture2D)ModContent.Request(Texture); SpriteEffects se = SpriteEffects.None; - if (player.direction == -1) + if (player.direction * player.gravDir == -1) { se = SpriteEffects.FlipVertically; } - float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction; - Main.spriteBatch.Draw(texMain, Projectile.Center - Main.screenPosition + DrawOffset - new Vector2(54, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, lightColor, rot, texMain.Size() / 2f, 1f, se, 0); + float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction * player.gravDir; + Main.spriteBatch.Draw(texMain, Projectile.Center - Main.screenPosition + GravityDrawOffset - new Vector2(54, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, lightColor, rot, texMain.Size() / 2f, 1f, se, 0); int timeMax = (int)(player.itemTimeMax / player.meleeSpeed); float duration = player.itemTime / (float)timeMax; duration *= 1.5f; @@ -154,11 +154,11 @@ public override bool PreDraw(ref Color lightColor) duration = MathF.Sin(duration * MathHelper.Pi); Texture2D star = Commons.ModAsset.StarSlash_black.Value; Color drawC = new Color(0.7f * lightColor.R / 255f, 2f * lightColor.G / 255f, 0.2f * lightColor.B / 255f, 0); - Main.spriteBatch.Draw(star, Projectile.Center - Main.screenPosition + DrawOffset + new Vector2(24, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, Color.White, MathHelper.PiOver2, star.Size() / 2f, 0.5f * duration, se, 0); - Main.spriteBatch.Draw(star, Projectile.Center - Main.screenPosition + DrawOffset + new Vector2(24, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, Color.White, 0, star.Size() / 2f, 0.5f * duration, se, 0); + Main.spriteBatch.Draw(star, Projectile.Center - Main.screenPosition + GravityDrawOffset + new Vector2(24, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, Color.White, MathHelper.PiOver2, star.Size() / 2f, 0.5f * duration, se, 0); + Main.spriteBatch.Draw(star, Projectile.Center - Main.screenPosition + GravityDrawOffset + new Vector2(24, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, Color.White, 0, star.Size() / 2f, 0.5f * duration, se, 0); star = Commons.ModAsset.StarSlash.Value; - Main.spriteBatch.Draw(star, Projectile.Center - Main.screenPosition + DrawOffset + new Vector2(24, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, drawC, MathHelper.PiOver2, star.Size() / 2f, 0.5f * duration, se, 0); - Main.spriteBatch.Draw(star, Projectile.Center - Main.screenPosition + DrawOffset + new Vector2(24, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, drawC, 0, star.Size() / 2f, 0.5f * duration, se, 0); + Main.spriteBatch.Draw(star, Projectile.Center - Main.screenPosition + GravityDrawOffset + new Vector2(24, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, drawC, MathHelper.PiOver2, star.Size() / 2f, 0.5f * duration, se, 0); + Main.spriteBatch.Draw(star, Projectile.Center - Main.screenPosition + GravityDrawOffset + new Vector2(24, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, drawC, 0, star.Size() / 2f, 0.5f * duration, se, 0); return false; } } diff --git a/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/AmberMagicOrb.cs b/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/AmberMagicOrb.cs index 271831e46c..fde1819caa 100644 --- a/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/AmberMagicOrb.cs +++ b/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/AmberMagicOrb.cs @@ -18,7 +18,6 @@ public override void SetDefaults() Item.rare = ItemRarityID.Blue; Item.UseSound = SoundID.Item1; Item.value = 4514; - Item.autoReuse = false; Item.DamageType = DamageClass.Magic; Item.mana = 18; Item.noUseGraphic = true; diff --git a/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/Auburn/RodSpear.cs b/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/Auburn/RodSpear.cs index 28e48412f6..1cb9bdef73 100644 --- a/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/Auburn/RodSpear.cs +++ b/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/Auburn/RodSpear.cs @@ -22,7 +22,7 @@ public override void SetDefaults() Item.crit = 4; Item.knockBack = 4f; - Item.value = ItemRarityID.Green; + Item.rare = ItemRarityID.Green; Item.value = Item.buyPrice(silver: 20); Item.shoot = ModContent.ProjectileType(); diff --git a/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/LightSeeker/HandgunOfEnlightment.cs b/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/LightSeeker/HandgunOfEnlightment.cs index 27db31c6a1..f9bfe3e400 100644 --- a/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/LightSeeker/HandgunOfEnlightment.cs +++ b/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/LightSeeker/HandgunOfEnlightment.cs @@ -27,7 +27,7 @@ public override void SetDefaults() Item.shootSpeed = 16f; Item.useAmmo = AmmoID.Bullet; - Item.value = ItemRarityID.Green; + Item.rare = ItemRarityID.Green; Item.value = Item.buyPrice(silver: 3); } diff --git a/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/LightSeeker/LightBeamStaff.cs b/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/LightSeeker/LightBeamStaff.cs index 983c639af1..6f405789a4 100644 --- a/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/LightSeeker/LightBeamStaff.cs +++ b/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/LightSeeker/LightBeamStaff.cs @@ -26,7 +26,7 @@ public override void SetDefaults() Item.noMelee = true; Item.mana = 5; - Item.value = ItemRarityID.Green; + Item.rare = ItemRarityID.Green; Item.value = Item.buyPrice(silver: 8); Item.shoot = ModContent.ProjectileType(); diff --git a/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/YggdrasilAmberLaser.cs b/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/YggdrasilAmberLaser.cs index 11d90c0fb2..7f1f856a25 100644 --- a/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/YggdrasilAmberLaser.cs +++ b/Sources/Modules/Yggdrasil/YggdrasilTown/Items/Weapons/YggdrasilAmberLaser.cs @@ -1,5 +1,4 @@ using Everglow.Yggdrasil.YggdrasilTown.Projectiles.Magic; -using Terraria.DataStructures; namespace Everglow.Yggdrasil.YggdrasilTown.Items.Weapons; @@ -32,9 +31,4 @@ public override void SetDefaults() Item.shoot = ModContent.ProjectileType(); Item.shootSpeed = 12; } - - public override bool Shoot(Player player, EntitySource_ItemUse_WithAmmo source, Vector2 position, Vector2 velocity, int type, int damage, float knockback) - { - return base.Shoot(player, source, position, velocity, type, damage, knockback); - } } diff --git a/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/AmberMagicOrb.cs b/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/AmberMagicOrb.cs index 14e8ddf1b7..356e03c8c1 100644 --- a/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/AmberMagicOrb.cs +++ b/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/AmberMagicOrb.cs @@ -1,35 +1,18 @@ using Everglow.Commons.Templates.Weapons; -using Terraria.DataStructures; namespace Everglow.Yggdrasil.YggdrasilTown.Projectiles.Magic; -public class AmberMagicOrb : HandholdProjectile +public class AmberMagicOrb : HandheldProjectile { public override string LocalizationCategory => Everglow.Commons.Utilities.LocalizationUtils.Categories.MagicProjectiles; public override void SetDef() { DepartLength = 60; - Projectile.usesLocalNPCImmunity = true; - Projectile.localNPCHitCooldown = 20; TextureRotation = 5 / 18f * MathHelper.Pi; Projectile.usesLocalNPCImmunity = true; Projectile.localNPCHitCooldown = 5; Projectile.ArmorPenetration = 45; - base.SetDef(); - } - - public override void OnSpawn(IEntitySource source) - { - Player player = Main.player[Projectile.owner]; - Vector2 mouseToPlayer = Main.MouseWorld - player.Center; - mouseToPlayer = Vector2.Normalize(mouseToPlayer); - Projectile.rotation = mouseToPlayer.ToRotation() + MathHelper.PiOver4; - } - - public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone) - { - base.OnHitNPC(target, hit, damageDone); } public override void AI() @@ -45,12 +28,13 @@ public override void HeldProjectileAI() if (player.HeldItem.type != ModContent.ItemType()) { Projectile.Kill(); + return; } player.heldProj = Projectile.whoAmI; - ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2); + ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2 * player.gravDir); - Vector2 mouseToPlayer = Main.MouseWorld - ArmRootPos; - mouseToPlayer = Vector2.Normalize(mouseToPlayer); + Vector2 mouseToPlayer = AimPosition - ArmRootPos; + mouseToPlayer = mouseToPlayer.SafeNormalize(Vector2.UnitX * player.direction); Projectile.rotation = mouseToPlayer.ToRotation() + MathHelper.PiOver4; player.SetCompositeArmFront(true, Player.CompositeArmStretchAmount.Full, (Projectile.rotation - MathF.PI * 0.25f) * player.gravDir - MathF.PI * 0.5f); @@ -91,14 +75,14 @@ public override bool PreDraw(ref Color lightColor) var texMain = (Texture2D)ModContent.Request(Texture); var texGlow = ModAsset.AmberMagicOrb_glow.Value; SpriteEffects se = SpriteEffects.None; - if (player.direction == -1) + if (player.direction * player.gravDir == -1) { se = SpriteEffects.FlipVertically; } - float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction; - Main.spriteBatch.Draw(texMain, Projectile.Center - Main.screenPosition + DrawOffset, null, lightColor, rot + (float)Main.time * 0.25f, texMain.Size() / 2f, 1f, se, 0); - Main.spriteBatch.Draw(texGlow, Projectile.Center - Main.screenPosition + DrawOffset, null, new Color(1f, 1f, 1f, 0), rot + (float)Main.time * 0.25f, texGlow.Size() / 2f, 1f, se, 0); + float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction * player.gravDir; + Main.spriteBatch.Draw(texMain, Projectile.Center - Main.screenPosition + GravityDrawOffset, null, lightColor, rot + (float)Main.time * 0.25f, texMain.Size() / 2f, 1f, se, 0); + Main.spriteBatch.Draw(texGlow, Projectile.Center - Main.screenPosition + GravityDrawOffset, null, new Color(1f, 1f, 1f, 0), rot + (float)Main.time * 0.25f, texGlow.Size() / 2f, 1f, se, 0); var bars = new List(); for (int t = 0; t < 3; t++) diff --git a/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/LightBeamStaff_proj.cs b/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/LightBeamStaff_proj.cs index 09455522da..4d8b116fc9 100644 --- a/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/LightBeamStaff_proj.cs +++ b/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/LightBeamStaff_proj.cs @@ -1,16 +1,16 @@ -using Everglow.Commons.DataStructures; using Everglow.Commons.Templates.Weapons; -using Everglow.Yggdrasil.YggdrasilTown.Items.Weapons; using Everglow.Yggdrasil.YggdrasilTown.Items.Weapons.LightSeeker; using Terraria.Audio; using Terraria.DataStructures; namespace Everglow.Yggdrasil.YggdrasilTown.Projectiles.Magic; -public class LightBeamStaff_proj : HandholdProjectile +public class LightBeamStaff_proj : HandheldProjectile { public override string LocalizationCategory => Everglow.Commons.Utilities.LocalizationUtils.Categories.MagicProjectiles; + private bool playedSpawnSound; + public override void SetDef() { DepartLength = 60; @@ -19,28 +19,25 @@ public override void SetDef() Projectile.localNPCHitCooldown = 5; Projectile.ArmorPenetration = 45; Projectile.friendly = false; - base.SetDef(); } public override void OnSpawn(IEntitySource source) { - SoundEngine.PlaySound(SoundID.Shimmer1.WithPitchOffset(1), Projectile.Center); - Player player = Main.player[Projectile.owner]; - Vector2 mouseToPlayer = Main.MouseWorld - player.Center; - mouseToPlayer = Vector2.Normalize(mouseToPlayer); - Projectile.rotation = mouseToPlayer.ToRotation() + MathHelper.PiOver4; - Projectile.NewProjectile(Projectile.GetSource_FromAI(), Projectile.Center + Projectile.velocity * 2, Projectile.velocity, ModContent.ProjectileType(), Projectile.damage, Projectile.knockBack, Projectile.owner); - } - - public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone) - { - base.OnHitNPC(target, hit, damageDone); + base.OnSpawn(source); + if (Projectile.owner == Main.myPlayer) + { + Projectile.NewProjectile(Projectile.GetSource_FromAI(), Projectile.Center + Projectile.velocity * 2, Projectile.velocity, ModContent.ProjectileType(), Projectile.damage, Projectile.knockBack, Projectile.owner); + } } public override void AI() { + if (!playedSpawnSound) + { + playedSpawnSound = true; + SoundEngine.PlaySound(SoundID.Shimmer1.WithPitchOffset(1), Projectile.Center); + } HeldProjectileAI(); - Player player = Main.player[Projectile.owner]; } public override void HeldProjectileAI() @@ -49,9 +46,10 @@ public override void HeldProjectileAI() if (player.HeldItem.type != ModContent.ItemType()) { Projectile.Kill(); + return; } player.heldProj = Projectile.whoAmI; - ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2); + ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2 * player.gravDir); int timeMax = (int)(player.itemTimeMax / player.meleeSpeed); if (Projectile.localNPCHitCooldown > timeMax - 1) @@ -61,6 +59,7 @@ public override void HeldProjectileAI() if (player.itemTime == 1) { Projectile.Kill(); + return; } player.SetCompositeArmFront(true, Player.CompositeArmStretchAmount.Full, (Projectile.rotation - MathF.PI * 0.25f) * player.gravDir - MathF.PI * 0.5f); DepartLength = 60; @@ -82,12 +81,12 @@ public override bool PreDraw(ref Color lightColor) Player player = Main.player[Projectile.owner]; var texMain = (Texture2D)ModContent.Request(Texture); SpriteEffects se = SpriteEffects.None; - if (player.direction == -1) + if (player.direction * player.gravDir == -1) { se = SpriteEffects.FlipVertically; } lightColor = Lighting.GetColor(Projectile.Center.ToTileCoordinates()); - float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction; + float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction * player.gravDir; var texMain_glow = ModAsset.LightBeamStaff_glow.Value; float duration = player.itemTime / (float)player.itemTimeMax; duration *= 1.5f; @@ -98,9 +97,9 @@ public override bool PreDraw(ref Color lightColor) } duration = MathF.Sin(duration * MathHelper.Pi); - Main.spriteBatch.Draw(texMain, Projectile.Center - Main.screenPosition + DrawOffset - new Vector2(54, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, lightColor, rot, texMain.Size() / 2f, 1f, se, 0); + Main.spriteBatch.Draw(texMain, Projectile.Center - Main.screenPosition + GravityDrawOffset - new Vector2(54, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, lightColor, rot, texMain.Size() / 2f, 1f, se, 0); var powerColor = new Color(duration + 0.3f, duration * duration, duration * duration, 0); - Main.spriteBatch.Draw(texMain_glow, Projectile.Center - Main.screenPosition + DrawOffset - new Vector2(54, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, powerColor, rot, texMain_glow.Size() / 2f, 1f, se, 0); + Main.spriteBatch.Draw(texMain_glow, Projectile.Center - Main.screenPosition + GravityDrawOffset - new Vector2(54, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, powerColor, rot, texMain_glow.Size() / 2f, 1f, se, 0); return false; } } diff --git a/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/YggdrasilAmberLaser_crystal.cs b/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/YggdrasilAmberLaser_crystal.cs index 07159888a0..dc4d4c4199 100644 --- a/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/YggdrasilAmberLaser_crystal.cs +++ b/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/YggdrasilAmberLaser_crystal.cs @@ -1,3 +1,4 @@ +using System.IO; using Everglow.Commons.DataStructures; using Everglow.Yggdrasil.YggdrasilTown.Buffs; using Everglow.Yggdrasil.YggdrasilTown.Dusts; @@ -19,11 +20,26 @@ public override void SetDefaults() Projectile.width = 10; Projectile.height = 10; Projectile.aiStyle = -1; + Projectile.hide = true; CrystalTrack = new Vector2[50]; } public Projectile LaserOwner; + private int laserOwnerIdentity = -1; + private bool trackInitialized; + + public override void SendExtraAI(BinaryWriter writer) + { + writer.Write(LaserOwner?.identity ?? laserOwnerIdentity); + } + + public override void ReceiveExtraAI(BinaryReader reader) + { + laserOwnerIdentity = reader.ReadInt32(); + LaserOwner = null; + } + public Vector2[] CrystalTrack = new Vector2[50]; public bool EndAI = false; @@ -31,18 +47,23 @@ public override void SetDefaults() public override void OnSpawn(IEntitySource source) { CrystalTrack[0] = Projectile.Center; + trackInitialized = true; Projectile.ai[0] = 0; - Projectile.hide = true; } public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone) { target.AddBuff(ModContent.BuffType(), 6); - base.OnHitNPC(target, hit, damageDone); } public override void AI() { + // Network-created projectiles skip OnSpawn. + if (!trackInitialized) + { + CrystalTrack[0] = Projectile.Center; + trackInitialized = true; + } if (Projectile.timeLeft < 60) { int length = 0; @@ -66,6 +87,26 @@ public override void AI() } } ++Projectile.ai[0]; + if (LaserOwner == null) + { + if (laserOwnerIdentity < 0) + { + return; + } + foreach (var candidate in Main.ActiveProjectiles) + { + if (candidate.owner == Projectile.owner && candidate.identity == laserOwnerIdentity + && candidate.ModProjectile is YggdrasilAmberLaser_proj) + { + LaserOwner = candidate; + break; + } + } + if (LaserOwner == null) + { + return; + } + } if (!LaserOwner.active) { EndAI = true; @@ -134,7 +175,7 @@ public override void AI() return false; } float value = 0; - if (Collision.CheckAABBvLineCollision(targetHitbox.TopLeft(), targetHitbox.Size(), CrystalTrack.ToArray()[i], CrystalTrack.ToArray()[i + 1], 20, ref value)) + if (Collision.CheckAABBvLineCollision(targetHitbox.TopLeft(), targetHitbox.Size(), CrystalTrack[i], CrystalTrack[i + 1], 20, ref value)) { return true; } @@ -142,10 +183,6 @@ public override void AI() return false; } - public override void ModifyHitNPC(NPC target, ref NPC.HitModifiers modifiers) - { - } - public override void DrawBehind(int index, List behindNPCsAndTiles, List behindNPCs, List behindProjectiles, List overPlayers, List overWiresUI) { behindNPCsAndTiles.Add(index); @@ -179,12 +216,12 @@ public override bool PreDraw(ref Color lightColor) } if (CrystalTrack[i + 1] != Vector2.zeroVector) { - Vector2 normal = (CrystalTrack.ToArray()[i + 1] - CrystalTrack.ToArray()[i]).SafeNormalize(Vector2.zeroVector).RotatedBy(MathHelper.PiOver2); - AddVertex(bars, CrystalTrack.ToArray()[i] + normal * width, new Vector3(i * 0.3f, 1, 0)); - AddVertex(bars, CrystalTrack.ToArray()[i] - normal * width, new Vector3(i * 0.3f, 0f, 0)); + Vector2 normal = (CrystalTrack[i + 1] - CrystalTrack[i]).SafeNormalize(Vector2.zeroVector).RotatedBy(MathHelper.PiOver2); + AddVertex(bars, CrystalTrack[i] + normal * width, new Vector3(i * 0.3f, 1, 0)); + AddVertex(bars, CrystalTrack[i] - normal * width, new Vector3(i * 0.3f, 0f, 0)); - AddVertex2(barsReflect, CrystalTrack.ToArray()[i] + normal * width, new Vector3(i * 0.3f, 1, 0)); - AddVertex2(barsReflect, CrystalTrack.ToArray()[i] - normal * width, new Vector3(i * 0.3f, 0f, 0)); + AddVertex2(barsReflect, CrystalTrack[i] + normal * width, new Vector3(i * 0.3f, 1, 0)); + AddVertex2(barsReflect, CrystalTrack[i] - normal * width, new Vector3(i * 0.3f, 0f, 0)); } } diff --git a/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/YggdrasilAmberLaser_proj.cs b/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/YggdrasilAmberLaser_proj.cs index 8088069a58..a5e80fc873 100644 --- a/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/YggdrasilAmberLaser_proj.cs +++ b/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Magic/YggdrasilAmberLaser_proj.cs @@ -4,14 +4,15 @@ using Everglow.Yggdrasil.YggdrasilTown.Items.Weapons; using Everglow.Yggdrasil.YggdrasilTown.VFXs; using Terraria.Audio; -using Terraria.DataStructures; namespace Everglow.Yggdrasil.YggdrasilTown.Projectiles.Magic; -public class YggdrasilAmberLaser_proj : HandholdProjectile +public class YggdrasilAmberLaser_proj : HandheldProjectile { public override string LocalizationCategory => Everglow.Commons.Utilities.LocalizationUtils.Categories.MagicProjectiles; + private bool playedSpawnSound; + public override void SetDef() { DepartLength = 60; @@ -19,30 +20,31 @@ public override void SetDef() Projectile.usesLocalNPCImmunity = true; Projectile.localNPCHitCooldown = 5; Projectile.ArmorPenetration = 45; - base.SetDef(); } public Vector2 EndPoint = Vector2.Zero; public bool Crystalized = false; - public override void OnSpawn(IEntitySource source) - { - SoundEngine.PlaySound(SoundID.Shimmer1.WithPitchOffset(1), Projectile.Center); - Player player = Main.player[Projectile.owner]; - Vector2 mouseToPlayer = Main.MouseWorld - player.Center; - mouseToPlayer = Vector2.Normalize(mouseToPlayer); - Projectile.rotation = mouseToPlayer.ToRotation() + MathHelper.PiOver4; - EndPoint = Vector2.Zero; - } - - public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone) - { - base.OnHitNPC(target, hit, damageDone); - } - public override void AI() { + if (!playedSpawnSound) + { + playedSpawnSound = true; + SoundEngine.PlaySound(SoundID.Shimmer1.WithPitchOffset(1), Projectile.Center); + } HeldProjectileAI(); + if (!Projectile.active) + { + return; + } + Vector2 direction = (Projectile.rotation - MathHelper.PiOver4).ToRotationVector2(); + int step = -6; + while (step < 200 && !Collision.SolidCollision(Projectile.Center + direction * step * 8, 0, 0)) + { + step++; + } + MaxStep = step; + EndPoint = Projectile.Center + direction * step * 8; Player player = Main.player[Projectile.owner]; int timeMax = (int)(player.itemTimeMax / player.meleeSpeed); float duration = player.itemTime / (float)timeMax; @@ -59,7 +61,7 @@ public override void AI() } if (EndPoint != Vector2.Zero) { - if (!Crystalized) + if (!Crystalized && Projectile.owner == Main.myPlayer) { Crystalized = true; var projectile = Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), EndPoint, Vector2.zeroVector, ModContent.ProjectileType(), Projectile.damage / 2, 0, Projectile.owner); @@ -67,8 +69,13 @@ public override void AI() if (yALc != null) { yALc.LaserOwner = Projectile; + projectile.netUpdate = true; } } + if (Main.dedServ) + { + return; + } for (int g = 0; g < 10; g++) { Vector2 newVelocity = new Vector2(0, Main.rand.NextFloat(0f, 12f)).RotatedByRandom(MathHelper.TwoPi); @@ -105,7 +112,7 @@ public override void AI() return false; } bool k = Collision.CheckAABBvLineCollision(targetHitbox.TopLeft(), new Vector2(targetHitbox.Width, targetHitbox.Height), Projectile.Center, EndPoint); - if (k) + if (k && !Main.dedServ) { for (int g = 0; g < 10; g++) { @@ -133,9 +140,10 @@ public override void HeldProjectileAI() if (player.HeldItem.type != ModContent.ItemType()) { Projectile.Kill(); + return; } player.heldProj = Projectile.whoAmI; - ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2); + ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2 * player.gravDir); int timeMax = (int)(player.itemTimeMax / player.meleeSpeed); if (Projectile.localNPCHitCooldown > timeMax - 1) @@ -145,6 +153,7 @@ public override void HeldProjectileAI() if (player.itemTime == 1) { Projectile.Kill(); + return; } player.SetCompositeArmFront(true, Player.CompositeArmStretchAmount.Full, (Projectile.rotation - MathF.PI * 0.25f) * player.gravDir - MathF.PI * 0.5f); DepartLength = 60; @@ -159,7 +168,7 @@ public override void HeldProjectileAI() { player.direction = 1; } - if (EndPoint != Vector2.zeroVector) + if (!Main.dedServ && EndPoint != Vector2.zeroVector) { int duplicateTimes = 1; for (int i = 0; i < duplicateTimes; i++) @@ -196,12 +205,12 @@ public override bool PreDraw(ref Color lightColor) Player player = Main.player[Projectile.owner]; var texMain = (Texture2D)ModContent.Request(Texture); SpriteEffects se = SpriteEffects.None; - if (player.direction == -1) + if (player.direction * player.gravDir == -1) { se = SpriteEffects.FlipVertically; } - float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction; + float rot = Projectile.rotation - (float)(Math.PI * 0.25) + TextureRotation * player.direction * player.gravDir; var texMain_glow = ModAsset.YggdrasilAmberLaser_glow_proj.Value; float duration = player.itemTime / (float)player.itemTimeMax; duration *= 1.5f; @@ -212,9 +221,9 @@ public override bool PreDraw(ref Color lightColor) } duration = MathF.Sin(duration * MathHelper.Pi); - Main.spriteBatch.Draw(texMain, Projectile.Center - Main.screenPosition + DrawOffset - new Vector2(54, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, lightColor, rot, texMain.Size() / 2f, 1f, se, 0); + Main.spriteBatch.Draw(texMain, Projectile.Center - Main.screenPosition + GravityDrawOffset - new Vector2(54, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, lightColor, rot, texMain.Size() / 2f, 1f, se, 0); var powerColor = new Color(duration, duration * duration, duration * duration, 0); - Main.spriteBatch.Draw(texMain_glow, Projectile.Center - Main.screenPosition + DrawOffset - new Vector2(54, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, powerColor, rot, texMain_glow.Size() / 2f, 1f, se, 0); + Main.spriteBatch.Draw(texMain_glow, Projectile.Center - Main.screenPosition + GravityDrawOffset - new Vector2(54, 0).RotatedBy(Projectile.rotation - MathHelper.PiOver4), null, powerColor, rot, texMain_glow.Size() / 2f, 1f, se, 0); SpriteBatchState sBS = Main.spriteBatch.GetState().Value; Main.spriteBatch.End(); @@ -252,8 +261,6 @@ public override bool PreDraw(ref Color lightColor) }; Ins.VFXManager.Add(somg); } - MaxStep = step; - EndPoint = Projectile.Center + MaxStep * mouseToPlayer * 8; } var bars = new List(); step = -6; diff --git a/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Ranged/PearShapedNeedle_HeldProj.cs b/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Ranged/PearShapedNeedle_HeldProj.cs index 0f5456e067..8dc235cd19 100644 --- a/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Ranged/PearShapedNeedle_HeldProj.cs +++ b/Sources/Modules/Yggdrasil/YggdrasilTown/Projectiles/Ranged/PearShapedNeedle_HeldProj.cs @@ -3,7 +3,7 @@ namespace Everglow.Yggdrasil.YggdrasilTown.Projectiles.Ranged; -public class PearShapedNeedle_HeldProj : HandholdProjectile +public class PearShapedNeedle_HeldProj : HandheldProjectile { public float Timer = 0; @@ -36,12 +36,13 @@ public override void HeldProjectileAI() if (player.HeldItem.type != ModContent.ItemType()) { Projectile.Kill(); + return; } player.heldProj = Projectile.whoAmI; - ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2); + ArmRootPos = player.MountedCenter + new Vector2(-4 * player.direction, -2 * player.gravDir); - Vector2 mouseToPlayer = Main.MouseWorld - ArmRootPos; - mouseToPlayer = Vector2.Normalize(mouseToPlayer); + Vector2 mouseToPlayer = AimPosition - ArmRootPos; + mouseToPlayer = mouseToPlayer.SafeNormalize(Vector2.UnitX * player.direction); Projectile.rotation = mouseToPlayer.ToRotation() + MathHelper.PiOver4; player.SetCompositeArmFront(true, Player.CompositeArmStretchAmount.Full, (Projectile.rotation - MathF.PI * 0.25f) * player.gravDir - MathF.PI * 0.5f); @@ -171,7 +172,6 @@ public float GetReflectValue(float rotation) public override void OnKill(int timeLeft) { ReleaseNeedles(); - base.OnKill(timeLeft); } public bool NeedleReady(int k) @@ -211,6 +211,10 @@ public void ReleaseNeedles() if (NeedleReady(k)) { succeed = true; + if (Projectile.owner != Main.myPlayer) + { + continue; + } Projectile.NewProjectileDirect(Projectile.GetSource_FromAI(), GetNeedleWorldPos(k), GetReleaseVel(k).NormalizeSafe(), ModContent.ProjectileType(), Projectile.damage, Projectile.knockBack, Projectile.owner); if (k > 0) {