using SoulHarvest.Items; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using Terraria; using Terraria.GameContent; using Terraria.ID; using Terraria.ModLoader; namespace SoulHarvest.Common; /// /// Client-only persistent presentation for a mastered form. Stage I/II weapon /// treatment is owned by the swing projectile; Stage III adds this form aura even /// while the player is between attacks. It never creates gameplay entities. /// [Autoload(Side = ModSide.Client)] internal sealed class ReaperStageVisualSystem : ModSystem { public override void PostDrawTiles() { if (Main.dedServ || Main.gameMenu || !HasVisibleAura()) return; SpriteBatch spriteBatch = Main.spriteBatch; spriteBatch.Begin( SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone, null, Main.GameViewMatrix.TransformationMatrix); Texture2D pixel = TextureAssets.MagicPixel.Value; for (int playerIndex = 0; playerIndex < Main.maxPlayers; playerIndex++) { Player player = Main.player[playerIndex]; if (TryGetAuraForm(player, out ReaperFormId form)) DrawAura(spriteBatch, pixel, player.MountedCenter, form, playerIndex); } spriteBatch.End(); } private static bool HasVisibleAura() { for (int playerIndex = 0; playerIndex < Main.maxPlayers; playerIndex++) { if (TryGetAuraForm(Main.player[playerIndex], out _)) return true; } return false; } private static bool TryGetAuraForm(Player player, out ReaperFormId form) { form = ReaperFormId.Base; if (!player.active || player.dead || player.HeldItem.ModItem is not NormalSickle) return false; MyPlayer modPlayer = player.GetModPlayer(); ReaperStage stage; if (Main.netMode != NetmodeID.MultiplayerClient || player.whoAmI == Main.myPlayer) { // The owner (and single-player) has the authoritative character tree. // VisibleReaper* is deliberately only the compact remote-player view. form = modPlayer.ReaperProgression.CurrentForm; stage = modPlayer.ReaperProgression.GetStage(form); } else { form = modPlayer.VisibleReaperForm; stage = modPlayer.VisibleReaperStage; } return form == ReaperFormId.Death || ReaperDefinitions.IsBranchForm(form) && stage >= ReaperStage.StageIII; } private static void DrawAura(SpriteBatch spriteBatch, Texture2D pixel, Vector2 center, ReaperFormId form, int playerIndex) { float time = Main.GlobalTimeWrappedHourly; float phase = time * 0.62f + playerIndex * 0.73f; float pulse = 1f + (float)Math.Sin(time * 3.6f + playerIndex) * 0.055f; float radius = (form == ReaperFormId.Death ? 49f : 42f) * pulse; Color primary = ReaperCombatRegistry.GetPrimaryColor(form) with { A = 0 }; Color secondary = ReaperCombatRegistry.GetSecondaryColor(form) with { A = 0 }; DrawRing(spriteBatch, pixel, center, radius, 30, primary * 0.45f, 3.4f, phase); DrawRing(spriteBatch, pixel, center, radius * 0.76f, 24, secondary * 0.34f, 1.45f, -phase * 0.72f); int points = form == ReaperFormId.Death ? 6 : 4; for (int index = 0; index < points; index++) { float angle = -phase * 0.48f + index * MathHelper.TwoPi / points; Vector2 direction = angle.ToRotationVector2(); Vector2 tangent = direction.RotatedBy(MathHelper.PiOver2); Vector2 runeCenter = center + direction * radius; DrawSegment(spriteBatch, pixel, runeCenter - tangent * 5f, runeCenter + tangent * 5f, secondary * 0.58f, 1.8f); DrawSegment(spriteBatch, pixel, runeCenter - direction * 3f, runeCenter + direction * 6f, primary * 0.50f, 2.4f); } } private static void DrawRing( SpriteBatch spriteBatch, Texture2D pixel, Vector2 center, float radius, int segments, Color color, float width, float rotation) { if (radius <= 0.1f || segments < 3) return; segments = Math.Max(segments, Math.Min(128, (int)Math.Ceiling(MathHelper.TwoPi * radius / 18f))); Vector2 previous = center + rotation.ToRotationVector2() * radius; for (int index = 1; index <= segments; index++) { Vector2 current = center + (rotation + index * MathHelper.TwoPi / segments).ToRotationVector2() * radius; DrawSegment(spriteBatch, pixel, previous, current, color, width); previous = current; } } private static void DrawSegment( SpriteBatch spriteBatch, Texture2D pixel, Vector2 start, Vector2 end, Color color, float width) { DeathDomainPrimitiveTextureSystem.DrawSmoothLine(spriteBatch, start - Main.screenPosition, end - Main.screenPosition, color, width); } }