rumpk/hal/matrix.zig

121 lines
3.3 KiB
Zig

// SPDX-License-Identifier: LCL-1.0
// Copyright (c) 2026 Markus Maiwald
// Stewardship: Self Sovereign Society Foundation
//
// This file is part of the Nexus Commonwealth.
// See legal/LICENSE_COMMONWEALTH.md for license terms.
//! Rumpk HAL: Matrix Protocol (Rainmaker Logic)
//!
//! Implements a "Matrix" falling rain effect for the boot splash.
//! Recedes into the background after a fixed lifetime.
//!
//! SAFETY: Uses a simple Xorshift PRNG. Animation state is global.
const std = @import("std");
const fb = @import("framebuffer.zig");
// Config
const FONT_W = 8;
const FONT_H = 8;
const COLS = fb.WIDTH / FONT_W;
const LIFETIME_FRAMES = 150; // ~5 seconds at 30FPS (reduced for impact)
// State
// SAFETY(Matrix): Drops array is initialized by `init()` before use.
// Initialized to `undefined` to save a 512B zero-sweep in BSS.
var drops: [COLS]i32 = undefined;
var frame_count: usize = 0;
var is_active: bool = false;
// Basic PRNG (Xorshift)
var rng_state: u32 = 0xDEADBEEF;
fn random() u32 {
var x = rng_state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
rng_state = x;
return x;
}
pub fn init() void {
is_active = true;
frame_count = 0;
// Initialize drops at random heights off-screen
var i: usize = 0;
while (i < COLS) : (i += 1) {
drops[i] = -@as(i32, @intCast(random() % 100));
}
}
// Returns: TRUE if still animating, FALSE if finished (The Void)
pub fn update() bool {
if (!is_active) return false;
// 1. Fade existing trails
fb.fade_screen(15); // Fade speed
// 2. Are we dying?
frame_count += 1;
const dying = (frame_count > LIFETIME_FRAMES);
// 3. Update Drops
var active_drops: usize = 0;
var i: usize = 0;
while (i < COLS) : (i += 1) {
// Draw Head (Bright White/Green)
const x = i * FONT_W;
const y = drops[i];
if (y >= 0 and y < @as(i32, @intCast(fb.HEIGHT))) {
// Draw a simple "pixel block" glyph
// White tip
fb.put_pixel(@intCast(x + 3), @intCast(y + 3), 0xFFFFFFFF);
fb.put_pixel(@intCast(x + 4), @intCast(y + 3), 0xFFFFFFFF);
fb.put_pixel(@intCast(x + 3), @intCast(y + 4), 0xFFFFFFFF);
fb.put_pixel(@intCast(x + 4), @intCast(y + 4), 0xFFFFFFFF);
// Green body follow
if (y > 8) {
fb.fill_rect(@intCast(x + 3), @intCast(y - 4), 2, 4, 0xFF00FF00);
}
}
// Move down
drops[i] += FONT_H;
// Reset if off screen
if (drops[i] > @as(i32, @intCast(fb.HEIGHT))) {
if (!dying) {
// Respawn at top
drops[i] = -@as(i32, @intCast(random() % 50));
active_drops += 1;
} else {
// Do NOT respawn. Let it fall into the void.
}
} else {
active_drops += 1; // It's still on screen
}
}
// 4. Check for Total Extinction
if (dying and active_drops == 0) {
// Ensure pure black at the end
fb.clear(0xFF000000);
is_active = false;
return false; // STOP THE GPU FLUSHING IF IDLE
}
return true; // Keep animating
}
// --- EXPORTS FOR NIM ---
export fn matrix_init() void {
init();
}
export fn matrix_update() bool {
return update();
}