Skip to content

Commit 80387b1

Browse files
Cumulative update changelog + harness raid/weather presets (v4.1.26)
UpdateDialog - Walks UpdateInfo.DeltasToTarget alongside TargetFullRelease so the user sees the changelog for every release between their installed version and the new one. Sections sorted newest-first with --- as separator, mirroring the GitHub CHANGELOG.md layout. Skipped releases no longer hide their notes. DecoratorHarnessUI presets - Added 7 raid presets matching RaidEffectProcessor: Ring Noir (M9, BPMHeartbeatEffect), The X-Ring (M10, BPMCircularPulseEffect), The Crown (M11, BPMSpinnerEffect), Hell on Rails (BPMMatrixEffect), The Ageless Necropolis / Lightless Abyss (BPMLaserEffect), Recollection (radial diagonal-gradient), The Unmaking (BPMThunderstrikeEffect). - Added 8 weather presets matching ReactiveWeather: Atmospheric Phantasms / Illusory Disturbances (CircularPulseEffect), Gravitational Flux (BPMHeartbeatEffect), Meteor Showers / Sporing Mist / Glass Storms (StarfieldDecorator), Annealing Winds (linear gradient), Bubble Bloom (BPMSpinnerEffect). - Each preset has matching defaults, BuildEffect instantiation, and BuildCodeSnippet output so live preview + paste-ready C# both work. Build clean, 130/130 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1e10ee2 commit 80387b1

6 files changed

Lines changed: 302 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
All notable changes to Chromatics are documented here.
44

5-
## 4.1.25
5+
## 4.1.26
66

77
- **New:** PlayStation DS4 and DS5 controller lighting support (Beta). The DualShock 4 lightbar, the DualSense lightbar and the five player-indicator LEDs can all be mapped from the Mappings tab. Both USB and Bluetooth controllers are supported, and your controller's input still works normally in games while Chromatics drives the lights.
88
Tip: if your controller is connected but Chromatics says it can't open it, another tool (like Steam Input, DS4Windows, or reWASD) is probably holding it in exclusive mode. Close it or disable its exclusive mode before launching Chromatics.

Chromatics.DecoratorHarnessUI/MainViewModel.cs

Lines changed: 253 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,21 @@ public record EffectEntry(string Name, string Category, string? BasedOn = null)
252252
// whichever they're building an effect for.
253253
new("Hunter's Ring — Howling Blade (M8)", "[RAID] Presets"),
254254
new("Hunter's Ring — Howling Blade (M8S)", "[RAID] Presets"),
255+
// Cruiserweight tier (M9-M11)
256+
new("Ring Noir (M9)", "[RAID] Presets"),
257+
new("The X-Ring (M10)", "[RAID] Presets"),
258+
new("The Crown (M11)", "[RAID] Presets"),
255259
// Arcadia (M12/M12S) runs a scripted three-beat choreography in-game.
256260
// Each beat is its own preset; the harness cannot replay the per-layer
257261
// state machine, so each one is previewable in isolation.
258262
new("Arcadia P1 Effect 1 — Intro", "[RAID] Presets"),
259263
new("Arcadia P1 Effect 2 — Post-Flash", "[RAID] Presets"),
260264
new("Arcadia P2 (M12)", "[RAID] Presets"),
265+
// Other zones with raid effects
266+
new("Hell on Rails", "[RAID] Presets"),
267+
new("The Ageless Necropolis / Lightless Abyss", "[RAID] Presets"),
268+
new("Recollection", "[RAID] Presets"),
269+
new("The Unmaking", "[RAID] Presets"),
261270
];
262271

