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 sealed class ReaperTreeCanvas : UIElement
{
private readonly ReaperProgressionPanel owner;
private readonly ReaperTreeViewport viewport;
private readonly IReadOnlyList<ReaperTreeNodeModel> nodes;
public ReaperTreeCanvas(ReaperProgressionPanel owner, ReaperTreeViewport viewport, IReadOnlyList<ReaperTreeNodeModel> nodes)
{
this.owner = owner;
this.viewport = viewport;
this.nodes = nodes;
}
protected override void DrawSelf(SpriteBatch spriteBatch)
{
Texture2D pixel = TextureAssets.MagicPixel.Value;
float scale = viewport.ContentScale;
DrawRunicBackdrop(spriteBatch, pixel, scale);
DrawTotem(spriteBatch, scale);
DrawConnections(spriteBatch, pixel, scale);
foreach (ReaperTreeNodeModel node in nodes)
DrawNode(spriteBatch, pixel, node, scale);
if (viewport.HoveredNode is ReaperTreeNodeModel hovered)
Main.instance.MouseText(hovered.Title);
}
private void DrawRunicBackdrop(SpriteBatch batch, Texture2D pixel, float scale)
{
Color grid = new Color(72, 71, 68) * 0.09f;
for (float x = 30f; x < ReaperTreeViewport.ContentWidth; x += 82f)
ReaperTreeDraw.DrawLine(batch, pixel, viewport.DesignToScreen(new Vector2(x, 0f)), viewport.DesignToScreen(new Vector2(x, ReaperTreeViewport.ContentHeight)), grid, Math.Max(1f, scale));
for (float y = 26f; y < ReaperTreeViewport.ContentHeight; y += 82f)
ReaperTreeDraw.DrawLine(batch, pixel, viewport.DesignToScreen(new Vector2(0f, y)), viewport.DesignToScreen(new Vector2(ReaperTreeViewport.ContentWidth, y)), grid, Math.Max(1f, scale));
Vector2 center = viewport.DesignToScreen(ReaperTreeViewport.ContentCenter);
ReaperTreeDraw.DrawRing(batch, pixel, center, 123f * scale, new Color(140, 43, 51) * 0.19f, Math.Max(1f, scale), 48);
ReaperTreeDraw.DrawRing(batch, pixel, center, 454f * scale, new Color(120, 109, 103) * 0.12f, Math.Max(1f, scale), 72);
for (int index = 0; index < 12; index++)
{
float angle = MathHelper.TwoPi * index / 12f;
Vector2 rune = center + new Vector2(MathF.Cos(angle), MathF.Sin(angle)) * 123f * scale;
ReaperTreeDraw.DrawDiamond(batch, pixel, rune, 3.5f * scale, index % 2 == 0 ? new Color(185, 51, 60) * 0.42f : new Color(130, 119, 111) * 0.28f);
}
}
private void DrawTotem(SpriteBatch batch, float scale)
{
Texture2D texture = ReaperUIData.GetFormTexture(ReaperFormId.Base);
Rectangle frame = texture.Frame();
Vector2 center = viewport.DesignToScreen(ReaperTreeViewport.ContentCenter + new Vector2(0f, -16f));
float drawScale = Math.Min(1f, 360f / Math.Max(frame.Width, frame.Height)) * scale;
batch.Draw(texture, center, frame, new Color(180, 187, 179) * 0.11f, -0.55f, frame.Size() * 0.5f, drawScale, SpriteEffects.None, 0f);
}
private void DrawConnections(SpriteBatch batch, Texture2D pixel, float scale)
{
ReaperTreeNodeModel root = Find(ReaperTreeNodeKind.Base, ReaperFormId.Base, 0)!;
ReaperTreeNodeModel death = Find(ReaperTreeNodeKind.Death, ReaperFormId.Death, 0)!;
foreach (ReaperCommonNode common in Enum.GetValues<ReaperCommonNode>())
{
if (ReaperDefinitions.IsDeathNode(common))
continue;
ReaperTreeNodeModel commonNode = FindCommon(common)!;
DrawPath(batch, pixel, root, commonNode, commonNode.Lit, new Color(190, 188, 174), scale, 0.62f);
}
foreach (ReaperCommonNode deathUpgrade in Enum.GetValues<ReaperCommonNode>())
{
if (!ReaperDefinitions.IsDeathNode(deathUpgrade))
continue;
ReaperTreeNodeModel upgradeNode = FindCommon(deathUpgrade)!;
DrawPath(batch, pixel, death, upgradeNode, upgradeNode.Lit,
ReaperUIData.GetPrimaryColor(ReaperFormId.Death), scale, 0.64f);
}
foreach (ReaperFormId form in ReaperDefinitions.BranchForms)
{
Color color = ReaperUIData.GetPrimaryColor(form);
ReaperTreeNodeModel previousStage = root;
ReaperTreeNodeModel? previousSkill = null;
for (int stage = 1; stage <= 3; stage++)
{
ReaperTreeNodeModel stageNode = Find(ReaperTreeNodeKind.Stage, form, stage)!;
ReaperTreeNodeModel skillNode = Find(ReaperTreeNodeKind.Skill, form, stage - 1)!;
DrawPath(batch, pixel, previousStage, stageNode, stageNode.Lit, color, scale, 1f);
DrawPath(batch, pixel, stageNode, skillNode, skillNode.Lit, color, scale, 0.78f);
if (previousSkill is not null)
DrawPath(batch, pixel, previousSkill, skillNode, skillNode.CurrentLevel >= ReaperDefinitions.MaximumSkillLevel, color, scale, 0.48f);
previousStage = stageNode;
previousSkill = skillNode;
}
bool mastered = owner.IsBranchMastered(form);
DrawPath(batch, pixel, previousStage, death, mastered, color, scale, 0.62f);
if (previousSkill is not null)
DrawPath(batch, pixel, previousSkill, death, mastered, color, scale, 0.42f);
}
}
private void DrawPath(SpriteBatch batch, Texture2D pixel, ReaperTreeNodeModel start, ReaperTreeNodeModel end, bool lit, Color color, float scale, float opacity)
{
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 line = lit ? color * opacity : new Color(65, 61, 62) * (opacity * 0.58f);
ReaperTreeDraw.DrawLine(batch, pixel, a, b, new Color(7, 7, 9) * Math.Min(0.94f, opacity + 0.2f), Math.Max(2f, 5f * scale));
ReaperTreeDraw.DrawLine(batch, pixel, a, b, line, Math.Max(1f, (lit ? 2.3f : 1.2f) * scale));
ReaperTreeDraw.DrawDiamond(batch, pixel, Vector2.Lerp(a, b, 0.5f), (lit ? 4.4f : 3f) * scale, line * 1.18f);
}
private void DrawNode(SpriteBatch batch, Texture2D pixel, ReaperTreeNodeModel node, float scale)
{
Vector2 center = viewport.DesignToScreen(node.Position);
float radius = node.Radius * scale;
Color primary = node.Kind == ReaperTreeNodeKind.Common
? new Color(196, 194, 177)
: ReaperUIData.GetPrimaryColor(node.Form);
bool selected = ReferenceEquals(viewport.SelectedNode, node) || viewport.SelectedNode is null && node.Kind == ReaperTreeNodeKind.Base;
bool hovered = ReferenceEquals(viewport.HoveredNode, node);
Color stateColor = node.Lit ? primary : node.CanRequest && node.Affordable ? primary * 0.82f : node.CanRequest ? new Color(180, 57, 65) : new Color(75, 70, 72);
float pulse = 0.86f + MathF.Sin(Main.GlobalTimeWrappedHourly * 3.2f + node.Position.X * 0.01f) * 0.1f;
ReaperTreeDraw.DrawDiamond(batch, pixel, center, radius * 0.88f, new Color(8, 8, 11) * 0.98f);
if (node.Kind is ReaperTreeNodeKind.Base or ReaperTreeNodeKind.Death)
{
ReaperTreeDraw.DrawRing(batch, pixel, center, radius, stateColor * (node.Lit ? pulse : 0.52f), Math.Max(1f, 2.6f * scale), 30);
ReaperTreeDraw.DrawRing(batch, pixel, center, radius - 6f * scale, new Color(104, 91, 84) * 0.56f, Math.Max(1f, scale), 24);
}
else if (node.Kind == ReaperTreeNodeKind.Skill)
ReaperTreeDraw.DrawRing(batch, pixel, center, radius, stateColor * (node.Lit ? pulse : 0.58f), Math.Max(1f, 2f * scale), 20);
else
ReaperTreeDraw.DrawDiamondOutline(batch, pixel, center, radius, stateColor * (node.Lit ? pulse : 0.62f), Math.Max(1f, 2f * scale));
if (selected || hovered)
ReaperTreeDraw.DrawRing(batch, pixel, center, radius + (selected ? 7f : 4f) * scale, selected ? Color.White * 0.72f : stateColor * 0.62f, Math.Max(1f, (selected ? 2f : 1f) * scale), 28);
DrawNodeContent(batch, node, center, radius, primary, scale);
DrawNodeLevels(batch, pixel, node, center + new Vector2(0f, radius + 9f * scale), primary, scale);
if (node.Kind == ReaperTreeNodeKind.Stage && node.Index == 1)
DrawBranchMarker(batch, pixel, node, center, radius, primary, scale);
if (node.Kind == ReaperTreeNodeKind.Stage && node.Index == 3)
{
float labelScale = 0.48f * scale;
string label = AltarDrawHelpers.FitText(ReaperUIData.GetFormName(node.Form), 112f * scale, labelScale);
Vector2 labelSize = FontAssets.MouseText.Value.MeasureString(label) * labelScale;
Utils.DrawBorderString(batch, label, center + new Vector2(-labelSize.X * 0.5f, radius + 19f * scale), primary * (node.Lit ? 1f : 0.62f), labelScale);
}
}
private void DrawNodeContent(SpriteBatch batch, ReaperTreeNodeModel node, Vector2 center, float radius, Color color, float scale)
{
if (node.Kind is ReaperTreeNodeKind.Base or ReaperTreeNodeKind.Death || node.Kind == ReaperTreeNodeKind.Stage && node.Index == 3)
{
Texture2D texture = ReaperUIData.GetFormTexture(node.Form);
Rectangle frame = texture.Frame();
float maximum = node.Kind is ReaperTreeNodeKind.Base or ReaperTreeNodeKind.Death ? radius * 1.3f : radius * 1.12f;
float iconScale = Math.Min(1f, maximum / Math.Max(frame.Width, frame.Height));
Color tint = node.Lit || node.Kind == ReaperTreeNodeKind.Base ? Color.White : new Color(92, 88, 91);
batch.Draw(texture, center, frame, tint, 0f, frame.Size() * 0.5f, iconScale, SpriteEffects.None, 0f);
return;
}
Color runeColor = node.Lit ? ReaperUIData.GetSecondaryColor(node.Form) : color * 0.68f;
if (node.Kind == ReaperTreeNodeKind.Skill)
{
ReaperTreeDraw.DrawSkillRune(batch, TextureAssets.MagicPixel.Value, center, radius, node.Form, node.Index, runeColor, scale);
return;
}
if (node.Kind is ReaperTreeNodeKind.Common
or ReaperTreeNodeKind.DeathUpgrade)
{
ReaperTreeDraw.DrawCommonRune(batch, TextureAssets.MagicPixel.Value, center, radius, node.CommonNode, runeColor, scale);
return;
}
string glyph = node.Kind == ReaperTreeNodeKind.Stage ? ReaperTreeDraw.Roman(node.Index) : string.Empty;
float textScale = 0.62f * scale;
Vector2 size = FontAssets.MouseText.Value.MeasureString(glyph) * textScale;
Utils.DrawBorderString(batch, glyph, center - size * 0.5f, runeColor, textScale);
}
private static void DrawBranchMarker(SpriteBatch batch, Texture2D pixel, ReaperTreeNodeModel node, Vector2 center, float radius, Color color, float scale)
{
Vector2 outward = (node.Position - ReaperTreeViewport.ContentCenter).SafeNormalize(Vector2.UnitY);
Vector2 tangent = new(-outward.Y, outward.X);
Vector2 markerCenter = center + outward * (radius + 28f * scale);
float markerRadius = 12f * scale;
ReaperTreeDraw.DrawDiamond(batch, pixel, markerCenter, markerRadius * 0.82f, new Color(7, 7, 9) * 0.98f);
ReaperTreeDraw.DrawRing(batch, pixel, markerCenter, markerRadius, color * (node.Lit ? 0.94f : 0.58f), Math.Max(1f, 1.7f * scale), 18);
Texture2D texture = ReaperUIData.GetFormTexture(node.Form);
Rectangle frame = texture.Frame();
float iconScale = Math.Min(1f, 17f * scale / Math.Max(frame.Width, frame.Height));
batch.Draw(texture, markerCenter, frame, node.Lit ? Color.White : new Color(116, 109, 109), 0f, frame.Size() * 0.5f, iconScale, SpriteEffects.None, 0f);
string label = ReaperUIData.GetBranchLabel(node.Form);
float labelScale = 0.46f * scale;
Vector2 labelCenter = markerCenter - tangent * (28f * scale);
Vector2 labelSize = FontAssets.MouseText.Value.MeasureString(label) * labelScale;
Utils.DrawBorderString(batch, label, labelCenter - labelSize * 0.5f, color * (node.Lit ? 1f : 0.72f), labelScale);
}
private static void DrawNodeLevels(SpriteBatch batch, Texture2D pixel, ReaperTreeNodeModel node, Vector2 center, Color color, float scale)
{
if (node.Kind is ReaperTreeNodeKind.Base or ReaperTreeNodeKind.Death)
return;
bool unlimited = (node.Kind is ReaperTreeNodeKind.Common
or ReaperTreeNodeKind.DeathUpgrade)
&& ReaperDefinitions.IsUnlimitedCommonNode(node.CommonNode);
int maximum = unlimited ? 8 : Math.Min(node.MaximumLevel, 10);
int filledLevels = unlimited && node.CurrentLevel > 0
? (node.CurrentLevel - 1) % maximum + 1
: node.CurrentLevel;
float spacing = maximum > 5 ? 8f * scale : 10f * scale;
float startX = center.X - (maximum - 1) * spacing * 0.5f;
for (int level = 0; level < maximum; level++)
ReaperTreeDraw.DrawDiamond(batch, pixel, new Vector2(startX + level * spacing, center.Y), (level < filledLevels ? 3.2f : 2.7f) * scale, level < filledLevels ? color : new Color(55, 52, 55));
}
private ReaperTreeNodeModel? Find(ReaperTreeNodeKind kind, ReaperFormId form, int index)
{
foreach (ReaperTreeNodeModel node in nodes)
if (node.Kind == kind && node.Form == form && node.Index == index)
return node;
return null;
}
private ReaperTreeNodeModel? FindCommon(ReaperCommonNode common)
{
foreach (ReaperTreeNodeModel node in nodes)
if ((node.Kind is ReaperTreeNodeKind.Common
or ReaperTreeNodeKind.DeathUpgrade)
&& node.CommonNode == common)
return node;
return null;
}
}
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 sealed class ReaperTreeCanvas : UIElement
{
private readonly ReaperProgressionPanel owner;
private readonly ReaperTreeViewport viewport;
private readonly IReadOnlyList<ReaperTreeNodeModel> nodes;
public ReaperTreeCanvas(ReaperProgressionPanel owner, ReaperTreeViewport viewport, IReadOnlyList<ReaperTreeNodeModel> nodes)
{
this.owner = owner;
this.viewport = viewport;
this.nodes = nodes;
}
protected override void DrawSelf(SpriteBatch spriteBatch)
{
Texture2D pixel = TextureAssets.MagicPixel.Value;
float scale = viewport.ContentScale;
DrawRunicBackdrop(spriteBatch, pixel, scale);
DrawTotem(spriteBatch, scale);
DrawConnections(spriteBatch, pixel, scale);
foreach (ReaperTreeNodeModel node in nodes)
DrawNode(spriteBatch, pixel, node, scale);
if (viewport.HoveredNode is ReaperTreeNodeModel hovered)
Main.instance.MouseText(hovered.Title);
}
private void DrawRunicBackdrop(SpriteBatch batch, Texture2D pixel, float scale)
{
Color grid = new Color(72, 71, 68) * 0.09f;
for (float x = 30f; x < ReaperTreeViewport.ContentWidth; x += 82f)
ReaperTreeDraw.DrawLine(batch, pixel, viewport.DesignToScreen(new Vector2(x, 0f)), viewport.DesignToScreen(new Vector2(x, ReaperTreeViewport.ContentHeight)), grid, Math.Max(1f, scale));
for (float y = 26f; y < ReaperTreeViewport.ContentHeight; y += 82f)
ReaperTreeDraw.DrawLine(batch, pixel, viewport.DesignToScreen(new Vector2(0f, y)), viewport.DesignToScreen(new Vector2(ReaperTreeViewport.ContentWidth, y)), grid, Math.Max(1f, scale));
Vector2 center = viewport.DesignToScreen(ReaperTreeViewport.ContentCenter);
ReaperTreeDraw.DrawRing(batch, pixel, center, 123f * scale, new Color(140, 43, 51) * 0.19f, Math.Max(1f, scale), 48);
ReaperTreeDraw.DrawRing(batch, pixel, center, 454f * scale, new Color(120, 109, 103) * 0.12f, Math.Max(1f, scale), 72);
for (int index = 0; index < 12; index++)
{
float angle = MathHelper.TwoPi * index / 12f;
Vector2 rune = center + new Vector2(MathF.Cos(angle), MathF.Sin(angle)) * 123f * scale;
ReaperTreeDraw.DrawDiamond(batch, pixel, rune, 3.5f * scale, index % 2 == 0 ? new Color(185, 51, 60) * 0.42f : new Color(130, 119, 111) * 0.28f);
}
}
private void DrawTotem(SpriteBatch batch, float scale)
{
Texture2D texture = ReaperUIData.GetFormTexture(ReaperFormId.Base);
Rectangle frame = texture.Frame();
Vector2 center = viewport.DesignToScreen(ReaperTreeViewport.ContentCenter + new Vector2(0f, -16f));
float drawScale = Math.Min(1f, 360f / Math.Max(frame.Width, frame.Height)) * scale;
batch.Draw(texture, center, frame, new Color(180, 187, 179) * 0.11f, -0.55f, frame.Size() * 0.5f, drawScale, SpriteEffects.None, 0f);
}
private void DrawConnections(SpriteBatch batch, Texture2D pixel, float scale)
{
ReaperTreeNodeModel root = Find(ReaperTreeNodeKind.Base, ReaperFormId.Base, 0)!;
ReaperTreeNodeModel death = Find(ReaperTreeNodeKind.Death, ReaperFormId.Death, 0)!;
foreach (ReaperCommonNode common in Enum.GetValues<ReaperCommonNode>())
{
if (ReaperDefinitions.IsDeathNode(common))
continue;
ReaperTreeNodeModel commonNode = FindCommon(common)!;
DrawPath(batch, pixel, root, commonNode, commonNode.Lit, new Color(190, 188, 174), scale, 0.62f);
}
foreach (ReaperCommonNode deathUpgrade in Enum.GetValues<ReaperCommonNode>())
{
if (!ReaperDefinitions.IsDeathNode(deathUpgrade))
continue;
ReaperTreeNodeModel upgradeNode = FindCommon(deathUpgrade)!;
DrawPath(batch, pixel, death, upgradeNode, upgradeNode.Lit,
ReaperUIData.GetPrimaryColor(ReaperFormId.Death), scale, 0.64f);
}
foreach (ReaperFormId form in ReaperDefinitions.BranchForms)
{
Color color = ReaperUIData.GetPrimaryColor(form);
ReaperTreeNodeModel previousStage = root;
ReaperTreeNodeModel? previousSkill = null;
for (int stage = 1; stage <= 3; stage++)
{
ReaperTreeNodeModel stageNode = Find(ReaperTreeNodeKind.Stage, form, stage)!;
ReaperTreeNodeModel skillNode = Find(ReaperTreeNodeKind.Skill, form, stage - 1)!;
DrawPath(batch, pixel, previousStage, stageNode, stageNode.Lit, color, scale, 1f);
DrawPath(batch, pixel, stageNode, skillNode, skillNode.Lit, color, scale, 0.78f);
if (previousSkill is not null)
DrawPath(batch, pixel, previousSkill, skillNode, skillNode.CurrentLevel >= ReaperDefinitions.MaximumSkillLevel, color, scale, 0.48f);
previousStage = stageNode;
previousSkill = skillNode;
}
bool mastered = owner.IsBranchMastered(form);
DrawPath(batch, pixel, previousStage, death, mastered, color, scale, 0.62f);
if (previousSkill is not null)
DrawPath(batch, pixel, previousSkill, death, mastered, color, scale, 0.42f);
}
}
private void DrawPath(SpriteBatch batch, Texture2D pixel, ReaperTreeNodeModel start, ReaperTreeNodeModel end, bool lit, Color color, float scale, float opacity)
{
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 line = lit ? color * opacity : new Color(65, 61, 62) * (opacity * 0.58f);
ReaperTreeDraw.DrawLine(batch, pixel, a, b, new Color(7, 7, 9) * Math.Min(0.94f, opacity + 0.2f), Math.Max(2f, 5f * scale));
ReaperTreeDraw.DrawLine(batch, pixel, a, b, line, Math.Max(1f, (lit ? 2.3f : 1.2f) * scale));
ReaperTreeDraw.DrawDiamond(batch, pixel, Vector2.Lerp(a, b, 0.5f), (lit ? 4.4f : 3f) * scale, line * 1.18f);
}
private void DrawNode(SpriteBatch batch, Texture2D pixel, ReaperTreeNodeModel node, float scale)
{
Vector2 center = viewport.DesignToScreen(node.Position);
float radius = node.Radius * scale;
Color primary = node.Kind == ReaperTreeNodeKind.Common
? new Color(196, 194, 177)
: ReaperUIData.GetPrimaryColor(node.Form);
bool selected = ReferenceEquals(viewport.SelectedNode, node) || viewport.SelectedNode is null && node.Kind == ReaperTreeNodeKind.Base;
bool hovered = ReferenceEquals(viewport.HoveredNode, node);
Color stateColor = node.Lit ? primary : node.CanRequest && node.Affordable ? primary * 0.82f : node.CanRequest ? new Color(180, 57, 65) : new Color(75, 70, 72);
float pulse = 0.86f + MathF.Sin(Main.GlobalTimeWrappedHourly * 3.2f + node.Position.X * 0.01f) * 0.1f;
ReaperTreeDraw.DrawDiamond(batch, pixel, center, radius * 0.88f, new Color(8, 8, 11) * 0.98f);
if (node.Kind is ReaperTreeNodeKind.Base or ReaperTreeNodeKind.Death)
{
ReaperTreeDraw.DrawRing(batch, pixel, center, radius, stateColor * (node.Lit ? pulse : 0.52f), Math.Max(1f, 2.6f * scale), 30);
ReaperTreeDraw.DrawRing(batch, pixel, center, radius - 6f * scale, new Color(104, 91, 84) * 0.56f, Math.Max(1f, scale), 24);
}
else if (node.Kind == ReaperTreeNodeKind.Skill)
ReaperTreeDraw.DrawRing(batch, pixel, center, radius, stateColor * (node.Lit ? pulse : 0.58f), Math.Max(1f, 2f * scale), 20);
else
ReaperTreeDraw.DrawDiamondOutline(batch, pixel, center, radius, stateColor * (node.Lit ? pulse : 0.62f), Math.Max(1f, 2f * scale));
if (selected || hovered)
ReaperTreeDraw.DrawRing(batch, pixel, center, radius + (selected ? 7f : 4f) * scale, selected ? Color.White * 0.72f : stateColor * 0.62f, Math.Max(1f, (selected ? 2f : 1f) * scale), 28);
DrawNodeContent(batch, node, center, radius, primary, scale);
DrawNodeLevels(batch, pixel, node, center + new Vector2(0f, radius + 9f * scale), primary, scale);
if (node.Kind == ReaperTreeNodeKind.Stage && node.Index == 1)
DrawBranchMarker(batch, pixel, node, center, radius, primary, scale);
if (node.Kind == ReaperTreeNodeKind.Stage && node.Index == 3)
{
float labelScale = 0.48f * scale;
string label = AltarDrawHelpers.FitText(ReaperUIData.GetFormName(node.Form), 112f * scale, labelScale);
Vector2 labelSize = FontAssets.MouseText.Value.MeasureString(label) * labelScale;
Utils.DrawBorderString(batch, label, center + new Vector2(-labelSize.X * 0.5f, radius + 19f * scale), primary * (node.Lit ? 1f : 0.62f), labelScale);
}
}
private void DrawNodeContent(SpriteBatch batch, ReaperTreeNodeModel node, Vector2 center, float radius, Color color, float scale)
{
if (node.Kind is ReaperTreeNodeKind.Base or ReaperTreeNodeKind.Death || node.Kind == ReaperTreeNodeKind.Stage && node.Index == 3)
{
Texture2D texture = ReaperUIData.GetFormTexture(node.Form);
Rectangle frame = texture.Frame();
float maximum = node.Kind is ReaperTreeNodeKind.Base or ReaperTreeNodeKind.Death ? radius * 1.3f : radius * 1.12f;
float iconScale = Math.Min(1f, maximum / Math.Max(frame.Width, frame.Height));
Color tint = node.Lit || node.Kind == ReaperTreeNodeKind.Base ? Color.White : new Color(92, 88, 91);
batch.Draw(texture, center, frame, tint, 0f, frame.Size() * 0.5f, iconScale, SpriteEffects.None, 0f);
return;
}
Color runeColor = node.Lit ? ReaperUIData.GetSecondaryColor(node.Form) : color * 0.68f;
if (node.Kind == ReaperTreeNodeKind.Skill)
{
ReaperTreeDraw.DrawSkillRune(batch, TextureAssets.MagicPixel.Value, center, radius, node.Form, node.Index, runeColor, scale);
return;
}
if (node.Kind is ReaperTreeNodeKind.Common
or ReaperTreeNodeKind.DeathUpgrade)
{
ReaperTreeDraw.DrawCommonRune(batch, TextureAssets.MagicPixel.Value, center, radius, node.CommonNode, runeColor, scale);
return;
}
string glyph = node.Kind == ReaperTreeNodeKind.Stage ? ReaperTreeDraw.Roman(node.Index) : string.Empty;
float textScale = 0.62f * scale;
Vector2 size = FontAssets.MouseText.Value.MeasureString(glyph) * textScale;
Utils.DrawBorderString(batch, glyph, center - size * 0.5f, runeColor, textScale);
}
private static void DrawBranchMarker(SpriteBatch batch, Texture2D pixel, ReaperTreeNodeModel node, Vector2 center, float radius, Color color, float scale)
{
Vector2 outward = (node.Position - ReaperTreeViewport.ContentCenter).SafeNormalize(Vector2.UnitY);
Vector2 tangent = new(-outward.Y, outward.X);
Vector2 markerCenter = center + outward * (radius + 28f * scale);
float markerRadius = 12f * scale;
ReaperTreeDraw.DrawDiamond(batch, pixel, markerCenter, markerRadius * 0.82f, new Color(7, 7, 9) * 0.98f);
ReaperTreeDraw.DrawRing(batch, pixel, markerCenter, markerRadius, color * (node.Lit ? 0.94f : 0.58f), Math.Max(1f, 1.7f * scale), 18);
Texture2D texture = ReaperUIData.GetFormTexture(node.Form);
Rectangle frame = texture.Frame();
float iconScale = Math.Min(1f, 17f * scale / Math.Max(frame.Width, frame.Height));
batch.Draw(texture, markerCenter, frame, node.Lit ? Color.White : new Color(116, 109, 109), 0f, frame.Size() * 0.5f, iconScale, SpriteEffects.None, 0f);
string label = ReaperUIData.GetBranchLabel(node.Form);
float labelScale = 0.46f * scale;
Vector2 labelCenter = markerCenter - tangent * (28f * scale);
Vector2 labelSize = FontAssets.MouseText.Value.MeasureString(label) * labelScale;
Utils.DrawBorderString(batch, label, labelCenter - labelSize * 0.5f, color * (node.Lit ? 1f : 0.72f), labelScale);
}
private static void DrawNodeLevels(SpriteBatch batch, Texture2D pixel, ReaperTreeNodeModel node, Vector2 center, Color color, float scale)
{
if (node.Kind is ReaperTreeNodeKind.Base or ReaperTreeNodeKind.Death)
return;
bool unlimited = (node.Kind is ReaperTreeNodeKind.Common
or ReaperTreeNodeKind.DeathUpgrade)
&& ReaperDefinitions.IsUnlimitedCommonNode(node.CommonNode);
int maximum = unlimited ? 8 : Math.Min(node.MaximumLevel, 10);
int filledLevels = unlimited && node.CurrentLevel > 0
? (node.CurrentLevel - 1) % maximum + 1
: node.CurrentLevel;
float spacing = maximum > 5 ? 8f * scale : 10f * scale;
float startX = center.X - (maximum - 1) * spacing * 0.5f;
for (int level = 0; level < maximum; level++)
ReaperTreeDraw.DrawDiamond(batch, pixel, new Vector2(startX + level * spacing, center.Y), (level < filledLevels ? 3.2f : 2.7f) * scale, level < filledLevels ? color : new Color(55, 52, 55));
}
private ReaperTreeNodeModel? Find(ReaperTreeNodeKind kind, ReaperFormId form, int index)
{
foreach (ReaperTreeNodeModel node in nodes)
if (node.Kind == kind && node.Form == form && node.Index == index)
return node;
return null;
}
private ReaperTreeNodeModel? FindCommon(ReaperCommonNode common)
{
foreach (ReaperTreeNodeModel node in nodes)
if ((node.Kind is ReaperTreeNodeKind.Common
or ReaperTreeNodeKind.DeathUpgrade)
&& node.CommonNode == common)
return node;
return null;
}
}