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() };
}
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() };
}