Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 37 additions & 48 deletions apps/sl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,24 @@ use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyModifiers};
use crossterm::terminal::{Clear, ClearType};
use crossterm::{cursor, terminal, ExecutableCommand, QueueableCommand};
use filedescriptor::{Error, FileDescriptor};
use libsl::{add_c51, add_d51, add_logo};
use libsl::{add_c51, add_d51, add_logo, RenderError, RenderTarget, ScreenSize, TrainOptions};
use std::fs;
use std::io::{stdin, stdout, BufRead, BufReader, IsTerminal, Stdin, Write};
use std::io::{stdin, stdout, BufRead, BufReader, IsTerminal, Stdin, Stdout, Write};
use std::sync::mpsc::Receiver;

mod cli;

struct TerminalDisplay {
cols: i32,
lines: i32,
struct TerminalRenderer<'a> {
stdout: &'a mut Stdout,
}

impl libsl::Display for TerminalDisplay {
fn add_str(&self, y: i32, x: i32, s: &str) {
let mut stdout = std::io::stdout();
stdout.queue(cursor::MoveTo(x as u16, y as u16)).unwrap();
stdout.write_all(s.as_bytes()).unwrap();
}

fn cols(&self) -> i32 {
self.cols
}
impl RenderTarget for TerminalRenderer<'_> {
type Error = Error;

fn lines(&self) -> i32 {
self.lines
}
}

