XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

公开
关注 0 Fork 0 Star 0
UTF-8
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));
        }
    }
}