diff --git a/LightlessSync/LightlessSync.csproj b/LightlessSync/LightlessSync.csproj
index b51bcd7..daf6cfd 100644
--- a/LightlessSync/LightlessSync.csproj
+++ b/LightlessSync/LightlessSync.csproj
@@ -66,9 +66,6 @@
PreserveNewest
-
-
-
diff --git a/LightlessSync/UI/Changelog/changelog.yaml b/LightlessSync/UI/Changelog/changelog.yaml
index c28a2c3..d3024c9 100644
--- a/LightlessSync/UI/Changelog/changelog.yaml
+++ b/LightlessSync/UI/Changelog/changelog.yaml
@@ -171,10 +171,4 @@ changelog:
- "Fixed owners being visible in moderator list view."
- "Removed Pin/Remove/Ban buttons on Owners when viewing as moderator."
- "Fixed nameplate bug in PvP."
- - "Added 1 or 3 day options for inactive check."
-
- - name: "Template"
- tagline: ""
- date: "October 15th 2025"
- is_current: false
- message: "Thank you for using Lightless Sync!\n\nThis update brings quality of life improvements and polish to the user experience.\nWe're committed to helping you share your character with others seamlessly.\n\nIf you have any suggestions or encounter any issues, please let us know on Discord or GitHub!\n\n- The Lightless Team"
+ - "Added 1 or 3 day options for inactive check."
\ No newline at end of file
diff --git a/LightlessSync/UI/Changelog/contributors.txt b/LightlessSync/UI/Changelog/contributors.txt
deleted file mode 100644
index fabb456..0000000
--- a/LightlessSync/UI/Changelog/contributors.txt
+++ /dev/null
@@ -1 +0,0 @@
-[Add contributor names - GitHub handles, etc.]
diff --git a/LightlessSync/UI/Changelog/credits.txt b/LightlessSync/UI/Changelog/credits.txt
deleted file mode 100644
index 4e2fe79..0000000
--- a/LightlessSync/UI/Changelog/credits.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-UI design inspired by Brio's update window (Etheirys/Brio). Thanks to their team for the great UX ideas.
-Special thanks to the Dalamud team and the XIV modding ecosystem for tooling & APIs.
diff --git a/LightlessSync/UI/Changelog/supporters.txt b/LightlessSync/UI/Changelog/supporters.txt
deleted file mode 100644
index f8a29df..0000000
--- a/LightlessSync/UI/Changelog/supporters.txt
+++ /dev/null
@@ -1 +0,0 @@
-[Your Names Here]
diff --git a/LightlessSync/UI/UpdateNotesUi.cs b/LightlessSync/UI/UpdateNotesUi.cs
index 83ac903..37763d3 100644
--- a/LightlessSync/UI/UpdateNotesUi.cs
+++ b/LightlessSync/UI/UpdateNotesUi.cs
@@ -6,7 +6,6 @@ using LightlessSync.LightlessConfiguration;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using Microsoft.Extensions.Logging;
-using System.IO;
using System.Numerics;
using System.Reflection;
using System.Text;
@@ -16,20 +15,15 @@ using Dalamud.Interface;
namespace LightlessSync.UI;
+// Inspiration taken from Brio and Character Select+ (goats)
public class UpdateNotesUi : WindowMediatorSubscriberBase
{
- private readonly LightlessConfigService _configService;
private readonly UiSharedService _uiShared;
- private readonly List _contributors = [];
- private readonly List _credits = [];
- private readonly List _supporters = [];
-
private ChangelogFile _changelog = new();
private bool _scrollToTop;
private int _selectedTab;
- // Particle system for visual effects
private struct Particle
{
public Vector2 Position;
@@ -37,27 +31,29 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
public float Life;
public float MaxLife;
public float Size;
- public Vector4 Color;
public ParticleType Type;
- public float Rotation;
- public float RotationSpeed;
public List? Trail;
- public bool IsLargeMoon;
+ public float Twinkle;
+ public float Depth;
+ public float Hue;
}
private enum ParticleType
{
- Star,
- Moon,
- Sparkle,
- FastFallingStar
+ TwinklingStar,
+ ShootingStar
}
private readonly List _particles = [];
- private float _particleTimer;
- private readonly Random _particleRandom = new();
- private Particle? _largeMoon;
- private float _largeMoonTimer;
+ private float _particleSpawnTimer;
+ private readonly Random _random = new();
+
+ private const float HeaderHeight = 150f;
+ private const float ParticleSpawnInterval = 0.2f;
+ private const int MaxParticles = 50;
+ private const int MaxTrailLength = 50;
+ private const float EdgeFadeDistance = 30f;
+ private const float ExtendedParticleHeight = 40f;
public UpdateNotesUi(ILogger logger,
LightlessMediator mediator,
@@ -66,7 +62,6 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
PerformanceCollectorService performanceCollectorService)
: base(logger, mediator, "Lightless Sync — Update Notes", performanceCollectorService)
{
- _configService = configService;
_uiShared = uiShared;
AllowClickthrough = false;
@@ -106,20 +101,20 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
var windowPos = ImGui.GetWindowPos();
var windowPadding = ImGui.GetStyle().WindowPadding;
var headerWidth = (800f * ImGuiHelpers.GlobalScale) - (windowPadding.X * 2);
- var headerHeight = 140f * ImGuiHelpers.GlobalScale;
var headerStart = windowPos + new Vector2(windowPadding.X, windowPadding.Y);
- var headerEnd = headerStart + new Vector2(headerWidth, headerHeight);
+ var headerEnd = headerStart + new Vector2(headerWidth, HeaderHeight);
+ var headerSize = new Vector2(headerWidth, HeaderHeight);
+
+ var extendedParticleSize = new Vector2(headerWidth, HeaderHeight + ExtendedParticleHeight);
DrawGradientBackground(headerStart, headerEnd);
- DrawParticleEffects(headerStart, new Vector2(headerWidth, headerHeight));
DrawHeaderText(headerStart);
DrawHeaderButtons(headerStart, headerWidth);
+ DrawBottomGradient(headerStart, headerEnd, headerWidth);
- ImGui.SetCursorPosY(windowPadding.Y + headerHeight + 5);
-
- // Version badge with icon
- ImGui.SetCursorPosX(12);
+ ImGui.SetCursorPosY(windowPadding.Y + HeaderHeight + 5);
+ ImGui.SetCursorPosX(20);
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGui.TextColored(UIColors.Get("LightlessGreen"), FontAwesomeIcon.Star.ToIconString());
@@ -140,16 +135,15 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
ImGui.TextColored(new Vector4(0.65f, 0.65f, 0.75f, 1.0f), $" – {_changelog.Subline}");
}
}
-
- ImGui.Separator();
ImGuiHelpers.ScaledDummy(3);
+
+ DrawParticleEffects(headerStart, extendedParticleSize);
}
private void DrawGradientBackground(Vector2 headerStart, Vector2 headerEnd)
{
var drawList = ImGui.GetWindowDrawList();
- // Dark night sky background with stars pattern
var darkPurple = new Vector4(0.08f, 0.05f, 0.15f, 1.0f);
var deepPurple = new Vector4(0.12f, 0.08f, 0.20f, 1.0f);
@@ -162,8 +156,7 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
ImGui.GetColorU32(deepPurple)
);
- // Add some static "distant stars" for depth
- var random = new Random(42); // Fixed seed for consistent pattern
+ var random = new Random(42);
for (int i = 0; i < 50; i++)
{
var starPos = headerStart + new Vector2(
@@ -173,27 +166,44 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
var brightness = 0.3f + (float)random.NextDouble() * 0.4f;
drawList.AddCircleFilled(starPos, 1f, ImGui.GetColorU32(new Vector4(1f, 1f, 1f, brightness)));
}
+ }
+
+ private void DrawBottomGradient(Vector2 headerStart, Vector2 headerEnd, float width)
+ {
+ var drawList = ImGui.GetWindowDrawList();
+ var gradientHeight = 60f;
- // Accent border at bottom with glow
- drawList.AddLine(
- new Vector2(headerStart.X, headerEnd.Y),
- headerEnd,
- ImGui.GetColorU32(UIColors.Get("LightlessPurple")),
- 2f
- );
+ for (int i = 0; i < gradientHeight; i++)
+ {
+ var progress = i / gradientHeight;
+ var smoothProgress = progress * progress;
+ var r = 0.12f + (0.0f - 0.12f) * smoothProgress;
+ var g = 0.08f + (0.0f - 0.08f) * smoothProgress;
+ var b = 0.20f + (0.0f - 0.20f) * smoothProgress;
+ var alpha = 1f - smoothProgress;
+ var gradientColor = new Vector4(r, g, b, alpha);
+ drawList.AddLine(
+ new Vector2(headerStart.X, headerEnd.Y + i),
+ new Vector2(headerStart.X + width, headerEnd.Y + i),
+ ImGui.GetColorU32(gradientColor),
+ 1f
+ );
+ }
}
private void DrawHeaderText(Vector2 headerStart)
{
- // Title text overlay - drawn after particles so it's on top
- ImGui.SetCursorScreenPos(headerStart + new Vector2(20, 30));
+ var textX = 20f;
+ var textY = 30f;
+
+ ImGui.SetCursorScreenPos(headerStart + new Vector2(textX, textY));
using (_uiShared.UidFont.Push())
{
ImGui.TextColored(new Vector4(0.95f, 0.95f, 0.95f, 1.0f), "Lightless Sync");
}
- ImGui.SetCursorScreenPos(headerStart + new Vector2(20, 75));
+ ImGui.SetCursorScreenPos(headerStart + new Vector2(textX, textY + 45f));
ImGui.TextColored(UIColors.Get("LightlessBlue"), "Update Notes");
}
@@ -203,8 +213,6 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
var spacing = 8f * ImGuiHelpers.GlobalScale;
var rightPadding = 15f * ImGuiHelpers.GlobalScale;
var topPadding = 15f * ImGuiHelpers.GlobalScale;
-
- // Position for buttons in top right
var buttonY = headerStart.Y + topPadding;
var gitButtonX = headerStart.X + headerWidth - rightPadding - buttonSize.X;
var discordButtonX = gitButtonX - buttonSize.X - spacing;
@@ -239,372 +247,202 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
private void DrawParticleEffects(Vector2 bannerStart, Vector2 bannerSize)
{
var deltaTime = ImGui.GetIO().DeltaTime;
- _particleTimer += deltaTime;
- _largeMoonTimer += deltaTime;
+ _particleSpawnTimer += deltaTime;
- // Spawn new particles
- if (_particleTimer > 0.3f && _particles.Count < 30)
+ if (_particleSpawnTimer > ParticleSpawnInterval && _particles.Count < MaxParticles)
{
- SpawnParticle(bannerStart, bannerSize);
- _particleTimer = 0f;
+ SpawnParticle(bannerSize);
+ _particleSpawnTimer = 0f;
}
- // Spawn or update large moon
- if (_largeMoon == null || _largeMoonTimer > 45f)
+ if (_random.NextDouble() < 0.003)
{
- SpawnLargeMoon(bannerStart, bannerSize);
- _largeMoonTimer = 0f;
+ SpawnShootingStar(bannerSize);
}
var drawList = ImGui.GetWindowDrawList();
- // Update and draw large moon first (background layer)
- if (_largeMoon != null)
- {
- var moon = _largeMoon.Value;
- moon.Position += moon.Velocity * deltaTime;
- moon.Life -= deltaTime;
-
- // Keep moon within banner bounds with padding
- var padding = moon.Size + 10;
- if (moon.Life <= 0 ||
- moon.Position.X < bannerStart.X - padding ||
- moon.Position.X > bannerStart.X + bannerSize.X + padding ||
- moon.Position.Y < bannerStart.Y - padding ||
- moon.Position.Y > bannerStart.Y + bannerSize.Y + padding)
- {
- _largeMoon = null;
- }
- else
- {
- float alpha = Math.Min(1f, moon.Life / moon.MaxLife);
- var color = moon.Color with { W = moon.Color.W * alpha };
- DrawMoon(drawList, moon.Position, moon.Size, color);
- _largeMoon = moon;
- }
- }
-
- // Update and draw regular particles
for (int i = _particles.Count - 1; i >= 0; i--)
{
var particle = _particles[i];
- // Update trail for stars
- if ((particle.Type == ParticleType.Star || particle.Type == ParticleType.FastFallingStar) && particle.Trail != null)
+ var screenPos = bannerStart + particle.Position;
+
+ if (particle.Type == ParticleType.ShootingStar && particle.Trail != null)
{
particle.Trail.Insert(0, particle.Position);
- var maxTrailLength = particle.Type == ParticleType.FastFallingStar ? 15 : 8;
- if (particle.Trail.Count > maxTrailLength)
+ if (particle.Trail.Count > MaxTrailLength)
particle.Trail.RemoveAt(particle.Trail.Count - 1);
}
+ if (particle.Type == ParticleType.TwinklingStar)
+ {
+ particle.Twinkle += 0.005f * particle.Depth;
+ }
+
particle.Position += particle.Velocity * deltaTime;
particle.Life -= deltaTime;
- particle.Rotation += particle.RotationSpeed * deltaTime;
- if (particle.Life <= 0 ||
- particle.Position.X > bannerStart.X + bannerSize.X + 50 ||
- particle.Position.X < bannerStart.X - 50 ||
- particle.Position.Y < bannerStart.Y - 50 ||
- particle.Position.Y > bannerStart.Y + bannerSize.Y + 50)
+ var isOutOfBounds = particle.Position.X < -50 || particle.Position.X > bannerSize.X + 50 ||
+ particle.Position.Y < -50 || particle.Position.Y > bannerSize.Y + 50;
+
+ if (particle.Life <= 0 || (particle.Type != ParticleType.TwinklingStar && isOutOfBounds))
{
_particles.RemoveAt(i);
continue;
}
- float alpha = Math.Min(1f, particle.Life / particle.MaxLife);
- var color = particle.Color with { W = particle.Color.W * alpha };
-
- // Draw trail for stars
- if ((particle.Type == ParticleType.Star || particle.Type == ParticleType.FastFallingStar) && particle.Trail != null && particle.Trail.Count > 1)
+ if (particle.Type == ParticleType.TwinklingStar)
{
+ if (particle.Position.X < 0 || particle.Position.X > bannerSize.X)
+ particle.Velocity = particle.Velocity with { X = -particle.Velocity.X };
+ if (particle.Position.Y < 0 || particle.Position.Y > bannerSize.Y)
+ particle.Velocity = particle.Velocity with { Y = -particle.Velocity.Y };
+ }
+
+ var fadeIn = Math.Min(1f, (particle.MaxLife - particle.Life) / 20f);
+ var fadeOut = Math.Min(1f, particle.Life / 20f);
+ var lifeFade = Math.Min(fadeIn, fadeOut);
+
+ var edgeFadeX = Math.Min(
+ Math.Min(1f, (particle.Position.X + EdgeFadeDistance) / EdgeFadeDistance),
+ Math.Min(1f, (bannerSize.X - particle.Position.X + EdgeFadeDistance) / EdgeFadeDistance)
+ );
+ var edgeFadeY = Math.Min(
+ Math.Min(1f, (particle.Position.Y + EdgeFadeDistance) / EdgeFadeDistance),
+ Math.Min(1f, (bannerSize.Y - particle.Position.Y + EdgeFadeDistance) / EdgeFadeDistance)
+ );
+ var edgeFade = Math.Min(edgeFadeX, edgeFadeY);
+
+ var baseAlpha = lifeFade * edgeFade;
+ var finalAlpha = particle.Type == ParticleType.TwinklingStar
+ ? baseAlpha * (0.6f + 0.4f * MathF.Sin(particle.Twinkle))
+ : baseAlpha;
+
+ if (particle.Type == ParticleType.ShootingStar && particle.Trail != null && particle.Trail.Count > 1)
+ {
+ var cyanColor = new Vector4(0.4f, 0.8f, 1.0f, 1.0f);
+
for (int t = 1; t < particle.Trail.Count; t++)
{
- float trailAlpha = alpha * (1f - (t / (float)particle.Trail.Count)) * (particle.Type == ParticleType.FastFallingStar ? 0.7f : 0.5f);
- var trailColor = color with { W = trailAlpha };
- float thickness = particle.Type == ParticleType.FastFallingStar ? 2.5f : 1.5f;
+ var trailProgress = (float)t / particle.Trail.Count;
+ var trailAlpha = Math.Min(1f, (1f - trailProgress) * finalAlpha * 1.8f);
+ var trailWidth = (1f - trailProgress) * 3f + 1f;
+
+ var glowAlpha = trailAlpha * 0.4f;
drawList.AddLine(
- particle.Trail[t - 1],
- particle.Trail[t],
- ImGui.GetColorU32(trailColor),
- thickness
+ bannerStart + particle.Trail[t - 1],
+ bannerStart + particle.Trail[t],
+ ImGui.GetColorU32(cyanColor with { W = glowAlpha }),
+ trailWidth + 4f
+ );
+
+ drawList.AddLine(
+ bannerStart + particle.Trail[t - 1],
+ bannerStart + particle.Trail[t],
+ ImGui.GetColorU32(cyanColor with { W = trailAlpha }),
+ trailWidth
);
}
}
-
- // Draw based on particle type
- switch (particle.Type)
+ else if (particle.Type == ParticleType.TwinklingStar)
{
- case ParticleType.Star:
- case ParticleType.FastFallingStar:
- DrawStar(drawList, particle.Position, particle.Size, color, particle.Rotation);
- break;
- case ParticleType.Moon:
- DrawMoon(drawList, particle.Position, particle.Size, color);
- break;
- case ParticleType.Sparkle:
- DrawSparkle(drawList, particle.Position, particle.Size, color, particle.Rotation);
- break;
+ DrawTwinklingStar(drawList, screenPos, particle.Size, particle.Hue, finalAlpha, particle.Depth);
}
_particles[i] = particle;
}
}
- private void DrawStar(ImDrawListPtr drawList, Vector2 position, float size, Vector4 color, float rotation)
+ private void DrawTwinklingStar(ImDrawListPtr drawList, Vector2 position, float size, float hue, float alpha, float depth)
{
- // Draw a 5-pointed star
- var points = new Vector2[10];
- for (int i = 0; i < 10; i++)
- {
- float angle = (i * MathF.PI / 5) + rotation;
- float radius = (i % 2 == 0) ? size : size * 0.4f;
- points[i] = position + new Vector2(MathF.Cos(angle) * radius, MathF.Sin(angle) * radius);
- }
+ var color = HslToRgb(hue, 1.0f, 0.85f);
+ color.W = alpha;
- // Draw filled star
- for (int i = 0; i < 5; i++)
- {
- drawList.AddTriangleFilled(
- position,
- points[i * 2],
- points[(i * 2 + 2) % 10],
- ImGui.GetColorU32(color)
- );
- }
-
- // Glow effect
- var glowColor = color with { W = color.W * 0.3f };
- drawList.AddCircleFilled(position, size * 1.5f, ImGui.GetColorU32(glowColor));
- }
-
- private void DrawMoon(ImDrawListPtr drawList, Vector2 position, float size, Vector4 color)
- {
- // Enhanced glow for larger moons
- var glowRadius = size > 15f ? 2.5f : 1.8f;
- var glowColor = color with { W = color.W * (size > 15f ? 0.15f : 0.25f) };
- drawList.AddCircleFilled(position, size * glowRadius, ImGui.GetColorU32(glowColor));
-
- // Draw crescent moon
drawList.AddCircleFilled(position, size, ImGui.GetColorU32(color));
- // Draw shadow circle to create crescent
- var shadowColor = new Vector4(0.08f, 0.05f, 0.15f, 1.0f);
- drawList.AddCircleFilled(position + new Vector2(size * 0.4f, 0), size * 0.8f, ImGui.GetColorU32(shadowColor));
-
- // Additional glow layer for large moons
- if (size > 15f)
- {
- var outerGlow = color with { W = color.W * 0.08f };
- drawList.AddCircleFilled(position, size * 3.5f, ImGui.GetColorU32(outerGlow));
- }
+ var glowColor = color with { W = alpha * 0.3f };
+ drawList.AddCircleFilled(position, size * (1.2f + depth * 0.3f), ImGui.GetColorU32(glowColor));
}
- private void DrawSparkle(ImDrawListPtr drawList, Vector2 position, float size, Vector4 color, float rotation)
+ private static Vector4 HslToRgb(float h, float s, float l)
{
- // Draw a 4-pointed sparkle (plus shape)
- var thickness = size * 0.3f;
+ h = h / 360f;
+ float c = (1 - MathF.Abs(2 * l - 1)) * s;
+ float x = c * (1 - MathF.Abs((h * 6) % 2 - 1));
+ float m = l - c / 2;
- // Horizontal line
- drawList.AddLine(
- position + new Vector2(-size, 0),
- position + new Vector2(size, 0),
- ImGui.GetColorU32(color),
- thickness
- );
+ float r, g, b;
+ if (h < 1f / 6f) { r = c; g = x; b = 0; }
+ else if (h < 2f / 6f) { r = x; g = c; b = 0; }
+ else if (h < 3f / 6f) { r = 0; g = c; b = x; }
+ else if (h < 4f / 6f) { r = 0; g = x; b = c; }
+ else if (h < 5f / 6f) { r = x; g = 0; b = c; }
+ else { r = c; g = 0; b = x; }
- // Vertical line
- drawList.AddLine(
- position + new Vector2(0, -size),
- position + new Vector2(0, size),
- ImGui.GetColorU32(color),
- thickness
- );
-
- // Center glow
- drawList.AddCircleFilled(position, size * 0.4f, ImGui.GetColorU32(color));
- var glowColor = color with { W = color.W * 0.4f };
- drawList.AddCircleFilled(position, size * 1.2f, ImGui.GetColorU32(glowColor));
+ return new Vector4(r + m, g + m, b + m, 1.0f);
}
- private void SpawnParticle(Vector2 bannerStart, Vector2 bannerSize)
+
+ private void SpawnParticle(Vector2 bannerSize)
{
- var typeRoll = _particleRandom.Next(100);
- var particleType = typeRoll switch
- {
- < 35 => ParticleType.Star,
- < 50 => ParticleType.Moon,
- < 65 => ParticleType.FastFallingStar,
- _ => ParticleType.Sparkle
- };
+ var position = new Vector2(
+ (float)_random.NextDouble() * bannerSize.X,
+ (float)_random.NextDouble() * bannerSize.Y
+ );
- Vector2 position;
- Vector2 velocity;
+ var depthLayers = new[] { 0.5f, 1.0f, 1.5f };
+ var depth = depthLayers[_random.Next(depthLayers.Length)];
- // Stars: spawn from top, move diagonally down
- if (particleType == ParticleType.Star)
- {
- // Spawn from top edge
- position = new Vector2(
- bannerStart.X + (float)_particleRandom.NextDouble() * bannerSize.X,
- bannerStart.Y - 10
- );
-
- // Move diagonally down (shooting star effect)
- var angle = MathF.PI * 0.25f + (float)(_particleRandom.NextDouble() - 0.5) * 0.5f; // 45° ± variation
- var speed = 30f + (float)_particleRandom.NextDouble() * 40f;
- velocity = new Vector2(MathF.Cos(angle) * speed, MathF.Sin(angle) * speed);
- }
- // Fast falling stars: spawn from top, fall straight down very fast
- else if (particleType == ParticleType.FastFallingStar)
- {
- // Spawn from top edge, random X position
- position = new Vector2(
- bannerStart.X + (float)_particleRandom.NextDouble() * bannerSize.X,
- bannerStart.Y - 10
- );
-
- // Fall almost straight down with slight horizontal drift
- var horizontalDrift = -10f + (float)_particleRandom.NextDouble() * 20f;
- var speed = 120f + (float)_particleRandom.NextDouble() * 80f; // Much faster!
- velocity = new Vector2(horizontalDrift, speed);
- }
- // Moons: drift slowly across
- else if (particleType == ParticleType.Moon)
- {
- // Spawn from left side
- position = new Vector2(
- bannerStart.X - 10,
- bannerStart.Y + (float)_particleRandom.NextDouble() * bannerSize.Y
- );
-
- // Drift slowly to the right with slight vertical movement
- velocity = new Vector2(
- 15f + (float)_particleRandom.NextDouble() * 10f,
- -5f + (float)_particleRandom.NextDouble() * 10f
- );
- }
- // Sparkles: float gently
- else
- {
- position = new Vector2(
- bannerStart.X + (float)_particleRandom.NextDouble() * bannerSize.X,
- bannerStart.Y + (float)_particleRandom.NextDouble() * bannerSize.Y
- );
-
- velocity = new Vector2(
- -5f + (float)_particleRandom.NextDouble() * 10f,
- -5f + (float)_particleRandom.NextDouble() * 10f
- );
- }
+ var velocity = new Vector2(
+ ((float)_random.NextDouble() - 0.5f) * 0.05f * depth,
+ ((float)_random.NextDouble() - 0.5f) * 0.05f * depth
+ );
- var particle = new Particle
+ var isBlue = _random.NextDouble() < 0.5;
+ var hue = isBlue ? 220f + (float)_random.NextDouble() * 30f : 270f + (float)_random.NextDouble() * 40f;
+ var size = (0.5f + (float)_random.NextDouble() * 2f) * depth;
+ var maxLife = 120f + (float)_random.NextDouble() * 60f;
+
+ _particles.Add(new Particle
{
Position = position,
Velocity = velocity,
- MaxLife = particleType switch
- {
- ParticleType.Star => 3f + (float)_particleRandom.NextDouble() * 2f,
- ParticleType.Moon => 8f + (float)_particleRandom.NextDouble() * 4f,
- ParticleType.FastFallingStar => 1.5f + (float)_particleRandom.NextDouble() * 1f,
- _ => 6f + (float)_particleRandom.NextDouble() * 4f
- },
- Size = particleType switch
- {
- ParticleType.Star => 2.5f + (float)_particleRandom.NextDouble() * 2f,
- ParticleType.Moon => 3f + (float)_particleRandom.NextDouble() * 2f,
- ParticleType.FastFallingStar => 3f + (float)_particleRandom.NextDouble() * 2f,
- _ => 2f + (float)_particleRandom.NextDouble() * 2f
- },
- Color = particleType switch
- {
- ParticleType.Star => new Vector4(1.0f, 1.0f, 0.9f, 0.9f),
- ParticleType.Moon => UIColors.Get("LightlessBlue") with { W = 0.7f },
- ParticleType.FastFallingStar => new Vector4(1.0f, 0.95f, 0.85f, 1.0f), // Bright white-yellow
- _ => UIColors.Get("LightlessPurple") with { W = 0.8f }
- },
- Type = particleType,
- Rotation = (float)_particleRandom.NextDouble() * MathF.PI * 2,
- RotationSpeed = particleType == ParticleType.Star || particleType == ParticleType.FastFallingStar ? 2f : -0.5f + (float)_particleRandom.NextDouble() * 1f,
- Trail = particleType == ParticleType.Star || particleType == ParticleType.FastFallingStar ? new List() : null,
- IsLargeMoon = false
- };
-
- particle.Life = particle.MaxLife;
- _particles.Add(particle);
- }
-
- private void SpawnLargeMoon(Vector2 bannerStart, Vector2 bannerSize)
- {
- // Large moon travels across the banner like a celestial body
- var spawnSide = _particleRandom.Next(4);
- Vector2 position;
- Vector2 velocity;
-
- switch (spawnSide)
- {
- case 0:
- // Spawn from left, move to right
- position = new Vector2(
- bannerStart.X - 50,
- bannerStart.Y + (float)_particleRandom.NextDouble() * bannerSize.Y
- );
- velocity = new Vector2(
- 15f + (float)_particleRandom.NextDouble() * 10f,
- -5f + (float)_particleRandom.NextDouble() * 10f
- );
- break;
- case 1:
- // Spawn from top, move down and across
- position = new Vector2(
- bannerStart.X + (float)_particleRandom.NextDouble() * bannerSize.X,
- bannerStart.Y - 50
- );
- velocity = new Vector2(
- -5f + (float)_particleRandom.NextDouble() * 10f,
- 10f + (float)_particleRandom.NextDouble() * 8f
- );
- break;
- case 2:
- // Spawn from right, move to left
- position = new Vector2(
- bannerStart.X + bannerSize.X + 50,
- bannerStart.Y + (float)_particleRandom.NextDouble() * bannerSize.Y
- );
- velocity = new Vector2(
- -(15f + (float)_particleRandom.NextDouble() * 10f),
- -5f + (float)_particleRandom.NextDouble() * 10f
- );
- break;
- default:
- // Spawn from top-left corner, move diagonally
- position = new Vector2(
- bannerStart.X - 30,
- bannerStart.Y - 30
- );
- velocity = new Vector2(
- 12f + (float)_particleRandom.NextDouble() * 8f,
- 12f + (float)_particleRandom.NextDouble() * 8f
- );
- break;
- }
-
- _largeMoon = new Particle
- {
- Position = position,
- Velocity = velocity,
- MaxLife = 40f,
- Life = 40f,
- Size = 25f + (float)_particleRandom.NextDouble() * 10f,
- Color = UIColors.Get("LightlessBlue") with { W = 0.35f },
- Type = ParticleType.Moon,
- Rotation = 0,
- RotationSpeed = 0,
+ Life = maxLife,
+ MaxLife = maxLife,
+ Size = size,
+ Type = ParticleType.TwinklingStar,
Trail = null,
- IsLargeMoon = true
- };
+ Twinkle = (float)_random.NextDouble() * MathF.PI * 2,
+ Depth = depth,
+ Hue = hue
+ });
}
-
+
+ private void SpawnShootingStar(Vector2 bannerSize)
+ {
+ var maxLife = 80f + (float)_random.NextDouble() * 40f;
+ var startX = bannerSize.X * (0.3f + (float)_random.NextDouble() * 0.6f);
+ var startY = -10f;
+
+ _particles.Add(new Particle
+ {
+ Position = new Vector2(startX, startY),
+ Velocity = new Vector2(
+ -50f - (float)_random.NextDouble() * 40f,
+ 30f + (float)_random.NextDouble() * 40f
+ ),
+ Life = maxLife,
+ MaxLife = maxLife,
+ Size = 2.5f,
+ Type = ParticleType.ShootingStar,
+ Trail = new List(),
+ Twinkle = 0,
+ Depth = 1.0f,
+ Hue = 270f
+ });
+ }
+
private void DrawCloseButton()
{
@@ -653,7 +491,7 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
{
var currentColor = entry.IsCurrent == true
? UIColors.Get("LightlessGreen")
- : new Vector4(0.75f, 0.75f, 0.85f, 1.0f);
+ : new Vector4(0.95f, 0.95f, 1.0f, 1.0f);
bool isOpen;
var flags = entry.IsCurrent == true
@@ -670,7 +508,7 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
}
ImGui.SameLine();
- ImGui.TextColored(new Vector4(0.75f, 0.75f, 0.85f, 1.0f), $" — {entry.Tagline}");
+ ImGui.TextColored(new Vector4(0.85f, 0.85f, 0.95f, 1.0f), $" — {entry.Tagline}");
if (!isOpen)
return;
@@ -711,11 +549,9 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
var backgroundMin = startPos + new Vector2(-8, -4);
var backgroundMax = startPos + new Vector2(availableWidth + 8, 28);
- // Background with subtle gradient
var bgColor = new Vector4(0.12f, 0.12f, 0.15f, 0.7f);
drawList.AddRectFilled(backgroundMin, backgroundMax, ImGui.GetColorU32(bgColor), 6f);
- // Accent line on left
drawList.AddRectFilled(
backgroundMin,
backgroundMin + new Vector2(4, backgroundMax.Y - backgroundMin.Y),
@@ -723,7 +559,6 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
3f
);
- // Subtle glow effect
var glowColor = accentColor with { W = 0.15f };
drawList.AddRect(
backgroundMin,
@@ -745,11 +580,6 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
try
{
var assembly = Assembly.GetExecutingAssembly();
-
- ReadLines(assembly, "LightlessSync.UI.Changelog.supporters.txt", _supporters);
- ReadLines(assembly, "LightlessSync.UI.Changelog.contributors.txt", _contributors);
- ReadLines(assembly, "LightlessSync.UI.Changelog.credits.txt", _credits);
-
using var changelogStream = assembly.GetManifestResourceStream("LightlessSync.UI.Changelog.changelog.yaml");
if (changelogStream != null)
{
@@ -767,22 +597,6 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
// Ignore - window will gracefully render with defaults
}
}
-
- private static void ReadLines(Assembly assembly, string resourceName, List target)
- {
- using var stream = assembly.GetManifestResourceStream(resourceName);
- if (stream == null)
- return;
-
- using var reader = new StreamReader(stream, Encoding.UTF8, true, 128);
- string? line;
- while ((line = reader.ReadLine()) != null)
- {
- if (!string.IsNullOrWhiteSpace(line))
- target.Add(line.Trim());
- }
- }
-
private sealed record ChangelogFile
{
public string Tagline { get; init; } = string.Empty;