109 lines
2.8 KiB
Zig
109 lines
2.8 KiB
Zig
// MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
|
// Phase 3.5d: The Matrix Protocol - Rainmaker Logic
|
|
// Greets the Subject, then recedes into the background.
|
|
|
|
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
|
|
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();
|
|
}
|