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

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

公开
关注 0 Fork 0 Star 0
UTF-8
using SoulHarvest.Common;
using SoulHarvest.Items;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ReLogic.Content;
using System;
using System.Collections.Generic;
using System.Text;
using Terraria;
using Terraria.Audio;
using Terraria.GameContent;
using Terraria.GameContent.UI.Elements;
using Terraria.GameInput;
using Terraria.ID;
using Terraria.Localization;
using Terraria.ModLoader;
using Terraria.UI;

namespace SoulHarvest.UI;

internal static class ReaperTreeDraw
{
    public static void DrawLine(SpriteBatch batch, Texture2D pixel, Vector2 start, Vector2 end, Color color, float widthInPixels)
    {
        Vector2 delta = end - start;
        if (delta.LengthSquared() < 0.01f)
            return;
        batch.Draw(pixel, start, null, color, delta.ToRotation(), Vector2.Zero,
            new Vector2(delta.Length() / pixel.Width, widthInPixels / pixel.Height), SpriteEffects.None, 0f);
    }

    public static void DrawRing(SpriteBatch batch, Texture2D pixel, Vector2 center, float radius, Color color, float width, int segments)
    {
        if (radius <= 0f)
            return;
        Vector2 previous = center + new Vector2(radius, 0f);
        for (int index = 1; index <= segments; index++)
        {
            float angle = MathHelper.TwoPi * index / segments;
            Vector2 current = center + new Vector2(MathF.Cos(angle), MathF.Sin(angle)) * radius;
            DrawLine(batch, pixel, previous, current, color, width);
            previous = current;
        }
    }

    public static void DrawDiamond(SpriteBatch batch, Texture2D pixel, Vector2 center, float sideLengthInPixels, Color color) =>
        batch.Draw(pixel, center, null, color, MathHelper.PiOver4, pixel.Size() * 0.5f,
            new Vector2(sideLengthInPixels / pixel.Width, sideLengthInPixels / pixel.Height), SpriteEffects.None, 0f);

    public static void DrawDiamondOutline(SpriteBatch batch, Texture2D pixel, Vector2 center, float radius, Color color, float width)
    {
        Vector2 top = center + new Vector2(0f, -radius);
        Vector2 right = center + new Vector2(radius, 0f);
        Vector2 bottom = center + new Vector2(0f, radius);
        Vector2 left = center + new Vector2(-radius, 0f);
        DrawLine(batch, pixel, top, right, color, width);
        DrawLine(batch, pixel, right, bottom, color, width);
        DrawLine(batch, pixel, bottom, left, color, width);
        DrawLine(batch, pixel, left, top, color, width);
    }

    public static void DrawSkillRune(
        SpriteBatch batch,
        Texture2D pixel,
        Vector2 center,
        float radius,
        ReaperFormId form,
        int skillIndex,
        Color color,
        float scale)
    {
        float runeRadius = Math.Max(5f * scale, radius * 0.62f);
        float stroke = Math.Max(1f, 1.65f * scale);
        float rotation = -MathHelper.PiOver4 + ((int)form - 1) * MathHelper.Pi / 6f;
        Vector2 axis = Rotate(Vector2.UnitX, rotation);
        Vector2 normal = new(-axis.Y, axis.X);

        switch (skillIndex)
        {
            case 0:
                // Twin echo blades: the second cut follows the first along a
                // parallel soul-track, matching the first skill's echo motif.
                DrawLine(batch, pixel,
                    center - axis * runeRadius * 0.72f - normal * runeRadius * 0.27f,
                    center + axis * runeRadius * 0.72f + normal * runeRadius * 0.12f,
                    color,
                    stroke);
                DrawLine(batch, pixel,
                    center - axis * runeRadius * 0.52f + normal * runeRadius * 0.30f,
                    center + axis * runeRadius * 0.57f + normal * runeRadius * 0.48f,
                    color * 0.78f,
                    stroke);
                DrawDiamond(batch, pixel, center + axis * runeRadius * 0.74f + normal * runeRadius * 0.13f, 2.7f * scale, color);
                break;

            case 1:
            {
                // Interlocked soul links represent the branch's second-stage
                // pact, restraint, refraction or chained movement mechanic.
                Vector2 offset = axis * runeRadius * 0.27f;
                float linkRadius = runeRadius * 0.34f;
                DrawRing(batch, pixel, center - offset, linkRadius, color, stroke, 12);
                DrawRing(batch, pixel, center + offset, linkRadius, color * 0.82f, stroke, 12);
                DrawLine(batch, pixel, center - axis * runeRadius * 0.12f, center + axis * runeRadius * 0.12f, color, stroke);
                break;
            }

            default:
                // Six-point execution seal: an inner core opens into the final
                // branch-specific mastery node without relying on text glyphs.
                DrawRing(batch, pixel, center, runeRadius * 0.36f, color * 0.86f, stroke, 14);
                DrawDiamond(batch, pixel, center, 3.2f * scale, color);
                for (int ray = 0; ray < 6; ray++)
                {
                    float angle = rotation + MathHelper.TwoPi * ray / 6f;
                    Vector2 direction = new(MathF.Cos(angle), MathF.Sin(angle));
                    DrawLine(batch, pixel,
                        center + direction * runeRadius * 0.48f,
                        center + direction * runeRadius * 0.82f,
                        color,
                        stroke);
                }
                break;
        }
    }

