mirror of
https://github.com/fairyglade/ly.git
synced 2026-03-23 08:46:04 +00:00
And make widget() functions return pointers to widgets instead of just widgets Signed-off-by: AnErrupTion <anerruption@disroot.org>
164 lines
5.0 KiB
Zig
164 lines
5.0 KiB
Zig
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const ly_ui = @import("ly-ui");
|
|
const Cell = ly_ui.Cell;
|
|
const TerminalBuffer = ly_ui.TerminalBuffer;
|
|
const Widget = ly_ui.Widget;
|
|
|
|
const ly_core = ly_ui.ly_core;
|
|
const interop = ly_core.interop;
|
|
const TimeOfDay = interop.TimeOfDay;
|
|
|
|
const Doom = @This();
|
|
|
|
pub const STEPS = 12;
|
|
pub const HEIGHT_MAX = 9;
|
|
pub const SPREAD_MAX = 4;
|
|
|
|
instance: ?Widget = null,
|
|
start_time: TimeOfDay,
|
|
allocator: Allocator,
|
|
terminal_buffer: *TerminalBuffer,
|
|
animate: *bool,
|
|
timeout_sec: u12,
|
|
frame_delay: u16,
|
|
buffer: []u8,
|
|
height: u8,
|
|
spread: u8,
|
|
fire: [STEPS + 1]Cell,
|
|
|
|
pub fn init(
|
|
allocator: Allocator,
|
|
terminal_buffer: *TerminalBuffer,
|
|
top_color: u32,
|
|
middle_color: u32,
|
|
bottom_color: u32,
|
|
fire_height: u8,
|
|
fire_spread: u8,
|
|
animate: *bool,
|
|
timeout_sec: u12,
|
|
frame_delay: u16,
|
|
) !Doom {
|
|
const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height);
|
|
initBuffer(buffer, terminal_buffer.width);
|
|
|
|
const levels =
|
|
[_]Cell{
|
|
Cell.init(' ', terminal_buffer.bg, terminal_buffer.bg),
|
|
Cell.init(0x2591, top_color, terminal_buffer.bg),
|
|
Cell.init(0x2592, top_color, terminal_buffer.bg),
|
|
Cell.init(0x2593, top_color, terminal_buffer.bg),
|
|
Cell.init(0x2588, top_color, terminal_buffer.bg),
|
|
Cell.init(0x2591, middle_color, top_color),
|
|
Cell.init(0x2592, middle_color, top_color),
|
|
Cell.init(0x2593, middle_color, top_color),
|
|
Cell.init(0x2588, middle_color, top_color),
|
|
Cell.init(0x2591, bottom_color, middle_color),
|
|
Cell.init(0x2592, bottom_color, middle_color),
|
|
Cell.init(0x2593, bottom_color, middle_color),
|
|
Cell.init(0x2588, bottom_color, middle_color),
|
|
};
|
|
|
|
return .{
|
|
.instance = null,
|
|
.start_time = try interop.getTimeOfDay(),
|
|
.allocator = allocator,
|
|
.terminal_buffer = terminal_buffer,
|
|
.animate = animate,
|
|
.timeout_sec = timeout_sec,
|
|
.frame_delay = frame_delay,
|
|
.buffer = buffer,
|
|
.height = @min(HEIGHT_MAX, fire_height),
|
|
.spread = @min(SPREAD_MAX, fire_spread),
|
|
.fire = levels,
|
|
};
|
|
}
|
|
|
|
pub fn widget(self: *Doom) *Widget {
|
|
if (self.instance) |*instance| return instance;
|
|
self.instance = Widget.init(
|
|
"Doom",
|
|
null,
|
|
self,
|
|
deinit,
|
|
realloc,
|
|
draw,
|
|
update,
|
|
null,
|
|
calculateTimeout,
|
|
);
|
|
return &self.instance.?;
|
|
}
|
|
|
|
fn deinit(self: *Doom) void {
|
|
self.allocator.free(self.buffer);
|
|
}
|
|
|
|
fn realloc(self: *Doom) !void {
|
|
const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height);
|
|
initBuffer(buffer, self.terminal_buffer.width);
|
|
self.buffer = buffer;
|
|
}
|
|
|
|
fn draw(self: *Doom) void {
|
|
if (!self.animate.*) return;
|
|
|
|
for (0..self.terminal_buffer.width) |x| {
|
|
// We start from 1 so that we always have the topmost line when spreading fire
|
|
for (1..self.terminal_buffer.height) |y| {
|
|
// Get index of current cell in fire level buffer
|
|
const from = y * self.terminal_buffer.width + x;
|
|
|
|
// Generate random data for fire propagation
|
|
const rand_loss = self.terminal_buffer.random.intRangeAtMost(u8, 0, HEIGHT_MAX);
|
|
const rand_spread = self.terminal_buffer.random.intRangeAtMost(u8, 0, self.spread * 2);
|
|
|
|
// Select semi-random target cell
|
|
const to = from -| self.terminal_buffer.width + self.spread -| rand_spread;
|
|
const to_x = to % self.terminal_buffer.width;
|
|
const to_y = to / self.terminal_buffer.width;
|
|
|
|
// Get fire level of current cell
|
|
const level_buf_from = self.buffer[from];
|
|
|
|
// Choose new fire level and store in level buffer
|
|
const level_buf_to = level_buf_from -| @intFromBool(rand_loss >= self.height);
|
|
self.buffer[to] = level_buf_to;
|
|
|
|
// Send known fire levels to terminal buffer
|
|
const from_cell = self.fire[level_buf_from];
|
|
const to_cell = self.fire[level_buf_to];
|
|
from_cell.put(x, y);
|
|
to_cell.put(to_x, to_y);
|
|
}
|
|
|
|
// Draw bottom line (fire source)
|
|
const src_cell = self.fire[STEPS];
|
|
src_cell.put(x, self.terminal_buffer.height - 1);
|
|
}
|
|
}
|
|
|
|
fn initBuffer(buffer: []u8, width: usize) void {
|
|
const length = buffer.len - width;
|
|
const slice_start = buffer[0..length];
|
|
const slice_end = buffer[length..];
|
|
|
|
// Initialize the framebuffer in black, except for the "fire source" as the
|
|
// last color
|
|
@memset(slice_start, 0);
|
|
@memset(slice_end, STEPS);
|
|
}
|
|
|
|
fn update(self: *Doom, _: *anyopaque) !void {
|
|
const time = try interop.getTimeOfDay();
|
|
|
|
if (self.timeout_sec > 0 and time.seconds - self.start_time.seconds > self.timeout_sec) {
|
|
self.animate.* = false;
|
|
}
|
|
}
|
|
|
|
fn calculateTimeout(self: *Doom, _: *anyopaque) !?usize {
|
|
return self.frame_delay;
|
|
}
|