mod constants; mod gates; mod player; mod state; use state::Bullet; use state::Drawable; use state::Positioned; enum FResult { Win, Loss, Nothing, Exit, } struct Textures<'a> { player: sdl2::render::Texture<'a>, enemy: sdl2::render::Texture<'a>, pbullet: sdl2::render::Texture<'a>, ebullet: sdl2::render::Texture<'a>, } const DURATION: u32 = 1_000_000_000u32 / 60; fn frame, U: state::Bullet>( canvas: &mut sdl2::render::WindowCanvas, state: &mut state::State, events: &mut sdl2::event::EventPollIterator, textures: &Textures, ) -> FResult { 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(); } match state.player.act(events) { Err(_) => { return FResult::Exit; } Ok(_) => {} } if count % state.player.fire_rate as u64 == 0 { state.player_bullets.push(state::PBullet::new( state.player.x, 0., state.player.y, -3.5, )); } if state .bullets .iter() .any(|bullet| state.player.overlaps(bullet)) || new_bullets .iter() .any(|bullet| state.player.overlaps(bullet)) { return FResult::Loss; } for enemy in state.enemies.iter_mut() { for bullet in state.player_bullets.iter_mut() { if enemy.overlaps(bullet) { enemy.injure(state::PBullet::damage()); bullet.delete(); } } } state .enemies .retain(|enemy| state::Enemy::health(enemy) > 0.); if state.enemies.len() == 0 { return FResult::Win; } state.bullets.retain(|bullet| !U::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, &textures.pbullet); } for enemy in state.enemies.iter() { enemy.draw(canvas, &textures.enemy); } state.player.draw(canvas, &textures.player); for bullet in state.bullets.iter() { bullet.draw(canvas, &textures.ebullet); } canvas.set_draw_color(sdl2::pixels::Color::RGB(255, 0, 0)); canvas .draw_rect(sdl2::rect::Rect::new( 0, 0, (state.enemies[0].health() * constants::X_DIM as f64).round() as u32, 20, )) .unwrap(); state.count += 1; FResult::Nothing } fn get_surface(bytes: &[u8]) -> sdl2::surface::Surface { (unsafe { let rw = sdl2_sys::SDL_RWFromMem( bytes.as_ptr() as *mut std::os::raw::c_void, bytes.len() as i32, ); let raw = sdl2_sys::image::IMG_Load_RW(rw, 1); if (raw as *mut ()).is_null() { Err("Returned null") } else { Ok(sdl2::surface::Surface::from_ll(raw)) } }) .unwrap() } fn get_player_surface() -> sdl2::surface::Surface<'static> { get_surface(include_bytes!("assets/stallman.png")) } fn get_enemy_surface() -> sdl2::surface::Surface<'static> { get_surface(include_bytes!("assets/gates.png")) } fn get_ebullet_surface() -> sdl2::surface::Surface<'static> { get_surface(include_bytes!("assets/binary_blob.png")) } fn get_pbullet_surface() -> sdl2::surface::Surface<'static> { get_surface(include_bytes!("assets/player_pepp.png")) } fn generic_message( canvas: &mut sdl2::render::WindowCanvas, event_pump: &mut sdl2::EventPump, image: &[u8], okay: sdl2::keyboard::Keycode, error: sdl2::keyboard::Keycode, ) -> Result<(), ()> { let surface = get_surface(image); let texture_creator = canvas.texture_creator(); let texture = texture_creator .create_texture_from_surface(surface) .unwrap(); let frame_duration = std::time::Duration::new(0, DURATION); loop { let start = std::time::Instant::now(); canvas.copy(&texture, None, None).unwrap(); canvas.present(); for event in event_pump.poll_iter() { match event { sdl2::event::Event::KeyDown { keycode: Some(keycode), .. } => { if keycode == okay { return Ok(()); } if keycode == error { return Err(()); } } sdl2::event::Event::Quit { .. } => { return Err(()); } _ => {} } } match frame_duration.checked_sub(start.elapsed()) { Some(t) => std::thread::sleep(t), None => (), } } } fn introduction( canvas: &mut sdl2::render::WindowCanvas, event_pump: &mut sdl2::EventPump, ) -> Result<(), ()> { return generic_message( canvas, event_pump, include_bytes!("assets/introduction.png"), sdl2::keyboard::Keycode::Return, sdl2::keyboard::Keycode::Escape, ); } fn loss( canvas: &mut sdl2::render::WindowCanvas, event_pump: &mut sdl2::EventPump, ) -> Result<(), ()> { return generic_message( canvas, event_pump, include_bytes!("assets/loss.png"), sdl2::keyboard::Keycode::R, sdl2::keyboard::Keycode::Escape, ); } fn victory( canvas: &mut sdl2::render::WindowCanvas, event_pump: &mut sdl2::EventPump, ) -> Result<(), ()> { return generic_message( canvas, event_pump, include_bytes!("assets/victory.png"), sdl2::keyboard::Keycode::R, sdl2::keyboard::Keycode::Escape, ); } 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(); let mut event_pump = sdl_context.event_pump().unwrap(); match introduction(&mut canvas, &mut event_pump) { Ok(_) => {} Err(_) => { return; } } 'retry: loop { let mut state: state::State = state::State::new(); let player_surface = get_player_surface(); let enemy_surface = get_enemy_surface(); let ebullet_surface = get_ebullet_surface(); let pbullet_surface = get_pbullet_surface(); let texture_creator = canvas.texture_creator(); let player_texture = texture_creator .create_texture_from_surface(player_surface) .unwrap(); let enemy_texture = texture_creator .create_texture_from_surface(enemy_surface) .unwrap(); let ebullet_texture = texture_creator .create_texture_from_surface(ebullet_surface) .unwrap(); let pbullet_texture = texture_creator .create_texture_from_surface(pbullet_surface) .unwrap(); let surfaces = Textures { player: player_texture, enemy: enemy_texture, pbullet: pbullet_texture, ebullet: ebullet_texture, }; let frame_duration = std::time::Duration::new(0, DURATION); loop { let start = std::time::Instant::now(); canvas.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255)); canvas.clear(); match frame( &mut canvas, &mut state, &mut event_pump.poll_iter(), &surfaces, ) { FResult::Loss => match loss(&mut canvas, &mut event_pump) { Ok(_) => { break; } Err(_) => { break 'retry; } }, FResult::Win => match victory(&mut canvas, &mut event_pump) { Ok(_) => { break; } Err(_) => { break 'retry; } }, FResult::Exit => { break 'retry; } FResult::Nothing => { canvas.present(); } } /* Make sure this iteration took a whole frame */ match frame_duration.checked_sub(start.elapsed()) { Some(t) => std::thread::sleep(t), None => (), } } } }