263272
public static EffectEntry[] WeatherPresets { get; } =
@@ -275,6 +284,15 @@ public record EffectEntry(string Name, string Category, string? BasedOn = null)
275284
new("Astromagnetic Storm", "[WEATHER] Presets"),
276285
new("Umbral Static", "[WEATHER] Presets"),
277286
new("Everlasting Light", "[WEATHER] Presets"),
287+
// 7.x weather animations
288+
new("Atmospheric Phantasms", "[WEATHER] Presets"),
289+
new("Illusory Disturbances", "[WEATHER] Presets"),
290+
new("Gravitational Flux", "[WEATHER] Presets"),
291+
new("Meteor Showers", "[WEATHER] Presets"),
292+
new("Sporing Mist", "[WEATHER] Presets"),
293+
new("Annealing Winds", "[WEATHER] Presets"),
294+
new("Glass Storms", "[WEATHER] Presets"),
295+
new("Bubble Bloom", "[WEATHER] Presets"),
278296
];
279297

280298
public ObservableCollection<EffectEntry> AllEffects { get; } = new(
@@ -743,6 +761,45 @@ private void ApplyDefaults(EffectEntry effect)
743761
ColorBase = AvColors.Black;
744762
SetColors(ToAv(255, 0, 4), ToAv(93, 0, 255), ToAv(255, 117, 0), ToAv(249, 255, 0));
745763
break;
764+
// Cruiserweight tier (M9-M11) — defaults mirror RaidEffectProcessor.ApplyRaidEffect
765+
case "Ring Noir (M9)":
766+
ParamBpm = 47; ParamBeamWidth = 2.0;
767+
ColorBase = AvColors.Black;
768+
SetColors(ToAv(220, 20, 60), ToAv(120, 0, 30));
769+
break;
770+
case "The X-Ring (M10)":
771+
ParamBpm = 180; ParamBpmSpeed = "/4"; ParamPulseRadius = 6.0; ParamFadeWidth = 2.0;
772+
ColorBase = AvColors.Black;
773+
SetColors(ToAv(255, 200, 0), ToAv(255, 255, 255));
774+
break;
775+
case "The Crown (M11)":
776+
ParamBpm = 135; ParamBpmSpeed = "/4"; ParamWedgeDegrees = 180;
777+
ColorBase = AvColors.Black;
778+
SetColors(ToAv(255, 215, 0), ToAv(255, 80, 0), ToAv(255, 0, 120), ToAv(180, 0, 255));
779+
break;
780+
// Other raid zones
781+
case "Hell on Rails":
782+
ParamBpm = 110; ParamBpmSpeed = "/2"; ParamFlickerOpacity = 0.5; ParamFlickerSpeed = 8;
783+
ParamMatrixDir = "Left";
784+
ColorBase = AvColors.Black;
785+
SetColors(ToAv(0, 255, 120), ToAv(0, 200, 80));
786+
break;
787+
case "The Ageless Necropolis / Lightless Abyss":
788+
ParamBpm = 160; ParamBpmSpeed = "/8"; ParamBeamWidth = 3.5;
789+
ParamLaserDir = "RandomDiagonal";
790+
ColorBase = AvColors.Black;
791+
SetColors(ToAv(180, 180, 220), ToAv(120, 80, 200), ToAv(80, 200, 200), ToAv(220, 220, 255));
792+
break;
793+
case "Recollection":
794+
ParamBpm = 27; ParamDiagonalDir = "Random"; ParamTextureType = "Conical";
795+
ColorBase = AvColors.Black;
796+
SetColors(ToAv(120, 200, 255), ToAv(255, 220, 180), ToAv(180, 120, 255));
797+
break;
798+
case "The Unmaking":
799+
ParamBpm = 175; ParamAccentEvery = 4; ParamDecay = 0.5;
800+
ColorBase = AvColors.Black;
801+
SetColors(ToAv(255, 60, 60), ToAv(255, 200, 100), ToAv(255, 255, 255));
802+
break;
746803
case "Moon Dust (Mare Lamentorum)":
747804
ParamInterval = 20; ParamFadeSpeed = 900;
748805
ColorBase = ToAv(0, 0, 0);
@@ -808,6 +865,47 @@ private void ApplyDefaults(EffectEntry effect)
808865
ColorBase = ToAv(255, 248, 220);
809866
SetColors(ToAv(255, 255, 0));
810867
break;
868+
// 7.x weather animations — defaults mirror ReactiveWeather.cs effect cases
869+
case "Atmospheric Phantasms":
870+
ParamRippleSpeed = 6.0; ParamPulseRadius = 12.0; ParamSpawnInterval = 0.5; ParamFadeWidth = 2.0;
871+
ColorBase = AvColors.Black;
872+
SetColors(ToAv(80, 200, 255), ToAv(180, 120, 255));
873+
break;
874+
case "Illusory Disturbances":
875+
ParamRippleSpeed = 6.0; ParamPulseRadius = 12.0; ParamSpawnInterval = 0.5; ParamFadeWidth = 2.0;
876+
ColorBase = AvColors.Black;
877+
SetColors(ToAv(255, 200, 80), ToAv(255, 80, 200), ToAv(120, 200, 255));
878+
break;
879+
case "Gravitational Flux":
880+
ParamBpm = 36; ParamBeamWidth = 2.0;
881+
ColorBase = AvColors.Black;
882+
SetColors(ToAv(180, 80, 255), ToAv(80, 40, 180));
883+
break;
884+
case "Meteor Showers":
885+
ParamInterval = 20; ParamFadeSpeed = 900; ParamNumberOfLeds = 10;
886+
ColorBase = ToAv(20, 10, 40);
887+
SetColors(ToAv(255, 200, 80), ToAv(255, 120, 40));
888+
break;
889+
case "Sporing Mist":
890+
ParamInterval = 20; ParamFadeSpeed = 900; ParamNumberOfLeds = 10;
891+
ColorBase = ToAv(20, 60, 30);
892+
SetColors(ToAv(180, 255, 120), ToAv(120, 200, 80));
893+
break;
894+
case "Annealing Winds":
895+
ParamSpeed = 200; ParamDirection = true;
896+
ColorBase = ToAv(180, 80, 40);
897+
SetColors(ToAv(255, 180, 80));
898+
break;
899+
case "Glass Storms":
900+
ParamInterval = 20; ParamFadeSpeed = 900; ParamNumberOfLeds = 10;
901+
ColorBase = ToAv(120, 140, 160);
902+
SetColors(ToAv(220, 230, 240));
903+
break;
904+
case "Bubble Bloom":
905+
ParamBpm = 32; ParamBpmSpeed = "Sync"; ParamWedgeDegrees = 180;
906+
ColorBase = ToAv(20, 60, 80);
907+
SetColors(ToAv(180, 220, 255));
908+
break;
811909

812910
// Raw decorators — sensible defaults
813911
case "StarfieldDecorator":
@@ -1271,12 +1369,79 @@ private string BuildCodeSnippet(EffectEntry effect)
12711369
$"var pulse = new BPMCircularPulseEffect(layer, {ParamBpm}, {ResolveBeatsPerCycle()}, {ParamPulseRadius}, {ParamFadeWidth}, colors, surface, baseCol);\n\n" +
12721370
"layer.Brush = new SolidColorBrush(baseCol);\nSetEffect(pulse, layer, runningEffects);",
12731371

1274-
"Arcadia P1 Effect 2 — Post-Flash" =>
1372+
"Arcadia P1 Effect 2 — Post-Flash" or "The Unmaking" =>
12751373
$"var baseCol = {bS};\n" +
12761374
$"var colors = {arr};\n" +
12771375
$"var strike = new BPMThunderstrikeEffect(layer, {ParamBpm}, {ParamAccentEvery}, {ParamDecay}, colors, surface, baseCol);\n\n" +
12781376
"layer.Brush = new SolidColorBrush(baseCol);\nSetEffect(strike, layer, runningEffects);",
12791377

1378+
"Ring Noir (M9)" =>
1379+
$"var baseCol = {bS};\n" +
1380+
$"var colors = {arr};\n" +
1381+
$"var heartbeat = new BPMHeartbeatEffect(layer, {ParamBpm}, {ParamBeamWidth}, colors, surface, baseCol);\n\n" +
1382+
"SetEffect(heartbeat, layer, runningEffects);",
1383+
1384+
"The X-Ring (M10)" =>
1385+
$"var baseCol = {bS};\n" +
1386+
$"var colors = {arr};\n" +
1387+
$"var pulse = new BPMCircularPulseEffect(layer, {ParamBpm}, {ResolveBeatsPerCycle()}, {ParamPulseRadius}, {ParamFadeWidth}, colors, surface, baseCol);\n\n" +
1388+
"SetEffect(pulse, layer, runningEffects);",
1389+
1390+
"The Crown (M11)" =>
1391+
$"var baseCol = {bS};\n" +
1392+
$"var colors = {arr};\n" +
1393+
$"var spinner = new BPMSpinnerEffect(layer, {ParamBpm}, {ResolveBeatsPerCycle()}, {ParamWedgeDegrees}, colors, surface, baseCol);\n\n" +
1394+
"SetEffect(spinner, layer, runningEffects);",
1395+
1396+
"Hell on Rails" =>
1397+
$"var baseCol = {bS};\n" +
1398+
$"var colors = {arr};\n" +
1399+
$"var matrix = new BPMMatrixEffect(layer, {ParamBpm}, {ResolveBeatsPerCycle()}, {ParamFlickerOpacity}, {ParamFlickerSpeed}, colors, surface, MatrixEffect.MatrixDirection.{ParamMatrixDir}, baseCol);\n\n" +
1400+
"SetEffect(matrix, layer, runningEffects);",
1401+
1402+
"The Ageless Necropolis / Lightless Abyss" =>
1403+
$"var baseCol = {bS};\n" +
1404+
$"var colors = {arr};\n" +
1405+
$"var laser = new BPMLaserEffect(layer, {ParamBpm}, {ResolveBeatsPerCycle()}, {ParamBeamWidth}, colors, surface, LaserEffect.LaserDirection.{ParamLaserDir}, baseCol);\n\n" +
1406+
"SetEffect(laser, layer, runningEffects);",
1407+
1408+
"Recollection" =>
1409+
$"var animationCol1 = {s0};\nvar animationCol2 = {s1};\nvar animationCol3 = {s2};\n\n" +
1410+
"var animationGradient = new LinearGradient(\n" +
1411+
" new GradientStop(0f, animationCol1), new GradientStop(0.33f, animationCol2), new GradientStop(0.66f, animationCol3));\n\n" +
1412+
$"var gradientMove = new MoveBPMDiagonalGradientDecorator(surface, {ParamBpm}, DiagonalDirection.{ParamDiagonalDir});\n\n" +
1413+
"SetRadialGradientEffect(animationGradient, gradientMove, layer, new Size(100, 100), runningEffects, masterlayer.layerID);",
1414+
1415+
"Atmospheric Phantasms" or "Illusory Disturbances" =>
1416+
$"var baseCol = {bS};\n" +
1417+
$"var colors = {arr};\n" +
1418+
$"var pulse = new CircularPulseEffect(layer, {ParamRippleSpeed}, {ParamPulseRadius}, {ParamSpawnInterval}, {ParamFadeWidth}, colors, surface, baseCol);\n\n" +
1419+
"SetEffect(pulse, layer, runningEffects);",
1420+
1421+
"Gravitational Flux" =>
1422+
$"var baseCol = {bS};\n" +
1423+
$"var colors = {arr};\n" +
1424+
$"var heartbeat = new BPMHeartbeatEffect(layer, {ParamBpm}, {ParamBeamWidth}, colors, surface, baseCol);\n\n" +
1425+
"SetEffect(heartbeat, layer, runningEffects);",
1426+
1427+
"Meteor Showers" or "Sporing Mist" or "Glass Storms" =>
1428+
$"var baseCol = {bS};\n" +
1429+
$"var animationCol = {arr};\n" +
1430+
$"var starfield = new StarfieldDecorator(layer, {ParamNumberOfLeds}, {ParamInterval}, {ParamFadeSpeed}, animationCol, surface, false, baseCol);\n\n" +
1431+
"layer.Brush = new SolidColorBrush(baseCol);\nSetEffect(starfield, layer, runningEffects);",
1432+
1433+
"Annealing Winds" =>
1434+
$"var baseCol = {bS};\nvar animationCol = {s0};\n\n" +
1435+
"var animationGradient = new LinearGradient(new GradientStop(0f, baseCol), new GradientStop(0.25f, animationCol), new GradientStop(0.75f, baseCol), new GradientStop(1f, animationCol));\n" +
1436+
$"var gradientMove = new MoveGradientDecorator(surface, {ParamSpeed}, true);\n\n" +
1437+
"SetLinearGradientEffect(animationGradient, gradientMove, layer, new Size(100, 100), runningEffects, _gradientEffects);",
1438+
1439+
"Bubble Bloom" =>
1440+
$"var baseCol = {bS};\n" +
1441+
$"var colors = {arr};\n" +
1442+
$"var spinner = new BPMSpinnerEffect(layer, {ParamBpm}, {ResolveBeatsPerCycle()}, {ParamWedgeDegrees}, colors, surface, baseCol);\n\n" +
1443+
"SetEffect(spinner, layer, runningEffects);",
1444+
12801445
"Blasting Ring (Brute Bomber)" =>
12811446
$"var baseCol = {bS};\n" +
12821447
$"var animationCol1 = {s0};\nvar animationCol2 = {s1};\nvar animationCol3 = {s2};\n\n" +
@@ -1668,25 +1833,112 @@ private Action BuildEffect(EffectEntry effect, ListLedGroup group)
16681833
return () => group.RemoveDecorator(dec);
16691834
}
16701835
case "Arcadia P1 Effect 2 — Post-Flash":
1836+
case "The Unmaking":
16711837
{
16721838
group.Brush = new SolidColorBrush(baseCol);
16731839
var dec = new BPMThunderstrikeEffect(group, ParamBpm, ParamAccentEvery, ParamDecay, colors, _surface, baseCol);
16741840
group.AddDecorator(dec);
16751841
return () => group.RemoveDecorator(dec);
16761842
}
1843+
case "Ring Noir (M9)":
1844+
{
1845+
group.Brush = new SolidColorBrush(baseCol);
1846+
var dec = new BPMHeartbeatEffect(group, ParamBpm, ParamBeamWidth, colors, _surface, baseCol);
1847+
group.AddDecorator(dec);
1848+
return () => group.RemoveDecorator(dec);
1849+
}
1850+
case "The X-Ring (M10)":
1851+
{
1852+
group.Brush = new SolidColorBrush(baseCol);
1853+
var dec = new BPMCircularPulseEffect(group, ParamBpm, ResolveBeatsPerCycle(), ParamPulseRadius, ParamFadeWidth, colors, _surface, baseCol);
1854+
group.AddDecorator(dec);
1855+
return () => group.RemoveDecorator(dec);
1856+
}
1857+
case "The Crown (M11)":
1858+
{
1859+
group.Brush = new SolidColorBrush(baseCol);
1860+
var dec = new BPMSpinnerEffect(group, ParamBpm, ResolveBeatsPerCycle(), ParamWedgeDegrees, colors, _surface, baseCol);
1861+
group.AddDecorator(dec);
1862+
return () => group.RemoveDecorator(dec);
1863+
}
1864+
case "Hell on Rails":
1865+
{
1866+
group.Brush = new SolidColorBrush(baseCol);
1867+
var dir = Enum.Parse<MatrixEffect.MatrixDirection>(ParamMatrixDir);
1868+
var dec = new BPMMatrixEffect(group, ParamBpm, ResolveBeatsPerCycle(), ParamFlickerOpacity, ParamFlickerSpeed, colors, _surface, dir, baseCol);
1869+
group.AddDecorator(dec);
1870+
return () => group.RemoveDecorator(dec);
1871+
}
1872+
case "The Ageless Necropolis / Lightless Abyss":
1873+
{
1874+
group.Brush = new SolidColorBrush(baseCol);
1875+
var dir = Enum.Parse<LaserEffect.LaserDirection>(ParamLaserDir);
1876+
var dec = new BPMLaserEffect(group, ParamBpm, ResolveBeatsPerCycle(), ParamBeamWidth, colors, _surface, dir, baseCol);
1877+
group.AddDecorator(dec);
1878+
return () => group.RemoveDecorator(dec);
1879+
}
1880+
case "Recollection":
1881+
{
1882+
// Diagonal-gradient effect — mirrors RaidEffectProcessor's
1883+
// SetRadialGradientEffect path with three colour stops, no
1884+
// base-colour stop (hence Build3StopGradient over only the
1885+
// animation colours, the same shape Recollection uses
1886+
// in-game).
1887+
var g = new LinearGradient(
1888+
new GradientStop(0f, s0),
1889+
new GradientStop(0.33f, s1),
1890+
new GradientStop(0.66f, s2));
1891+
var dec = new MoveBPMDiagonalGradientDecorator(_surface, ParamBpm, Enum.Parse<DiagonalDirection>(ParamDiagonalDir));
1892+
g.AddDecorator(dec);
1893+
group.Brush = new TextureBrush(new ConicalGradientTexture(new Size(100, 100), g));
1894+
return () => g.RemoveDecorator(dec);
1895+
}
16771896

16781897
// ── Weather presets (starfield-based) ──
16791898
case "Moon Dust (Mare Lamentorum)":
16801899
case "Rain":
16811900
case "Showers":
16821901
case "Snow":
16831902
case "Blizzards":
1903+
case "Meteor Showers":
1904+
case "Sporing Mist":
1905+
case "Glass Storms":
16841906
{
16851907
group.Brush = new SolidColorBrush(baseCol);
16861908
var dec = new StarfieldDecorator(group, Math.Max(4, group.Count() / 4), ParamInterval, ParamFadeSpeed, colors, _surface, false, baseCol);
16871909
group.AddDecorator(dec);
16881910
return () => group.RemoveDecorator(dec);
16891911
}
1912+
case "Atmospheric Phantasms":
1913+
case "Illusory Disturbances":
1914+
{
1915+
group.Brush = new SolidColorBrush(baseCol);
1916+
var dec = new CircularPulseEffect(group, ParamRippleSpeed, ParamPulseRadius, ParamSpawnInterval, ParamFadeWidth, colors, _surface, baseCol);
1917+
group.AddDecorator(dec);
1918+
return () => group.RemoveDecorator(dec);
1919+
}
1920+
case "Gravitational Flux":
1921+
{
1922+
group.Brush = new SolidColorBrush(baseCol);
1923+
var dec = new BPMHeartbeatEffect(group, ParamBpm, ParamBeamWidth, colors, _surface, baseCol);
1924+
group.AddDecorator(dec);
1925+
return () => group.RemoveDecorator(dec);
1926+
}
1927+
case "Annealing Winds":
1928+
{
1929+
var g = Build2ColorOscGradient(baseCol, s0); g.WrapGradient = true;
1930+
var dec = new MoveGradientDecorator(_surface, ParamSpeed, ParamDirection);
1931+
g.AddDecorator(dec);
1932+
group.Brush = new TextureBrush(new LinearGradientTexture(new Size(100, 100), g));
1933+
return () => g.RemoveDecorator(dec);
1934+
}
1935+
case "Bubble Bloom":
1936+
{
1937+
group.Brush = new SolidColorBrush(baseCol);
1938+
var dec = new BPMSpinnerEffect(group, ParamBpm, ResolveBeatsPerCycle(), ParamWedgeDegrees, colors, _surface, baseCol);
1939+
group.AddDecorator(dec);
1940+
return () => group.RemoveDecorator(dec);
1941+
}
16901942
// ── Weather presets (strobe) ──
16911943
case "Thunder / Thunderstorms":
16921944
{

Chromatics/Chromatics.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<OutputType>WinExe</OutputType>
55
<TargetFramework>net10.0-windows7.0</TargetFramework>
66
<StartupObject>Chromatics.Program</StartupObject>
7-
<Version>4.1.25.0</Version>
7+
<Version>4.1.26.0</Version>
88
<Authors>Danielle Thompson</Authors>
99
<ApplicationManifest>app.manifest</ApplicationManifest>
1010
<Copyright>logicallysynced 2026</Copyright>

0 commit comments

Comments
 (0)