Move boll handling and rendering into an app state
[kaka/rust-sdl-test.git] / src / app.rs
1 use rand::Rng;
2 use sdl2::event::Event;
3 use sdl2::EventPump;
4 use sdl2::keyboard::Keycode;
5 use sdl2::pixels::Color;
6 use sdl2::render::BlendMode;
7 use sdl2::render::Canvas;
8 use sdl2::video::Window;
9
10 use {SCREEN_HEIGHT, SCREEN_WIDTH};
11 use boll::*;
12 use common::Point2D;
13 use sprites::SpriteManager;
14 use NS_PER_FRAME;
15
16 macro_rules! point { // because I don't know how to import it from common.rs ...
17     ( $x:expr, $y:expr ) => { Point2D { x:$x, y:$y } };
18 }
19
20 pub type Nanoseconds = u64;
21
22 pub struct App {
23     pub canvas: Canvas<Window>,
24     pub event_pump: EventPump,
25     pub sprites: SpriteManager,
26     pub state: Box<AppState>,
27 }
28
29 impl App {
30     pub fn new() -> App {
31         let context = sdl2::init().unwrap();
32         sdl2::image::init(sdl2::image::InitFlag::PNG).unwrap();
33         let window = context.video().unwrap().window("SDL test", SCREEN_WIDTH, SCREEN_HEIGHT)
34             .position_centered()
35             .opengl()
36             .build()
37             .unwrap();
38         context.mouse().show_cursor(false);
39         let mut canvas = window.into_canvas().build().unwrap();
40         canvas.set_blend_mode(BlendMode::Add);
41         canvas.set_draw_color(Color::RGB(0, 0, 0));
42         canvas.clear();
43         canvas.present();
44         let event_pump = context.event_pump().unwrap();
45         let sprites = SpriteManager::new(canvas.texture_creator());
46         App {
47             canvas,
48             event_pump,
49             sprites,
50             state: Box::new(ActiveState::new()),
51         }
52     }
53
54     pub fn load_sprites(&mut self, sprites: &[(&'static str, &str)]) {
55         for (name, file) in sprites {
56             self.sprites.load(name, file);
57         }
58     }
59 }
60
61 pub trait AppState {
62     fn update(&mut self, dt: Nanoseconds);
63     fn render(&self, canvas: &mut Canvas<Window>);
64     fn leave(&self);
65     fn on_event(&mut self, event: Event);
66 }
67
68 type Bollar = Vec<Box<dyn Boll>>;
69
70 pub struct ActiveState {
71     bolls: Bollar,
72     boll_size: u32,
73 }
74
75 impl ActiveState {
76     fn new() -> ActiveState {
77         ActiveState {
78             bolls: Bollar::new(),
79             boll_size: 1,
80         }
81     }
82
83     fn change_boll_count(&mut self, delta: i32) {
84         if delta > 0 {
85             for _i in 0..delta {
86                 self.add_boll();
87             }
88         } else if delta < 0 {
89             for _i in 0..delta {
90                 self.bolls.pop();
91             }
92         }
93     }
94
95     fn add_boll(&mut self) {
96         let mut rng = rand::thread_rng();
97         self.bolls.push(Box::new(SquareBoll {
98             pos: point!(rng.gen_range(0, SCREEN_WIDTH) as f64, rng.gen_range(0, SCREEN_HEIGHT) as f64),
99             vel: point!(rng.gen_range(-2.0, 2.0), rng.gen_range(-2.0, 2.0)),
100         }));
101     }
102 }
103
104 impl AppState for ActiveState {
105     fn update(&mut self, dt: Nanoseconds) {
106         for mut b in &mut self.bolls {
107             b.update();
108         }
109
110         match dt {
111             ns if ns < (NS_PER_FRAME - 90_0000) as u64 => { self.change_boll_count(100) }
112             ns if ns > (NS_PER_FRAME + 90_0000) as u64 => { self.change_boll_count(-100) }
113             _ => {}
114         }
115     }
116
117     fn render(&self, canvas: &mut Canvas<Window>) {
118         for mut b in &self.bolls {
119             b.draw(canvas, self.boll_size);
120         }
121     }
122
123     fn leave(&self) {
124         println!("number of bolls: {}", self.bolls.len());
125     }
126
127     fn on_event(&mut self, event: Event) {
128         match event {
129             Event::KeyDown { keycode: Some(Keycode::KpPlus), .. } => { self.boll_size = std::cmp::min(self.boll_size + 1, 32) }
130             Event::KeyDown { keycode: Some(Keycode::KpMinus), .. } => { self.boll_size = std::cmp::max(self.boll_size - 1, 1) }
131             Event::MouseMotion { x, y, .. } => {
132                 self.bolls.push(Box::new(CircleBoll::new(
133                     point!(x as f64, y as f64),
134                     point!(0.0, 0.0),
135                 )))
136             }
137             _ => {}
138         }
139     }
140 }