Add simple random map
This commit is contained in:
		
							parent
							
								
									875948519f
								
							
						
					
					
						commit
						0ee6fc1afe
					
				
					 3 changed files with 192 additions and 33 deletions
				
			
		
							
								
								
									
										111
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										111
									
								
								src/main.rs
									
										
									
									
									
								
							|  | @ -1,19 +1,19 @@ | |||
| #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] | ||||
| 
 | ||||
| use bracket_lib::prelude::*; | ||||
| use map::{Map, TileType, MAP_HEIGHT, MAP_WIDTH, MAP_X, MAP_Y}; | ||||
| use sound::{Sound, SoundEffect, SoundEffectSamples, SoundSystem}; | ||||
| use specs::prelude::*; | ||||
| use specs_derive::Component; | ||||
| use std::{ | ||||
|     array, | ||||
|     cmp::{max, min}, | ||||
|     iter, | ||||
|     array, iter, | ||||
|     time::{Duration, Instant}, | ||||
| }; | ||||
| use vga_color as vga; | ||||
| 
 | ||||
| use crate::sound::SoundType; | ||||
| 
 | ||||
| mod map; | ||||
| mod sidebar; | ||||
| mod sound; | ||||
| pub mod vga_color; | ||||
|  | @ -25,6 +25,7 @@ struct SoundEffects { | |||
|     step_sound: SoundEffectSamples, | ||||
|     pickup_sound: SoundEffectSamples, | ||||
|     bad_key_sound: SoundEffectSamples, | ||||
|     blocked_sound: SoundEffectSamples, | ||||
| } | ||||
| 
 | ||||
