Made the wasm build work again
[bug-basher.git] / src / main.rs
1 #[macro_use]
2 extern crate gate;
3
4 gate_header!();
5
6 use gate::{App, AppContext, AppInfo, KeyCode};
7 use gate::renderer::{Renderer, Affine};
8
9 extern crate rand;
10
11 use rand::distributions::IndependentSample;
12 use rand::Isaac64Rng;
13 use std::f64::consts::PI;
14 use std::f64::consts::FRAC_PI_2;
15
16 mod asset_id { include!(concat!(env!("OUT_DIR"), "/asset_id.rs")); }
17 use asset_id::*;
18
19 mod geometry;
20 use geometry::*;
21     
22 mod hitbox;
23 use hitbox::*;
24
25 mod entities;
26 use entities::bug::Bug;
27 use entities::home::Home;
28
29 struct BugBasherGame {
30     rng: Isaac64Rng,
31     bugs: Vec<Bug>,
32     home: Home,
33     points: i64,
34     lives: i64,
35     game_over: bool,
36     time_to_next_bug: f64,
37     total_time: f64,
38     camera: Affine
39 }
40
41 impl App<AssetId> for BugBasherGame {
42     fn start(&mut self, _ctx: &mut AppContext<AssetId>) {
43     }
44
45     fn advance(&mut self, seconds: f64, _ctx: &mut AppContext<AssetId>) {
46         if !self.game_over {
47             self.bugs.retain(|b| b.alive);
48             for bug in self.bugs.iter_mut() {
49                 bug.advance(seconds);
50                 if self.home.touches_circle(bug) {
51                     bug.alive = false;
52                     self.lives -= 1;
53                 }
54             }
55             self.home.advance(seconds);
56             if self.lives <= 0 {
57                 self.game_over = true;
58             }
59         
60             self.time_to_next_bug -= seconds;
61             self.total_time += seconds;
62             
63             if self.time_to_next_bug <= 0. {
64                 let mean = if self.total_time < 30. {
65                     f64::max(4. - (self.total_time as f64 * 0.2), 1.)
66                 } else if self.total_time < 60. {
67                     f64::max(1. - ((self.total_time as f64 - 30.) * 0.05), 0.5)
68                 } else if self.total_time < 90. {
69                     f64::max(0.5 - ((self.total_time as f64 - 60.) * 0.05), 0.3)
70                 } else {
71                     f64::max(0.3 - ((self.total_time as f64 - 90.) * 0.005), 0.2)
72                 };
73                 
74                 let sd = mean / 3.;
75                 let time_dist = rand::distributions::Normal::new(mean, sd);
76                 self.time_to_next_bug = time_dist.ind_sample(&mut self.rng);
77
78                 let angle_dist = rand::distributions::Range::new(0., 2.*PI);
79                 let angle = angle_dist.ind_sample(&mut self.rng);
80                 self.bugs.push(Bug::new(
81                     angle.cos()*1000.,
82                     angle.sin()*1000.
83                 ));
84                 
85             }
86         }
87     }
88
89     fn key_down(&mut self, key: KeyCode, ctx: &mut AppContext<AssetId>) {
90         let (x, y) = self.camera.invert_translate().apply_f64(ctx.cursor());
91         match key {
92             KeyCode::MouseLeft => {
93                 for bug in self.bugs.iter_mut().filter(|bug| bug.touches_point(Vec2d { x, y })) {
94                     if !self.game_over && bug.alive == true {
95                         self.points += 1;
96                     }
97
98                     bug.alive = false;
99                 }
100             },
101             KeyCode::Return => {
102                 self.reset();
103             },
104             _ => {}
105         }
106     }
107
108     fn render(&mut self, renderer: &mut Renderer<AssetId>, ctx: &AppContext<AssetId>) {
109         let (app_width, app_height) = ctx.dims();
110         let (cursor_x, cursor_y) = ctx.cursor();
111         self.camera = Affine::translate(app_width/2., app_height/2.);
112         
113         renderer.clear((255,255,255));
114         {
115             let points_str = format!("{}", self.points);
116             let lives_str = format!("{}", self.lives);
117             BugBasherGame::print_string(renderer, &points_str, Alignment::Left, 50., app_height - 50.);
118             BugBasherGame::print_string(renderer, &lives_str, Alignment::Right, app_width - 50., app_height - 50.);
119         }
120         {
121             let mut renderer = renderer.sprite_mode();
122             renderer.draw(
123                 &self.camera.post_translate(self.home.pos.x, self.home.pos.y),
124                 self.home.sprite
125             );
126             for bug in &self.bugs {
127                 let affine = if bug.rotation <= FRAC_PI_2 && bug.rotation >= -FRAC_PI_2 {
128                     self.camera.post_translate(bug.pos.x, bug.pos.y).pre_rotate(bug.rotation)
129                 } else {
130                     self.camera.post_translate(bug.pos.x, bug.pos.y).pre_rotate(bug.rotation).pre_scale_axes(1., -1.)
131                 };
132                 
133                 renderer.draw(
134                     &affine,
135                     SpriteId::Pom
136                 );
137             }
138         }
139         
140         if self.game_over {
141             {
142                 let mut renderer = renderer.sprite_mode();
143                 renderer.draw(
144                     &self.camera,
145                     SpriteId::Gameover
146                 );
147             }
148             {
149                 let points_str = format!("{}", self.points);
150                 BugBasherGame::print_string(renderer, &points_str, Alignment::Center, app_width/2., app_height/2.-25.);
151             }
152         }
153
154         {
155             let mut renderer = renderer.sprite_mode();
156             renderer.draw(
157                 &Affine::translate(cursor_x, cursor_y),
158                 SpriteId::Cursor
159             );
160         }
161     }
162 }
163
164 enum Alignment {
165     Left,
166     Right,
167     Center
168 }
169
170 impl BugBasherGame {
171     fn new() -> BugBasherGame {
172         let mut game = BugBasherGame {
173             rng: Isaac64Rng::new_unseeded(),
174             home: Home::new(0., 0.),
175             bugs: Vec::with_capacity(1000),
176             points: 0,
177             lives: 0,
178             game_over: true,
179             time_to_next_bug: 0.,
180             total_time: 0.,
181             camera: Affine::id()
182         };
183         game.reset();
184         game
185     }
186
187     fn reset(&mut self) {
188         self.bugs = Vec::with_capacity(1000);
189         self.points = 0;
190         self.lives = 3;
191         self.game_over = false;
192         self.time_to_next_bug = 0.;
193         self.total_time = 0.;
194     }
195
196     fn print_string(renderer: &mut Renderer<AssetId>, string: &str, alignment: Alignment, x: f64, y: f64) {
197         let letter_spacing = 45.;
198         let left = match alignment {
199             Alignment::Left => x,
200             Alignment::Right => x - string.len() as f64 * letter_spacing,
201             Alignment::Center => x - string.len() as f64 * letter_spacing / 2.
202         };
203         
204         let mut renderer = renderer.sprite_mode();
205         for (i, c) in string.chars().enumerate() {
206             let affine = Affine::translate(left + i as f64 * letter_spacing, y);
207             let tile = match c {
208                 '-' => SpriteId::NumberFontR0C0,
209                 '0' => SpriteId::NumberFontR0C1,
210                 '1' => SpriteId::NumberFontR0C2,
211                 '2' => SpriteId::NumberFontR0C3,
212                 '3' => SpriteId::NumberFontR0C4,
213                 '4' => SpriteId::NumberFontR0C5,
214                 '5' => SpriteId::NumberFontR0C6,
215                 '6' => SpriteId::NumberFontR0C7,
216                 '7' => SpriteId::NumberFontR0C8,
217                 '8' => SpriteId::NumberFontR0C9,
218                 '9' => SpriteId::NumberFontR0C10,
219                 _ => SpriteId::NumberFontR0C0,
220             };
221             renderer.draw(&affine, tile);
222         };
223     }
224 }
225
226 fn main() {
227     let info = AppInfo::with_max_dims(2000., 1000.)
228         .tile_width(16)
229         .title("Nap Attack");
230     gate::run(info, BugBasherGame::new());
231 }