diff options
| author | Raymond Hogenson <rhogenson@posteo.net> | 2018-08-18 00:25:29 -0400 |
|---|---|---|
| committer | Raymond Hogenson <rhogenson@posteo.net> | 2018-08-18 00:25:29 -0400 |
| commit | daa61a51eb97f868a56a52fc61b560832dde353b (patch) | |
| tree | 4da42fd8b8265b7e639275e613d4c7f78bf980e9 /src | |
| download | stallman-shooter-daa61a51eb97f868a56a52fc61b560832dde353b.tar.zst | |
Write a working bullet hell
I just fucked up the enemy movement, but hopefully I won't ever need
that old code. The new one is going to look really cool and sweet.
Anything with just modifying acceleration must look very cool and
smooth.
Diffstat (limited to 'src')
| -rw-r--r-- | src/constants.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 124 | ||||
| -rw-r--r-- | src/player.rs | 100 | ||||
| -rw-r--r-- | src/state.rs | 237 |
4 files changed, 463 insertions, 0 deletions
diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..3868ce3 --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,2 @@ +pub const X_DIM: u32 = 800; +pub const Y_DIM: u32 = 600; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..76b7a5b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,124 @@ +extern crate sdl2; + +mod state; +mod player; +mod constants; + +use state::Positioned; +use state::Drawable; + +enum Result { + Win, + Loss, + Nothing, +} + +fn spawn(count: u64) -> Vec<state::Enemy> { + if count == 0 { + vec![state::Enemy::new()] + } else { + Vec::new() + } +} + +fn frame(canvas: &mut sdl2::render::WindowCanvas, state: &mut state::State, events: &mut sdl2::event::EventPollIterator) -> Result { + let mut new_bullets = Vec::new(); + let count = state.count; + for enemy in state.enemies.iter_mut() { + new_bullets.append(&mut enemy.act(count)); + } + for bullet in state.bullets.iter_mut() { + bullet.act(); + } + for bullet in state.player_bullets.iter_mut() { + bullet.act(); + } + state.player.act(events); + if count % state.player.fire_rate as u64 == 0 { + state.player_bullets.push(state::PBullet::new(state.player.x, 0., state.player.y, -1.)); + } + if state.bullets.iter().any(|bullet| state.player.overlaps(bullet)) || new_bullets.iter().any(|bullet| state.player.overlaps(bullet)) { + return Result::Loss; + } + for enemy in state.enemies.iter_mut() { + for bullet in state.player_bullets.iter_mut() { + if enemy.overlaps(bullet) { + bullet.delete(); + enemy.health -= 1; + } + } + } + state.enemies.retain(|enemy| state::Enemy::health(enemy) > 0); + state.enemies.append(&mut spawn(count)); + if state.enemies.len() == 0 { + return Result::Win; + } + state.bullets.retain(|bullet| !state::EBullet::outside(bullet)); + state.bullets.append(&mut new_bullets); + state.player_bullets.retain(|bullet| !state::PBullet::outside(bullet)); + for bullet in state.player_bullets.iter() { + bullet.draw(canvas); + } + for enemy in state.enemies.iter() { + enemy.draw(canvas); + } + for bullet in state.bullets.iter() { + bullet.draw(canvas); + } + state.player.draw(canvas); + state.count += 1; + Result::Nothing +} + +fn main() { + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + + let window = video_subsystem.window("rust-sdl2 demo", constants::X_DIM, constants::Y_DIM).position_centered().build().unwrap(); + let mut canvas = window.into_canvas().build().unwrap(); + + canvas.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255)); + canvas.clear(); + canvas.present(); + let mut event_pump = sdl_context.event_pump().unwrap(); + let start = std::time::Instant::now(); + let frame_duration = std::time::Duration::new(0, 1_000_000_000u32 / 60); + let mut frame_count = 0; + let mut state = state::State::new(); + let mut won = false; + 'running: loop { + if won { + for event in event_pump.poll_iter() { + match event { + sdl2::event::Event::Quit {..} | sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. } => { + std::process::exit(0); + }, + _ => { }, + } + } + } else { + canvas.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255)); + canvas.clear(); + match frame(&mut canvas, &mut state, &mut event_pump.poll_iter()) { + Result::Loss => { + break; + } + Result::Win => { + canvas.set_draw_color(sdl2::pixels::Color::RGB(255, 204, 102)); + canvas.clear(); + canvas.present(); + won = true; + } + _ => { + canvas.present(); + } + } + } + let calc_time = frame_duration * frame_count; + match calc_time.checked_sub(start.elapsed()) { + Some(t) => std::thread::sleep(t), + None => (), + } + frame_count += 1; + } +} diff --git a/src/player.rs b/src/player.rs new file mode 100644 index 0000000..97c98f6 --- /dev/null +++ b/src/player.rs @@ -0,0 +1,100 @@ +extern crate sdl2; +extern crate std; + +use super::state; + +pub struct Player { + pub x: i32, + pub y: i32, + left_p: bool, + right_p: bool, + up_p: bool, + down_p: bool, + y_d: i32, + x_d: i32, + pub fire_rate: u32, +} + +impl Player { + pub fn new() -> Player { + Player { x: 400, y: 500, x_d: 0, y_d: 0, down_p: false, left_p: false, right_p: false, up_p: false, fire_rate: 30 } + } + + pub fn act(&mut self, events: &mut sdl2::event::EventPollIterator){ + for event in events { + match event { + sdl2::event::Event::Quit {..} | + sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. } => { + std::process::exit(0); + }, + sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Down), .. } => { + self.down_p = true; + self.y_d = -1; + } + sdl2::event::Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::Down), .. } => { + self.down_p = false; + } + sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Up), .. } => { + self.up_p = true; + self.y_d = 1; + } + sdl2::event::Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::Up), .. } => { + self.up_p = false; + } + sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Left), .. } => { + self.left_p = true; + self.x_d = -1; + } + sdl2::event::Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::Left), .. } => { + self.left_p = false; + } + sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Right), .. } => { + self.right_p = true; + self.x_d = 1; + } + sdl2::event::Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::Right), .. } => { + self.right_p = false; + } + _ => { }, + } + } + if self.left_p { + if !self.right_p { + self.x_d = -1; + } + } else if self.right_p { + self.x_d = 1; + } else { + self.x_d = 0; + } + if self.up_p { + if !self.down_p { + self.y_d = -1; + } + } else if self.down_p { + self.y_d = 1; + } else { + self.y_d = 0; + } + self.x += self.x_d; + self.y += self.y_d; + } +} + +impl state::Positioned for Player { + fn x(&self) -> i32 { + self.x + } + fn y(&self) -> i32 { + self.y + } + fn radius(&self) -> u32 { + 5 + } +} + +impl state::Drawable for Player { + fn color(&self) -> sdl2::pixels::Color { + sdl2::pixels::Color::RGB(0, 0, 0) + } +} diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..74ac73b --- /dev/null +++ b/src/state.rs @@ -0,0 +1,237 @@ +extern crate sdl2; +extern crate std; +extern crate rand; + +use super::player; +use super::constants; + +pub struct EBullet { + x: i32, + y: i32, + radius: u32, + real_x: f64, + real_y: f64, + d_x: f64, + d_y: f64, +} + +pub struct Enemy { + pub x: i32, + pub y: i32, + action_count: u64, + v_x: f64, + v_y: f64, + a_x: f64, + a_y: f64, + target_x: f64, + target_y: f64, + radius: u32, + pub health: i64, +} + +const STARTING_HEALTH: i64 = 100; + +impl EBullet { + pub fn new(x: i32, d_x: f64, y: i32, d_y: f64) -> EBullet { + EBullet { x: x, real_x: x as f64, y: y, real_y: y as f64, radius: 10, d_x: d_x, d_y: d_y } + } + + pub fn act(&mut self) { + self.real_x += self.d_x; + self.real_y += self.d_y; + self.x = self.real_x.round() as i32; + self.y = self.real_y.round() as i32; + } +} + +pub struct PBullet { + b: EBullet, +} + +impl PBullet { + pub fn new(x: i32, d_x: f64, y: i32, d_y: f64) -> PBullet { + PBullet { b: EBullet::new(x, d_x, y, d_y) } + } + pub fn act(&mut self) { + self.b.act() + } + pub fn delete(&mut self) { + self.b.x = -10; + } +} + +fn random_bullet(enemy: &Enemy) -> EBullet { + let multiplier = 2. + (STARTING_HEALTH as i64 - enemy.health) as f64 / STARTING_HEALTH as f64 * 2.; + let theta: f64 = rand::random::<f64>()*2.*std::f64::consts::PI/3. + std::f64::consts::PI/6.; + let dx = f64::cos(theta) * multiplier; + let dy = f64::sin(theta) * multiplier; + EBullet::new(enemy.x, dx, enemy.y, dy) +} + +fn random_position() -> (f64, f64) { + (rand::random() * (constants::X_DIM - 200) as f64 + 100., rand::random() * 200) +} + +impl Enemy { + pub fn new() -> Enemy { + Enemy { x: 200, y: 40, action_count: 1, v_x: 0., v_y: 0., a_x: 0., a_y: 0., radius: 30, health: STARTING_HEALTH, target_x: 200, target_y: 400 } + } + pub fn act(&mut self, count: u64) -> Vec<EBullet> { + let dx = self.target_x - self.x; + let dy = self.target_y - self.y; + /* Made it; pick a new destination */ + if f64::sqrt(dx*dx + dy*dy) < 10 { + let (self.target_x, self.target_y) = random_position(); + } + let vec_x = self.target_x - self.real_x; + let vec_y = self.target_y - self.real_y; + let len = f64::sqrt(vec_x*vec_x + vec_y*vec_y); + self.a_x = vec_x / len / 2.; + self.a_y = vec_y / len / 2.; + + self.v_x += self.a_x; + self.x = (self.x as f64 + self.v_x).round() as i32; + self.v_y += self.a_y; + self.y = (self.y as f64 + self.v_y).round() as i32; + if count % 20 == 0 { + let num_bullets; + if self.health >= 80 { + num_bullets = 4; + } else if self.health >= 50 { + num_bullets = 5; + } else if self.health >= 30 { + num_bullets = 6; + } else if self.health >= 20 { + num_bullets = 7; + } else { + num_bullets = 8; + } + let mut result = Vec::new(); + for _ in 0..num_bullets { + result.push(random_bullet(&*self)); + } + result + } else { + Vec::new() + } + } + pub fn health(&self) -> i64 { + self.health + } +} + +pub struct State { + pub player: player::Player, + pub enemies: Vec<Enemy>, + pub bullets: Vec<EBullet>, + pub player_bullets: Vec<PBullet>, + pub count: u64, +} + +impl State { + pub fn new() -> State { + State { player: player::Player::new(), enemies: Vec::new(), bullets: Vec::new(), count: 0, player_bullets: Vec::new() } + } +} + +fn dist(x1: i32, y1: i32, x2: i32, y2: i32) -> f64 { + let xdist = x1 - x2; + let ydist = y1 - y2; + f64::sqrt((xdist*xdist + ydist*ydist) as f64) +} + +pub trait Positioned { + fn x(&self) -> i32; + fn y(&self) -> i32; + fn radius(&self) -> u32; + fn contains(&self, x: i32, y: i32) -> bool { + let d = dist(self.x(), self.y(), x, y); + d <= self.radius() as f64 + } + fn overlaps<T: Positioned>(&self, other: &T) -> bool { + let dist = dist(self.x(), self.y(), other.x(), other.y()); + dist < (self.radius() + other.radius()) as f64 + } + fn outside(&self) -> bool { + let x = self.x(); + let y = self.y(); + x < 0 || x as u32 > constants::X_DIM || y < 0 || y as u32 > constants::Y_DIM + } +} + +impl Positioned for EBullet { + fn x(&self) -> i32 { + self.x + } + fn y(&self) -> i32 { + self.y + } + fn radius(&self) -> u32 { + self.radius + } +} + +impl Positioned for PBullet { + fn x(&self) -> i32 { + self.b.x() + } + fn y(&self) -> i32 { + self.b.y() + } + fn radius(&self) -> u32 { + self.b.radius() + } +} + +impl Positioned for Enemy { + fn x(&self) -> i32 { + self.x + } + fn y(&self) -> i32 { + self.y + } + fn radius(&self) -> u32 { + self.radius + } +} + +pub trait Drawable : Positioned { + fn color(&self) -> sdl2::pixels::Color; + fn draw(&self, canvas: &mut sdl2::render::WindowCanvas) { + let x = self.x(); + let y = self.y(); + let r = self.radius(); + let r = r as i32; + let x_min = x - r; + let x_max = x + r; + let y_min = y - r; + let y_max = y + r; + canvas.set_draw_color(self.color()); + for x in x_min..x_max + 1 { + for y in y_min..y_max + 1 { + if self.contains(x, y) { + canvas.fill_rect(sdl2::rect::Rect::new(x, y, 1, 1)).unwrap(); + } + } + } + } +} + +impl Drawable for Enemy { + fn color(&self) -> sdl2::pixels::Color { + let red_scale = self.health as f64 / STARTING_HEALTH as f64 * 255.; + sdl2::pixels::Color::RGB(red_scale as u8, 255 - red_scale as u8, 0) + } +} + +impl Drawable for EBullet { + fn color(&self) -> sdl2::pixels::Color { + sdl2::pixels::Color::RGB(0, 0, 255) + } +} + +impl Drawable for PBullet { + fn color(&self) -> sdl2::pixels::Color { + sdl2::pixels::Color::RGB(230, 255, 230) + } +} |