| #[derive(Component)] | ||||
|  | @ -61,23 +62,40 @@ struct State { | |||
| fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World, sound_system: &mut SoundSystem) { | ||||
|     let mut positions = ecs.write_storage::<Position>(); | ||||
|     let mut players = ecs.write_storage::<Player>(); | ||||
| 
 | ||||
|     let mut stepped = false; | ||||
|     let map = ecs.fetch::<Map>(); | ||||
| 
 | ||||
|     for (player, pos) in (&mut players, &mut positions).join() { | ||||
|         let now = Instant::now(); | ||||
|         if now - player.last_moved > Duration::from_secs_f32(1. / 7.5) { | ||||
|             pos.x = min(65, max(0, pos.x + delta_x)); | ||||
|             pos.y = min(24, max(0, pos.y + delta_y)); | ||||
|             stepped = true; | ||||
|         if now - player.last_moved > Duration::from_secs_f32(0.15) { | ||||
|             let destination_x = pos.x + delta_x; | ||||
|             let destination_y = pos.y + delta_y; | ||||
| 
 | ||||
|             let sound_effects = ecs.fetch::<SoundEffects>(); | ||||
| 
 | ||||
|             if destination_x < 0 | ||||
|                 || (destination_x as usize) >= MAP_WIDTH | ||||
|                 || destination_y < 0 | ||||
|                 || (destination_y as usize) >= MAP_HEIGHT | ||||
|             { | ||||
|                 let blocked_sound = sound_effects.blocked_sound.clone(); | ||||
|                 sound_system.play_sound(blocked_sound); | ||||
|                 player.last_moved = now; | ||||
|         } | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|     if stepped { | ||||
|         let sound_effects = ecs.fetch::<SoundEffects>(); | ||||
|             let destination_tile = map.tile_at(destination_x as usize, destination_y as usize); | ||||
|             if destination_tile != TileType::Wall { | ||||
|                 pos.x = destination_x; | ||||
|                 pos.y = destination_y; | ||||
| 
 | ||||
|                 let step_sound = sound_effects.step_sound.clone(); | ||||
|                 sound_system.play_sound(step_sound); | ||||
|             } else { | ||||
|                 let blocked_sound = sound_effects.blocked_sound.clone(); | ||||
|                 sound_system.play_sound(blocked_sound); | ||||
|             } | ||||
|             player.last_moved = now; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -89,24 +107,35 @@ fn player_input(gs: &mut State, ctx: &mut BTerm) { | |||
|             VirtualKeyCode::Left | VirtualKeyCode::J => { | ||||
|                 try_move_player(-1, 0, &mut gs.ecs, &mut gs.sound_system); | ||||
|             } | ||||
|             VirtualKeyCode::U => try_move_player(-1, -1, &mut gs.ecs, &mut gs.sound_system), | ||||
|             VirtualKeyCode::U | VirtualKeyCode::Home => { | ||||
|                 try_move_player(-1, -1, &mut gs.ecs, &mut gs.sound_system) | ||||
|             } | ||||
|             VirtualKeyCode::Up | VirtualKeyCode::I => { | ||||
|                 try_move_player(0, -1, &mut gs.ecs, &mut gs.sound_system); | ||||
|             } | ||||
|             VirtualKeyCode::O => try_move_player(1, -1, &mut gs.ecs, &mut gs.sound_system), | ||||
|             VirtualKeyCode::O | VirtualKeyCode::PageUp => { | ||||
|                 try_move_player(1, -1, &mut gs.ecs, &mut gs.sound_system) | ||||
|             } | ||||
|             VirtualKeyCode::Right | VirtualKeyCode::K => { | ||||
|                 try_move_player(1, 0, &mut gs.ecs, &mut gs.sound_system); | ||||
|             } | ||||
|             VirtualKeyCode::Comma => try_move_player(1, 1, &mut gs.ecs, &mut gs.sound_system), | ||||
|             VirtualKeyCode::Comma | VirtualKeyCode::PageDown => { | ||||
|                 try_move_player(1, 1, &mut gs.ecs, &mut gs.sound_system) | ||||
|             } | ||||
|             VirtualKeyCode::Down | VirtualKeyCode::M => { | ||||
|                 try_move_player(0, 1, &mut gs.ecs, &mut gs.sound_system); | ||||
|             } | ||||
|             VirtualKeyCode::N => try_move_player(-1, 1, &mut gs.ecs, &mut gs.sound_system), | ||||
|             VirtualKeyCode::N | VirtualKeyCode::End => { | ||||
|                 try_move_player(-1, 1, &mut gs.ecs, &mut gs.sound_system) | ||||
|             } | ||||
|             VirtualKeyCode::S => { | ||||
|                 let sound_effects = gs.ecs.fetch::<SoundEffects>(); | ||||
|                 let pickup_sound = sound_effects.pickup_sound.clone(); | ||||
|                 gs.sound_system.play_sound(pickup_sound); | ||||
|             } | ||||
|             VirtualKeyCode::Escape => { | ||||
|                 ctx.quit(); | ||||
|             } | ||||
|             _ => { | ||||
|                 let sound_effects = gs.ecs.fetch::<SoundEffects>(); | ||||
|                 let bad_key_sound = sound_effects.bad_key_sound.clone(); | ||||
|  | @ -123,11 +152,20 @@ impl GameState for State { | |||
|         player_input(self, ctx); | ||||
|         self.run_systems(); | ||||
| 
 | ||||
|         let map = self.ecs.fetch::<Map>(); | ||||
|         map.draw(ctx); | ||||
| 
 | ||||
|         let positions = self.ecs.read_storage::<Position>(); | ||||
|         let renderables = self.ecs.read_storage::<Renderable>(); | ||||
| 
 | ||||
|         for (pos, render) in (&positions, &renderables).join() { | ||||
|             ctx.set(pos.x, pos.y, render.fg, render.bg, render.glyph); | ||||
|             ctx.set( | ||||
|                 pos.x + MAP_X as i32, | ||||
|                 pos.y + MAP_Y as i32, | ||||
|                 render.fg, | ||||
|                 render.bg, | ||||
|                 render.glyph, | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         sidebar::draw(&self.ecs, ctx); | ||||
|  | @ -218,6 +256,17 @@ fn main() -> BError { | |||
|         .collect(), | ||||
|     }); | ||||
| 
 | ||||
|     let blocked_sound = ss.render_sound_effect(&SoundEffect { | ||||
|         sounds: (30..=60) | ||||
|             .rev() | ||||
|             .step_by(6) | ||||
|             .map(|x| Sound { | ||||
|                 sound_type: SoundType::Tone(x), | ||||
|                 duration: Duration::from_millis(18), | ||||
|             }) | ||||
|             .collect(), | ||||
|     }); | ||||
| 
 | ||||
|     let mut gs = State { | ||||
|         ecs: World::new(), | ||||
|         // sound_sender: tx,
 | ||||
|  | @ -230,7 +279,9 @@ fn main() -> BError { | |||
|         step_sound, | ||||
|         pickup_sound, | ||||
|         bad_key_sound, | ||||
|         blocked_sound, | ||||
|     }); | ||||
|     gs.ecs.insert(Map::new()); | ||||
| 
 | ||||
|     gs.ecs.register::<Position>(); | ||||
|     gs.ecs.register::<Renderable>(); | ||||
|  | @ -239,7 +290,7 @@ fn main() -> BError { | |||
| 
 | ||||
|     gs.ecs | ||||
|         .create_entity() | ||||
|         .with(Position { x: 40, y: 24 }) | ||||
|         .with(Position { x: 40, y: 22 }) | ||||
|         .with(Renderable { | ||||
|             glyph: to_cp437('☻'), | ||||
|             fg: RGB::named(vga::YELLOW_BRIGHT), | ||||
|  | @ -255,18 +306,18 @@ fn main() -> BError { | |||
|         }) | ||||
|         .build(); | ||||
| 
 | ||||
|     for i in 0..10 { | ||||
|         gs.ecs | ||||
|             .create_entity() | ||||
|             .with(Position { x: i * 7, y: 20 }) | ||||
|             .with(Renderable { | ||||
|                 glyph: to_cp437('Ä'), | ||||
|                 fg: RGB::named(vga::RED_BRIGHT), | ||||
|                 bg: RGB::named(vga::BLACK), | ||||
|             }) | ||||
|             .with(LeftMover {}) | ||||
|             .build(); | ||||
|     } | ||||
|     // for i in 0..10 {
 | ||||
|     //     gs.ecs
 | ||||
|     //         .create_entity()
 | ||||
|     //         .with(Position { x: i * 7, y: 20 })
 | ||||
|     //         .with(Renderable {
 | ||||
|     //             glyph: to_cp437('Ä'),
 | ||||
|     //             fg: RGB::named(vga::RED_BRIGHT),
 | ||||
|     //             bg: RGB::named(vga::BLACK),
 | ||||
|     //         })
 | ||||
|     //         .with(LeftMover {})
 | ||||
|     //         .build();
 | ||||
|     // }
 | ||||
| 
 | ||||
|     // let descent_sounds: Vec<Sound> = (20..100)
 | ||||
|     //     .rev()
 | ||||
|  |  | |||
							
								
								
									
										86
									
								
								src/map.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/map.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | |||
| use crate::vga_color as vga; | ||||
| use bracket_lib::{ | ||||
|     prelude::{to_cp437, BTerm, Rect, RGB}, | ||||
|     random::RandomNumberGenerator, | ||||
| }; | ||||
| 
 | ||||
| pub const MAP_WIDTH: usize = 64; | ||||
| pub const MAP_HEIGHT: usize = 23; | ||||
| const MAP_SIZE: usize = MAP_WIDTH * MAP_HEIGHT; | ||||
| 
 | ||||
| pub const MAP_X: usize = 1; | ||||
| pub const MAP_Y: usize = 1; | ||||
| 
 | ||||
| #[derive(PartialEq, Copy, Clone)] | ||||
| pub enum TileType { | ||||
|     Wall, | ||||
|     Floor, | ||||
| } | ||||
| 
 | ||||
| pub struct Map { | ||||
|     tiles: Vec<TileType>, | ||||
|     border_fg: RGB, | ||||
|     border_bg: RGB, | ||||
| } | ||||
| 
 | ||||
| impl Map { | ||||
|     pub fn new() -> Self { | ||||
|         let mut tiles = vec![TileType::Floor; MAP_SIZE]; | ||||
|         let mut rng = RandomNumberGenerator::new(); | ||||
|         for tile in &mut tiles { | ||||
|             if rng.roll_dice(1, 4) < 2 { | ||||
|                 *tile = TileType::Wall; | ||||
|             } | ||||
|         } | ||||
|         Self { | ||||
|             tiles, | ||||
|             border_fg: RGB::named(vga::get_by_index(rng.range(8, 15))), | ||||
|             border_bg: RGB::named(vga::get_by_index(rng.range(1, 7) as usize)), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn draw(&self, ctx: &mut BTerm) { | ||||
|         // Border
 | ||||
|         ctx.fill_region( | ||||
|             Rect { | ||||
|                 x1: MAP_X as i32 - 1, | ||||
|                 x2: MAP_WIDTH as i32 + MAP_X as i32 + 1, | ||||
|                 y1: MAP_Y as i32 - 1, | ||||
|                 y2: MAP_HEIGHT as i32 + MAP_Y as i32 + 1, | ||||
|             }, | ||||
|             to_cp437('▓'), | ||||
|             self.border_fg, | ||||
|             self.border_bg, | ||||
|         ); | ||||
| 
 | ||||
|         for (i, tile) in self.tiles.iter().enumerate() { | ||||
|             let x = i % MAP_WIDTH; | ||||
|             let y = (i - x) / MAP_WIDTH; | ||||
| 
 | ||||
|             match tile { | ||||
|                 TileType::Floor => { | ||||
|                     ctx.set( | ||||
|                         x + MAP_X, | ||||
|                         y + MAP_Y, | ||||
|                         RGB::named(vga::BLACK), | ||||
|                         RGB::named(vga::BLACK), | ||||
|                         to_cp437(' '), | ||||
|                     ); | ||||
|                 } | ||||
|                 TileType::Wall => { | ||||
|                     ctx.set( | ||||
|                         x + MAP_X, | ||||
|                         y + MAP_Y, | ||||
|                         RGB::named(vga::YELLOW), | ||||
|                         RGB::named(vga::BLACK), | ||||
|                         to_cp437('▓'), | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn tile_at(&self, x: usize, y: usize) -> TileType { | ||||
|         self.tiles[(y * MAP_WIDTH) + x] | ||||
|     } | ||||
| } | ||||
|  | @ -55,3 +55,25 @@ pub const MAGENTA_BRIGHT: (u8, u8, u8) = (255, 85, 255); | |||
| pub const YELLOW_BRIGHT: (u8, u8, u8) = (255, 255, 85); | ||||
| /// Index `15`
 | ||||
| pub const WHITE_BRIGHT: (u8, u8, u8) = (255, 255, 255); | ||||
| 
 | ||||
| pub fn get_by_index(index: usize) -> (u8, u8, u8) { | ||||
|     match index { | ||||
|         0 => BLACK, | ||||
|         1 => BLUE, | ||||
|         2 => GREEN, | ||||
|         3 => CYAN, | ||||
|         4 => RED, | ||||
|         5 => MAGENTA, | ||||
|         6 => YELLOW, | ||||
|         7 => WHITE, | ||||
|         8 => BLACK_BRIGHT, | ||||
|         9 => BLUE_BRIGHT, | ||||
|         10 => GREEN_BRIGHT, | ||||
|         11 => CYAN_BRIGHT, | ||||
|         12 => RED_BRIGHT, | ||||
|         13 => MAGENTA_BRIGHT, | ||||
|         14 => YELLOW_BRIGHT, | ||||
|         15 => WHITE_BRIGHT, | ||||
|         _ => BLACK, | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue