world.rs (6040B)
1 use crate::entities::markers::Wall; 2 use crate::plugins::player::PLAYER_START; 3 use crate::{AssetsLoading, GameState}; 4 use bevy::asset::LoadState; 5 use bevy::prelude::*; 6 use noise::{NoiseFn, OpenSimplex, Seedable}; 7 use rand::Rng; 8 use std::ops; 9 10 pub struct MainCamera; 11 12 #[derive(Default, Clone)] 13 pub struct WorldState { 14 pub map_loaded: bool, 15 pub collisions_loaded: bool, 16 pub level: usize, 17 pub requested_level: usize, 18 pub world: Option<Entity>, 19 pub collisions: Vec<Vec2>, 20 pub world_noise: OpenSimplex, 21 pub seed: Option<u32>, 22 } 23 24 pub struct WorldPlugin; 25 26 #[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)] 27 pub struct GridLocation(pub i32, pub i32); 28 29 impl ops::Add<GridLocation> for GridLocation { 30 type Output = GridLocation; 31 32 fn add(self, rhs: GridLocation) -> Self::Output { 33 GridLocation(self.0 + rhs.0, self.1 + rhs.1) 34 } 35 } 36 37 impl From<Vec2> for GridLocation { 38 fn from(vec: Vec2) -> Self { 39 Self { 40 0: vec.x.round() as i32, 41 1: vec.y.round() as i32, 42 } 43 } 44 } 45 46 impl From<[f64; 2]> for GridLocation { 47 fn from(coords: [f64; 2]) -> Self { 48 Self { 49 0: coords[0] as i32, 50 1: coords[1] as i32, 51 } 52 } 53 } 54 55 impl From<GridLocation> for [f64; 2] { 56 fn from(loc: GridLocation) -> Self { 57 [loc.0 as f64, loc.1 as f64] 58 } 59 } 60 61 const LERP_LAMBDA: f32 = 5.0; 62 63 fn render_grid_location_to_transform( 64 time: Res<Time>, 65 mut query: Query<(&GridLocation, &mut Transform)>, 66 ) { 67 for (grid_location, mut transform) in query.iter_mut() { 68 let target_x = SPRITE_WIDTH * grid_location.0 as f32; 69 transform.translation.x = transform.translation.x 70 * (1.0 - LERP_LAMBDA * time.delta_seconds()) 71 + target_x * LERP_LAMBDA * time.delta_seconds(); 72 let target_y = SPRITE_WIDTH * grid_location.1 as f32; 73 transform.translation.y = transform.translation.y 74 * (1.0 - LERP_LAMBDA * time.delta_seconds()) 75 + target_y * LERP_LAMBDA * time.delta_seconds(); 76 } 77 } 78 79 impl Plugin for WorldPlugin { 80 fn build(&self, app: &mut AppBuilder) { 81 app.add_system(draw.system()) 82 .add_system(render_grid_location_to_transform.system()); 83 } 84 } 85 86 pub const SPRITE_WIDTH: f32 = 16.0; 87 pub const SPRITE_HEIGHT: f32 = 16.0; 88 89 #[derive(Bundle)] 90 struct WallBundle { 91 pub grid_location: GridLocation, 92 pub _wall: Wall, 93 94 #[bundle] 95 pub sprite: SpriteSheetBundle, 96 } 97 98 fn setup_wall( 99 grid_location: GridLocation, 100 game_state: &ResMut<GameState>, 101 texture_atlases: &mut ResMut<Assets<TextureAtlas>>, 102 textures: &Res<Assets<Texture>>, 103 ) -> WallBundle { 104 let texture_handle = game_state.asset_map.get("Sunnyland").unwrap(); 105 let texture: &Texture = textures.get(texture_handle.id).unwrap(); 106 let cols = texture.size.width / 16; 107 let rows = texture.size.height / 16; 108 let texture_atlas = TextureAtlas::from_grid( 109 texture_handle.clone(), 110 Vec2::new(17.0, 17.0), 111 cols as usize, 112 rows as usize, 113 ); 114 let texture_atlas_handle = texture_atlases.add(texture_atlas); 115 WallBundle { 116 grid_location, 117 _wall: Wall, 118 sprite: SpriteSheetBundle { 119 sprite: TextureAtlasSprite { 120 index: 48, 121 ..Default::default() 122 }, 123 transform: Transform::from_translation(Vec3::new( 124 SPRITE_WIDTH * grid_location.0 as f32, 125 SPRITE_HEIGHT * grid_location.1 as f32, 126 -1.0, 127 )), 128 texture_atlas: texture_atlas_handle, 129 ..Default::default() 130 }, 131 } 132 } 133 134 fn draw( 135 mut commands: Commands, 136 mut game_state: ResMut<GameState>, 137 mut texture_atlases: ResMut<Assets<TextureAtlas>>, 138 server: Res<AssetServer>, 139 loading: Res<AssetsLoading>, 140 textures: Res<Assets<Texture>>, 141 ) { 142 if game_state.world_state.map_loaded 143 && game_state.world_state.level == game_state.world_state.requested_level 144 { 145 return; 146 } 147 148 let mut ready = true; 149 for handle in loading.0.iter() { 150 match server.get_load_state(handle) { 151 LoadState::Failed => { 152 ready = false; 153 } 154 LoadState::Loaded => {} 155 _ => { 156 ready = false; 157 } 158 } 159 } 160 161 if !ready { 162 return; 163 } 164 165 if game_state.world_state.seed.is_none() { 166 let mut rng = rand::thread_rng(); 167 game_state.world_state.seed = Some(rng.gen()); 168 } 169 170 game_state.world_state.world_noise = 171 OpenSimplex::new().set_seed(game_state.world_state.seed.unwrap()); 172 let noise = &game_state.world_state.world_noise; 173 174 for chunk_y in -2..2 { 175 for chunk_x in -2..2 { 176 let chunk = generate_chunk( 177 chunk_x, 178 chunk_y, 179 noise, 180 &game_state, 181 &mut texture_atlases, 182 &textures, 183 ); 184 commands.spawn_batch(chunk); 185 } 186 } 187 188 game_state.world_state.level = game_state.world_state.requested_level; 189 game_state.world_state.map_loaded = true; 190 } 191 192 fn generate_chunk( 193 chunk_x: i32, 194 chunk_y: i32, 195 noise: &OpenSimplex, 196 game_state: &ResMut<GameState>, 197 texture_atlases: &mut ResMut<Assets<TextureAtlas>>, 198 textures: &Res<Assets<Texture>>, 199 ) -> Vec<WallBundle> { 200 let mut bundles = Vec::new(); 201 for y in -8..8 { 202 for x in -8..8 { 203 let full_x = (chunk_x * 16) + x; 204 let full_y = (chunk_y * 16) + y; 205 let coord = GridLocation(full_x, full_y); 206 207 if full_x == PLAYER_START.0 && full_y == PLAYER_START.1 { 208 continue; 209 } 210 let f = noise.get([(full_x as f32 / 16.0) as f64, (full_y as f32 / 16.0) as f64]); 211 let noise_value = f * 16.0 + (16.0 / 2.0); 212 213 if noise_value > 4.8 { 214 bundles.push(setup_wall(coord, game_state, texture_atlases, textures)); 215 } 216 } 217 } 218 bundles 219 }