using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using SoulHarvest.Common; using Terraria; using Terraria.GameContent; using Terraria.ID; namespace SoulHarvest.Projectiles; internal static class ReaperProjectileHelper { internal static void DamageOnDedicatedServer(Projectile projectile) { if (Main.netMode != NetmodeID.Server || projectile.owner < 0 || projectile.owner >= Main.maxPlayers) { return; } // Projectile.Damage only runs friendly-NPC collision for Main.myPlayer. // A dedicated server has no local player (255), even when it authored the // projectile. Scope the standard collision pipeline to the real caster so // all vanilla/tML modifiers, OnHit hooks, immunity and net sync still run. int previousLocalPlayer = Main.myPlayer; try { Main.myPlayer = projectile.owner; projectile.Damage(); } finally { Main.myPlayer = previousLocalPlayer; } } internal static void SyncNewProjectile(Projectile projectile) { projectile.netUpdate = true; if (Main.netMode == NetmodeID.Server && projectile.active && projectile.whoAmI >= 0 && projectile.whoAmI < Main.maxProjectiles) { // NewProjectile can publish the base projectile before a ModProjectile // has written its snapshot, geometry and damage metadata. Send one // completed initial state immediately; short-lived slashes otherwise // finish before their delayed netUpdate reaches clients. NetMessage.SendData(MessageID.SyncProjectile, -1, -1, null, projectile.whoAmI); } } public static NPC? FindTarget(Projectile projectile, float maximumDistance) { NPC? target = null; foreach (NPC npc in Main.ActiveNPCs) { if (!ReaperTargeting.IsValidWeaponTarget(npc) || !ReaperTargeting.IsTrainingDummy(npc) && !npc.CanBeChasedBy(projectile)) continue; float distance = Vector2.Distance(npc.Center, projectile.Center); if (distance >= maximumDistance) continue; maximumDistance = distance; target = npc; } return target; } public static bool DrawTrail(Projectile projectile, Color color, float rotationOffset = 0f, float scaleMultiplier = 1f, int copies = 8) { Texture2D texture = TextureAssets.Projectile[projectile.type].Value; Vector2 origin = texture.Size() / 2f; int count = System.Math.Min(copies, projectile.oldPos.Length); for (int index = count - 1; index >= 0; index--) { if (projectile.oldPos[index] == Vector2.Zero) continue; float opacity = (count - index) / (float)(count + 1); Vector2 drawPosition = projectile.oldPos[index] + projectile.Size / 2f - Main.screenPosition; float echoScale = projectile.scale * scaleMultiplier * (0.82f + opacity * 0.18f); Main.EntitySpriteDraw(texture, drawPosition, null, (color with { A = 0 }) * opacity * 0.18f, projectile.oldRot[index] + rotationOffset, origin, echoScale * 1.34f, SpriteEffects.None); Main.EntitySpriteDraw(texture, drawPosition, null, (color with { A = 0 }) * opacity * 0.48f, projectile.oldRot[index] + rotationOffset, origin, echoScale, SpriteEffects.None); } Main.EntitySpriteDraw(texture, projectile.Center - Main.screenPosition, null, (color with { A = 0 }) * 0.42f, projectile.rotation + rotationOffset, origin, projectile.scale * scaleMultiplier * 1.22f, SpriteEffects.None); Main.EntitySpriteDraw(texture, projectile.Center - Main.screenPosition, null, Color.White, projectile.rotation + rotationOffset, origin, projectile.scale * scaleMultiplier, SpriteEffects.None); return false; } }