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;
/// <summary>
/// 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.
/// </summary>
[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<MyPlayer>();
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);
}
}
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;
/// <summary>
/// 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.
/// </summary>
[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<MyPlayer>();
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);
}
}