champions-of-luneraria/Assets/Luneraria/UI/Theme/LuneGlyphs.cs
Martin Stickelbroeck 82066d17a6 S2 Glyphe, Item-Rezepte und Ancient-Stufen (Dota 2, LoL, Legion TD 2)
Hinter MatchOptions.Extended: Glyphe als Teamwahl einer Lane (Türme 5 s
unverwundbar, sobald einer unter 50 % fällt; 4 Runden Abklingzeit nur nach
Auslösen), vier Rezepte (Bollwerk, Glutsturm, Quellherz, Splitterkrone),
Ancient-Stufen 1-3 (150 x Stufe, Reparatur 1.000, Schuss 45/75/110),
Mid-Bots jagen bei sicherer Lane die Grotte. Teampanel mit Glyphe und
Ancient, Rezepte im Shop (TEILE FEHLEN), Glyphen-Ring und Ancient-Schüsse.
Kern 566/566 (s2 35), EditMode 32/32, PlayMode 17/17, Verify 718/0,
Selbsttest 30/30, Golden 0, Fingerabdruck unverändert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:54:46 +02:00

492 lines
26 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace Luneraria.UI
{
/// <summary>
/// Vector symbols of the HUD (port of scripts/luneraria/ui/theme/lune_glyphs.gd), drawn from a
/// unit square so they stay sharp at every UI scale. Team emblems follow the G2 world: Blau =
/// Mondsichel (round shapes), Rot = Sonnenscheibe (rayed, angular), the grotto = hexagon.
/// </summary>
public static class LuneGlyphs
{
public static readonly string[] Names = { "coin", "emblem_0", "emblem_1", "emblem_2", "shape_0", "shape_1", "shape_2",
"tank", "melee", "ranged", "splash", "support", "siege", "crown", "champion", "hourglass", "tower",
"ancient", "warning", "check", "close", "bot", "offline", "clock", "heart", "tempo", "range", "armor",
"move", "star", "lock", "up", "lane_top", "lane_mid", "lane_bot", "grotto", "pause", "play", "menu",
"gear", "human", "sword", "basaltschild", "glutklinge", "rankenmantel", "funkenfokus", "quellamulett",
"kristallsplitter", "hero", "skull", "bollwerk", "glutsturm", "quellherz", "splitterkrone" };
const float Tau = Mathf.PI * 2f;
static readonly Dictionary<string, Vector2[]> Cache = new Dictionary<string, Vector2[]>();
/// <summary>Draws one symbol into rect (unit square centred, aspect kept). `accent` is an optional second colour for inner details.</summary>
public static void Draw(Painter2D painter, string glyph, Rect rect, Color color, Color accent = default)
{
if (string.IsNullOrEmpty(glyph))
return;
var f = new Frame(rect);
Color dark = accent.a > 0f ? accent : new Color(color.r * .45f, color.g * .45f, color.b * .45f, color.a);
// S2 recipes: the first part's symbol inside a forged ring with a small spark.
Luneraria.Core.ItemSpec recipe;
if (Luneraria.Core.ContentData.Items.TryGetValue(glyph, out recipe) && recipe.IsRecipe)
{
float inset = rect.width * .16f;
Draw(painter, recipe.Parts[0], new Rect(rect.x + inset, rect.y + inset, rect.width - 2f * inset, rect.height - 2f * inset), color, accent);
ArcLine(painter, f.P(.5f, .5f), f.S * .47f, 0f, Tau, color, Mathf.Max(1f, f.S * .06f));
Shard(painter, f, V(.82f, .18f), 0f, .14f, LuneUiTheme.Hex("fff2c0"));
return;
}
switch (glyph)
{
case "coin":
{
Vector2 c = f.P(.5f, .5f);
float r = f.S * .42f;
Disc(painter, c, r, color);
ArcLine(painter, c, r, 0f, Tau, LuneUiTheme.Darkened(color, .35f), Mathf.Max(1f, r * .12f));
ArcLine(painter, c, r * .62f, 0f, Tau, LuneUiTheme.Darkened(color, .28f), Mathf.Max(1f, r * .1f));
ArcLine(painter, c, r * .8f, Mathf.PI * 1.1f, Mathf.PI * 1.45f, new Color(1f, 1f, 1f, .55f * color.a), Mathf.Max(1f, r * .1f));
break;
}
case "emblem_0":
Fill(painter, f, Crescent(), color);
break;
case "emblem_1":
Disc(painter, f.P(.5f, .5f), f.S * .2f, color);
for (int index = 0; index < 8; index++)
{
float angle = Tau * index / 8f - Mathf.PI * .5f;
Vector2 tip = new Vector2(.5f, .5f) + Dir(angle) * .47f;
Vector2 left = new Vector2(.5f, .5f) + Dir(angle - .22f) * .27f;
Vector2 right = new Vector2(.5f, .5f) + Dir(angle + .22f) * .27f;
Fill(painter, f, new[] { left, tip, right }, color);
}
break;
case "emblem_2":
{
List<Vector2> hexagon = Circle(6, new Vector2(.5f, .5f), .42f, -Mathf.PI * .5f);
hexagon.Add(hexagon[0]);
Line(painter, f, hexagon.ToArray(), color, .08f);
Fill(painter, f, new[] { V(.5f, .3f), V(.64f, .5f), V(.5f, .7f), V(.36f, .5f) }, color);
break;
}
case "shape_0":
Disc(painter, f.P(.5f, .5f), f.S * .4f, color);
break;
case "shape_1":
Fill(painter, f, new[] { V(.5f, .06f), V(.94f, .5f), V(.5f, .94f), V(.06f, .5f) }, color);
break;
case "shape_2":
Fill(painter, f, Circle(6, new Vector2(.5f, .5f), .44f, -Mathf.PI * .5f).ToArray(), color);
break;
case "tank":
case "armor":
{
var shield = new List<Vector2> { V(.2f, .16f), V(.8f, .16f), V(.8f, .48f), V(.72f, .66f), V(.5f, .87f), V(.28f, .66f), V(.2f, .48f) };
if (glyph == "tank")
{
Fill(painter, f, shield.ToArray(), color);
Line(painter, f, new[] { V(.5f, .24f), V(.5f, .74f) }, dark, .07f);
}
else
{
shield.Add(shield[0]);
Line(painter, f, shield.ToArray(), color, .09f);
}
break;
}
case "melee":
case "sword":
{
Vector2[] blade = { V(.5f, .06f), V(.56f, .14f), V(.56f, .62f), V(.44f, .62f), V(.44f, .14f) };
Fill(painter, f, Rot(blade, Mathf.PI * .25f), color);
Fill(painter, f, Rot(new[] { V(.3f, .62f), V(.7f, .62f), V(.7f, .69f), V(.3f, .69f) }, Mathf.PI * .25f), color);
Fill(painter, f, Rot(new[] { V(.46f, .69f), V(.54f, .69f), V(.54f, .88f), V(.46f, .88f) }, Mathf.PI * .25f), color);
break;
}
case "ranged":
{
var bow = new Vector2[13];
for (int index = 0; index < 13; index++)
{
float angle = Mathf.PI * .62f + Mathf.PI * .76f * index / 12f;
bow[index] = new Vector2(.78f, .5f) + Dir(angle) * .5f;
}
Line(painter, f, Rot(bow, -Mathf.PI * .25f), color, .08f);
Line(painter, f, Rot(new[] { bow[0], bow[12] }, -Mathf.PI * .25f), color, .03f);
Line(painter, f, Rot(new[] { V(.14f, .5f), V(.82f, .5f) }, -Mathf.PI * .25f), color, .06f);
Fill(painter, f, Rot(new[] { V(.9f, .5f), V(.76f, .41f), V(.76f, .59f) }, -Mathf.PI * .25f), color);
break;
}
case "splash":
Fill(painter, f, Star(8, .46f, .2f), color);
Disc(painter, f.P(.5f, .5f), f.S * .1f, dark);
break;
case "support":
Fill(painter, f, new[] { V(.4f, .14f), V(.6f, .14f), V(.6f, .4f), V(.86f, .4f), V(.86f, .6f), V(.6f, .6f),
V(.6f, .86f), V(.4f, .86f), V(.4f, .6f), V(.14f, .6f), V(.14f, .4f), V(.4f, .4f) }, color);
break;
case "siege":
Fill(painter, f, Rot(new[] { V(.18f, .18f), V(.74f, .18f), V(.74f, .42f), V(.18f, .42f) }, -Mathf.PI * .2f), color);
Fill(painter, f, Rot(new[] { V(.42f, .4f), V(.52f, .4f), V(.52f, .92f), V(.42f, .92f) }, -Mathf.PI * .2f), color);
break;
case "crown":
case "hero":
Fill(painter, f, new[] { V(.12f, .74f), V(.12f, .3f), V(.32f, .5f), V(.5f, .2f), V(.68f, .5f), V(.88f, .3f), V(.88f, .74f) }, color);
Fill(painter, f, new[] { V(.12f, .78f), V(.88f, .78f), V(.88f, .88f), V(.12f, .88f) }, color);
if (glyph == "crown")
Disc(painter, f.P(.5f, .6f), f.S * .06f, dark);
break;
case "skull":
Fill(painter, f, Circle(20, new Vector2(.5f, .44f), .34f).ToArray(), color);
Fill(painter, f, new[] { V(.32f, .6f), V(.68f, .6f), V(.64f, .86f), V(.36f, .86f) }, color);
Disc(painter, f.P(.37f, .45f), f.S * .09f, dark);
Disc(painter, f.P(.63f, .45f), f.S * .09f, dark);
break;
case "champion":
Fill(painter, f, new[] { V(.5f, .06f), V(.82f, .46f), V(.61f, .46f), V(.61f, .9f), V(.39f, .9f), V(.39f, .46f), V(.18f, .46f) }, color);
break;
case "hourglass":
Fill(painter, f, new[] { V(.24f, .12f), V(.76f, .12f), V(.56f, .5f), V(.44f, .5f) }, color);
Fill(painter, f, new[] { V(.44f, .5f), V(.56f, .5f), V(.76f, .88f), V(.24f, .88f) }, color);
Line(painter, f, new[] { V(.18f, .1f), V(.82f, .1f) }, color, .06f);
Line(painter, f, new[] { V(.18f, .9f), V(.82f, .9f) }, color, .06f);
break;
case "tower":
Fill(painter, f, new[] { V(.28f, .92f), V(.28f, .36f), V(.22f, .36f), V(.22f, .14f), V(.33f, .14f), V(.33f, .23f),
V(.44f, .23f), V(.44f, .14f), V(.56f, .14f), V(.56f, .23f), V(.67f, .23f), V(.67f, .14f),
V(.78f, .14f), V(.78f, .36f), V(.72f, .36f), V(.72f, .92f) }, color);
Fill(painter, f, new[] { V(.44f, .92f), V(.44f, .72f), V(.5f, .66f), V(.56f, .72f), V(.56f, .92f) }, dark);
break;
case "ancient":
Fill(painter, f, new[] { V(.5f, .05f), V(.76f, .4f), V(.6f, .95f), V(.4f, .95f), V(.24f, .4f) }, color);
Line(painter, f, new[] { V(.5f, .05f), V(.5f, .95f) }, dark, .04f);
Line(painter, f, new[] { V(.24f, .4f), V(.5f, .52f), V(.76f, .4f) }, dark, .04f);
break;
case "warning":
Fill(painter, f, new[] { V(.5f, .08f), V(.94f, .88f), V(.06f, .88f) }, color);
Fill(painter, f, new[] { V(.46f, .34f), V(.54f, .34f), V(.53f, .64f), V(.47f, .64f) }, dark);
Disc(painter, f.P(.5f, .75f), f.S * .05f, dark);
break;
case "check":
Line(painter, f, new[] { V(.16f, .52f), V(.4f, .76f), V(.86f, .24f) }, color, .13f);
break;
case "close":
Line(painter, f, new[] { V(.2f, .2f), V(.8f, .8f) }, color, .12f);
Line(painter, f, new[] { V(.8f, .2f), V(.2f, .8f) }, color, .12f);
break;
case "bot":
case "gear":
{
Vector2 c = f.P(.5f, .5f);
for (int index = 0; index < 8; index++)
Fill(painter, f, Rot(new[] { V(.43f, .06f), V(.57f, .06f), V(.57f, .24f), V(.43f, .24f) }, Tau * index / 8f), color);
Disc(painter, c, f.S * .3f, color);
Disc(painter, c, f.S * .12f, dark);
break;
}
case "human":
Disc(painter, f.P(.5f, .3f), f.S * .17f, color);
Fill(painter, f, new[] { V(.2f, .9f), V(.24f, .62f), V(.38f, .52f), V(.62f, .52f), V(.76f, .62f), V(.8f, .9f) }, color);
break;
case "offline":
ArcLine(painter, f.P(.5f, .5f), f.S * .38f, 0f, Tau, color, Mathf.Max(1f, f.S * .1f));
Line(painter, f, new[] { V(.22f, .78f), V(.78f, .22f) }, color, .1f);
break;
case "clock":
ArcLine(painter, f.P(.5f, .5f), f.S * .4f, 0f, Tau, color, Mathf.Max(1f, f.S * .09f));
Line(painter, f, new[] { V(.5f, .24f), V(.5f, .5f), V(.68f, .62f) }, color, .08f);
break;
case "heart":
Fill(painter, f, Heart(), color);
break;
case "tempo":
Fill(painter, f, new[] { V(.6f, .04f), V(.22f, .56f), V(.46f, .56f), V(.38f, .96f), V(.78f, .42f), V(.54f, .42f), V(.66f, .04f) }, color);
break;
case "range":
{
Vector2 c = f.P(.5f, .5f);
ArcLine(painter, c, f.S * .38f, 0f, Tau, color, Mathf.Max(1f, f.S * .08f));
ArcLine(painter, c, f.S * .18f, 0f, Tau, color, Mathf.Max(1f, f.S * .08f));
Disc(painter, c, f.S * .06f, color);
Line(painter, f, new[] { V(.5f, .02f), V(.5f, .16f) }, color, .07f);
Line(painter, f, new[] { V(.5f, .84f), V(.5f, .98f) }, color, .07f);
Line(painter, f, new[] { V(.02f, .5f), V(.16f, .5f) }, color, .07f);
Line(painter, f, new[] { V(.84f, .5f), V(.98f, .5f) }, color, .07f);
break;
}
case "move":
Line(painter, f, new[] { V(.18f, .2f), V(.46f, .5f), V(.18f, .8f) }, color, .11f);
Line(painter, f, new[] { V(.52f, .2f), V(.8f, .5f), V(.52f, .8f) }, color, .11f);
break;
case "star":
Fill(painter, f, Star(5, .47f, .2f), color);
break;
case "lock":
Fill(painter, f, new[] { V(.22f, .46f), V(.78f, .46f), V(.78f, .9f), V(.22f, .9f) }, color);
ArcLine(painter, f.P(.5f, .46f), f.S * .19f, Mathf.PI, Tau, color, Mathf.Max(1f, f.S * .09f));
Disc(painter, f.P(.5f, .64f), f.S * .06f, dark);
break;
case "up":
Fill(painter, f, new[] { V(.5f, .08f), V(.86f, .48f), V(.62f, .48f), V(.62f, .9f), V(.38f, .9f), V(.38f, .48f), V(.14f, .48f) }, color);
break;
case "lane_top":
case "lane_mid":
case "lane_bot":
{
Line(painter, f, new[] { V(.12f, .12f), V(.88f, .12f), V(.88f, .88f), V(.12f, .88f), V(.12f, .12f) }, LuneUiTheme.WithAlpha(color, color.a * .3f), .05f);
Vector2[] path = glyph == "lane_top" ? new[] { V(.2f, .8f), V(.2f, .2f), V(.8f, .2f) }
: glyph == "lane_mid" ? new[] { V(.2f, .8f), V(.8f, .2f) } : new[] { V(.2f, .8f), V(.8f, .8f), V(.8f, .2f) };
Line(painter, f, path, color, .12f);
break;
}
case "grotto":
{
var arch = new List<Vector2> { V(.08f, .9f) };
for (int index = 0; index < 13; index++)
{
float angle = Mathf.PI + Mathf.PI * index / 12f;
arch.Add(new Vector2(.5f, .62f) + new Vector2(Mathf.Cos(angle) * .42f, Mathf.Sin(angle) * .5f));
}
arch.Add(V(.92f, .9f));
Fill(painter, f, arch.ToArray(), color);
var mouth = new List<Vector2> { V(.3f, .9f) };
for (int index = 0; index < 9; index++)
{
float angle = Mathf.PI + Mathf.PI * index / 8f;
mouth.Add(new Vector2(.5f, .72f) + new Vector2(Mathf.Cos(angle) * .2f, Mathf.Sin(angle) * .26f));
}
mouth.Add(V(.7f, .9f));
Fill(painter, f, mouth.ToArray(), dark);
break;
}
case "pause":
Fill(painter, f, new[] { V(.24f, .16f), V(.42f, .16f), V(.42f, .84f), V(.24f, .84f) }, color);
Fill(painter, f, new[] { V(.58f, .16f), V(.76f, .16f), V(.76f, .84f), V(.58f, .84f) }, color);
break;
case "play":
Fill(painter, f, new[] { V(.26f, .14f), V(.84f, .5f), V(.26f, .86f) }, color);
break;
case "menu":
foreach (float y in new[] { .24f, .5f, .76f })
Line(painter, f, new[] { V(.16f, y), V(.84f, y) }, color, .1f);
break;
case "basaltschild":
{
Fill(painter, f, Circle(6, new Vector2(.5f, .5f), .44f, -Mathf.PI * .5f).ToArray(), color);
List<Vector2> inner = Circle(6, new Vector2(.5f, .5f), .27f, -Mathf.PI * .5f);
inner.Add(inner[0]);
Line(painter, f, inner.ToArray(), dark, .07f);
break;
}
case "glutklinge":
{
Vector2[] blade = { V(.5f, .06f), V(.57f, .15f), V(.57f, .66f), V(.43f, .66f), V(.43f, .15f) };
Fill(painter, f, Rot(blade, Mathf.PI * .25f), color);
Fill(painter, f, Rot(new[] { V(.3f, .66f), V(.7f, .66f), V(.7f, .73f), V(.3f, .73f) }, Mathf.PI * .25f), color);
Fill(painter, f, new[] { V(.2f, .5f), V(.3f, .3f), V(.36f, .42f), V(.42f, .22f), V(.5f, .44f), V(.4f, .6f) }, accent.a > 0f ? dark : LuneUiTheme.Hex("ffb070"));
break;
}
case "rankenmantel":
Fill(painter, f, new[] { V(.5f, .1f), V(.66f, .18f), V(.8f, .9f), V(.5f, .8f), V(.2f, .9f), V(.34f, .18f) }, color);
Line(painter, f, new[] { V(.5f, .22f), V(.42f, .4f), V(.56f, .56f), V(.46f, .74f) }, dark, .05f);
break;
case "funkenfokus":
ArcLine(painter, f.P(.5f, .5f), f.S * .42f, 0f, Tau, color, Mathf.Max(1f, f.S * .08f));
Fill(painter, f, new[] { V(.56f, .16f), V(.3f, .54f), V(.47f, .54f), V(.42f, .84f), V(.7f, .44f), V(.53f, .44f), V(.62f, .16f) }, color);
break;
case "quellamulett":
ArcLine(painter, f.P(.5f, .2f), f.S * .14f, Mathf.PI * .9f, Mathf.PI * 2.1f, color, Mathf.Max(1f, f.S * .06f));
Fill(painter, f, Drop(), color);
Disc(painter, f.P(.44f, .66f), f.S * .06f, new Color(1f, 1f, 1f, .45f * color.a));
break;
case "kristallsplitter":
Shard(painter, f, V(.5f, .5f), 0f, .44f, color);
Shard(painter, f, V(.3f, .62f), -.55f, .3f, color);
Shard(painter, f, V(.7f, .62f), .55f, .3f, color);
break;
default:
Disc(painter, f.P(.5f, .5f), f.S * .3f, color);
break;
}
}
static void Shard(Painter2D painter, Frame f, Vector2 center, float angle, float length, Color color)
{
Vector2[] shard = { center + new Vector2(0f, -length), center + new Vector2(.08f, 0f), center + new Vector2(0f, length * .45f), center + new Vector2(-.08f, 0f) };
Fill(painter, f, Rot(shard, angle, center), color);
}
/// <summary>The unit square fitted into a rect (centred, aspect kept).</summary>
struct Frame
{
public readonly float S;
public readonly Vector2 Origin;
public Frame(Rect rect)
{
S = Mathf.Min(rect.width, rect.height);
Origin = rect.position + (rect.size - new Vector2(S, S)) * .5f;
}
public Vector2 P(float x, float y) { return Origin + new Vector2(x, y) * S; }
public Vector2 P(Vector2 p) { return Origin + p * S; }
}
static Vector2 V(float x, float y) { return new Vector2(x, y); }
static Vector2 Dir(float angle) { return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)); }
static Vector2[] Rot(Vector2[] points, float angle) { return Rot(points, angle, new Vector2(.5f, .5f)); }
static Vector2[] Rot(Vector2[] points, float angle, Vector2 pivot)
{
var result = new Vector2[points.Length];
float c = Mathf.Cos(angle);
float s = Mathf.Sin(angle);
for (int index = 0; index < points.Length; index++)
{
Vector2 d = points[index] - pivot;
result[index] = pivot + new Vector2(d.x * c - d.y * s, d.x * s + d.y * c);
}
return result;
}
static void Fill(Painter2D painter, Frame f, Vector2[] points, Color color)
{
if (points.Length < 3)
return;
painter.fillColor = color;
painter.BeginPath();
painter.MoveTo(f.P(points[0]));
for (int index = 1; index < points.Length; index++)
painter.LineTo(f.P(points[index]));
painter.ClosePath();
painter.Fill(FillRule.NonZero);
}
static void Line(Painter2D painter, Frame f, Vector2[] points, Color color, float width)
{
painter.strokeColor = color;
painter.lineWidth = Mathf.Max(1f, width * f.S);
painter.lineJoin = LineJoin.Miter;
painter.lineCap = LineCap.Butt;
painter.BeginPath();
painter.MoveTo(f.P(points[0]));
for (int index = 1; index < points.Length; index++)
painter.LineTo(f.P(points[index]));
painter.Stroke();
}
static void Disc(Painter2D painter, Vector2 center, float radius, Color color)
{
painter.fillColor = color;
painter.BeginPath();
painter.Arc(center, radius, Angle.Degrees(0f), Angle.Degrees(360f));
painter.ClosePath();
painter.Fill();
}
/// <summary>Godot draw_arc: start to end angle (radians, clockwise on screen).</summary>
static void ArcLine(Painter2D painter, Vector2 center, float radius, float start, float end, Color color, float width)
{
painter.strokeColor = color;
painter.lineWidth = width;
painter.lineCap = LineCap.Butt;
painter.BeginPath();
painter.Arc(center, radius, Angle.Radians(start), Angle.Radians(end));
if (end - start >= Tau - .001f)
painter.ClosePath();
painter.Stroke();
}
static List<Vector2> Circle(int points, Vector2 center, float radius, float start = 0f)
{
var result = new List<Vector2>(points);
for (int index = 0; index < points; index++)
result.Add(center + Dir(start + Tau * index / points) * radius);
return result;
}
static Vector2[] Star(int points, float outer, float inner, float start = -Mathf.PI * .5f)
{
var result = new Vector2[points * 2];
for (int index = 0; index < points * 2; index++)
result[index] = new Vector2(.5f, .5f) + Dir(start + Mathf.PI * index / points) * (index % 2 == 0 ? outer : inner);
return result;
}
/// <summary>Crescent (Blau): the outer disc minus an offset disc, as Geometry2D.clip_polygons gave in Godot.</summary>
static Vector2[] Crescent()
{
if (Cache.TryGetValue("crescent", out Vector2[] cached))
return cached;
var outerCenter = new Vector2(.5f, .5f);
var innerCenter = new Vector2(.66f, .4f);
const float outerRadius = .42f;
const float innerRadius = .33f;
List<Vector2> outer = Circle(48, outerCenter, outerRadius);
List<Vector2> inner = Circle(48, innerCenter, innerRadius);
// The outer arc outside the inner disc, then the inner arc inside the outer disc, backwards.
int startOuter = 0;
while (!(Inside(outer[startOuter], innerCenter, innerRadius) && !Inside(outer[(startOuter + 1) % 48], innerCenter, innerRadius)))
startOuter++;
var result = new List<Vector2>();
for (int step = 1; step <= 48; step++)
{
Vector2 point = outer[(startOuter + step) % 48];
if (Inside(point, innerCenter, innerRadius))
break;
result.Add(point);
}
int startInner = 0;
while (!(Inside(inner[startInner], outerCenter, outerRadius) && !Inside(inner[(startInner + 1) % 48], outerCenter, outerRadius)))
startInner++;
for (int step = 0; step < 48; step++)
{
Vector2 point = inner[((startInner - step) % 48 + 48) % 48];
if (!Inside(point, outerCenter, outerRadius))
break;
result.Add(point);
}
cached = result.ToArray();
Cache["crescent"] = cached;
return cached;
}
static bool Inside(Vector2 point, Vector2 center, float radius)
{
return (point - center).sqrMagnitude < radius * radius;
}
static Vector2[] Heart()
{
if (Cache.TryGetValue("heart", out Vector2[] cached))
return cached;
cached = new Vector2[40];
for (int index = 0; index < 40; index++)
{
float t = Tau * index / 40f;
float x = 16f * Mathf.Pow(Mathf.Sin(t), 3);
float y = 13f * Mathf.Cos(t) - 5f * Mathf.Cos(2f * t) - 2f * Mathf.Cos(3f * t) - Mathf.Cos(4f * t);
cached[index] = new Vector2(.5f + x / 38f, .47f - y / 38f);
}
Cache["heart"] = cached;
return cached;
}
static Vector2[] Drop()
{
var result = new Vector2[18];
result[0] = new Vector2(.5f, .16f);
for (int index = 0; index < 17; index++)
{
float angle = -Mathf.PI * .15f + Mathf.PI * 1.3f * index / 16f;
result[1 + index] = new Vector2(.5f, .64f) + Dir(angle) * .22f;
}
return result;
}
}
}