    public static void DrawCommonRune(
        SpriteBatch batch,
        Texture2D pixel,
        Vector2 center,
        float radius,
        ReaperCommonNode node,
        Color color,
        float scale)
    {
        float runeRadius = Math.Max(5f * scale, radius * 0.62f);
        float stroke = Math.Max(1f, 1.55f * scale);
        switch (node)
        {
            case ReaperCommonNode.Damage:
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.65f, runeRadius * 0.58f), center + new Vector2(runeRadius * 0.58f, -runeRadius * 0.65f), color, stroke);
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.58f, -runeRadius * 0.65f), center + new Vector2(runeRadius * 0.65f, runeRadius * 0.58f), color * 0.86f, stroke);
                DrawDiamond(batch, pixel, center, 3f * scale, color);
                break;

            case ReaperCommonNode.AttackSpeed:
                for (int chevron = 0; chevron < 3; chevron++)
                {
                    float x = -runeRadius * 0.55f + chevron * runeRadius * 0.43f;
                    Vector2 point = center + new Vector2(x + runeRadius * 0.30f, 0f);
                    DrawLine(batch, pixel, center + new Vector2(x - runeRadius * 0.18f, -runeRadius * 0.38f), point, color, stroke);
                    DrawLine(batch, pixel, center + new Vector2(x - runeRadius * 0.18f, runeRadius * 0.38f), point, color, stroke);
                }
                break;

            case ReaperCommonNode.Crit:
                DrawRing(batch, pixel, center, runeRadius * 0.43f, color, stroke, 14);
                DrawDiamond(batch, pixel, center, 2.7f * scale, color);
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.82f, 0f), center + new Vector2(-runeRadius * 0.52f, 0f), color, stroke);
                DrawLine(batch, pixel, center + new Vector2(runeRadius * 0.52f, 0f), center + new Vector2(runeRadius * 0.82f, 0f), color, stroke);
                DrawLine(batch, pixel, center + new Vector2(0f, -runeRadius * 0.82f), center + new Vector2(0f, -runeRadius * 0.52f), color, stroke);
                DrawLine(batch, pixel, center + new Vector2(0f, runeRadius * 0.52f), center + new Vector2(0f, runeRadius * 0.82f), color, stroke);
                break;

            case ReaperCommonNode.ArmorPenetration:
                DrawDiamondOutline(batch, pixel, center, runeRadius * 0.58f, color * 0.74f, stroke);
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.72f, runeRadius * 0.72f), center + new Vector2(runeRadius * 0.72f, -runeRadius * 0.72f), color, stroke);
                DrawDiamond(batch, pixel, center + new Vector2(runeRadius * 0.68f, -runeRadius * 0.68f), 2.5f * scale, color);
                break;

            case ReaperCommonNode.SpecialCooldown:
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.58f, -runeRadius * 0.65f), center + new Vector2(runeRadius * 0.58f, -runeRadius * 0.65f), color, stroke);
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.58f, runeRadius * 0.65f), center + new Vector2(runeRadius * 0.58f, runeRadius * 0.65f), color, stroke);
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.50f, -runeRadius * 0.55f), center + new Vector2(runeRadius * 0.50f, runeRadius * 0.55f), color, stroke);
                DrawLine(batch, pixel, center + new Vector2(runeRadius * 0.50f, -runeRadius * 0.55f), center + new Vector2(-runeRadius * 0.50f, runeRadius * 0.55f), color, stroke);
                DrawDiamond(batch, pixel, center, 2.5f * scale, color);
                break;

            case ReaperCommonNode.EnergyGain:
            {
                Vector2[] bolt =
                [
                    center + new Vector2(runeRadius * 0.18f, -runeRadius * 0.82f),
                    center + new Vector2(-runeRadius * 0.38f, -runeRadius * 0.05f),
                    center + new Vector2(runeRadius * 0.03f, -runeRadius * 0.05f),
                    center + new Vector2(-runeRadius * 0.18f, runeRadius * 0.82f),
                    center + new Vector2(runeRadius * 0.48f, runeRadius * 0.08f),
                    center + new Vector2(runeRadius * 0.08f, runeRadius * 0.08f)
                ];
                for (int index = 1; index < bolt.Length; index++)
                    DrawLine(batch, pixel, bolt[index - 1], bolt[index], color, stroke);
                break;
            }

            case ReaperCommonNode.AssemblySpeed:
                DrawRing(batch, pixel, center, runeRadius * 0.62f, color * 0.72f,
                    stroke, 12);
                DrawLine(batch, pixel,
                    center + new Vector2(-runeRadius * 0.48f, runeRadius * 0.28f),
                    center + new Vector2(0f, -runeRadius * 0.42f), color, stroke);
                DrawLine(batch, pixel,
                    center + new Vector2(runeRadius * 0.48f, runeRadius * 0.28f),
                    center + new Vector2(0f, -runeRadius * 0.42f), color, stroke);
                DrawDiamond(batch, pixel, center + new Vector2(0f,
                    runeRadius * 0.35f), 2.6f * scale, color);
                break;

            case ReaperCommonNode.DeathDamage:
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.72f,
                    runeRadius * 0.62f), center + new Vector2(runeRadius * 0.72f,
                    -runeRadius * 0.62f), color, stroke * 1.35f);
                DrawDiamond(batch, pixel, center, 4f * scale, color);
                break;

            case ReaperCommonNode.DeathInvocationSlashes:
                for (int slash = -1; slash <= 1; slash++)
                {
                    float offset = slash * runeRadius * 0.34f;
                    DrawLine(batch, pixel,
                        center + new Vector2(-runeRadius * 0.62f, offset + runeRadius * 0.32f),
                        center + new Vector2(runeRadius * 0.62f, offset - runeRadius * 0.32f),
                        color * (0.82f + (slash == 0 ? 0.18f : 0f)), stroke);
                }
                break;

            case ReaperCommonNode.DeathTempoCap:
                for (int ray = 0; ray < 5; ray++)
                {
                    float angle = -0.8f + ray * 0.4f;
                    Vector2 direction = new(MathF.Cos(angle), MathF.Sin(angle));
                    DrawLine(batch, pixel, center - direction * runeRadius * 0.15f,
                        center + direction * runeRadius * 0.82f, color, stroke);
                }
                break;

            case ReaperCommonNode.DeathRange:
                DrawRing(batch, pixel, center, runeRadius * 0.38f, color, stroke, 16);
                DrawRing(batch, pixel, center, runeRadius * 0.76f, color * 0.78f,
                    stroke, 20);
                break;

            case ReaperCommonNode.DeathSpaceBreak:
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.72f,
                    runeRadius * 0.70f), center + new Vector2(-runeRadius * 0.12f,
                    -runeRadius * 0.08f), color, stroke);
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.12f,
                    -runeRadius * 0.08f), center + new Vector2(runeRadius * 0.18f,
                    runeRadius * 0.18f), color, stroke);
                DrawLine(batch, pixel, center + new Vector2(runeRadius * 0.18f,
                    runeRadius * 0.18f), center + new Vector2(runeRadius * 0.72f,
                    -runeRadius * 0.70f), color, stroke);
                break;

            case ReaperCommonNode.DeathRiftWidth:
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.72f,
                    -runeRadius * 0.42f), center + new Vector2(runeRadius * 0.72f,
                    -runeRadius * 0.42f), color, stroke);
                DrawLine(batch, pixel, center + new Vector2(-runeRadius * 0.72f,
                    runeRadius * 0.42f), center + new Vector2(runeRadius * 0.72f,
                    runeRadius * 0.42f), color, stroke);
                DrawDiamond(batch, pixel, center, 3f * scale, color);
                break;
        }
    }

    private static Vector2 Rotate(Vector2 value, float radians)
    {
        float cosine = MathF.Cos(radians);
        float sine = MathF.Sin(radians);
        return new Vector2(value.X * cosine - value.Y * sine, value.X * sine + value.Y * cosine);
    }

    public static void DrawBorder(SpriteBatch batch, Texture2D pixel, Rectangle bounds, Color color, int thickness)
    {
        thickness = Math.Clamp(thickness, 1, Math.Max(1, Math.Min(bounds.Width, bounds.Height) / 2));
        batch.Draw(pixel, new Rectangle(bounds.X, bounds.Y, bounds.Width, thickness), color);
        batch.Draw(pixel, new Rectangle(bounds.X, bounds.Bottom - thickness, bounds.Width, thickness), color);
        batch.Draw(pixel, new Rectangle(bounds.X, bounds.Y, thickness, bounds.Height), color);
        batch.Draw(pixel, new Rectangle(bounds.Right - thickness, bounds.Y, thickness, bounds.Height), color);
    }

    public static void DrawInsetBorder(SpriteBatch batch, Texture2D pixel, Rectangle bounds, int inset, Color color, int thickness)
    {
        if (bounds.Width > inset * 2 && bounds.Height > inset * 2)
            DrawBorder(batch, pixel, new Rectangle(bounds.X + inset, bounds.Y + inset, bounds.Width - inset * 2, bounds.Height - inset * 2), color, thickness);
    }

    public static float DrawWrappedText(SpriteBatch batch, string text, Vector2 position, float maximumWidth, float scale, Color color, int maximumLines)
    {
        List<string> lines = WrapText(text, maximumWidth, scale, maximumLines);
        float lineHeight = 18f * scale;
        for (int index = 0; index < lines.Count; index++)
            Utils.DrawBorderString(batch, lines[index], position + new Vector2(0f, lineHeight * index), color, scale);
        return position.Y + lineHeight * lines.Count;
    }

    private static List<string> WrapText(string text, float maximumWidth, float scale, int maximumLines)
    {
        List<string> lines = [];
        if (string.IsNullOrEmpty(text) || maximumLines <= 0)
            return lines;
        StringBuilder line = new();
        foreach (char character in text)
        {
            if (character == '\r')
                continue;
            if (character == '\n')
            {
                lines.Add(line.ToString().TrimEnd());
                line.Clear();
                if (lines.Count >= maximumLines)
                    break;
                continue;
            }
            line.Append(character);
            if (FontAssets.MouseText.Value.MeasureString(line.ToString()).X * scale <= maximumWidth)
                continue;
            line.Length--;
            if (line.Length > 0)
                lines.Add(line.ToString().TrimEnd());
            line.Clear();
            if (character != ' ')
                line.Append(character);
            if (lines.Count >= maximumLines)
                break;
        }
        if (lines.Count < maximumLines && line.Length > 0)
            lines.Add(line.ToString().TrimEnd());
        if (lines.Count == maximumLines && lines[^1].Length > 0 && FontAssets.MouseText.Value.MeasureString(lines[^1] + "…").X * scale <= maximumWidth)
            lines[^1] += "…";
        return lines;
    }

    public static string Roman(int value) => value switch { 1 => "I", 2 => "II", 3 => "III", _ => value.ToString() };
}