use std::{f32::consts::PI, ops::Range}; use avian2d::prelude::*; use bevy::{ color::palettes::{ css::WHITE, tailwind::{LIME_400, RED_400}, }, prelude::*, }; use rand::{Rng as _, SeedableRng}; use wyrand::WyRand; use super::state::AppState; use super::{ objects::{Ball, Player, Radius, Wall}, rng::thread_rng, seed::Seed, }; const BALL_COUNT: u8 = 32; const BALL_SIZES: Range = 10.0..25.0; const DIMENSION_SIZES: Range = 500.0..2000.0; pub fn check_for_seed(seed: Option>, mut next_state: ResMut>) { if seed.is_some() { next_state.set(AppState::InGame); } } /// The size of the playable area (x, y) #[derive(Resource, Debug)] pub struct PlayableArea(f32, f32); /// Initialize deterministic values pub fn setup_from_seed(mut commands: Commands, seed: Res) { let mut rng = WyRand::from_seed((*seed).into()); commands.insert_resource(PlayableArea( rng.random_range(DIMENSION_SIZES), rng.random_range(DIMENSION_SIZES), )); } /// I mean, a camera is technically a user interface, I guess pub fn setup_camera(mut commands: Commands) { commands.spawn((Name::new("Camera"), Camera2d, IsDefaultUiCamera)); } /// Create the playable character pub fn setup_player( mut commands: Commands, mut materials: ResMut>, mut meshes: ResMut>, ) { let mut random = thread_rng(); let radius = random.random_range(BALL_SIZES); let circle = Circle::new(radius); commands.spawn(( Name::new("Player"), Player, Radius(radius), Collider::from(circle), Mesh2d(meshes.add(circle)), MeshMaterial2d(materials.add(Color::from(LIME_400))), )); } /// Create a random distribution of balls in the playable area pub fn setup_balls( mut commands: Commands, mut materials: ResMut>, mut meshes: ResMut>, region: Res, ) { let mut random = thread_rng(); for i in 0..BALL_COUNT { let radius = random.random_range(BALL_SIZES); let circle = Circle::new(radius); commands.spawn(( Name::new(format!("Ball[{i}]")), Ball, Radius(radius), Collider::from(circle), Mesh2d(meshes.add(circle)), MeshMaterial2d(materials.add(Color::from(RED_400))), Transform::from_xyz( random.random::() * (region.0 - 2.0 * radius) + radius - region.0 / 2.0, random.random::() * (region.1 - 2.0 * radius) + radius - region.1 / 2.0, 0.0, ), )); } } /// Create the 4 walls that enclose the playable area pub fn setup_walls( mut commands: Commands, mut materials: ResMut>, mut meshes: ResMut>, region: Res, ) { let thickness = 20.0; for i in 0..4 { let (offset, length) = if i % 2 == 0 { (region.0, region.1 + thickness) } else { (region.1, region.0 + thickness) }; let mut transform = Transform::from_xyz(0.0, offset / 2.0, 0.0); transform.rotate_around( Vec3::ZERO, Quat::from_rotation_z(((i + 1) as f32) * PI / 2.0), ); commands.spawn(( Name::new(format!("Wall[{i}]")), Wall, Collider::rectangle(length, thickness), Mesh2d(meshes.add(Rectangle::new(length, thickness))), MeshMaterial2d(materials.add(Color::from(WHITE))), transform, )); } }