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 Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using Terraria;
using Terraria.Audio;
using Terraria.GameContent;
using Terraria.ID;
using Terraria.Localization;
using Terraria.ModLoader;
using Terraria.UI;

namespace SoulHarvest.UI;

internal sealed class AltarEquipmentTreeCanvas : UIElement
{
    private readonly AltarEquipmentTreeViewport viewport;
    private readonly IReadOnlyList<AltarEquipmentTreeNode> nodes;
    private readonly float contentWidth;
    private readonly float contentHeight;

    public AltarEquipmentTreeCanvas(AltarEquipmentTreeViewport viewport, IReadOnlyList<AltarEquipmentTreeNode> nodes, float contentWidth, float contentHeight)
    {
        this.viewport = viewport;
        this.nodes = nodes;
        this.contentWidth = contentWidth;
        this.contentHeight = contentHeight;
    }

    protected override void DrawSelf(SpriteBatch spriteBatch)
    {
        Texture2D pixel = TextureAssets.MagicPixel.Value;
        float scale = viewport.LayoutScale;
        DrawBackdrop(spriteBatch, pixel, scale);
        DrawConnections(spriteBatch, pixel, scale);
        foreach (AltarEquipmentTreeNode node in nodes)
            DrawNode(spriteBatch, pixel, node, scale);
        if (viewport.HoveredNode is AltarEquipmentTreeNode hovered)
            Main.instance.MouseText(hovered.Title);
    }

    private void DrawBackdrop(SpriteBatch batch, Texture2D pixel, float scale)
    {
        Color grid = new Color(84, 71, 70) * 0.08f;
        for (float x = 34f; x < contentWidth; x += 88f)
            ReaperTreeDraw.DrawLine(batch, pixel, viewport.DesignToScreen(new Vector2(x, 0f)), viewport.DesignToScreen(new Vector2(x, contentHeight)), grid, Math.Max(1f, scale));
        for (float y = 30f; y < contentHeight; y += 88f)
            ReaperTreeDraw.DrawLine(batch, pixel, viewport.DesignToScreen(new Vector2(0f, y)), viewport.DesignToScreen(new Vector2(contentWidth, y)), grid, Math.Max(1f, scale));
    }

    private void DrawConnections(SpriteBatch batch, Texture2D pixel, float scale)
    {
        if (nodes.Count == 0)
            return;
        AltarEquipmentTreeNode root = nodes[0];
        foreach (AltarEquipmentTreeNode node in nodes)
        {
            if (node.IsRoot || node.UpgradeType is not DeathAltarUpgradeType type)
                continue;
            IReadOnlyList<DeathAltarUpgradeType> prerequisites = AltarEquipmentProgressionPanel.GetPrerequisites(type);
            if (prerequisites.Count == 0)
            {
                DrawPath(batch, pixel, root, node, node.Completed, scale);
                continue;
            }
            foreach (DeathAltarUpgradeType prerequisite in prerequisites)
            {
                AltarEquipmentTreeNode? parent = Find(prerequisite);
                if (parent is not null)
                    DrawPath(batch, pixel, parent, node, parent.Completed, scale);
            }
        }
    }

    private void DrawPath(SpriteBatch batch, Texture2D pixel, AltarEquipmentTreeNode start, AltarEquipmentTreeNode end, bool lit, float scale)
    {
        Vector2 a = viewport.DesignToScreen(start.Position);
        Vector2 b = viewport.DesignToScreen(end.Position);
        Vector2 delta = b - a;
        if (delta.LengthSquared() < 1f)
            return;
        Vector2 direction = Vector2.Normalize(delta);
        a += direction * start.Radius * scale;
        b -= direction * end.Radius * scale;
        Color color = lit ? new Color(184, 54, 63) : new Color(64, 57, 58);
        ReaperTreeDraw.DrawLine(batch, pixel, a, b, new Color(6, 6, 8) * 0.9f, Math.Max(2f, 5f * scale));
        ReaperTreeDraw.DrawLine(batch, pixel, a, b, color * 0.78f, Math.Max(1f, (lit ? 2.3f : 1.2f) * scale));
        ReaperTreeDraw.DrawDiamond(batch, pixel, Vector2.Lerp(a, b, 0.5f), (lit ? 4f : 3f) * scale, color * 0.92f);
    }

    private void DrawNode(SpriteBatch batch, Texture2D pixel, AltarEquipmentTreeNode node, float scale)
    {
        Vector2 center = viewport.DesignToScreen(node.Position);
        float radius = node.Radius * scale;
        bool selected = ReferenceEquals(viewport.SelectedNode, node);
        bool hovered = ReferenceEquals(viewport.HoveredNode, node);
        Color color = node.Completed ? new Color(207, 161, 116) : node.CanRequest && node.Affordable ? new Color(207, 57, 68) : node.CanRequest ? new Color(145, 58, 61) : new Color(79, 71, 70);
        ReaperTreeDraw.DrawDiamond(batch, pixel, center, radius * 0.88f, new Color(8, 8, 10) * 0.99f);
        if (node.IsRoot)
            ReaperTreeDraw.DrawRing(batch, pixel, center, radius, new Color(212, 60, 69), Math.Max(1f, 2.8f * scale), 30);
        else
            ReaperTreeDraw.DrawDiamondOutline(batch, pixel, center, radius, color, Math.Max(1f, 2.2f * scale));
        if (selected || hovered)
            ReaperTreeDraw.DrawRing(batch, pixel, center, radius + (selected ? 7f : 4f) * scale, selected ? Color.White * 0.76f : color * 0.62f, Math.Max(1f, (selected ? 2f : 1f) * scale), 26);
        Color runeColor = node.Lit ? new Color(239, 219, 199) : new Color(121, 111, 108);
        if (node.IsRoot)
        {
            ReaperTreeDraw.DrawDiamondOutline(batch, pixel, center, radius * 0.34f, runeColor, Math.Max(1f, 1.7f * scale));
            ReaperTreeDraw.DrawRing(batch, pixel, center, radius * 0.16f, runeColor * 0.84f, Math.Max(1f, 1.2f * scale), 16);
        }
        else
        {
            DrawUpgradeRune(batch, pixel, node.UpgradeType!.Value, center, radius, runeColor, scale);
        }
        if (!node.IsRoot)
        {
            float labelScale = 0.47f * scale;
            string label = AltarDrawHelpers.FitText(node.Title, 166f * scale, labelScale);
            Vector2 size = FontAssets.MouseText.Value.MeasureString(label) * labelScale;
            Utils.DrawBorderString(batch, label, center + new Vector2(-size.X * 0.5f, radius + 13f * scale), node.Lit ? new Color(221, 203, 188) : new Color(128, 117, 114), labelScale);
        }
    }

    private AltarEquipmentTreeNode? Find(DeathAltarUpgradeType type)
    {
        foreach (AltarEquipmentTreeNode node in nodes)
            if (node.UpgradeType == type)
                return node;
        return null;
    }

    private static void DrawUpgradeRune(
        SpriteBatch batch,
        Texture2D pixel,
        DeathAltarUpgradeType type,
        Vector2 center,
        float radius,
        Color color,
        float scale)
    {
        string name = type.ToString();
        float lineWidth = Math.Max(1f, 1.65f * scale);
        float arm = radius * 0.45f;
        if (name.StartsWith("Domain", StringComparison.Ordinal))
        {
            ReaperTreeDraw.DrawRing(batch, pixel, center, arm * 0.72f, color, lineWidth, 22);
            ReaperTreeDraw.DrawLine(batch, pixel, center + new Vector2(-arm, 0f), center + new Vector2(arm, 0f), color * 0.86f, lineWidth);
            ReaperTreeDraw.DrawLine(batch, pixel, center + new Vector2(-arm * 0.62f, arm * 0.48f), center + new Vector2(arm * 0.62f, -arm * 0.48f), color, lineWidth);
        }
        else if (name.StartsWith("Wing", StringComparison.Ordinal))
        {
            Vector2 left = center + new Vector2(-arm, arm * 0.52f);
            Vector2 right = center + new Vector2(arm, arm * 0.52f);
            ReaperTreeDraw.DrawLine(batch, pixel, center, left, color, lineWidth);
            ReaperTreeDraw.DrawLine(batch, pixel, center, right, color, lineWidth);
            ReaperTreeDraw.DrawLine(batch, pixel, center + new Vector2(-arm * 0.18f, 0f), center + new Vector2(-arm * 0.72f, -arm * 0.38f), color * 0.8f, lineWidth);
            ReaperTreeDraw.DrawLine(batch, pixel, center + new Vector2(arm * 0.18f, 0f), center + new Vector2(arm * 0.72f, -arm * 0.38f), color * 0.8f, lineWidth);
        }
        else if (name.StartsWith("Robe", StringComparison.Ordinal))
        {
            ReaperTreeDraw.DrawLine(batch, pixel, center + new Vector2(0f, -arm), center + new Vector2(0f, arm), color, lineWidth);
            ReaperTreeDraw.DrawLine(batch, pixel, center + new Vector2(-arm * 0.72f, -arm * 0.28f), center + new Vector2(arm * 0.72f, -arm * 0.28f), color, lineWidth);
            ReaperTreeDraw.DrawDiamondOutline(batch, pixel, center + new Vector2(0f, arm * 0.55f), arm * 0.30f, color * 0.82f, lineWidth);
        }
        else if (name.StartsWith("Necklace", StringComparison.Ordinal))
        {
            ReaperTreeDraw.DrawRing(batch, pixel, center + new Vector2(0f, -arm * 0.18f), arm * 0.62f, color, lineWidth, 20);
            ReaperTreeDraw.DrawLine(batch, pixel, center + new Vector2(0f, arm * 0.43f), center + new Vector2(0f, arm), color, lineWidth);
            ReaperTreeDraw.DrawDiamondOutline(batch, pixel, center + new Vector2(0f, arm), arm * 0.22f, color * 0.88f, lineWidth);
        }
        else
        {
            ReaperTreeDraw.DrawLine(batch, pixel, center + new Vector2(-arm, -arm), center + new Vector2(arm, arm), color, lineWidth);
            ReaperTreeDraw.DrawLine(batch, pixel, center + new Vector2(arm, -arm), center + new Vector2(-arm, arm), color, lineWidth);
        }

        // Small orbital marks distinguish siblings without falling back to language-
        // dependent abbreviations that read like placeholder text in localized UIs.
        int marks = 1 + Math.Abs((int)type) % 3;
        for (int index = 0; index < marks; index++)
        {
            float angle = -MathHelper.PiOver2 + MathHelper.TwoPi * index / marks;
            Vector2 mark = center + angle.ToRotationVector2() * radius * 0.70f;
            ReaperTreeDraw.DrawDiamond(batch, pixel, mark, Math.Max(1.8f * scale, radius * 0.075f), color * 0.84f);
        }
    }
}