b37e8fd987d5da63f7a6a066d1cb15ab46ea26b9
[kaka/rust-sdl-test.git] / src / game / 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, 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     fps: u8,
23     state: Option<Box<dyn AppState>>,
24     title: Option<String>,
25 }
26
27 impl AppBuilder {
28     pub fn with_resolution(mut self, width: u16, height: u16) -> Self {
29         self.resolution = Rect { width, height };
30         self
31     }
32
33     pub fn with_fps(mut self, fps: u8) -> Self {
34         unimplemented!()
35         // self.fps = fps;
36         // self
37     }
38
39     pub fn with_state(mut self, state: Box<dyn AppState>) -> Self {
40         self.state = Some(state);
41         self
42     }
43
44     pub fn with_title(mut self, title: &str) -> Self {
45         self.title = Some(title.to_string());
46         self
47     }
48
49     pub fn start(self) -> App {
50         let context = sdl2::init().unwrap();
51         sdl2::image::init(sdl2::image::InitFlag::PNG).unwrap();
52         let window = context
53             .video().unwrap()
54             .window(&self.title.unwrap(), self.resolution.width.into(), self.resolution.height.into())
55             .position_centered()
56             .opengl()
57             .build().unwrap();
58         context.mouse().show_cursor(false);
59
60         let mut canvas = window.into_canvas().build().unwrap();
61         canvas.set_blend_mode(BlendMode::Add);
62         canvas.set_draw_color(Color::RGB(0, 0, 0));
63         canvas.clear();
64         canvas.present();
65
66         let event_pump = context.event_pump().unwrap();
67         let sprites = SpriteManager::new(canvas.texture_creator());
68
69         App {
70             canvas,
71             event_pump,
72             sprites,
73             state: self.state.unwrap_or(Box::new(ActiveState::new())),
74         }
75     }
76 }
77
78 pub struct App {
79     pub canvas: Canvas<Window>,
80     pub event_pump: EventPump,
81     pub sprites: SpriteManager,
82     pub state: Box<dyn AppState>,
83 }
84
85 impl App {
86     pub fn new() -> AppBuilder {
87         Default::default()
88     }
89
90     pub fn load_sprites(&mut self, sprites: &[(&str, &str)]) {
91         for (name, file) in sprites {
92             self.sprites.load(name, file);
93         }
94     }
95 }
96
97 pub trait AppState {
98     fn update(&mut self, dt: Nanoseconds);
99     fn render(&self, canvas: &mut Canvas<Window>);
100     fn leave(&self);
101     fn on_event(&mut self, event: Event);
102 }
103
104 type Bollar = Vec<Box<dyn Boll>>;
105
106 pub struct ActiveState {
107     bolls: Bollar,
108     boll_size: u32,
109 }
110
111 impl ActiveState {
112     pub fn new() -> ActiveState {
113         ActiveState {
114             bolls: Bollar::new(),
115             boll_size: 1,
116         }
117     }
118
119     fn change_boll_count(&mut self, delta: i32) {
120         if delta > 0 {
121             for _i in 0..delta {
122                 self.add_boll();
123             }
124         } else if delta < 0 {
125             for _i in 0..(-delta) {
126                 self.bolls.pop();
127             }
128         }
129     }
130
131     fn add_boll(&mut self) {
132         let mut rng = rand::thread_rng();
133         self.bolls.push(Box::new(SquareBoll {
134             pos: point!(rng.gen_range(0, SCREEN_WIDTH) as f64, rng.gen_range(0, SCREEN_HEIGHT) as f64),
135             vel: point!(rng.gen_range(-2.0, 2.0), rng.gen_range(-2.0, 2.0)),
136         }));
137     }
138 }
139
140 impl AppState for ActiveState {
141     fn update(&mut self, dt: Nanoseconds) {
142         for b in &mut self.bolls {
143             b.update();
144         }
145
146         match dt {
147             ns if ns < (NS_PER_FRAME - 90_0000) as u64 => { self.change_boll_count(100) }
148             ns if ns > (NS_PER_FRAME + 90_0000) as u64 => { self.change_boll_count(-100) }
149             _ => {}
150         }
151     }
152
153     fn render(&self, canvas: &mut Canvas<Window>) {
154         for b in &self.bolls {
155             b.draw(canvas, self.boll_size);
156         }
157     }
158
159     fn leave(&self) {
160         println!("number of bolls: {}", self.bolls.len());
161     }
162
163     fn on_event(&mut self, event: Event) {
164         match event {
165             Event::KeyDown { keycode: Some(Keycode::KpPlus), .. } => { self.boll_size = std::cmp::min(self.boll_size + 1, 32) }
166             Event::KeyDown { keycode: Some(Keycode::KpMinus), .. } => { self.boll_size = std::cmp::max(self.boll_size - 1, 1) }
167             Event::MouseMotion { x, y, .. } => {
168                 self.bolls.push(Box::new(CircleBoll::new(
169                     point!(x as f64, y as f64),
170                     point!(0.0, 0.0),
171                 )))
172             }
173             _ => {}
174         }
175     }
176 }