362af2ef4f209876129b45af3544cae2c3cd5459
[kaka/rust-sdl-test.git] / src / game / app.rs
1 use rand::Rng;
2 use sdl2::event::Event;
3 use sdl2::{EventPump, VideoSubsystem};
4 use sdl2::keyboard::Keycode;
5 use sdl2::pixels::Color;
6 use sdl2::render::BlendMode;
7 use sdl2::render::Canvas;
8 use sdl2::video::{SwapInterval, Window};
9
10 use ::{SCREEN_HEIGHT, SCREEN_WIDTH};
11 use boll::*;
12 use common::{Point2D, Rect};
13 use sprites::SpriteManager;
14 use NS_PER_FRAME;
15 use point; // defined in common, but loaded from main...
16
17 pub type Nanoseconds = u64;
18
19 #[derive(Default)]
20 pub struct AppBuilder {
21     resolution: Rect<u16>,
22     state: Option<Box<dyn AppState>>,
23     title: Option<String>,
24 }
25
26 impl AppBuilder {
27     pub fn with_resolution(mut self, width: u16, height: u16) -> Self {
28         self.resolution = Rect { width, height };
29         self
30     }
31
32     pub fn with_state(mut self, state: Box<dyn AppState>) -> Self {
33         self.state = Some(state);
34         self
35     }
36
37     pub fn with_title(mut self, title: &str) -> Self {
38         self.title = Some(title.to_string());
39         self
40     }
41
42     pub fn build(self) -> Result<App, String> {
43         let context = sdl2::init().unwrap();
44         sdl2::image::init(sdl2::image::InitFlag::PNG)?;
45         let video = context.video()?;
46
47         self.print_video_display_modes(&video);
48
49         let window = video
50             .window(&self.title.unwrap(), self.resolution.width.into(), self.resolution.height.into())
51             .position_centered()
52         // .fullscreen()
53         // .fullscreen_desktop()
54             .opengl()
55             .build().unwrap();
56         context.mouse().show_cursor(false);
57
58         let mut canvas = window.into_canvas().build().unwrap();
59         canvas.set_blend_mode(BlendMode::Add);
60         canvas.set_draw_color(Color::RGB(0, 0, 0));
61         canvas.clear();
62         canvas.present();
63
64         video.gl_set_swap_interval(SwapInterval::VSync)?;
65
66         let event_pump = context.event_pump()?;
67         let sprites = SpriteManager::new(canvas.texture_creator());
68
69         Ok(App {
70             canvas,
71             event_pump,
72             sprites,
73             state: self.state.unwrap_or(Box::new(ActiveState::new())),
74         })
75     }
76
77     fn print_video_display_modes(&self, video: &VideoSubsystem) {
78         println!("video subsystem: {:?}", video);
79         println!("current_video_driver: {:?}", video.current_video_driver());
80         for display in 0..video.num_video_displays().unwrap() {
81             println!("=== display {} - {} ===", display, video.display_name(display).unwrap());
82             println!(" display_bounds: {:?}", video.display_bounds(display).unwrap());
83             println!(" num_display_modes: {:?}", video.num_display_modes(display).unwrap());
84             println!(" desktop_display_mode: {:?}", video.desktop_display_mode(display).unwrap());
85             println!(" current_display_mode: {:?}", video.current_display_mode(display).unwrap());
86             for mode in 0..video.num_display_modes(display).unwrap() {
87                 println!("  {:2}: {:?}", mode, video.display_mode(display, mode).unwrap());
88             }
89         }
90         println!("swap interval: {:?}", video.gl_get_swap_interval());
91     }
92 }
93
94 pub struct App {
95     pub canvas: Canvas<Window>,
96     pub event_pump: EventPump,
97     pub sprites: SpriteManager,
98     pub state: Box<dyn AppState>,
99 }
100
101 impl App {
102     pub fn new() -> AppBuilder {
103         Default::default()
104     }
105
106     pub fn load_sprites(&mut self, sprites: &[(&str, &str)]) {
107         for (name, file) in sprites {
108             self.sprites.load(name, file);
109         }
110     }
111 }
112
113 pub trait AppState {
114     fn update(&mut self, dt: Nanoseconds);
115     fn render(&self, canvas: &mut Canvas<Window>);
116     fn leave(&self);
117     fn on_event(&mut self, event: Event);
118 }
119
120 type Bollar = Vec<Box<dyn Boll>>;
121
122 pub struct ActiveState {
123     bolls: Bollar,
124     boll_size: u32,
125 }
126
127 impl ActiveState {
128     pub fn new() -> ActiveState {
129         ActiveState {
130             bolls: Bollar::new(),
131             boll_size: 1,
132         }
133     }
134
135     fn change_boll_count(&mut self, delta: i32) {
136         if delta > 0 {
137             for _i in 0..delta {
138                 self.add_boll();
139             }
140         } else if delta < 0 {
141             for _i in 0..(-delta) {
142                 self.bolls.pop();
143             }
144         }
145     }
146
147     fn add_boll(&mut self) {
148         let mut rng = rand::thread_rng();
149         self.bolls.push(Box::new(SquareBoll {
150             pos: point!(rng.gen_range(0, SCREEN_WIDTH) as f64, rng.gen_range(0, SCREEN_HEIGHT) as f64),
151             vel: point!(rng.gen_range(-2.0, 2.0), rng.gen_range(-2.0, 2.0)),
152         }));
153     }
154 }
155
156 impl AppState for ActiveState {
157     fn update(&mut self, dt: Nanoseconds) {
158         for b in &mut self.bolls {
159             b.update();
160         }
161
162         match dt {
163             ns if ns < (NS_PER_FRAME - 90_0000) as u64 => { self.change_boll_count(100) }
164             ns if ns > (NS_PER_FRAME + 90_0000) as u64 => { self.change_boll_count(-100) }
165             _ => {}
166         }
167     }
168
169     fn render(&self, canvas: &mut Canvas<Window>) {
170         for b in &self.bolls {
171             b.draw(canvas, self.boll_size);
172         }
173     }
174
175     fn leave(&self) {
176         println!("number of bolls: {}", self.bolls.len());
177     }
178
179     fn on_event(&mut self, event: Event) {
180         match event {
181             Event::KeyDown { keycode: Some(Keycode::KpPlus), .. } => { self.boll_size = std::cmp::min(self.boll_size + 1, 32) }
182             Event::KeyDown { keycode: Some(Keycode::KpMinus), .. } => { self.boll_size = std::cmp::max(self.boll_size - 1, 1) }
183             Event::MouseMotion { x, y, .. } => {
184                 self.bolls.push(Box::new(CircleBoll::new(
185                     point!(x as f64, y as f64),
186                     point!(0.0, 0.0),
187                 )))
188             }
189             _ => {}
190         }
191     }
192 }