#![allow(
unused_parens,
clippy::collapsible_if,
clippy::double_parens,
clippy::manual_is_multiple_of,
clippy::ptr_arg,
clippy::unnecessary_cast
)]
use super::grid::GridCell;
use rand::seq::SliceRandom;
use rand::RngExt;
use std::f32::consts::PI;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum AnimationStyle {
Random,
Rotate,
Shuffle,
Wave,
Spiral,
Explode,
Implosion,
Bounce,
Illuminati,
Matrix,
Vortex,
Glitch,
Infinity,
DNA,
Hypnosis,
Galaxy,
FireStorm,
DigitalRain,
}
impl AnimationStyle {
pub fn name(&self) -> &'static str {
match self {
AnimationStyle::Random => "Random",
AnimationStyle::Rotate => "Rotate",
AnimationStyle::Shuffle => "Shuffle",
AnimationStyle::Wave => "Wave",
AnimationStyle::Spiral => "Spiral",
AnimationStyle::Explode => "Explode",
AnimationStyle::Implosion => "Implosion",
AnimationStyle::Bounce => "Bounce",
AnimationStyle::Illuminati => "Illuminati",
AnimationStyle::Matrix => "Matrix",
AnimationStyle::Vortex => "Vortex",
AnimationStyle::Glitch => "Glitch",
AnimationStyle::Infinity => "Infinity",
AnimationStyle::DNA => "DNA",
AnimationStyle::Hypnosis => "Hypnosis",
AnimationStyle::Galaxy => "Galaxy",
AnimationStyle::FireStorm => "Fire Storm",
AnimationStyle::DigitalRain => "Digital Rain",
}
}
pub fn cli_name(&self) -> &'static str {
match self {
AnimationStyle::Random => "random",
AnimationStyle::Rotate => "rotate",
AnimationStyle::Shuffle => "shuffle",
AnimationStyle::Wave => "wave",
AnimationStyle::Spiral => "spiral",
AnimationStyle::Explode => "explode",
AnimationStyle::Implosion => "implosion",
AnimationStyle::Bounce => "bounce",
AnimationStyle::Illuminati => "illuminati",
AnimationStyle::Matrix => "matrix",
AnimationStyle::Vortex => "vortex",
AnimationStyle::Glitch => "glitch",
AnimationStyle::Infinity => "infinity",
AnimationStyle::DNA => "dna",
AnimationStyle::Hypnosis => "hypnosis",
AnimationStyle::Galaxy => "galaxy",
AnimationStyle::FireStorm => "firestorm",
AnimationStyle::DigitalRain => "digital-rain",
}
}
pub fn from_name(name: &str) -> Option<AnimationStyle> {
let normalized: String = name
.to_ascii_lowercase()
.chars()
.filter(|c| c.is_ascii_alphanumeric())
.collect();
Self::ALL.iter().copied().find(|s| {
let expected: String = s
.cli_name()
.chars()
.filter(|c| c.is_ascii_alphanumeric())
.collect();
expected == normalized
})
}
pub const ALL: &'static [AnimationStyle] = &[
AnimationStyle::Illuminati,
AnimationStyle::Matrix,
AnimationStyle::Vortex,
AnimationStyle::Glitch,
AnimationStyle::Infinity,
AnimationStyle::Spiral,
AnimationStyle::Wave,
AnimationStyle::DNA,
AnimationStyle::Hypnosis,
AnimationStyle::Galaxy,
AnimationStyle::FireStorm,
AnimationStyle::DigitalRain,
AnimationStyle::Explode,
AnimationStyle::Implosion,
AnimationStyle::Bounce,
AnimationStyle::Rotate,
AnimationStyle::Shuffle,
AnimationStyle::Random,
];
}
pub struct AnimationController {
pub current_style: AnimationStyle,
pub frame_count: usize,
pub screen_width: i32,
pub screen_height: i32,
pub grid_size: i32,
pub cell_width: i32,
pub cell_height: i32,
}
impl AnimationController {
pub fn new(screen_width: i32, screen_height: i32, grid_size: i32) -> Self {
let cell_width = screen_width / grid_size;
let cell_height = screen_height / grid_size;
AnimationController {
current_style: AnimationStyle::Illuminati,
frame_count: 0,
screen_width,
screen_height,
grid_size,
cell_width,
cell_height,
}
}
pub fn next_style(&mut self) -> AnimationStyle {
let idx = AnimationStyle::ALL
.iter()
.position(|s| *s == self.current_style)
.unwrap_or(0);
self.current_style = AnimationStyle::ALL[(idx + 1) % AnimationStyle::ALL.len()];
self.current_style
}
pub fn prev_style(&mut self) -> AnimationStyle {
let idx = AnimationStyle::ALL
.iter()
.position(|s| *s == self.current_style)
.unwrap_or(0);
self.current_style = AnimationStyle::ALL[(idx + AnimationStyle::ALL.len() - 1) % AnimationStyle::ALL.len()];
self.current_style
}
pub fn apply_animation(&self, grid_cells: &mut Vec<GridCell>) {
match self.current_style {
AnimationStyle::Random => self.apply_random(grid_cells),
AnimationStyle::Rotate => self.apply_rotate(grid_cells),
AnimationStyle::Shuffle => self.apply_shuffle(grid_cells),
AnimationStyle::Wave => self.apply_wave(grid_cells),
AnimationStyle::Spiral => self.apply_spiral(grid_cells),
AnimationStyle::Explode => self.apply_explode(grid_cells),
AnimationStyle::Implosion => self.apply_implosion(grid_cells),
AnimationStyle::Bounce => self.apply_bounce(grid_cells),
AnimationStyle::Illuminati => self.apply_illuminati(grid_cells),
AnimationStyle::Matrix => self.apply_matrix(grid_cells),
AnimationStyle::Vortex => self.apply_vortex(grid_cells),
AnimationStyle::Glitch => self.apply_glitch(grid_cells),
AnimationStyle::Infinity => self.apply_infinity(grid_cells),
AnimationStyle::DNA => self.apply_dna(grid_cells),
AnimationStyle::Hypnosis => self.apply_hypnosis(grid_cells),
AnimationStyle::Galaxy => self.apply_galaxy(grid_cells),
AnimationStyle::FireStorm => self.apply_firestorm(grid_cells),
AnimationStyle::DigitalRain => self.apply_digital_rain(grid_cells),
}
}
fn apply_random(&self, grid_cells: &mut Vec<GridCell>) {
if self.frame_count % 20 == 0 {
let mut rng = rand::rng();
for cell in grid_cells.iter_mut() {
cell.target_x = rng.random_range(0..(self.screen_width - self.cell_width));
cell.target_y = rng.random_range(0..(self.screen_height - self.cell_height));
}
}
}
fn apply_rotate(&self, grid_cells: &mut Vec<GridCell>) {
if self.frame_count % 5 == 0 {
let center_x = self.screen_width / 2;
let center_y = self.screen_height / 2;
for cell in grid_cells.iter_mut() {
let dx = cell.current_x - center_x;
let dy = cell.current_y - center_y;
let angle = (dy as f32).atan2(dx as f32);
let new_angle = angle + 0.1;
let distance = ((dx * dx + dy * dy) as f32).sqrt();
cell.target_x = center_x + (new_angle.cos() * distance) as i32;
cell.target_y = center_y + (new_angle.sin() * distance) as i32;
}
}
}
fn apply_shuffle(&self, grid_cells: &mut Vec<GridCell>) {
if self.frame_count % 40 == 0 {
let mut rng = rand::rng();
let mut positions = grid_cells.iter().map(|cell| (cell.original_x, cell.original_y)).collect::<Vec<_>>();
positions.shuffle(&mut rng);
for (i, cell) in grid_cells.iter_mut().enumerate() {
cell.target_x = positions[i].0;
cell.target_y = positions[i].1;
}
}
}
fn apply_wave(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.1;
for (i, cell) in grid_cells.iter_mut().enumerate() {
let row = i as i32 / self.grid_size;
let col = i as i32 % self.grid_size;
let wave_x = (time + col as f32 * 0.3).sin() * 50.0;
let wave_y = (time + row as f32 * 0.3).cos() * 50.0;
cell.target_x = cell.original_x + wave_x as i32;
cell.target_y = cell.original_y + wave_y as i32;
}
}
fn apply_spiral(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.1;
let center_x = self.screen_width / 2;
let center_y = self.screen_height / 2;
for (i, cell) in grid_cells.iter_mut().enumerate() {
let index = i as f32;
let spiral_radius = 50.0 + index * 2.0;
let spiral_angle = time + index * 0.1;
cell.target_x = center_x + (spiral_angle.cos() * spiral_radius) as i32;
cell.target_y = center_y + (spiral_angle.sin() * spiral_radius) as i32;
}
}
fn apply_explode(&self, grid_cells: &mut Vec<GridCell>) {
if self.frame_count % 60 == 0 {
let center_x = self.screen_width / 2;
let center_y = self.screen_height / 2;
for cell in grid_cells.iter_mut() {
let dx = cell.original_x - center_x;
let dy = cell.original_y - center_y;
let length = ((dx * dx + dy * dy) as f32).sqrt().max(1.0);
let normalized_dx = dx as f32 / length;
let normalized_dy = dy as f32 / length;
let explosion_distance = length * 1.5;
cell.target_x = center_x + (normalized_dx * explosion_distance) as i32;
cell.target_y = center_y + (normalized_dy * explosion_distance) as i32;
}
}
}
fn apply_implosion(&self, grid_cells: &mut Vec<GridCell>) {
if self.frame_count % 10 == 0 {
let center_x = self.screen_width / 2;
let center_y = self.screen_height / 2;
for cell in grid_cells.iter_mut() {
cell.target_x = center_x;
cell.target_y = center_y;
if (cell.current_x - center_x).abs() < 10 &&
(cell.current_y - center_y).abs() < 10 {
cell.target_x = cell.original_x;
cell.target_y = cell.original_y;
}
}
}
}
fn apply_bounce(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.05;
for (i, cell) in grid_cells.iter_mut().enumerate() {
let row = i as i32 / self.grid_size;
let col = i as i32 % self.grid_size;
let bounce_height = 100.0;
let phase = (col as f32 * 0.2) + (row as f32 * 0.3);
let bounce_y = (time + phase).sin().abs() * bounce_height;
let bounce_x = (time * 0.7 + phase).cos() * 30.0;
cell.target_x = cell.original_x + bounce_x as i32;
cell.target_y = cell.original_y - bounce_y as i32;
}
}
fn apply_illuminati(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.05;
let center_x = self.screen_width / 2;
let center_y = self.screen_height / 2;
let triangle_size = self.screen_height as f32 * 0.8;
let triangle_height = triangle_size * 0.866;
let top_x = center_x;
let top_y = center_y - (triangle_height / 2.0) as i32;
let bottom_left_x = center_x - (triangle_size / 2.0) as i32;
let bottom_left_y = center_y + (triangle_height / 2.0) as i32;
let bottom_right_x = center_x + (triangle_size / 2.0) as i32;
let bottom_right_y = bottom_left_y;
let eye_radius = triangle_size * 0.2;
let eye_center_x = center_x;
let eye_center_y = center_y;
let cell_count = grid_cells.len();
let third_of_cells = cell_count / 3;
let two_thirds_of_cells = cell_count * 2 / 3;
for (i, cell) in grid_cells.iter_mut().enumerate() {
let index = i as f32 / cell_count as f32;
if i < third_of_cells {
let angle = index * PI * 2.0 * 3.0 + time;
let progress = (angle % (2.0 * PI)) / (2.0 * PI);
let (target_x, target_y) = if progress < 1.0/3.0 {
let t = progress * 3.0;
let x = top_x + ((bottom_right_x - top_x) as f32 * t) as i32;
let y = top_y + ((bottom_right_y - top_y) as f32 * t) as i32;
(x, y)
} else if progress < 2.0/3.0 {
let t = (progress - 1.0/3.0) * 3.0;
let x = bottom_right_x + ((bottom_left_x - bottom_right_x) as f32 * t) as i32;
let y = bottom_right_y + ((bottom_left_y - bottom_right_y) as f32 * t) as i32;
(x, y)
} else {
let t = (progress - 2.0/3.0) * 3.0;
let x = bottom_left_x + ((top_x - bottom_left_x) as f32 * t) as i32;
let y = bottom_left_y + ((top_y - bottom_left_y) as f32 * t) as i32;
(x, y)
};
cell.target_x = target_x;
cell.target_y = target_y;
} else if i < two_thirds_of_cells {
let angle = index * PI * 10.0 + time * 2.0;
let wobble = (time * 0.5).sin() * 10.0;
cell.target_x = eye_center_x + ((angle.cos() * (eye_radius + wobble)) as i32);
cell.target_y = eye_center_y + ((angle.sin() * (eye_radius + wobble)) as i32);
} else {
let angle = index * 20.0 + time;
let spiral_radius = 50.0 + (time * 0.2).sin() * 100.0;
cell.target_x = center_x + ((angle.cos() * spiral_radius) as i32);
cell.target_y = center_y + ((angle.sin() * spiral_radius) as i32);
}
}
}
fn apply_matrix(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.05;
let mut rng = rand::rng();
for (i, cell) in grid_cells.iter_mut().enumerate() {
let col = i as i32 % self.grid_size;
let column_speed = 1.0 + (col as f32 * 0.13).sin().abs() * 5.0;
let vertical_offset = (time * column_speed) % self.screen_height as f32;
let wrap_y = (cell.original_y as f32 + vertical_offset) % self.screen_height as f32;
let wobble_x = (time + col as f32 * 0.2).sin() * 15.0;
cell.target_x = cell.original_x + wobble_x as i32;
cell.target_y = wrap_y as i32;
if rng.random::<f32>() < 0.005 {
cell.target_x = rng.random_range(0..self.screen_width);
cell.target_y = rng.random_range(0..self.screen_height);
}
}
}
fn apply_vortex(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.02;
let center_x = self.screen_width / 2;
let center_y = self.screen_height / 2;
for cell in grid_cells.iter_mut() {
let dx = center_x - cell.original_x;
let dy = center_y - cell.original_y;
let distance = ((dx * dx + dy * dy) as f32).sqrt().max(1.0);
let normalized_dx = dx as f32 / distance;
let normalized_dy = dy as f32 / distance;
let spiral_factor = (1.0 - (distance / (self.screen_width as f32))) * 100.0;
let angle = time + distance * 0.01;
let spiral_x = (angle * spiral_factor).sin() * distance * 0.2;
let spiral_y = (angle * spiral_factor).cos() * distance * 0.2;
let pull_factor = (1.0 - (distance / (self.screen_width as f32 * 0.8))).max(0.0) * distance * 0.9;
cell.target_x = cell.original_x + normalized_dx as i32 * pull_factor as i32 + spiral_x as i32;
cell.target_y = cell.original_y + normalized_dy as i32 * pull_factor as i32 + spiral_y as i32;
if distance < 20.0 {
cell.target_x = cell.original_x;
cell.target_y = cell.original_y;
}
}
}
fn apply_glitch(&self, grid_cells: &mut Vec<GridCell>) {
let mut rng = rand::rng();
if self.frame_count % 5 == 0 {
for cell in grid_cells.iter_mut() {
if rng.random::<f32>() < 0.3 {
let glitch_amount = rng.random_range(-200..200);
cell.target_x = cell.original_x + glitch_amount;
cell.target_y = cell.original_y;
} else if rng.random::<f32>() < 0.05 {
cell.target_x = rng.random_range(0..self.screen_width);
cell.target_y = rng.random_range(0..self.screen_height);
} else if rng.random::<f32>() < 0.02 {
cell.target_x = cell.original_x * 2 - self.screen_width / 2;
cell.target_y = cell.original_y;
} else {
cell.target_x = cell.original_x;
cell.target_y = cell.original_y;
}
}
}
if self.frame_count % 60 == 0 {
for cell in grid_cells.iter_mut() {
cell.target_x = cell.original_x;
cell.target_y = cell.original_y;
}
}
}
fn apply_infinity(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.02;
let center_x = self.screen_width / 2;
let center_y = self.screen_height / 2;
let infinity_width = self.screen_width as f32 * 0.4;
let infinity_height = self.screen_height as f32 * 0.3;
let cell_count = grid_cells.len();
for (i, cell) in grid_cells.iter_mut().enumerate() {
let cell_position = (i as f32 / cell_count as f32) * PI * 2.0 + time;
let a = infinity_width;
let b = infinity_height;
let x = center_x + (a * cell_position.sin() / (1.0 + cell_position.cos() * cell_position.cos())) as i32;
let y = center_y + (b * cell_position.sin() * cell_position.cos() / (1.0 + cell_position.cos() * cell_position.cos())) as i32;
cell.target_x = x;
cell.target_y = y;
}
}
fn apply_dna(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.05;
let center_x = self.screen_width / 2;
let center_y = self.screen_height / 2;
let dna_length = self.screen_height as f32 * 0.8;
let dna_radius = self.screen_width as f32 * 0.15;
let twists = 5.0;
let cell_count = grid_cells.len();
let half_cells = cell_count / 2;
for (i, cell) in grid_cells.iter_mut().enumerate() {
let position = if i < half_cells {
i as f32 / half_cells as f32
} else {
(i - half_cells) as f32 / (cell_count - half_cells) as f32
};
let y_offset = (position - 0.5) * dna_length;
let strand_offset = if i < half_cells { 0.0 } else { PI };
let angle = position * twists * 2.0 * PI + time + strand_offset;
let x = center_x + (angle.sin() * dna_radius) as i32;
let y = center_y + y_offset as i32;
cell.target_x = x;
cell.target_y = y;
if i % (cell_count / 20) == 0 && i < cell_count - (cell_count / 20) {
let opposite_index = if i < half_cells {
i + half_cells
} else {
i - half_cells
};
if opposite_index < cell_count {
let mid_x = center_x + (angle.sin() * dna_radius * 0.5) as i32;
cell.target_x = mid_x;
}
}
}
}
fn apply_hypnosis(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.03;
let center_x = self.screen_width / 2;
let center_y = self.screen_height / 2;
let max_radius = (self.screen_width.min(self.screen_height) / 2) as f32;
let num_rings = 10.0;
let ring_width = max_radius / num_rings;
for cell in grid_cells.iter_mut() {
let dx = cell.original_x - center_x;
let dy = cell.original_y - center_y;
let distance = ((dx * dx + dy * dy) as f32).sqrt();
let angle = (dy as f32).atan2(dx as f32);
let ring = (distance / ring_width).floor() as i32;
let rotation_speed = 0.5 + (num_rings - ring as f32) * 0.1;
let ring_angle = angle + time * rotation_speed * (if ring % 2 == 0 { 1.0 } else { -1.0 });
let breathing = (time * 0.5).sin() * ring_width * 0.5;
let ring_radius = ring as f32 * ring_width + breathing;
cell.target_x = center_x + (ring_angle.cos() * ring_radius) as i32;
cell.target_y = center_y + (ring_angle.sin() * ring_radius) as i32;
if self.frame_count % 120 < 10 && ring % 2 == 0 {
let pulse = ((self.frame_count % 120) as f32 / 10.0) * PI;
let pulse_factor = pulse.sin() * 20.0;
cell.target_x = center_x + ((ring_angle.cos() * (ring_radius + pulse_factor))) as i32;
cell.target_y = center_y + ((ring_angle.sin() * (ring_radius + pulse_factor))) as i32;
}
}
}
fn apply_galaxy(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.01;
let center_x = self.screen_width / 2;
let center_y = self.screen_height / 2;
let galaxy_radius = (self.screen_width.min(self.screen_height) / 2) as f32 * 0.9;
let num_arms = 5; let arm_tightness = 0.3; let dust_ratio = 0.3;
let mut rng = rand::rng();
let cell_count = grid_cells.len();
for (i, cell) in grid_cells.iter_mut().enumerate() {
if i < ((1.0 - dust_ratio) * cell_count as f32) as usize {
let arm = i % num_arms;
let arm_angle_offset = arm as f32 * (2.0 * PI / num_arms as f32);
let position = (i / num_arms) as f32 / ((cell_count / num_arms) as f32);
let radius = position * galaxy_radius;
let spiral_angle = position * 2.0 * PI * arm_tightness + arm_angle_offset + time;
cell.target_x = center_x + (spiral_angle.cos() * radius) as i32;
cell.target_y = center_y + (spiral_angle.sin() * radius) as i32;
if position > 0.1 {
let noise_factor = position * 10.0;
cell.target_x += (rng.random::<f32>() * 2.0 - 1.0) as i32 * noise_factor as i32;
cell.target_y += (rng.random::<f32>() * 2.0 - 1.0) as i32 * noise_factor as i32;
}
} else {
let angle = rng.random::<f32>() * 2.0 * PI;
let center_pull = rng.random::<f32>().powf(2.0); let radius = center_pull * galaxy_radius * 1.2;
cell.target_x = center_x + (angle.cos() * radius) as i32;
cell.target_y = center_y + (angle.sin() * radius) as i32;
let drift_speed = 0.5;
let drift_x = ((time + i as f32 * 0.1).sin() * drift_speed) as i32;
let drift_y = ((time + i as f32 * 0.1).cos() * drift_speed) as i32;
cell.target_x += drift_x;
cell.target_y += drift_y;
}
}
let galaxy_rotation = time * 0.05;
for cell in grid_cells.iter_mut() {
let dx = cell.target_x - center_x;
let dy = cell.target_y - center_y;
let distance = ((dx * dx + dy * dy) as f32).sqrt();
let angle = (dy as f32).atan2(dx as f32) + galaxy_rotation;
cell.target_x = center_x + (angle.cos() * distance) as i32;
cell.target_y = center_y + (angle.sin() * distance) as i32;
}
}
fn apply_firestorm(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.05;
let center_x = self.screen_width / 2;
let mut rng = rand::rng();
let max_height = self.screen_height as f32 * 0.7;
let flame_width = self.screen_width as f32 * 0.8;
let tornado_radius = self.screen_width as f32 * 0.3;
let tornado_height = self.screen_height as f32 * 0.6;
let cell_count = grid_cells.len();
for (i, cell) in grid_cells.iter_mut().enumerate() {
let normalized_index = i as f32 / cell_count as f32;
if i % 3 == 0 {
let phase = normalized_index * 20.0 + time * 2.0;
let flame_x = center_x + ((phase * 2.0).sin() * flame_width * 0.5) as i32;
let flame_height = ((phase).sin().abs() * max_height) as i32;
let flame_y = self.screen_height - flame_height;
let flicker_x = (rng.random::<f32>() * 10.0 - 5.0) as i32;
let flicker_y = (rng.random::<f32>() * 10.0 - 5.0) as i32;
cell.target_x = flame_x + flicker_x;
cell.target_y = flame_y + flicker_y;
}
else if i % 3 == 1 {
let spiral_height = (normalized_index * tornado_height) as i32;
let y_pos = self.screen_height - spiral_height;
let height_ratio = spiral_height as f32 / tornado_height;
let radius = tornado_radius * (1.0 - height_ratio * 0.7);
let angle = time * 3.0 + spiral_height as f32 * 0.05;
cell.target_x = center_x + (angle.cos() * radius) as i32;
cell.target_y = y_pos;
if rng.random::<f32>() < 0.1 {
let flicker_x = rng.random_range(-15..15);
let flicker_y = rng.random_range(-15..15);
cell.target_x += flicker_x;
cell.target_y += flicker_y;
}
}
else {
if self.frame_count % 2 == 0 && rng.random::<f32>() < 0.1 {
let start_x = center_x + rng.random_range(-(flame_width as i32 / 2)..(flame_width as i32 / 2));
let start_y = self.screen_height - rng.random_range(0..50);
let angle = rng.random::<f32>() * PI * 0.7 + PI * 0.15; let speed = rng.random::<f32>() * 10.0 + 5.0;
let age = (i % (cell_count / 3)) as f32 / (cell_count / 3) as f32;
let x = start_x + (angle.cos() * speed * age * 100.0) as i32;
let y = start_y - (angle.sin() * speed * age * 200.0) as i32;
cell.target_x = x;
cell.target_y = y;
if age > 0.8 {
cell.target_x = self.screen_width * 2;
cell.target_y = self.screen_height * 2;
}
}
else {
if cell.current_y < 0 || cell.current_y > self.screen_height * 2 ||
cell.current_x < 0 || cell.current_x > self.screen_width * 2 {
let start_x = center_x + rng.random_range(-(flame_width as i32 / 2)..(flame_width as i32 / 2));
cell.target_x = start_x;
cell.target_y = self.screen_height;
}
else {
let upward_speed = rng.random::<f32>() * 10.0 + 5.0;
let drift = (time + i as f32 * 0.1).sin() * 5.0;
cell.target_x = cell.current_x + drift as i32;
cell.target_y = cell.current_y - upward_speed as i32;
}
}
}
}
}
fn apply_digital_rain(&self, grid_cells: &mut Vec<GridCell>) {
let time = self.frame_count as f32 * 0.03;
let mut rng = rand::rng();
let num_columns = 30;
let column_spacing = self.screen_width as f32 / num_columns as f32;
let max_speed = 20.0;
let min_speed = 3.0;
let drop_density = 0.6;
let cell_count = grid_cells.len();
let cells_per_column = (cell_count / num_columns) as usize;
for (i, cell) in grid_cells.iter_mut().enumerate() {
let column = i / cells_per_column;
let position_in_column = i % cells_per_column;
let column_phase = column as f32 * 0.5;
let column_center_x = column as f32 * column_spacing + column_spacing * 0.5;
let wave_x = (time * 0.5 + column_phase).sin() * column_spacing * 0.3;
let column_x = column_center_x + wave_x;
let speed_seed = ((column ^ (position_in_column * 3)) % 17) as f32 / 17.0;
let drop_speed = min_speed + speed_seed * (max_speed - min_speed);
let start_offset = (column as f32 * 123.456 + position_in_column as f32 * 45.67) % self.screen_height as f32;
let y_position = (start_offset + time * drop_speed * 60.0) % (self.screen_height as f32 * (1.0 + drop_density));
if y_position < self.screen_height as f32 {
cell.target_x = column_x as i32;
cell.target_y = y_position as i32;
if i % 7 == 0 {
let glow_phase = (time * 3.0 + i as f32 * 0.1).sin();
let glow_offset = glow_phase * 3.0;
cell.target_x += glow_offset as i32;
}
if position_in_column % cells_per_column == (time * 5.0 + column as f32) as usize % cells_per_column {
let char_move = (time * 10.0 + column as f32 * 0.5).sin() * 10.0;
cell.target_x += char_move as i32;
}
} else {
cell.target_x = column_x as i32;
cell.target_y = self.screen_height * 2; }
if rng.random::<f32>() < 0.001 {
let glitch_row = rng.random_range(0..self.screen_height);
if (cell.target_y - glitch_row).abs() < 5 {
let glitch_offset = rng.random_range(-50..50);
cell.target_x += glitch_offset;
}
}
if column > 0 && column < num_columns - 1 {
if i % 73 == 0 && (time + i as f32 * 0.01).sin() > 0.8 {
let ripple_x = (time * 5.0 + i as f32 * 0.05).sin() * 30.0;
cell.target_x = (column_x + ripple_x) as i32;
let ripple_speed = 5.0 + (time + column as f32).sin() * 3.0;
let ripple_y = y_position - ripple_speed;
if ripple_y > 0.0 {
cell.target_y = ripple_y as i32;
}
}
}
}
}
}