--- /dev/null
+use rand::Rng;
+use sdl2::event::Event;
+use sdl2::EventPump;
+use sdl2::keyboard::Keycode;
+use sdl2::pixels::Color;
+use sdl2::render::BlendMode;
+use sdl2::render::Canvas;
+use sdl2::video::Window;
+
+use ::{SCREEN_HEIGHT, SCREEN_WIDTH};
+use boll::*;
+use common::Point2D;
+use sprites::SpriteManager;
+use NS_PER_FRAME;
+use point; // defined in common, but loaded from main...
+
+pub type Nanoseconds = u64;
+
+pub struct App {
+ pub canvas: Canvas<Window>,
+ pub event_pump: EventPump,
+ pub sprites: SpriteManager,
+ pub state: Box<dyn AppState>,
+}
+
+impl App {
+ pub fn new() -> App {
+ let context = sdl2::init().unwrap();
+ sdl2::image::init(sdl2::image::InitFlag::PNG).unwrap();
+ let window = context.video().unwrap().window("SDL test", SCREEN_WIDTH, SCREEN_HEIGHT)
+ .position_centered()
+ .opengl()
+ .build()
+ .unwrap();
+ context.mouse().show_cursor(false);
+ let mut canvas = window.into_canvas().build().unwrap();
+ canvas.set_blend_mode(BlendMode::Add);
+ canvas.set_draw_color(Color::RGB(0, 0, 0));
+ canvas.clear();
+ canvas.present();
+ let event_pump = context.event_pump().unwrap();
+ let sprites = SpriteManager::new(canvas.texture_creator());
+ App {
+ canvas,
+ event_pump,
+ sprites,
+ state: Box::new(ActiveState::new()),
+ }
+ }
+
+ pub fn load_sprites(&mut self, sprites: &[(&'static str, &str)]) {
+ for (name, file) in sprites {
+ self.sprites.load(name, file);
+ }
+ }
+}
+
+pub trait AppState {
+ fn update(&mut self, dt: Nanoseconds);
+ fn render(&self, canvas: &mut Canvas<Window>);
+ fn leave(&self);
+ fn on_event(&mut self, event: Event);
+}
+
+type Bollar = Vec<Box<dyn Boll>>;
+
+pub struct ActiveState {
+ bolls: Bollar,
+ boll_size: u32,
+}
+
+impl ActiveState {
+ fn new() -> ActiveState {
+ ActiveState {
+ bolls: Bollar::new(),
+ boll_size: 1,
+ }
+ }
+
+ fn change_boll_count(&mut self, delta: i32) {
+ if delta > 0 {
+ for _i in 0..delta {
+ self.add_boll();
+ }
+ } else if delta < 0 {
+ for _i in 0..delta {
+ self.bolls.pop();
+ }
+ }
+ }
+
+ fn add_boll(&mut self) {
+ let mut rng = rand::thread_rng();
+ self.bolls.push(Box::new(SquareBoll {
+ pos: point!(rng.gen_range(0, SCREEN_WIDTH) as f64, rng.gen_range(0, SCREEN_HEIGHT) as f64),
+ vel: point!(rng.gen_range(-2.0, 2.0), rng.gen_range(-2.0, 2.0)),
+ }));
+ }
+}
+
+impl AppState for ActiveState {
+ fn update(&mut self, dt: Nanoseconds) {
+ for b in &mut self.bolls {
+ b.update();
+ }
+
+ match dt {
+ ns if ns < (NS_PER_FRAME - 90_0000) as u64 => { self.change_boll_count(100) }
+ ns if ns > (NS_PER_FRAME + 90_0000) as u64 => { self.change_boll_count(-100) }
+ _ => {}
+ }
+ }
+
+ fn render(&self, canvas: &mut Canvas<Window>) {
+ for b in &self.bolls {
+ b.draw(canvas, self.boll_size);
+ }
+ }
+
+ fn leave(&self) {
+ println!("number of bolls: {}", self.bolls.len());
+ }
+
+ fn on_event(&mut self, event: Event) {
+ match event {
+ Event::KeyDown { keycode: Some(Keycode::KpPlus), .. } => { self.boll_size = std::cmp::min(self.boll_size + 1, 32) }
+ Event::KeyDown { keycode: Some(Keycode::KpMinus), .. } => { self.boll_size = std::cmp::max(self.boll_size - 1, 1) }
+ Event::MouseMotion { x, y, .. } => {
+ self.bolls.push(Box::new(CircleBoll::new(
+ point!(x as f64, y as f64),
+ point!(0.0, 0.0),
+ )))
+ }
+ _ => {}
+ }
+ }
+}