• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

orion-ecs / keen-eye / 30138674683

25 Jul 2026 01:31AM UTC coverage: 63.89% (-0.9%) from 64.782%
30138674683

push

github

tyevco
feat(samples): Add NOVAFALL Phase C - floor personalities, Flashover, Adrenaline Save, modes, persistence, TestBridge

Phase C of the NOVAFALL flagship sample: modes and meta on top of the
Phase A core and Phase B juice, plus a determinism test project.

- Floor personalities (deterministic per (seed, floor index), phased in
  by depth, ~25% combined cap): Brittle (crack telegraph 0.65s >= the
  0.6s telegraph contract, then crumbles into the smash fragment
  vocabulary), Bumper (elastic upward launch, wobble + boing), Pulse
  (gap breathes on the music-clock beat; the shrinking-edges telegraph
  IS the hitbox via a shared EffectiveGapWidth function)
- Flashover Surge: triggers every 40 cleared floors (floor index, not
  time), 10 music-clock seconds of scroll spike + smash-at-any-tier +
  white-hot palette override + surge lead stem (new 4.75s sample-locked
  WAV), +1000 Surge Sweep for 5+ smashes in one window
- Adrenaline Save: once per run, converts the crush kill into 1.5 REAL
  seconds (raw-dt timer, still simulate-deterministic) at 20% time with
  music duck, desaturated palette, vignette, thicker trail, HUD pip
- Modes as RunConfig configuration, not code paths: FREEFALL,
  DAILY INFERNO (3-minute limit, yyyyMMdd-through-splitmix64 seed,
  3 attempts/day, local depth medals, mid-air heat decay, denser floors,
  halved smash cost), EMBER GARDEN (no crusher, fixed gentle scroll,
  ducked pad-only mix, heat drives visuals only)
- Ready-screen menu keeps the one-axis grammar: Left/Right cycles the
  active row, Tab switches mode <-> cosmetic style, Space/Enter dives
- Persistence via KeenEyes.Persistence: versioned profile world
  (schema header + per-mode bests + daily records) saved to app-data;
  corrupt/first-run loads fresh; cosmetic unlocks derived from bests;
  --simulate never touches the disk (hermetic CI)
- TestBridge: windowed mode serves pipe KeenEyes.NovaFall.TestBridge
  (TestBridgePlugin + IpcBridgeServer, editor integration pattern... (continued)

8874 of 13117 branches covered (67.65%)

Branch coverage included in aggregate %.

305 of 852 new or added lines in 23 files covered. (35.8%)

9 existing lines in 4 files now uncovered.

52569 of 83053 relevant lines covered (63.3%)

1.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

26.92
/samples/KeenEyes.Sample.NovaFall/Systems/ProfileSystem.cs
1
namespace KeenEyes.Sample.NovaFall;
2

3
/// <summary>
4
/// Keeps the persistent player profile in step with play: folds each finished
5
/// run into the per-mode lifetime bests (score, depth, combo) and the Daily
6
/// Inferno medal history, then writes the profile to disk whenever it is dirty.
7
/// </summary>
8
/// <remarks>
9
/// <para>
10
/// Saving is edge-triggered and batched: systems that change the profile (this
11
/// one, and the menu's attempt/cosmetic writes in <see cref="GameFlowSystem"/>)
12
/// only set <see cref="ProfileState.Dirty"/>; the single disk write happens
13
/// here, at most once per frame, through <see cref="ProfilePersistence"/>.
14
/// </para>
15
/// <para>
16
/// With <see cref="ProfileState.SaveEnabled"/> false (headless
17
/// <c>--simulate</c> mode) the bests still update in memory — the simulation is
18
/// identical — but nothing ever touches the disk, keeping CI hermetic.
19
/// </para>
20
/// </remarks>
21
public sealed class ProfileSystem : SystemBase
22
{
23
    private bool runRecorded;
24

25
    /// <inheritdoc />
26
    public override void Update(float deltaTime)
27
    {
28
        ref var profileState = ref World.GetSingleton<ProfileState>();
1✔
29
        if (profileState.Profile is not { } profile)
1✔
30
        {
NEW
31
            return;
×
32
        }
33

34
        var phase = World.GetSingleton<GameState>().Phase;
1✔
35

36
        if (phase == GamePhase.Dead && !runRecorded)
1✔
37
        {
NEW
38
            RecordFinishedRun(profile, ref profileState);
×
NEW
39
            runRecorded = true;
×
40
        }
41
        else if (phase != GamePhase.Dead)
1✔
42
        {
43
            runRecorded = false;
1✔
44
        }
45

46
        if (profileState.Dirty && profileState.SaveEnabled && profileState.SaveDirectory is { } directory)
1✔
47
        {
NEW
48
            ProfilePersistence.Save(profile, directory);
×
NEW
49
            profileState.Dirty = false;
×
50
        }
51
        else if (profileState.Dirty && !profileState.SaveEnabled)
1✔
52
        {
53
            // Headless: acknowledge the change without touching the disk.
NEW
54
            profileState.Dirty = false;
×
55
        }
56
    }
1✔
57

58
    private void RecordFinishedRun(PlayerProfile profile, ref ProfileState profileState)
59
    {
NEW
60
        var mode = World.GetSingleton<RunConfig>().Mode;
×
NEW
61
        var score = World.GetSingleton<ScoreState>().Score;
×
NEW
62
        var depth = World.GetSingleton<ScrollState>().Depth;
×
NEW
63
        var maxCombo = World.GetSingleton<ComboState>().MaxCombo;
×
64

NEW
65
        ref var best = ref profile.ModeBests[(int)mode];
×
NEW
66
        var improved = false;
×
67

NEW
68
        if (score > best.BestScore)
×
69
        {
NEW
70
            best.BestScore = score;
×
NEW
71
            improved = true;
×
72
        }
73

NEW
74
        if (depth > best.BestDepth)
×
75
        {
NEW
76
            best.BestDepth = depth;
×
NEW
77
            improved = true;
×
78
        }
79

NEW
80
        if (maxCombo > best.BestCombo)
×
81
        {
NEW
82
            best.BestCombo = maxCombo;
×
NEW
83
            improved = true;
×
84
        }
85

NEW
86
        if (mode == GameMode.DailyInferno)
×
87
        {
NEW
88
            var index = profile.DailyRecordIndexFor(profileState.TodayKey);
×
NEW
89
            var record = profile.DailyHistory[index];
×
NEW
90
            var medal = DailySchedule.MedalForDepth(depth);
×
NEW
91
            if (medal > record.Medal)
×
92
            {
NEW
93
                record.Medal = medal;
×
NEW
94
                profile.DailyHistory[index] = record;
×
NEW
95
                improved = true;
×
96
            }
97
        }
98

NEW
99
        if (improved)
×
100
        {
NEW
101
            profileState.Dirty = true;
×
102
        }
NEW
103
    }
×
104
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc