use crate::terminal::Terminal;
use crate::config::Config;
use crate::train::ascii::*;
use crate::smoke::{add_smoke, update_smoke, get_smoke_particles, set_generation_gate};
use std::io;
pub fn render_frame(terminal: &Terminal, x: i32, pattern: usize, config: &Config) -> io::Result<()> {
let frame = build_frame(terminal, x, pattern, config);
terminal.render_frame(frame)?;
Ok(())
}
fn build_frame(terminal: &Terminal, x: i32, pattern: usize, config: &Config) -> String {
let mut frame = String::new();
frame.push_str("\x1B[2J\x1B[H");
set_generation_gate(x % 4 == 0);
update_smoke();
build_smoke(&mut frame, terminal);
if config.logo {
build_logo(&mut frame, terminal, x, pattern, config);
} else if config.c51 {
build_c51(&mut frame, terminal, x, pattern, config);
} else {
build_d51(&mut frame, terminal, x, pattern, config);
}
if config.accident {
build_man(&mut frame, terminal, x, config);
}
frame
}
fn build_d51(frame: &mut String, terminal: &Terminal, x: i32, pattern: usize, config: &Config) {
let train_height = 10i32;
let y_center = (terminal.height() as i32 - train_height) / 2;
let y_base = if config.flying {
y_center - (x / 4)
} else {
y_center
};
let pattern = pattern % D51_PATTERNS;
for (i, line) in D51_STR.iter().enumerate() {
let y = y_base + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x, y, line, terminal);
}
}
let funnel_y = y_base - 1;
add_smoke(x + D51_FUNNEL as i32, funnel_y);
for (i, line) in D51_WHL[pattern].iter().enumerate() {
let y = y_base + (D51_STR.len() as i32) + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x, y, line, terminal);
}
}
for (i, line) in D51_COAL.iter().enumerate() {
let y = y_base + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x + 53, y, line, terminal);
}
}
}
fn build_c51(frame: &mut String, terminal: &Terminal, x: i32, pattern: usize, config: &Config) {
let train_height = 10i32;
let y_center = (terminal.height() as i32 - train_height) / 2;
let y_base = if config.flying {
y_center - (x / 4)
} else {
y_center
};
let pattern = pattern % C51_PATTERNS;
for (i, line) in C51_STR.iter().enumerate() {
let y = y_base + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x, y, line, terminal);
}
}
let funnel_y = y_base - 1;
add_smoke(x + C51_FUNNEL as i32, funnel_y);
for (i, line) in C51_WHL[pattern].iter().enumerate() {
let y = y_base + (C51_STR.len() as i32) + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x, y, line, terminal);
}
}
for (i, line) in C51_COAL.iter().enumerate() {
let y = y_base + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x + 55, y, line, terminal);
}
}
}
fn build_logo(frame: &mut String, terminal: &Terminal, x: i32, pattern: usize, config: &Config) {
let train_height = 10i32;
let y_center = (terminal.height() as i32 - train_height) / 2;
let y_base = if config.flying {
y_center - (x / 4)
} else {
y_center
};
let pattern = pattern % 6;
for (i, line) in LOGO_STR.iter().enumerate() {
let y = y_base + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x, y, line, terminal);
}
}
let funnel_y = y_base - 1;
add_smoke(x + LOGO_FUNNEL as i32, funnel_y);
for (i, line) in LOGO_WHL[pattern].iter().enumerate() {
let y = y_base + LOGO_STR.len() as i32 + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x, y, line, terminal);
}
}
for (i, line) in LOGO_COAL.iter().enumerate() {
let y = y_base + LOGO_STR.len() as i32 + 2 + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x, y, line, terminal);
}
}
for (i, line) in LOGO_CAR.iter().enumerate() {
let y = y_base + LOGO_STR.len() as i32 + 2 + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x, y, line, terminal);
}
}
}
fn build_man(frame: &mut String, terminal: &Terminal, x: i32, _config: &Config) {
let y = terminal.height() as i32 - 3;
for (i, line) in MAN.iter().enumerate() {
let y = y + i as i32;
if y >= 0 && y < terminal.height() as i32 {
add_line_to_frame(frame, x + 15, y, line, terminal);
}
}
}
fn build_smoke(frame: &mut String, terminal: &Terminal) {
let particles = get_smoke_particles();
for particle in particles {
if particle.x >= 0 && particle.x < terminal.width() as i32
&& particle.y >= 0 && particle.y < terminal.height() as i32 {
let pattern_idx = particle.pattern.min(15) as usize;
let kind = particle.kind % 2;
if let Some(smoke_str) = SMOKE_PATTERN.get(kind) {
if let Some(ch_str) = smoke_str.get(pattern_idx) {
let mut x_offset = 0;
for ch in ch_str.chars() {
let draw_x = (particle.x as i32 + x_offset) as u16;
if (particle.x as i32 + x_offset) >= 0
&& (particle.x as i32 + x_offset) < terminal.width() as i32
&& ch != ' ' {
add_char_to_frame(frame, draw_x, particle.y as u16, ch);
}
x_offset += 1;
}
}
}
}
}
}
fn add_line_to_frame(frame: &mut String, start_x: i32, y: i32, line: &str, terminal: &Terminal) {
if y < 0 || y >= terminal.height() as i32 {
return;
}
for (i, ch) in line.chars().enumerate() {
let x = start_x + i as i32;
if x >= 0 && x < terminal.width() as i32 {
add_char_to_frame(frame, x as u16, y as u16, ch);
}
}
}
fn add_char_to_frame(frame: &mut String, x: u16, y: u16, ch: char) {
frame.push_str(&format!("\x1B[{};{}H{}", y + 1, x + 1, ch));
}