Added game over popup
[bug-basher.git] / src / main.rs
1 extern crate gate;
2
3 use gate::{App, Audio};
4 use gate::app_info::AppInfo;
5 use gate::input::*;
6 use gate::renderer::{Renderer, Affine};
7
8 extern crate rand;
9
10 use rand::distributions::IndependentSample;
11 use rand::Isaac64Rng;
12 use std::f64::consts::PI;
13                           
14 mod asset_id { include!(concat!(env!("OUT_DIR"), "/asset_id.rs")); }
15 use asset_id::*;
16
17 mod geometry;
18 use geometry::*;
19
20 mod hitbox;
21 use hitbox::*;
22
23 mod entities;
24 use entities::bug::Bug;
25 use entities::home::Home;
26
27 struct BugBasherGame {
28     rng: Isaac64Rng,
29     bugs: Vec<Bug>,
30     home: Home,
31     points: i64,
32     lives: i64,
33     game_over: bool,
34     time_to_next_bug: f64
35 }
36
37 impl App<AssetId> for BugBasherGame {
38     fn start(&mut self, _audio: &mut Audio<AssetId>) {
39     }
40
41     fn advance(&mut self, seconds: f64, _audio: &mut Audio<AssetId>) -> bool {
42         if !self.game_over {
43             self.bugs.retain(|b| b.alive);
44             for bug in self.bugs.iter_mut() {
45                 bug.advance(seconds);
46                 if self.home.touches_circle(bug) {
47                     bug.alive = false;
48                     self.lives -= 1;
49                 }
50             }
51             if self.lives <= 0 {
52                 self.game_over = true;
53             }
54         
55             self.time_to_next_bug -= seconds;
56             if self.time_to_next_bug <= 0. {
57                 let time_dist = rand::distributions::Normal::new(2.0, 1.0);
58                 self.time_to_next_bug = time_dist.ind_sample(&mut self.rng);
59
60                 let angle_dist = rand::distributions::Range::new(0., 2.*PI);
61                 let angle = angle_dist.ind_sample(&mut self.rng);
62                 self.bugs.push(Bug::new(
63                     angle.cos()*1000.,
64                     angle.sin()*1000.,
65                     angle + PI
66                 ));
67                 
68             }
69         }
70         
71         true
72     }
73
74     fn input(&mut self, evt: InputEvent, _audio: &mut Audio<AssetId>) -> bool {
75         match evt {
76             InputEvent::MousePressed(MouseButton::Left, x, y) => {
77                 for bug in self.bugs.iter_mut().filter(|bug| bug.touches_point(Vec2d { x, y })) {
78                     if !self.game_over && bug.alive == true {
79                         self.points += 1;
80                     }
81
82                     bug.alive = false;
83                 }
84             },
85             InputEvent::KeyPressed(KeyCode::Return) => {
86                 self.reset();
87             },
88             _ => {}
89         }
90         true
91     }
92
93     fn render(&mut self, renderer: &mut Renderer<AssetId>) {
94         let (app_width, app_height) = (renderer.app_width(), renderer.app_height());
95         renderer.clear((255,255,255));
96         {
97             let points_str = format!("{}", self.points);
98             let lives_str = format!("{}", self.lives);
99             BugBasherGame::print_string(renderer, &points_str, Alignment::Left, - app_width / 2. + 50., app_height / 2. - 50.);
100             BugBasherGame::print_string(renderer, &lives_str, Alignment::Right, app_width / 2. - 50., app_height / 2. - 50.);
101         }
102         {
103             let mut renderer = renderer.sprite_mode();
104             renderer.draw(
105                 &Affine::translate(self.home.pos.x, self.home.pos.y),
106                 SpriteId::Home
107             );
108             for bug in &self.bugs {
109                 renderer.draw(
110                     &Affine::translate(bug.pos.x, bug.pos.y).pre_rotate(bug.rotation),
111                     SpriteId::Bug
112                 );
113             }
114         }
115         
116         if self.game_over {
117             {
118                 let mut renderer = renderer.sprite_mode();
119                 renderer.draw(
120                     &Affine::translate(0.,0.),
121                     SpriteId::Gameover
122                 );
123             }
124             {
125                 let points_str = format!("{}", self.points);
126                 BugBasherGame::print_string(renderer, &points_str, Alignment::Center, 0., -25.);
127             }
128         }
129     }
130 }
131
132 enum Alignment {
133     Left,
134     Right,
135     Center
136 }
137
138 impl BugBasherGame {
139     fn new() -> BugBasherGame {
140         let mut game = BugBasherGame {
141             rng: Isaac64Rng::new_unseeded(),
142             home: Home::new(0., 0.),
143             bugs: Vec::new(),
144             points: 0,
145             lives: 0,
146             game_over: true,
147             time_to_next_bug: 0.
148         };
149         game.reset();
150         game
151     }
152
153     fn reset(&mut self) {
154         self.bugs = Vec::new();
155         self.points = 0;
156         self.lives = 3;
157         self.game_over = false;
158     }
159
160     fn print_string(renderer: &mut Renderer<AssetId>, string: &str, alignment: Alignment, x: f64, y: f64) {
161         let letter_spacing = 25.;
162         let left = match alignment {
163             Alignment::Left => x,
164             Alignment::Right => x - string.len() as f64 * letter_spacing,
165             Alignment::Center => x - string.len() as f64 * letter_spacing / 2.
166         };
167         
168         let mut renderer = renderer.tiled_mode(-left, -y);
169
170         for (i, c) in string.chars().enumerate() {
171             let affine = Affine::translate(i as f64 * letter_spacing, 0.);
172             let tile = match c {
173                 '-' => TileId::NumberFontR0C0,
174                 '0' => TileId::NumberFontR0C1,
175                 '1' => TileId::NumberFontR0C2,
176                 '2' => TileId::NumberFontR0C3,
177                 '3' => TileId::NumberFontR0C4,
178                 '4' => TileId::NumberFontR0C5,
179                 '5' => TileId::NumberFontR0C6,
180                 '6' => TileId::NumberFontR0C7,
181                 '7' => TileId::NumberFontR0C8,
182                 '8' => TileId::NumberFontR0C9,
183                 '9' => TileId::NumberFontR0C10,
184                 _ => TileId::NumberFontR0C0,
185             };
186             renderer.draw(&affine, tile);
187         };
188     }
189 }
190
191 fn main() {
192     let info = AppInfo::with_app_height(1000.).title("Bug Basher").build();
193     gate::run(info, BugBasherGame::new());
194 }