use crate::render::TerminalRenderer;
use crossterm::style::Color;
use std::io;
struct Star {
x: u16,
y: u16,
brightness: f32,
phase: f32,
}
struct ShootingStar {
x: f32,
y: f32,
speed_x: f32,
speed_y: f32,
length: usize,
active: bool,
}
pub struct StarSystem {
stars: Vec<Star>,
shooting_star: Option<ShootingStar>,
terminal_width: u16,
terminal_height: u16,
}
impl StarSystem {
pub fn new(terminal_width: u16, terminal_height: u16) -> Self {
let count = (terminal_width as usize * terminal_height as usize) / 80; let mut stars = Vec::with_capacity(count);
for _ in 0..count {
stars.push(Star {
x: rand::random::<u16>() % terminal_width,
y: rand::random::<u16>() % (terminal_height / 2), brightness: rand::random::<f32>(),
phase: rand::random::<f32>() * std::f32::consts::TAU,
});
}
Self {
stars,
shooting_star: None,
terminal_width,
terminal_height,
}
}
pub fn update(&mut self, terminal_width: u16, terminal_height: u16) {
self.terminal_width = terminal_width;
self.terminal_height = terminal_height;
for star in &mut self.stars {
star.phase += 0.05;
star.brightness = (star.phase.sin() + 1.0) / 2.0; }
if let Some(ref mut star) = self.shooting_star {
star.x += star.speed_x;
star.y += star.speed_y;
if star.x < 0.0 || star.y as u16 >= terminal_height || star.length == 0 {
self.shooting_star = None;
}
} else if rand::random::<f32>() < 0.005 {
let start_x = (rand::random::<u16>() % (terminal_width / 2)) + (terminal_width / 4);
let start_y = rand::random::<u16>() % (terminal_height / 4);
self.shooting_star = Some(ShootingStar {
x: start_x as f32,
y: start_y as f32,
speed_x: if rand::random::<bool>() { 1.5 } else { -1.5 },
speed_y: 0.5 + (rand::random::<f32>() * 0.5),
length: 5,
active: true,
});
}
}
pub fn render(&self, renderer: &mut TerminalRenderer) -> io::Result<()> {
for star in &self.stars {
let ch = if star.brightness > 0.8 {
'*'
} else if star.brightness > 0.4 {
'+'
} else {
'.'
};
let color = if star.brightness > 0.6 {
Color::White
} else {
Color::DarkGrey
};
renderer.render_char(star.x, star.y, ch, color)?;
}
if let Some(ref star) = self.shooting_star {
if star.active {
let head_x = star.x as i16;
let head_y = star.y as i16;
if head_x >= 0
&& head_x < self.terminal_width as i16
&& head_y >= 0
&& head_y < self.terminal_height as i16
{
renderer.render_char(head_x as u16, head_y as u16, '*', Color::White)?;
}
for i in 1..star.length {
let trail_x = (star.x - (star.speed_x * i as f32)) as i16;
let trail_y = (star.y - (star.speed_y * i as f32)) as i16;
if trail_x >= 0
&& trail_x < self.terminal_width as i16
&& trail_y >= 0
&& trail_y < self.terminal_height as i16
{
let ch = if i == 1 { '+' } else { '.' };
renderer.render_char(trail_x as u16, trail_y as u16, ch, Color::White)?;
}
}
}
}
Ok(())
}
}