impl libsl::Options for CliOptions {
fn accident(&self) -> bool {
self.accident
}

fn fly(&self) -> bool {
self.fly
}

fn smoke(&self) -> bool {
true
fn draw_str(&mut self, line: i32, column: i32, value: &str) -> Result<(), Self::Error> {
self.stdout
.queue(cursor::MoveTo(column as u16, line as u16))?;
Ok(self.stdout.write_all(value.as_bytes())?)
}
}

Expand All @@ -57,24 +36,15 @@ fn main() -> Result<(), Error> {
let mut stdout = stdout();
stdout.execute(cursor::Hide)?;

let add_train = if args.logo {
add_logo
} else if args.c51 {
add_c51
} else {
add_d51
};

stdout.queue(Clear(ClearType::All))?;
let mut names: Vec<String> = vec![];

let size = terminal::size()?;
let mut display = TerminalDisplay {
cols: size.0 as i32,
lines: size.1 as i32,
};
let mut screen = ScreenSize::new(size.0 as i32, size.1 as i32);
let options = TrainOptions::new(args.accident, args.fly, true);
let mut render_error = None;

let mut x = display.cols - 1;
let mut x = screen.columns - 1;

loop {
match names_receiver.try_recv() {
Expand All @@ -84,8 +54,24 @@ fn main() -> Result<(), Error> {
Err(_) => {}
}

if add_train(x, &names, &display, &args).is_err() {
break;
let mut renderer = TerminalRenderer {
stdout: &mut stdout,
};
let result = if args.logo {
add_logo(x, &names, screen, &mut renderer, &options)
} else if args.c51 {
add_c51(x, &names, screen, &mut renderer, &options)
} else {
add_d51(x, &names, screen, &mut renderer, &options)
};

match result {
Ok(()) => {}
Err(RenderError::Offscreen) => break,
Err(RenderError::Target(error)) => {
render_error = Some(error);
break;
}
}

stdout.flush()?;
Expand All @@ -100,8 +86,7 @@ fn main() -> Result<(), Error> {
}) => break,
Event::Resize(cols, lines) => {
stdout.queue(Clear(ClearType::All))?;
display.cols = cols as i32;
display.lines = lines as i32;
screen = ScreenSize::new(cols as i32, lines as i32);
}
_ => {}
}
Expand All @@ -112,6 +97,10 @@ fn main() -> Result<(), Error> {
stdout.flush()?;
terminal::disable_raw_mode()?;

if let Some(error) = render_error {
return Err(error);
}

Ok(())
}

Expand Down
17 changes: 12 additions & 5 deletions libraries/libsl/src/add_man.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
use crate::Display;
use crate::{RenderTarget, ScreenSize};

use super::mvaddstr::mvaddstr;

pub fn add_man<T: Display>(y: i32, x: i32, display: &T) {
pub fn add_man<T: RenderTarget>(
y: i32,
x: i32,
screen: ScreenSize,
target: &mut T,
) -> Result<(), T::Error> {
const MAN: [[&str; 2]; 2] = [["", "Help!"], ["(O)", "\\O/"]];
MAN.iter().enumerate().for_each(|(i, row)| {
for (i, row) in MAN.iter().enumerate() {
let man = row[(x.abs() / 12 % 2) as usize];
_ = mvaddstr(y + i as i32, x, man, display);
});
mvaddstr(y + i as i32, x, man, screen, target)?;
}

Ok(())
}
30 changes: 22 additions & 8 deletions libraries/libsl/src/add_smoke.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::cmp::min;

use crate::Display;
use crate::{RenderTarget, ScreenSize};

use super::mvaddstr::mvaddstr;

Expand All @@ -19,7 +19,12 @@ static mut SMOKES: [Smokes; 1000] = [Smokes {
kind: 0,
}; 1000];

pub fn add_smoke<T: Display>(y: i32, x: i32, display: &T) {
pub fn add_smoke<T: RenderTarget>(
y: i32,
x: i32,
screen: ScreenSize,
target: &mut T,
) -> Result<(), T::Error> {
const SMOKE: [[&str; 16]; 2] = [
[
"( )", "( )", "( )", "( )", "( )", "( )", "( )", "( )", "()", "()", "O",
Expand All @@ -40,25 +45,34 @@ pub fn add_smoke<T: Display>(y: i32, x: i32, display: &T) {

if x % 4 == 0 {
unsafe {
let sum = (((display.cols() - (min(x, display.cols()))) / 4) % display.cols()) as usize;
let sum = (((screen.columns - (min(x, screen.columns))) / 4) % screen.columns) as usize;
for i in 0..sum {
_ = mvaddstr(SMOKES[i].y, SMOKES[i].x, ERASER[SMOKES[i].ptrn], display);
mvaddstr(
SMOKES[i].y,
SMOKES[i].x,
ERASER[SMOKES[i].ptrn],
screen,
target,
)?;
SMOKES[i].y -= DY[SMOKES[i].ptrn];
SMOKES[i].x += DX[SMOKES[i].ptrn];
SMOKES[i].ptrn += if SMOKES[i].ptrn < 15 { 1 } else { 0 };
_ = mvaddstr(
mvaddstr(
SMOKES[i].y,
SMOKES[i].x,
SMOKE[SMOKES[i].kind][SMOKES[i].ptrn],
display,
);
screen,
target,
)?;
}

_ = mvaddstr(y, x, SMOKE[sum % 2][0], display);
mvaddstr(y, x, SMOKE[sum % 2][0], screen, target)?;
SMOKES[sum].y = y;
SMOKES[sum].x = x;
SMOKES[sum].ptrn = 0;
SMOKES[sum].kind = sum % 2;
}
}

Ok(())
}
88 changes: 53 additions & 35 deletions libraries/libsl/src/add_train.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::Display;
use crate::Options;
use crate::{RenderError, RenderTarget, ScreenSize, TrainOptions};

use super::add_man::add_man;
use super::add_smoke::add_smoke;
Expand All @@ -21,46 +20,46 @@ pub struct TrainOffsets<const ENGINE_WINDOWS: usize, const CAR_WINDOWS: usize> {
pub car_text_width: usize,
}

pub enum Error {
Offscreen,
}

pub fn add_train<
const ANIMATIONS: usize,
const HEIGHT: usize,
const ENGINE_WINDOWS: usize,
const CAR_WINDOWS: usize,
T: AsRef<str>,
U: Display,
V: Options,
U: RenderTarget,
>(
x: i32,
engine: &[[&str; HEIGHT]; ANIMATIONS],
coal: &[&str; HEIGHT],
car: &[&str; HEIGHT],
offsets: TrainOffsets<ENGINE_WINDOWS, CAR_WINDOWS>,
names: &[T],
display: &U,
options: &V,
) -> Result<(), Error> {
screen: ScreenSize,
target: &mut U,
options: &TrainOptions,
) -> Result<(), RenderError<U::Error>> {
if screen.columns <= 0 || screen.lines <= 0 {
return Err(RenderError::Offscreen);
}

let car_length: i32 = (car[0].len() - 1).try_into().unwrap();
let frames: i32 = (ANIMATIONS + 1).try_into().unwrap();
let count: i32 = names.len().try_into().unwrap();
let engine_length: i32 = engine[0][0].len().try_into().unwrap();
let front_length: i32 = engine_length + coal[0].len() as i32;
let fly_factor = if options.fly() { 1 } else { 0 };
let fly_factor = if options.fly { 1 } else { 0 };
if x < -(front_length + (if count > 0 { count * car_length } else { 0 })) {
return Err(Error::Offscreen);
return Err(RenderError::Offscreen);
}

let engine_height: i32 = engine.len().try_into().unwrap();
let mut y = display.lines() / 2 - engine_height / 2;
let mut y = screen.lines / 2 - engine_height / 2;
let mut dy = 0;
if options.fly() {
y = (((x / frames) + display.lines()) - display.cols() / frames) - engine_height;
if options.fly {
y = (((x / frames) + screen.lines) - screen.columns / frames) - engine_height;
// Try to estimate when the train is off screen enough.
if y < -(engine_height * display.cols() / display.lines()) {
return Err(Error::Offscreen);
if y < -(engine_height * screen.columns / screen.lines) {
return Err(RenderError::Offscreen);
}

dy = 1;
Expand All @@ -73,17 +72,26 @@ pub fn add_train<
y + i,
x,
engine[((x + front_length) % engine.len() as i32) as usize][ui],
display,
);
mvaddstr((y + i) + dy, x + engine_length - 1, coal[ui], display);
screen,
target,
)
.map_err(RenderError::Target)?;
mvaddstr(
(y + i) + dy,
x + engine_length - 1,
coal[ui],
screen,
target,
)
.map_err(RenderError::Target)?;
}

for j in 0..count {
let uj: usize = j.try_into().unwrap();
let pos = (front_length + x) + (car_length * (j + 1));
if pos < 0 {
continue;
} else if pos > (display.cols() + front_length) {
} else if pos > (screen.columns + front_length) {
break;
}

Expand All @@ -98,45 +106,55 @@ pub fn add_train<
((y + i) + (fly_factor * (j + 1))) + dy,
(x + engine_length - 1) + (car_length * (j + 1)),
str::from_utf8(&car_name).unwrap(),
display,
);
screen,
target,
)
.map_err(RenderError::Target)?;
}
}

if options.accident() {
if options.accident {
offsets
.engine_windows
.window_positions
.iter()
.for_each(|offset| {
add_man(y + offsets.engine_windows.height, x + offset, display);
});
.try_for_each(|offset| {
add_man(
y + offsets.engine_windows.height,
x + offset,
screen,
target,
)
.map_err(RenderError::Target)
})?;

for uj in 0..count {
let j = uj;
let pos = (front_length + x) + (car_length * (j + 1));
if pos < 0 {
continue;
} else if pos > (display.cols() + front_length) {
} else if pos > (screen.columns + front_length) {
break;
}

offsets
.car_windows
.window_positions
.iter()
.for_each(|offset| {
.try_for_each(|offset| {
add_man(
(y + offsets.car_windows.height) + (fly_factor * (j + 2)),
((x + front_length) + offset) + (car_length * j),
display,
);
});
screen,
target,
)
.map_err(RenderError::Target)
})?;
}
}

if options.smoke() {
add_smoke(y - 1, x + offsets.funnel, display);
if options.smoke {
add_smoke(y - 1, x + offsets.funnel, screen, target).map_err(RenderError::Target)?;
}

Ok(())
Expand Down
Loading
Loading