using SoulHarvest.Items;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using Terraria;
using Terraria.GameContent;
using Terraria.ModLoader;
namespace SoulHarvest.Common;
[Autoload(Side = ModSide.Client)]
internal sealed class SickleTrailPrimitiveSystem : ModSystem
{
private const int CurveSubdivisions = 4;
internal static void DrawTrail(IReadOnlyList<Vector2> controlPoints, NormalSickle sickle, bool alternateAttack)
{
if (Main.dedServ || controlPoints.Count < 2)
return;
List<Vector2> curve = BuildSmoothCurve(controlPoints);
if (curve.Count < 2)
return;
// Projectile.PreDraw runs inside Terraria's shared SpriteBatch. Ending that
// batch and applying a BasicEffect here leaves FNA's vertex shader state in
// a format that the following player draw cannot consume. Draw the ribbon
// with the already active batch so this effect owns no shared render state.
DrawCurveLayer(curve, sickle, alternateAttack, core: false);
DrawCurveLayer(curve, sickle, alternateAttack, core: true);
}
private static List<Vector2> BuildSmoothCurve(IReadOnlyList<Vector2> controlPoints)
{
List<Vector2> result = new((controlPoints.Count - 1) * CurveSubdivisions + 1)
{
controlPoints[0]
};
for (int segment = 0; segment < controlPoints.Count - 1; segment++)
{
Vector2 p0 = controlPoints[Math.Max(0, segment - 1)];
Vector2 p1 = controlPoints[segment];
Vector2 p2 = controlPoints[segment + 1];
Vector2 p3 = controlPoints[Math.Min(controlPoints.Count - 1, segment + 2)];
for (int subdivision = 1; subdivision <= CurveSubdivisions; subdivision++)
{
float amount = subdivision / (float)CurveSubdivisions;
Vector2 point = Vector2.CatmullRom(p0, p1, p2, p3, amount);
if (Vector2.DistanceSquared(result[^1], point) > 0.04f)
result.Add(point);
}
}
return result;
}
private static void DrawCurveLayer(
IReadOnlyList<Vector2> points,
NormalSickle sickle,
bool alternateAttack,
bool core)
{
float attackScale = alternateAttack ? 1.25f : 1f;
for (int index = 0; index < points.Count - 1; index++)
{
Vector2 start = points[index];
Vector2 end = points[index + 1];
Vector2 difference = end - start;
float length = difference.Length();
if (length <= 0.2f)
continue;
float progress = (index + 0.5f) / (points.Count - 1f);
float strength = progress * progress * (3f - 2f * progress);
float width = sickle.TrailWidth * attackScale * MathHelper.Lerp(0.08f, 1f, strength);
if (sickle.SwingStyle == SickleSwingStyle.Rising)
width *= 0.8f + strength * 0.65f;
else if (sickle.SwingStyle == SickleSwingStyle.VoidOrbit)
width *= 1f + (float)Math.Sin(progress * MathHelper.Pi * 5f + Main.GlobalTimeWrappedHourly * 8f) * 0.08f;
if (core)
width *= 0.42f;
else
width += 4f;
float opacity = MathHelper.Lerp(0f, core ? 0.68f : 0.38f, (float)Math.Pow(strength, 0.72f));
Color color = core
? Color.Lerp(sickle.SickleSecondaryColor, Color.White, strength * 0.18f)
: Color.Lerp(sickle.SickleColor, sickle.SickleSecondaryColor, strength * 0.22f);
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(
start, end, color * opacity, Math.Max(1f, width));
}
}
}
using SoulHarvest.Items;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using Terraria;
using Terraria.GameContent;
using Terraria.ModLoader;
namespace SoulHarvest.Common;
[Autoload(Side = ModSide.Client)]
internal sealed class SickleTrailPrimitiveSystem : ModSystem
{
private const int CurveSubdivisions = 4;
internal static void DrawTrail(IReadOnlyList<Vector2> controlPoints, NormalSickle sickle, bool alternateAttack)
{
if (Main.dedServ || controlPoints.Count < 2)
return;
List<Vector2> curve = BuildSmoothCurve(controlPoints);
if (curve.Count < 2)
return;
// Projectile.PreDraw runs inside Terraria's shared SpriteBatch. Ending that
// batch and applying a BasicEffect here leaves FNA's vertex shader state in
// a format that the following player draw cannot consume. Draw the ribbon
// with the already active batch so this effect owns no shared render state.
DrawCurveLayer(curve, sickle, alternateAttack, core: false);
DrawCurveLayer(curve, sickle, alternateAttack, core: true);
}
private static List<Vector2> BuildSmoothCurve(IReadOnlyList<Vector2> controlPoints)
{
List<Vector2> result = new((controlPoints.Count - 1) * CurveSubdivisions + 1)
{
controlPoints[0]
};
for (int segment = 0; segment < controlPoints.Count - 1; segment++)
{
Vector2 p0 = controlPoints[Math.Max(0, segment - 1)];
Vector2 p1 = controlPoints[segment];
Vector2 p2 = controlPoints[segment + 1];
Vector2 p3 = controlPoints[Math.Min(controlPoints.Count - 1, segment + 2)];
for (int subdivision = 1; subdivision <= CurveSubdivisions; subdivision++)
{
float amount = subdivision / (float)CurveSubdivisions;
Vector2 point = Vector2.CatmullRom(p0, p1, p2, p3, amount);
if (Vector2.DistanceSquared(result[^1], point) > 0.04f)
result.Add(point);
}
}
return result;
}
private static void DrawCurveLayer(
IReadOnlyList<Vector2> points,
NormalSickle sickle,
bool alternateAttack,
bool core)
{
float attackScale = alternateAttack ? 1.25f : 1f;
for (int index = 0; index < points.Count - 1; index++)
{
Vector2 start = points[index];
Vector2 end = points[index + 1];
Vector2 difference = end - start;
float length = difference.Length();
if (length <= 0.2f)
continue;
float progress = (index + 0.5f) / (points.Count - 1f);
float strength = progress * progress * (3f - 2f * progress);
float width = sickle.TrailWidth * attackScale * MathHelper.Lerp(0.08f, 1f, strength);
if (sickle.SwingStyle == SickleSwingStyle.Rising)
width *= 0.8f + strength * 0.65f;
else if (sickle.SwingStyle == SickleSwingStyle.VoidOrbit)
width *= 1f + (float)Math.Sin(progress * MathHelper.Pi * 5f + Main.GlobalTimeWrappedHourly * 8f) * 0.08f;
if (core)
width *= 0.42f;
else
width += 4f;
float opacity = MathHelper.Lerp(0f, core ? 0.68f : 0.38f, (float)Math.Pow(strength, 0.72f));
Color color = core
? Color.Lerp(sickle.SickleSecondaryColor, Color.White, strength * 0.18f)
: Color.Lerp(sickle.SickleColor, sickle.SickleSecondaryColor, strength * 0.22f);
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(
start, end, color * opacity, Math.Max(1f, width));
}
}
}