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

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

公开
关注 0 Fork 0 Star 0
UTF-8
using Microsoft.Xna.Framework;
using System;
using Terraria;

namespace SoulHarvest.Common;

/// <summary>
/// Shared client-side presentation for every Death-domain summoning portal.
/// The continuous tapered tear forms the opening, while restrained broken rims,
/// hairline fractures, and inward-flowing embers keep it readable as a portal
/// without covering the weapon that is passing through it.
/// </summary>
internal static class DeathDomainPortalRenderer
{
    private const int RimSegments = 22;

    internal static void Draw(Vector2 center, Vector2 travelDirection,
        float scale, float aperture, int seed)
    {
        if (Main.dedServ || scale <= 0.05f || aperture <= 0.001f)
            return;

        aperture = Smooth01(aperture);

        Vector2 travel = travelDirection.SafeNormalize(Vector2.UnitX);
        Vector2 axis = travel.RotatedBy(MathHelper.PiOver2);
        Vector2 normal = axis.RotatedBy(MathHelper.PiOver2);
        float rotation = axis.ToRotation();
        float pulse = 1f + (float)Math.Sin(Main.GlobalTimeWrappedHourly * 5.2f
            + seed * 0.173f) * 0.035f;
        float length = (51f + Hash01(seed, 17) * 8f) * aperture * scale
            * pulse;
        float width = (15f + Hash01(seed, 31) * 3f) * aperture * scale;
        const float opacity = 1f;

        // A single continuous mask produces a clean tapered tear. It replaces
        // the old chain of thick, sharply joined line segments.
        DeathDomainPrimitiveTextureSystem.DrawDeathPortalRift(center, rotation,
            length, width, opacity);

        DrawBrokenRim(center, axis, normal, length, width, opacity, seed);
        DrawFractures(center, axis, normal, length, width, opacity, seed);
        DrawInwardEmbers(center, axis, normal, length, width, opacity, seed);
    }

    private static void DrawBrokenRim(Vector2 center, Vector2 axis,
        Vector2 normal, float length, float width, float opacity, int seed)
    {
        Vector2 previous = GetRimPoint(center, axis, normal, length, width,
            seed, 0f);
        for (int segment = 0; segment < RimSegments; segment++)
        {
            float start = segment / (float)RimSegments;
            float end = (segment + 1f) / RimSegments;
            Vector2 current = GetRimPoint(center, axis, normal, length, width,
                seed, end);
            bool gap = Hash01(seed + segment * 97, 71) < 0.27f
                || segment is 5 or 16;
            if (!gap)
            {
                float segmentOpacity = opacity * MathHelper.Lerp(0.52f, 0.88f,
                    Hash01(seed, segment * 43 + 103));
                DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(previous,
                    current, new Color(126, 0, 25, 0) * (segmentOpacity * 0.62f),
                    3.2f);
                DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(previous,
                    current, new Color(242, 27, 55, 205) * segmentOpacity,
                    1.25f);
                if ((segment + seed & 3) == 0)
                {
                    DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(previous,
                        current, new Color(255, 214, 205, 215)
                            * (segmentOpacity * 0.52f), 0.48f);
                }
            }
            previous = current;
        }
    }

    private static Vector2 GetRimPoint(Vector2 center, Vector2 axis,
        Vector2 normal, float length, float width, int seed, float progress)
    {
        float angle = progress * MathHelper.TwoPi;
        float roughness = 1f
            + (float)Math.Sin(angle * 3f + seed * 0.37f) * 0.075f
            + (float)Math.Sin(angle * 7f - seed * 0.19f) * 0.035f;
        return center + axis * ((float)Math.Cos(angle) * length * 0.57f
            * roughness) + normal * ((float)Math.Sin(angle) * width * 0.88f
            * roughness);
    }

    private static void DrawFractures(Vector2 center, Vector2 axis,
        Vector2 normal, float length, float width, float opacity, int seed)
    {
        for (int branch = 0; branch < 4; branch++)
        {
            float side = (branch & 1) == 0 ? -1f : 1f;
            float along = MathHelper.Lerp(-0.34f, 0.34f,
                Hash01(seed, branch * 59 + 211));
            Vector2 root = center + axis * length * along
                + normal * width * side * MathHelper.Lerp(0.32f, 0.48f,
                    Hash01(seed, branch * 61 + 229));
            Vector2 elbow = root + axis * length
                * (Hash01(seed, branch * 67 + 251) * 0.13f - 0.065f)
                + normal * width * side * 0.43f;
            Vector2 tip = elbow + axis * length
                * (Hash01(seed, branch * 71 + 277) * 0.17f - 0.085f)
                + normal * width * side * 0.34f;
            float alpha = opacity * MathHelper.Lerp(0.34f, 0.62f,
                Hash01(seed, branch * 73 + 293));
            DrawHairline(root, elbow, alpha);
            DrawHairline(elbow, tip, alpha * 0.72f);
        }
    }

    private static void DrawHairline(Vector2 start, Vector2 end, float opacity)
    {
        DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(start, end,
            new Color(100, 0, 22, 0) * (opacity * 0.55f), 2.1f);
        DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(start, end,
            new Color(235, 20, 50, 205) * opacity, 0.72f);
    }

    private static void DrawInwardEmbers(Vector2 center, Vector2 axis,
        Vector2 normal, float length, float width, float opacity, int seed)
    {
        float time = Main.GlobalTimeWrappedHourly * 1.45f;
        for (int ember = 0; ember < 7; ember++)
        {
            float flow = Frac(time + Hash01(seed, ember * 89 + 337));
            float angle = Hash01(seed, ember * 101 + 359) * MathHelper.TwoPi;
            Vector2 outer = axis * ((float)Math.Cos(angle) * length * 0.78f)
                + normal * ((float)Math.Sin(angle) * width * 1.65f);
            Vector2 position = center + outer * (1f - Smooth01(flow));
            Vector2 inward = (center - position).SafeNormalize(-normal);
            float alpha = opacity * (float)Math.Sin(flow * MathHelper.Pi) * 0.64f;
            float size = MathHelper.Lerp(1.55f, 0.45f, flow);
            DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(position,
                position - inward * (3.5f + 4f * (1f - flow)),
                new Color(249, 37, 62, 0) * alpha, size);
            DeathDomainPrimitiveTextureSystem.DrawSmoothDisc(Main.spriteBatch,
                position - Main.screenPosition, size * 0.62f,
                new Color(255, 221, 205, 220) * (alpha * 0.72f));
        }
    }

    private static float Hash01(int a, int b)
    {
        uint value = unchecked((uint)a * 0x9E3779B9u
            ^ (uint)b * 0x85EBCA6Bu ^ 0xC2B2AE35u);
        value ^= value >> 16;
        value *= 0x7FEB352Du;
        value ^= value >> 15;
        value *= 0x846CA68Bu;
        value ^= value >> 16;
        return (value & 0x00FFFFFFu) / 16777215f;
    }

    private static float Frac(float value) => value - (float)Math.Floor(value);

    private static float Smooth01(float value)
    {
        value = MathHelper.Clamp(value, 0f, 1f);
        return value * value * (3f - 2f * value);
    }
}