Browse Source

GameBridge->Bridge, Game->Harbour, State->Game

Inderjit Gill 1 year ago
parent
commit
19e27025d5
5 changed files with 783 additions and 779 deletions
  1. 629
    63
      src/game.rs
  2. 107
    0
      src/harbour.rs
  3. 28
    26
      src/lib.rs
  4. 0
    671
      src/state.rs
  5. 19
    19
      web/js/index.js

+ 629
- 63
src/game.rs View File

@@ -1,105 +1,671 @@
1 1
 use log;
2
-use controller::{Controller, ControllerButton, ControllerAction};
3
-use state::State;
2
+use std::collections::HashMap;
3
+use units::*;
4
+use sprite::Sprite;
5
+use controller::Controller;
4 6
 use geometry::Geometry;
5 7
 
6
-#[derive(PartialEq)]
7
-pub enum GameMode {
8
-    Playing,
9
-    Paused,
8
+#[derive(Clone, Copy)]
9
+pub struct Block {
10
+    active: bool,
11
+    offset: Vec2D
12
+}
13
+
14
+impl Block {
15
+    pub fn new() -> Block {
16
+        Block {
17
+            active: false,
18
+            offset: Vec2D {x: 0.0, y: 0.0 },
19
+        }
20
+    }
21
+}
22
+
23
+pub struct Board {
24
+    board: Vec<Vec<Block>>,
25
+    width: usize,
26
+    height: usize,
27
+}
28
+
29
+impl Board {
30
+    pub fn new(width: usize, height: usize) -> Board {
31
+        Board {
32
+            board: vec![vec![Block::new(); width]; height],
33
+            width,
34
+            height,
35
+        }
36
+    }
37
+}
38
+
39
+#[derive(Debug, Clone, Copy)]
40
+pub struct BoardPos {
41
+    pub x: i32,
42
+    pub y: i32,
43
+}
44
+
45
+#[derive(Clone, Copy, Hash, Eq, PartialEq)]
46
+enum ShapeKind {
47
+    I,
48
+    J,
49
+    L,
50
+    O,
51
+    S,
52
+    T,
53
+    Z,
54
+}
55
+
56
+pub struct Shape {
57
+    aabb_width: i32,
58
+    aabb_height: i32,
59
+    real_height: i32,
60
+    fully_rotate: bool,
61
+    form: Vec<Vec<bool>>,
62
+}
63
+
64
+enum PieceAngle {
65
+    R0,
66
+    R90,
67
+    R180,
68
+    R270,
69
+}
70
+
71
+pub struct Piece {
72
+    shape_kind: ShapeKind,
73
+    pos: BoardPos,
74
+    angle: PieceAngle,
10 75
 }
11 76
 
12 77
 pub struct Game {
13
-    delta: f32,
78
+    board: Board,
79
+    board_offset: Block2D,
80
+
81
+    shapes: HashMap<ShapeKind, Shape>,
82
+    piece: Option<Piece>,               // the current piece under player control
83
+    next_shape: ShapeKind,
84
+
85
+    score: i32,
86
+    level: i32,
87
+    lines: i32,
14 88
 
15
-    mode: GameMode,
16
-    resume_from_paused: bool,
17 89
 
18
-    controller: Controller,
19
-    state: State,
20
-    geometry: Geometry,
90
+    time_to_next_lowering: f32,     // ms
91
+    piece_is_dropping: bool,
92
+
93
+    default_cooldown: f32,              // ms
94
+    left_cooldown: f32,
95
+    right_cooldown: f32,
96
+    up_cooldown: f32,
97
+    down_cooldown: f32,
98
+    a_cooldown: f32,
99
+    b_cooldown: f32,
100
+
101
+    terrible_rng: i32,
21 102
 }
22 103
 
23 104
 impl Game {
24
-    pub fn new(
25
-        canvas_width: i32,
26
-        canvas_height: i32,
27
-        block_size: i32,
28
-        tileset_texture_width: i32,  // tileset texture width
29
-        tileset_texture_height: i32, // tileset texture height
30
-    ) -> Game {
105
+    pub fn new() -> Game {
31 106
         Game {
32
-            delta: 0.0,
107
+            board: Board::new(10, 20),
108
+            board_offset: Block2D { x: 5, y: 5 },
109
+
110
+            shapes: define_shapes(),
111
+            piece: Some(Piece {
112
+                shape_kind: ShapeKind::J,
113
+                pos: BoardPos {
114
+                    x: 5,
115
+                    y: 19,
116
+                },
117
+                angle: PieceAngle::R0,
118
+            }),
119
+            next_shape: ShapeKind::L,
33 120
 
34
-            mode: GameMode::Playing,
35
-            resume_from_paused: false,
121
+            score: 0,
122
+            level: 0,
123
+            lines: 0,
36 124
 
37
-            controller: Controller::new(),
125
+            time_to_next_lowering: get_time_to_next_lowering(0),
126
+            piece_is_dropping: false,
38 127
 
39
-            state: State::new(),
128
+            default_cooldown: 100.0,
129
+            left_cooldown: 0.0,
130
+            right_cooldown: 0.0,
131
+            up_cooldown: 0.0,
132
+            down_cooldown: 0.0,
133
+            a_cooldown: 0.0,
134
+            b_cooldown: 0.0,
40 135
 
41
-            geometry: Geometry::new(canvas_width,
42
-                                    canvas_height,
43
-                                    block_size,
44
-                                    tileset_texture_width,
45
-                                    tileset_texture_height),
136
+            terrible_rng: 0
46 137
         }
47 138
     }
48 139
 
49 140
     pub fn init(&mut self, random: f32) {
50
-        self.state.init(random);
141
+        self.terrible_rng = (random * 100.0) as i32;
142
+        self.next_shape = get_random_shape(self);
143
+        self.terrible_rng = (random * 140.0) as i32;
144
+        use_next_shape(self);
51 145
     }
52 146
 
53
-    // return true if we need to call tick after receiving an input event
54
-    // (only true when we resume playing after being paused)
55
-    pub fn input(&mut self, button: ControllerButton, action: ControllerAction) -> bool {
56
-        self.controller.input(button, action);
147
+    pub fn update_geometry(&self, geometry: &mut Geometry) {
148
+        log("game: update_geometry");
57 149
 
58
-        if button == ControllerButton::Start && action == ControllerAction::Up {
59
-            if self.mode == GameMode::Playing {
60
-                log("Paused");
61
-                self.mode = GameMode::Paused;
62
-            } else {
63
-                log("Playing");
64
-                self.mode = GameMode::Playing;
65
-                self.resume_from_paused = true;
66
-                return true;
150
+        geometry.clear();
151
+
152
+        // zero vert
153
+        geometry.push(0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0);
154
+        render_board(geometry, &self.board.board, &self.board_offset);
155
+        render_score(geometry, self.score, Block2D { x: 19, y: 24 }, Col::new(1.0, 0.2, 0.3, 1.0));
156
+        render_level(geometry, self.level, Block2D { x: 19, y: 20 }, Col::new(1.0, 0.2, 0.3, 1.0));
157
+        render_lines(geometry, self.lines, Block2D { x: 19, y: 16 }, Col::new(1.0, 0.2, 0.3, 1.0));
158
+
159
+        if let Some(shape) = self.shapes.get(&self.next_shape) {
160
+            render_next_shape(geometry, &shape, Block2D { x: 19, y: 10 }, Col::new(1.0, 0.2, 0.3, 1.0));
161
+        }
162
+
163
+    }
164
+
165
+    pub fn update_geometry_debug(&self, _geometry: &mut Geometry) {
166
+
167
+    }
168
+
169
+    // counting on the fact that delta will always be a 'sensible' value.
170
+    // so if the player has paused the game for a while, on the first resumed call to tick_playing
171
+    // delta should be 0 rather than a large multi-second value
172
+    pub fn tick_playing(&mut self, controller: &Controller, delta: f32, elapsed: f32) -> bool {
173
+
174
+        self.terrible_rng = elapsed as i32;
175
+
176
+        if let Some(ref piece) = self.piece {
177
+            if let Some(shape) = self.shapes.get(&piece.shape_kind) {
178
+                remove_shape_from_board(&mut self.board, shape, &piece.pos, &piece.angle);
179
+            }
180
+        }
181
+
182
+        apply_user_input(self, controller, delta);
183
+
184
+        if self.piece_is_dropping {
185
+            if let Some(ref mut piece) = self.piece {
186
+                if let Some(shape) = self.shapes.get(&piece.shape_kind) {
187
+                    let has_lowered = lower_piece(&self.board, shape, piece);
188
+                    if !has_lowered {
189
+                        self.piece_is_dropping = false;
190
+                    }
191
+                }
67 192
             }
193
+        } else if apply_lowering(self, delta) {
194
+            use_next_shape(self);
68 195
         }
69 196
 
197
+        if let Some(ref piece) = self.piece {
198
+            if let Some(shape) = self.shapes.get(&piece.shape_kind) {
199
+                add_shape_to_board(&mut self.board, shape, &piece.pos, &piece.angle);
200
+            }
201
+        }
202
+
203
+        true
204
+    }
205
+}
206
+
207
+fn use_next_shape(game: &mut Game) {
208
+    if let Some(ref mut piece) = game.piece {
209
+        piece.shape_kind = game.next_shape.clone();
210
+        if let Some(shape) = game.shapes.get(&piece.shape_kind) {
211
+            piece.pos.x = ((game.board.width as i32) - shape.aabb_width) / 2;
212
+            piece.pos.y = (game.board.height as i32) - shape.real_height;
213
+            piece.angle = PieceAngle::R0;
214
+        }
215
+    }
216
+    game.next_shape = get_random_shape(game);
217
+}
218
+
219
+fn get_random_shape(game: &mut Game) -> ShapeKind {
220
+    match game.terrible_rng % 7 {
221
+        0 => ShapeKind::I,
222
+        1 => ShapeKind::J,
223
+        2 => ShapeKind::L,
224
+        3 => ShapeKind::O,
225
+        4 => ShapeKind::S,
226
+        5 => ShapeKind::T,
227
+        6 => ShapeKind::Z,
228
+        _ => ShapeKind::Z,      // never going to get here, but compiler insists
229
+    }
230
+}
231
+
232
+fn apply_lowering(game: &mut Game, delta: f32) -> bool {
233
+    game.time_to_next_lowering -= delta;
234
+    if game.time_to_next_lowering < 0.0 {
235
+        game.time_to_next_lowering = get_time_to_next_lowering(game.level);
236
+
237
+        if let Some(ref mut piece) = game.piece {
238
+            if let Some(shape) = game.shapes.get(&piece.shape_kind) {
239
+                if !lower_piece(&game.board, shape, piece) {
240
+                    add_shape_to_board(&mut game.board, shape, &piece.pos, &piece.angle);
241
+                    return true;
242
+                }
243
+            }
244
+        }
245
+    }
246
+    false
247
+}
248
+
249
+// if possible vertically drop the piece by one row, otherwise return false
250
+//
251
+fn lower_piece(board: &Board, shape: &Shape, piece: &mut Piece) -> bool {
252
+    let new_position = BoardPos { x: piece.pos.x, y: piece.pos.y - 1 };
253
+
254
+    if is_allowed(board, shape, &new_position, &piece.angle) {
255
+        piece.pos = new_position;
256
+        true
257
+    } else {
70 258
         false
71 259
     }
260
+}
72 261
 
73
-    pub fn tick(&mut self, delta: f32, elapsed: f32) -> bool {
74
-        // always followed by a render
75
-        // returns true if tick should be called again
76
-        //
262
+fn apply_user_input(game: &mut Game, controller: &Controller, delta: f32) {
263
+    if let Some(ref mut piece) = game.piece {
264
+        if let Some(shape) = game.shapes.get(&piece.shape_kind) {
265
+
266
+            if can_apply_input(&mut game.left_cooldown, controller.left, game.default_cooldown, delta) {
267
+                let new_position = BoardPos { x: piece.pos.x - 1, y: piece.pos.y };
268
+                if is_allowed(&game.board, shape, &new_position, &piece.angle) {
269
+                    piece.pos = new_position;
270
+                }
271
+            }
272
+
273
+            if can_apply_input(&mut game.right_cooldown, controller.right, game.default_cooldown, delta) {
274
+                let new_position = BoardPos { x: piece.pos.x + 1, y: piece.pos.y };
275
+                if is_allowed(&game.board, shape, &new_position, &piece.angle) {
276
+                    piece.pos = new_position;
277
+                }
278
+            }
279
+
280
+            if can_apply_input(&mut game.up_cooldown, controller.up, game.default_cooldown, delta) {
281
+                let new_angle;
282
+                if shape.fully_rotate {
283
+                    new_angle = match piece.angle {
284
+                        PieceAngle::R0 => PieceAngle::R270,
285
+                        PieceAngle::R270 => PieceAngle::R180,
286
+                        PieceAngle::R180 => PieceAngle::R90,
287
+                        PieceAngle::R90 => PieceAngle::R0,
288
+                    };
289
+                } else {
290
+                    new_angle = match piece.angle {
291
+                        PieceAngle::R0 => PieceAngle::R90,
292
+                        _ => PieceAngle::R0,
293
+                    };
294
+                }
295
+
296
+                if is_allowed(&game.board, shape, &piece.pos, &new_angle) {
297
+                    piece.angle = new_angle;
298
+                }
299
+            }
300
+
301
+            if can_apply_input(&mut game.down_cooldown, controller.down, game.default_cooldown, delta) {
302
+                game.piece_is_dropping = true;
303
+            }
304
+
305
+            if can_apply_input(&mut game.a_cooldown, controller.a, game.default_cooldown, delta) {
306
+                let new_angle;
307
+                if shape.fully_rotate {
308
+                    new_angle = match piece.angle {
309
+                        PieceAngle::R0 => PieceAngle::R90,
310
+                        PieceAngle::R90 => PieceAngle::R180,
311
+                        PieceAngle::R180 => PieceAngle::R270,
312
+                        PieceAngle::R270 => PieceAngle::R0,
313
+                    };
314
+                } else {
315
+                    new_angle = match piece.angle {
316
+                        PieceAngle::R0 => PieceAngle::R90,
317
+                        _ => PieceAngle::R0,
318
+                    };
319
+                }
320
+
321
+                if is_allowed(&game.board, shape, &piece.pos, &new_angle) {
322
+                    piece.angle = new_angle;
323
+                }
324
+            }
77 325
 
78
-        // prevent a crazy delta value if we're resuming from a paused state
79
-        if self.resume_from_paused {
80
-            self.resume_from_paused = false;
81
-            self.delta = 0.0;
326
+            if can_apply_input(&mut game.b_cooldown, controller.b, game.default_cooldown, delta) {
327
+                let new_angle;
328
+                if shape.fully_rotate {
329
+                    new_angle = match piece.angle {
330
+                        PieceAngle::R0 => PieceAngle::R270,
331
+                        PieceAngle::R270 => PieceAngle::R180,
332
+                        PieceAngle::R180 => PieceAngle::R90,
333
+                        PieceAngle::R90 => PieceAngle::R0,
334
+                    };
335
+                } else {
336
+                    new_angle = match piece.angle {
337
+                        PieceAngle::R0 => PieceAngle::R90,
338
+                        _ => PieceAngle::R0,
339
+                    };
340
+                }
341
+
342
+                if is_allowed(&game.board, shape, &piece.pos, &new_angle) {
343
+                    piece.angle = new_angle;
344
+                }
345
+            }
346
+        }
347
+    }
348
+}
349
+
350
+// apply a cooldown effect on input that remains pressed, this way it is only
351
+// acted upon every 'default_cooldown' ms
352
+fn can_apply_input(cooldown: &mut f32, pressed: bool, default_cooldown: f32, delta: f32) -> bool {
353
+    if pressed {
354
+        if cooldown <= &mut 0.0 {
355
+            *cooldown = default_cooldown;
356
+            return true;
82 357
         } else {
83
-            self.delta = delta;
358
+            *cooldown -= delta;
359
+        }
360
+    } else {
361
+        *cooldown = 0.0;
362
+    }
363
+    false
364
+}
365
+
366
+fn get_time_to_next_lowering(level: i32) -> f32 {
367
+    if level < 100 {
368
+        1000.0
369
+    } else {
370
+        500.0
371
+    }
372
+}
373
+
374
+fn is_allowed(board: &Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle) -> bool {
375
+    !is_outside(board, shape, pos, angle) && !is_colliding(board, shape, pos, angle)
376
+}
377
+
378
+fn is_outside(board: &Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle) -> bool {
379
+    let w = board.width as i32;
380
+
381
+    get_board_positions(shape, pos, angle)
382
+        .iter()
383
+        .any(|p| p.x < 0 || p.x >= w || p.y < 0)
384
+}
385
+
386
+fn is_colliding(board: &Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle) -> bool {
387
+    get_board_positions(shape, pos, angle)
388
+        .iter()
389
+        .filter(|p| is_valid_board_position(board, p))
390
+        .any(|p| board.board[p.y as usize][p.x as usize].active)
391
+}
392
+
393
+fn add_shape_to_board(board: &mut Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle) {
394
+    for pos in get_board_positions(shape, pos, angle) {
395
+        if is_valid_board_position(board, &pos) {
396
+            set_block(board, pos.x, pos.y, true);
84 397
         }
398
+    }
399
+}
85 400
 
86
-        match self.mode {
87
-            GameMode::Playing => self.state.tick_playing(&self.controller, delta, elapsed),
88
-            GameMode::Paused => false,
401
+fn remove_shape_from_board(board: &mut Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle) {
402
+    for pos in get_board_positions(shape, &pos, &angle) {
403
+        if is_valid_board_position(board, &pos) {
404
+            set_block(board, pos.x, pos.y, false);
89 405
         }
90 406
     }
407
+}
408
+
409
+fn is_valid_board_position(board: &Board, pos: &BoardPos) -> bool {
410
+    let w = board.width as i32;
411
+    let h = board.height as i32;
412
+
413
+    pos.x >= 0 && pos.x < w && pos.y >= 0 && pos.y < h
414
+}
415
+
416
+fn get_board_positions(shape: &Shape, pos: &BoardPos, angle: &PieceAngle) -> Vec<BoardPos> {
417
+    let mut res: Vec<BoardPos> = Vec::with_capacity(4);
418
+    let mut x: i32;
419
+    let mut y: i32;
91 420
 
92
-    // use the current gamestate to update geometry
93
-    pub fn update_geometry(&mut self) {
94
-        self.state.update_geometry(&mut self.geometry);
95
-        self.state.update_geometry_debug(&mut self.geometry);
421
+    for y_offset in 0..shape.aabb_height {
422
+        for x_offset in 0..shape.aabb_width {
423
+            if shape.form[y_offset as usize][x_offset as usize] {
424
+
425
+                match angle {
426
+                    PieceAngle::R0 => {
427
+                        x = x_offset;
428
+                        y = y_offset;
429
+                    }
430
+                    PieceAngle::R90 => {
431
+                        x = y_offset;
432
+                        y = shape.aabb_width - x_offset - 1;
433
+                    },
434
+                    PieceAngle::R180 => {
435
+                        x = shape.aabb_width - x_offset - 1;
436
+                        y = shape.aabb_height - y_offset - 1;
437
+                    }
438
+                    PieceAngle::R270 => {
439
+                        x = shape.aabb_height - y_offset - 1;
440
+                        y = x_offset;
441
+                    }
442
+                }
443
+
444
+                res.push(BoardPos {x: pos.x + x, y: pos.y + y});
445
+            }
446
+        }
96 447
     }
97 448
 
98
-    pub fn geo_len(&self) -> usize {
99
-        self.geometry.geo.len()
449
+    res
450
+}
451
+
452
+fn set_block(board: &mut Board, x: i32, y: i32, active: bool) {
453
+    board.board[y as usize][x as usize].active = active;
454
+}
455
+
456
+fn define_shapes() -> HashMap<ShapeKind, Shape> {
457
+    let mut res = HashMap::new();
458
+
459
+
460
+    // origin is in the bottom left
461
+    // indices are in [y][x] order
462
+    //
463
+    fn i_shape() -> Shape {
464
+        //
465
+        // -----
466
+        // __X__
467
+        // __X__
468
+        // __X__
469
+        // __X__
470
+        //
471
+        let mut v = vec![vec![false; 5]; 5];
472
+        v[0][2] = true;
473
+        v[1][2] = true;
474
+        v[2][2] = true;
475
+        v[3][2] = true;
476
+
477
+        Shape {
478
+            aabb_width: 5,
479
+            aabb_height: 5,
480
+            real_height: 4,
481
+            fully_rotate: false,
482
+            form: v,
483
+        }
484
+    }
485
+
486
+    fn o_shape() -> Shape {
487
+        //
488
+        // XX
489
+        // XX
490
+        //
491
+        let v = vec![vec![true; 2]; 2];
492
+
493
+        Shape {
494
+            aabb_width: 2,
495
+            aabb_height: 2,
496
+            real_height: 2,
497
+            fully_rotate: true,
498
+            form: v,
499
+        }
500
+    }
501
+
502
+    fn l_shape() -> Shape {
503
+        //
504
+        // _X_
505
+        // _X_
506
+        // _XX
507
+        //
508
+        let mut v = vec![vec![true; 3]; 3];
509
+        v[0][0] = false;
510
+        v[1][0] = false;
511
+        v[2][0] = false;
512
+        v[1][2] = false;
513
+        v[2][2] = false;
514
+
515
+        Shape {
516
+            aabb_width: 3,
517
+            aabb_height: 3,
518
+            real_height: 3,
519
+            fully_rotate: true,
520
+            form: v,
521
+        }
100 522
     }
101 523
 
102
-    pub fn geo_ptr(&self) -> *const f32 {
103
-        self.geometry.geo.as_ptr() as *const f32
524
+    fn j_shape() -> Shape {
525
+        //
526
+        // _X_
527
+        // _X_
528
+        // XX_
529
+        //
530
+        let mut v = vec![vec![true; 3]; 3];
531
+        v[0][2] = false;
532
+        v[1][2] = false;
533
+        v[2][2] = false;
534
+        v[1][0] = false;
535
+        v[2][0] = false;
536
+
537
+        Shape {
538
+            aabb_width: 3,
539
+            aabb_height: 3,
540
+            real_height: 3,
541
+            fully_rotate: true,
542
+            form: v,
543
+        }
544
+    }
545
+
546
+    fn s_shape() -> Shape {
547
+        //
548
+        // ___
549
+        // _XX
550
+        // XX_
551
+        //
552
+        let mut v = vec![vec![true; 3]; 3];
553
+        v[0][2] = false;
554
+        v[1][0] = false;
555
+        v[2][0] = false;
556
+        v[2][1] = false;
557
+        v[2][2] = false;
558
+
559
+        Shape {
560
+            aabb_width: 3,
561
+            aabb_height: 3,
562
+            real_height: 2,
563
+            fully_rotate: false,
564
+            form: v,
565
+        }
566
+    }
567
+
568
+    fn t_shape() -> Shape {
569
+        //
570
+        // ___
571
+        // XXX
572
+        // _X_
573
+        //
574
+        let mut v = vec![vec![true; 3]; 3];
575
+        v[0][0] = false;
576
+        v[0][2] = false;
577
+        v[2][0] = false;
578
+        v[2][1] = false;
579
+        v[2][2] = false;
580
+
581
+        Shape {
582
+            aabb_width: 3,
583
+            aabb_height: 3,
584
+            real_height: 2,
585
+            fully_rotate: true,
586
+            form: v,
587
+        }
588
+    }
589
+
590
+    fn z_shape() -> Shape {
591
+        //
592
+        // ___
593
+        // XX_
594
+        // _XX
595
+        //
596
+        let mut v = vec![vec![true; 3]; 3];
597
+        v[0][0] = false;
598
+        v[1][2] = false;
599
+        v[2][0] = false;
600
+        v[2][1] = false;
601
+        v[2][2] = false;
602
+
603
+        Shape {
604
+            aabb_width: 3,
605
+            aabb_height: 3,
606
+            real_height: 2,
607
+            fully_rotate: false,
608
+            form: v,
609
+        }
610
+    }
611
+
612
+    res.insert(ShapeKind::I, i_shape());
613
+    res.insert(ShapeKind::J, j_shape());
614
+    res.insert(ShapeKind::L, l_shape());
615
+    res.insert(ShapeKind::O, o_shape());
616
+    res.insert(ShapeKind::S, s_shape());
617
+    res.insert(ShapeKind::T, t_shape());
618
+    res.insert(ShapeKind::Z, z_shape());
619
+
620
+    res
621
+}
622
+
623
+fn render_board(geometry: &mut Geometry, board: &Vec<Vec<Block>>, board_offset: &Block2D) {
624
+
625
+    let mut x = 0;
626
+    let mut y = 0;
627
+
628
+    for row in board {
629
+        for block in row {
630
+            let c = if block.active {
631
+                Col::new(1.0, 0.0, 0.0, 1.0)
632
+            } else {
633
+                Col::new(1.0, 1.0, 0.0, 0.1)
634
+            };
635
+
636
+            geometry.push_sprite(Sprite::Block,
637
+                                 c,
638
+                                 Block2D{ x: x + board_offset.x, y: y + board_offset.y },
639
+                                 block.offset);
640
+            x += 1;
641
+        }
642
+        x = 0;
643
+        y += 1;
644
+    }
645
+
646
+}
647
+
648
+fn render_score(geometry: &mut Geometry, score: i32, offset: Block2D, colour: Col) {
649
+    geometry.push_text("SCORE", offset, colour);
650
+    geometry.push_text(&score.to_string(), Block2D { x: offset.x, y: offset.y - 1 }, colour);
651
+}
652
+
653
+fn render_level(geometry: &mut Geometry, level: i32, offset: Block2D, colour: Col) {
654
+    geometry.push_text("LEVEL", offset, colour);
655
+    geometry.push_text(&level.to_string(), Block2D { x: offset.x, y: offset.y - 1 }, colour);
656
+}
657
+
658
+fn render_lines(geometry: &mut Geometry, lines: i32, offset: Block2D, colour: Col) {
659
+    geometry.push_text("LINES", offset, colour);
660
+    geometry.push_text(&lines.to_string(), Block2D { x: offset.x, y: offset.y - 1 }, colour);
661
+}
662
+
663
+fn render_next_shape(geometry: &mut Geometry, shape: &Shape, offset: Block2D, colour: Col) {
664
+    let board_positions = get_board_positions(shape, &BoardPos { x: 0, y: 0}, &PieceAngle::R0);
665
+    for pos in board_positions {
666
+        geometry.push_sprite(Sprite::Block,
667
+                             colour,
668
+                             Block2D {x: pos.x + offset.x, y: pos.y + offset.y},
669
+                             Vec2D {x: 0.0, y: 0.0 });
104 670
     }
105 671
 }

+ 107
- 0
src/harbour.rs View File

@@ -0,0 +1,107 @@
1
+use log;
2
+use controller::{Controller, ControllerButton, ControllerAction};
3
+use game::Game;
4
+use geometry::Geometry;
5
+
6
+#[derive(PartialEq)]
7
+pub enum GameMode {
8
+    Playing,
9
+    Paused,
10
+}
11
+
12
+// harbour - a place of refuge.
13
+//
14
+pub struct Harbour {
15
+    delta: f32,
16
+
17
+    mode: GameMode,
18
+    resume_from_paused: bool,
19
+
20
+    controller: Controller,
21
+    game: Game,
22
+    geometry: Geometry,
23
+}
24
+
25
+impl Harbour {
26
+    pub fn new(
27
+        canvas_width: i32,
28
+        canvas_height: i32,
29
+        block_size: i32,
30
+        tileset_texture_width: i32,  // tileset texture width
31
+        tileset_texture_height: i32, // tileset texture height
32
+    ) -> Harbour {
33
+        Harbour {
34
+            delta: 0.0,
35
+
36
+            mode: GameMode::Playing,
37
+            resume_from_paused: false,
38
+
39
+            controller: Controller::new(),
40
+
41
+            game: Game::new(),
42
+
43
+            geometry: Geometry::new(canvas_width,
44
+                                    canvas_height,
45
+                                    block_size,
46
+                                    tileset_texture_width,
47
+                                    tileset_texture_height),
48
+        }
49
+    }
50
+
51
+    pub fn init(&mut self, random: f32) {
52
+        self.game.init(random);
53
+    }
54
+
55
+    // return true if we need to call tick after receiving an input event
56
+    // (only true when we resume playing after being paused)
57
+    pub fn input(&mut self, button: ControllerButton, action: ControllerAction) -> bool {
58
+        self.controller.input(button, action);
59
+
60
+        if button == ControllerButton::Start && action == ControllerAction::Up {
61
+            if self.mode == GameMode::Playing {
62
+                log("Paused");
63
+                self.mode = GameMode::Paused;
64
+            } else {
65
+                log("Playing");
66
+                self.mode = GameMode::Playing;
67
+                self.resume_from_paused = true;
68
+                return true;
69
+            }
70
+        }
71
+
72
+        false
73
+    }
74
+
75
+    pub fn tick(&mut self, delta: f32, elapsed: f32) -> bool {
76
+        // always followed by a render
77
+        // returns true if tick should be called again
78
+        //
79
+
80
+        // prevent a crazy delta value if we're resuming from a paused state
81
+        if self.resume_from_paused {
82
+            self.resume_from_paused = false;
83
+            self.delta = 0.0;
84
+        } else {
85
+            self.delta = delta;
86
+        }
87
+
88
+        match self.mode {
89
+            GameMode::Playing => self.game.tick_playing(&self.controller, delta, elapsed),
90
+            GameMode::Paused => false,
91
+        }
92
+    }
93
+
94
+    // use the current gamestate to update geometry
95
+    pub fn update_geometry(&mut self) {
96
+        self.game.update_geometry(&mut self.geometry);
97
+        self.game.update_geometry_debug(&mut self.geometry);
98
+    }
99
+
100
+    pub fn geo_len(&self) -> usize {
101
+        self.geometry.geo.len()
102
+    }
103
+
104
+    pub fn geo_ptr(&self) -> *const f32 {
105
+        self.geometry.geo.as_ptr() as *const f32
106
+    }
107
+}

+ 28
- 26
src/lib.rs View File

@@ -8,13 +8,13 @@ use wasm_bindgen::prelude::*;
8 8
 pub mod units;
9 9
 pub mod sprite;
10 10
 pub mod text;
11
+pub mod harbour;
11 12
 pub mod game;
12
-pub mod state;
13 13
 pub mod geometry;
14 14
 pub mod controller;
15 15
 
16 16
 use controller::{ControllerButton, ControllerAction};
17
-use game::Game;
17
+use harbour::Harbour;
18 18
 
19 19
 #[wasm_bindgen(js_namespace = console)]
20 20
 extern "C" {
@@ -39,12 +39,12 @@ impl KeyEventReturn {
39 39
 }
40 40
 
41 41
 #[wasm_bindgen]
42
-pub struct GameBridge {
43
-    game: Game,
42
+pub struct Bridge {
43
+    harbour: Harbour,
44 44
 }
45 45
 
46 46
 #[wasm_bindgen]
47
-impl GameBridge {
47
+impl Bridge {
48 48
     #[wasm_bindgen(constructor)]
49 49
     pub fn new(
50 50
         canvas_width: i32,
@@ -52,57 +52,59 @@ impl GameBridge {
52 52
         block_size: i32,
53 53
         tileset_texture_width: i32,  // tileset texture width
54 54
         tileset_texture_height: i32, // tileset texture height
55
-    ) -> GameBridge {
56
-        GameBridge {
57
-            game: Game::new(canvas_width, canvas_height,
58
-                            block_size,
59
-                            tileset_texture_width, tileset_texture_height)
55
+    ) -> Bridge {
56
+        Bridge {
57
+            harbour: Harbour::new(canvas_width,
58
+                                  canvas_height,
59
+                                  block_size,
60
+                                  tileset_texture_width,
61
+                                  tileset_texture_height)
60 62
         }
61 63
     }
62 64
 
63 65
     pub fn init(&mut self, random: f32) {
64
-        self.game.init(random);
66
+        self.harbour.init(random);
65 67
     }
66 68
 
67 69
     pub fn event_key_down(&mut self, key: String) -> KeyEventReturn {
68
-        input_event(&mut self.game, key, ControllerAction::Down)
70
+        input_event(&mut self.harbour, key, ControllerAction::Down)
69 71
     }
70 72
 
71 73
     pub fn event_key_up(&mut self, key: String) -> KeyEventReturn {
72
-        input_event(&mut self.game, key, ControllerAction::Up)
74
+        input_event(&mut self.harbour, key, ControllerAction::Up)
73 75
     }
74 76
 
75 77
     pub fn tick(&mut self, delta: f32, elapsed: f32) -> bool {
76
-        self.game.tick(delta, elapsed)
78
+        self.harbour.tick(delta, elapsed)
77 79
     }
78 80
 
79 81
     // use the current gamestate to update geometry
80 82
     pub fn update_geometry(&mut self) {
81
-        self.game.update_geometry();
83
+        self.harbour.update_geometry();
82 84
     }
83 85
 
84 86
     pub fn geo_len(&self) -> usize {
85
-        self.game.geo_len()
87
+        self.harbour.geo_len()
86 88
     }
87 89
 
88 90
     pub fn geo_ptr(&self) -> *const f32 {
89
-        self.game.geo_ptr()
91
+        self.harbour.geo_ptr()
90 92
     }
91 93
 }
92 94
 
93
-fn input_event(game: &mut Game, key: String, action: ControllerAction) -> KeyEventReturn {
95
+fn input_event(harbour: &mut Harbour, key: String, action: ControllerAction) -> KeyEventReturn {
94 96
     let mut prevent_default = true;
95 97
     let mut call_tick = false;
96 98
 
97 99
     match key.as_ref() {
98
-        "ArrowLeft" => call_tick = game.input(ControllerButton::Left, action),
99
-        "ArrowRight" => call_tick = game.input(ControllerButton::Right, action),
100
-        "ArrowUp" => call_tick = game.input(ControllerButton::Up, action),
101
-        "ArrowDown" => call_tick = game.input(ControllerButton::Down, action),
102
-        "a" => call_tick = game.input(ControllerButton::A, action),
103
-        "z" => call_tick = game.input(ControllerButton::B, action),
104
-        "Enter" => call_tick = game.input(ControllerButton::Start, action),
105
-        "Shift" => call_tick = game.input(ControllerButton::Select, action),
100
+        "ArrowLeft" => call_tick = harbour.input(ControllerButton::Left, action),
101
+        "ArrowRight" => call_tick = harbour.input(ControllerButton::Right, action),
102
+        "ArrowUp" => call_tick = harbour.input(ControllerButton::Up, action),
103
+        "ArrowDown" => call_tick = harbour.input(ControllerButton::Down, action),
104
+        "a" => call_tick = harbour.input(ControllerButton::A, action),
105
+        "z" => call_tick = harbour.input(ControllerButton::B, action),
106
+        "Enter" => call_tick = harbour.input(ControllerButton::Start, action),
107
+        "Shift" => call_tick = harbour.input(ControllerButton::Select, action),
106 108
         _ => prevent_default = false,
107 109
     }
108 110
 

+ 0
- 671
src/state.rs View File

@@ -1,671 +0,0 @@
1
-use log;
2
-use std::collections::HashMap;
3
-use units::*;
4
-use sprite::Sprite;
5
-use controller::Controller;
6
-use geometry::Geometry;
7
-
8
-#[derive(Clone, Copy)]
9
-pub struct Block {
10
-    active: bool,
11
-    offset: Vec2D
12
-}
13
-
14
-impl Block {
15
-    pub fn new() -> Block {
16
-        Block {
17
-            active: false,
18
-            offset: Vec2D {x: 0.0, y: 0.0 },
19
-        }
20
-    }
21
-}
22
-
23
-pub struct Board {
24
-    board: Vec<Vec<Block>>,
25
-    width: usize,
26
-    height: usize,
27
-}
28
-
29
-impl Board {
30
-    pub fn new(width: usize, height: usize) -> Board {
31
-        Board {
32
-            board: vec![vec![Block::new(); width]; height],
33
-            width,
34
-            height,
35
-        }
36
-    }
37
-}
38
-
39
-#[derive(Debug, Clone, Copy)]
40
-pub struct BoardPos {
41
-    pub x: i32,
42
-    pub y: i32,
43
-}
44
-
45
-#[derive(Clone, Copy, Hash, Eq, PartialEq)]
46
-enum ShapeKind {
47
-    I,
48
-    J,
49
-    L,
50
-    O,
51
-    S,
52
-    T,
53
-    Z,
54
-}
55
-
56
-pub struct Shape {
57
-    aabb_width: i32,
58
-    aabb_height: i32,
59
-    real_height: i32,
60
-    fully_rotate: bool,
61
-    form: Vec<Vec<bool>>,
62
-}
63
-
64
-enum PieceAngle {
65
-    R0,
66
-    R90,
67
-    R180,
68
-    R270,
69
-}
70
-
71
-pub struct Piece {
72
-    shape_kind: ShapeKind,
73
-    pos: BoardPos,
74
-    angle: PieceAngle,
75
-}
76
-
77
-pub struct State {
78
-    board: Board,
79
-    board_offset: Block2D,
80
-
81
-    shapes: HashMap<ShapeKind, Shape>,
82
-    piece: Option<Piece>,               // the current piece under player control
83
-    next_shape: ShapeKind,
84
-
85
-    score: i32,
86
-    level: i32,
87
-    lines: i32,
88
-
89
-
90
-    time_to_next_lowering: f32,     // ms
91
-    piece_is_dropping: bool,
92
-
93
-    default_cooldown: f32,              // ms
94
-    left_cooldown: f32,
95
-    right_cooldown: f32,
96
-    up_cooldown: f32,
97
-    down_cooldown: f32,
98
-    a_cooldown: f32,
99
-    b_cooldown: f32,
100
-
101
-    terrible_rng: i32,
102
-}
103
-
104
-impl State {
105
-    pub fn new() -> State {
106
-        State {
107
-            board: Board::new(10, 20),
108
-            board_offset: Block2D { x: 5, y: 5 },
109
-
110
-            shapes: define_shapes(),
111
-            piece: Some(Piece {
112
-                shape_kind: ShapeKind::J,
113
-                pos: BoardPos {
114
-                    x: 5,
115
-                    y: 19,
116
-                },
117
-                angle: PieceAngle::R0,
118
-            }),
119
-            next_shape: ShapeKind::L,
120
-
121
-            score: 0,
122
-            level: 0,
123
-            lines: 0,
124
-
125
-            time_to_next_lowering: get_time_to_next_lowering(0),
126
-            piece_is_dropping: false,
127
-
128
-            default_cooldown: 100.0,
129
-            left_cooldown: 0.0,
130
-            right_cooldown: 0.0,
131
-            up_cooldown: 0.0,
132
-            down_cooldown: 0.0,
133
-            a_cooldown: 0.0,
134
-            b_cooldown: 0.0,
135
-
136
-            terrible_rng: 0
137
-        }
138
-    }
139
-
140
-    pub fn init(&mut self, random: f32) {
141
-        self.terrible_rng = (random * 100.0) as i32;
142
-        self.next_shape = get_random_shape(self);
143
-        self.terrible_rng = (random * 140.0) as i32;
144
-        use_next_shape(self);
145
-    }
146
-
147
-    pub fn update_geometry(&self, geometry: &mut Geometry) {
148
-        log("state: update_geometry");
149
-
150
-        geometry.clear();
151
-
152
-        // zero vert
153
-        geometry.push(0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0);
154
-        render_board(geometry, &self.board.board, &self.board_offset);
155
-        render_score(geometry, self.score, Block2D { x: 19, y: 24 }, Col::new(1.0, 0.2, 0.3, 1.0));
156
-        render_level(geometry, self.level, Block2D { x: 19, y: 20 }, Col::new(1.0, 0.2, 0.3, 1.0));
157
-        render_lines(geometry, self.lines, Block2D { x: 19, y: 16 }, Col::new(1.0, 0.2, 0.3, 1.0));
158
-
159
-        if let Some(shape) = self.shapes.get(&self.next_shape) {
160
-            render_next_shape(geometry, &shape, Block2D { x: 19, y: 10 }, Col::new(1.0, 0.2, 0.3, 1.0));
161
-        }
162
-
163
-    }
164
-
165
-    pub fn update_geometry_debug(&self, _geometry: &mut Geometry) {
166
-
167
-    }
168
-
169
-    // counting on the fact that delta will always be a 'sensible' value.
170
-    // so if the player has paused the game for a while, on the first resumed call to tick_playing
171
-    // delta should be 0 rather than a large multi-second value
172
-    pub fn tick_playing(&mut self, controller: &Controller, delta: f32, elapsed: f32) -> bool {
173
-
174
-        self.terrible_rng = elapsed as i32;
175
-
176
-        if let Some(ref piece) = self.piece {
177
-            if let Some(shape) = self.shapes.get(&piece.shape_kind) {
178
-                remove_shape_from_board(&mut self.board, shape, &piece.pos, &piece.angle);
179
-            }
180
-        }
181
-
182
-        apply_user_input(self, controller, delta);
183
-
184
-        if self.piece_is_dropping {
185
-            if let Some(ref mut piece) = self.piece {
186
-                if let Some(shape) = self.shapes.get(&piece.shape_kind) {
187
-                    let has_lowered = lower_piece(&self.board, shape, piece);
188
-                    if !has_lowered {
189
-                        self.piece_is_dropping = false;
190
-                    }
191
-                }
192
-            }
193
-        } else if apply_lowering(self, delta) {
194
-            use_next_shape(self);
195
-        }
196
-
197
-        if let Some(ref piece) = self.piece {
198
-            if let Some(shape) = self.shapes.get(&piece.shape_kind) {
199
-                add_shape_to_board(&mut self.board, shape, &piece.pos, &piece.angle);
200
-            }
201
-        }
202
-
203
-        true
204
-    }
205
-}
206
-
207
-fn use_next_shape(state: &mut State) {
208
-    if let Some(ref mut piece) = state.piece {
209
-        piece.shape_kind = state.next_shape.clone();
210
-        if let Some(shape) = state.shapes.get(&piece.shape_kind) {
211
-            piece.pos.x = ((state.board.width as i32) - shape.aabb_width) / 2;
212
-            piece.pos.y = (state.board.height as i32) - shape.real_height;
213
-            piece.angle = PieceAngle::R0;
214
-        }
215
-    }
216
-    state.next_shape = get_random_shape(state);
217
-}
218
-
219
-fn get_random_shape(state: &mut State) -> ShapeKind {
220
-    match state.terrible_rng % 7 {
221
-        0 => ShapeKind::I,
222
-        1 => ShapeKind::J,
223
-        2 => ShapeKind::L,
224
-        3 => ShapeKind::O,
225
-        4 => ShapeKind::S,
226
-        5 => ShapeKind::T,
227
-        6 => ShapeKind::Z,
228
-        _ => ShapeKind::Z,      // never going to get here, but compiler insists
229
-    }
230
-}
231
-
232
-fn apply_lowering(state: &mut State, delta: f32) -> bool {
233
-    state.time_to_next_lowering -= delta;
234
-    if state.time_to_next_lowering < 0.0 {
235
-        state.time_to_next_lowering = get_time_to_next_lowering(state.level);
236
-
237
-        if let Some(ref mut piece) = state.piece {
238
-            if let Some(shape) = state.shapes.get(&piece.shape_kind) {
239
-                if !lower_piece(&state.board, shape, piece) {
240
-                    add_shape_to_board(&mut state.board, shape, &piece.pos, &piece.angle);
241
-                    return true;
242
-                }
243
-            }
244
-        }
245
-    }
246
-    false
247
-}
248
-
249
-// if possible vertically drop the piece by one row, otherwise return false
250
-//
251
-fn lower_piece(board: &Board, shape: &Shape, piece: &mut Piece) -> bool {
252
-    let new_position = BoardPos { x: piece.pos.x, y: piece.pos.y - 1 };
253
-
254
-    if is_allowed(board, shape, &new_position, &piece.angle) {
255
-        piece.pos = new_position;
256
-        true
257
-    } else {
258
-        false
259
-    }
260
-}
261
-
262
-fn apply_user_input(state: &mut State, controller: &Controller, delta: f32) {
263
-    if let Some(ref mut piece) = state.piece {
264
-        if let Some(shape) = state.shapes.get(&piece.shape_kind) {
265
-
266
-            if can_apply_input(&mut state.left_cooldown, controller.left, state.default_cooldown, delta) {
267
-                let new_position = BoardPos { x: piece.pos.x - 1, y: piece.pos.y };
268
-                if is_allowed(&state.board, shape, &new_position, &piece.angle) {
269
-                    piece.pos = new_position;
270
-                }
271
-            }
272
-
273
-            if can_apply_input(&mut state.right_cooldown, controller.right, state.default_cooldown, delta) {
274
-                let new_position = BoardPos { x: piece.pos.x + 1, y: piece.pos.y };
275
-                if is_allowed(&state.board, shape, &new_position, &piece.angle) {
276
-                    piece.pos = new_position;
277
-                }
278
-            }
279
-
280
-            if can_apply_input(&mut state.up_cooldown, controller.up, state.default_cooldown, delta) {
281
-                let new_angle;
282
-                if shape.fully_rotate {
283
-                    new_angle = match piece.angle {
284
-                        PieceAngle::R0 => PieceAngle::R270,
285
-                        PieceAngle::R270 => PieceAngle::R180,
286
-                        PieceAngle::R180 => PieceAngle::R90,
287
-                        PieceAngle::R90 => PieceAngle::R0,
288
-                    };
289
-                } else {
290
-                    new_angle = match piece.angle {
291
-                        PieceAngle::R0 => PieceAngle::R90,
292
-                        _ => PieceAngle::R0,
293
-                    };
294
-                }
295
-
296
-                if is_allowed(&state.board, shape, &piece.pos, &new_angle) {
297
-                    piece.angle = new_angle;
298
-                }
299
-            }
300
-
301
-            if can_apply_input(&mut state.down_cooldown, controller.down, state.default_cooldown, delta) {
302
-                state.piece_is_dropping = true;
303
-            }
304
-
305
-            if can_apply_input(&mut state.a_cooldown, controller.a, state.default_cooldown, delta) {
306
-                let new_angle;
307
-                if shape.fully_rotate {
308
-                    new_angle = match piece.angle {
309
-                        PieceAngle::R0 => PieceAngle::R90,
310
-                        PieceAngle::R90 => PieceAngle::R180,
311
-                        PieceAngle::R180 => PieceAngle::R270,
312
-                        PieceAngle::R270 => PieceAngle::R0,
313
-                    };
314
-                } else {
315
-                    new_angle = match piece.angle {
316
-                        PieceAngle::R0 => PieceAngle::R90,
317
-                        _ => PieceAngle::R0,
318
-                    };
319
-                }
320
-
321
-                if is_allowed(&state.board, shape, &piece.pos, &new_angle) {
322
-                    piece.angle = new_angle;
323
-                }
324
-            }
325
-
326
-            if can_apply_input(&mut state.b_cooldown, controller.b, state.default_cooldown, delta) {
327
-                let new_angle;
328
-                if shape.fully_rotate {
329
-                    new_angle = match piece.angle {
330
-                        PieceAngle::R0 => PieceAngle::R270,
331
-                        PieceAngle::R270 => PieceAngle::R180,
332
-                        PieceAngle::R180 => PieceAngle::R90,
333
-                        PieceAngle::R90 => PieceAngle::R0,
334
-                    };
335
-                } else {
336
-                    new_angle = match piece.angle {
337
-                        PieceAngle::R0 => PieceAngle::R90,
338
-                        _ => PieceAngle::R0,
339
-                    };
340
-                }
341
-
342
-                if is_allowed(&state.board, shape, &piece.pos, &new_angle) {
343
-                    piece.angle = new_angle;
344
-                }
345
-            }
346
-        }
347
-    }
348
-}
349
-
350
-// apply a cooldown effect on input that remains pressed, this way it is only
351
-// acted upon every 'default_cooldown' ms
352
-fn can_apply_input(cooldown: &mut f32, pressed: bool, default_cooldown: f32, delta: f32) -> bool {
353
-    if pressed {
354
-        if cooldown <= &mut 0.0 {
355
-            *cooldown = default_cooldown;
356
-            return true;
357
-        } else {
358
-            *cooldown -= delta;
359
-        }
360
-    } else {
361
-        *cooldown = 0.0;
362
-    }
363
-    false
364
-}
365
-
366
-fn get_time_to_next_lowering(level: i32) -> f32 {
367
-    if level < 100 {
368
-        1000.0
369
-    } else {
370
-        500.0
371
-    }
372
-}
373
-
374
-fn is_allowed(board: &Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle) -> bool {
375
-    !is_outside(board, shape, pos, angle) && !is_colliding(board, shape, pos, angle)
376
-}
377
-
378
-fn is_outside(board: &Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle) -> bool {
379
-    let w = board.width as i32;
380
-
381
-    get_board_positions(shape, pos, angle)
382
-        .iter()
383
-        .any(|p| p.x < 0 || p.x >= w || p.y < 0)
384
-}
385
-
386
-fn is_colliding(board: &Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle) -> bool {
387
-    get_board_positions(shape, pos, angle)
388
-        .iter()
389
-        .filter(|p| is_valid_board_position(board, p))
390
-        .any(|p| board.board[p.y as usize][p.x as usize].active)
391
-}
392
-
393
-fn add_shape_to_board(board: &mut Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle) {
394
-    for pos in get_board_positions(shape, pos, angle) {
395
-        if is_valid_board_position(board, &pos) {
396
-            set_block(board, pos.x, pos.y, true);
397
-        }
398
-    }
399
-}
400
-
401
-fn remove_shape_from_board(board: &mut Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle) {
402
-    for pos in get_board_positions(shape, &pos, &angle) {
403
-        if is_valid_board_position(board, &pos) {
404
-            set_block(board, pos.x, pos.y, false);
405
-        }
406
-    }
407
-}
408
-
409
-fn is_valid_board_position(board: &Board, pos: &BoardPos) -> bool {
410
-    let w = board.width as i32;
411
-    let h = board.height as i32;
412
-
413
-    pos.x >= 0 && pos.x < w && pos.y >= 0 && pos.y < h
414
-}
415
-
416
-fn get_board_positions(shape: &Shape, pos: &BoardPos, angle: &PieceAngle) -> Vec<BoardPos> {
417
-    let mut res: Vec<BoardPos> = Vec::with_capacity(4);
418
-    let mut x: i32;
419
-    let mut y: i32;
420
-
421
-    for y_offset in 0..shape.aabb_height {
422
-        for x_offset in 0..shape.aabb_width {
423
-            if shape.form[y_offset as usize][x_offset as usize] {
424
-
425
-                match angle {
426
-                    PieceAngle::R0 => {
427
-                        x = x_offset;
428
-                        y = y_offset;
429
-                    }
430
-                    PieceAngle::R90 => {
431
-                        x = y_offset;
432
-                        y = shape.aabb_width - x_offset - 1;
433
-                    },
434
-                    PieceAngle::R180 => {
435
-                        x = shape.aabb_width - x_offset - 1;
436
-                        y = shape.aabb_height - y_offset - 1;
437
-                    }
438
-                    PieceAngle::R270 => {
439
-                        x = shape.aabb_height - y_offset - 1;
440
-                        y = x_offset;
441
-                    }
442
-                }
443
-
444
-                res.push(BoardPos {x: pos.x + x, y: pos.y + y});
445
-            }
446
-        }
447
-    }
448
-
449
-    res
450
-}
451
-
452
-fn set_block(board: &mut Board, x: i32, y: i32, active: bool) {
453
-    board.board[y as usize][x as usize].active = active;
454
-}
455
-
456
-fn define_shapes() -> HashMap<ShapeKind, Shape> {
457
-    let mut res = HashMap::new();
458
-
459
-
460
-    // origin is in the bottom left
461
-    // indices are in [y][x] order
462
-    //
463
-    fn i_shape() -> Shape {
464
-        //
465
-        // -----
466
-        // __X__
467
-        // __X__
468
-        // __X__
469
-        // __X__
470
-        //
471
-        let mut v = vec![vec![false; 5]; 5];
472
-        v[0][2] = true;
473
-        v[1][2] = true;
474
-        v[2][2] = true;
475
-        v[3][2] = true;
476
-
477
-        Shape {
478
-            aabb_width: 5,
479
-            aabb_height: 5,
480
-            real_height: 4,
481
-            fully_rotate: false,
482
-            form: v,
483
-        }
484
-    }
485
-
486
-    fn o_shape() -> Shape {
487
-        //
488
-        // XX
489
-        // XX
490
-        //
491
-        let v = vec![vec![true; 2]; 2];
492
-
493
-        Shape {
494
-            aabb_width: 2,
495
-            aabb_height: 2,
496
-            real_height: 2,
497
-            fully_rotate: true,
498
-            form: v,
499
-        }
500
-    }
501
-
502
-    fn l_shape() -> Shape {
503
-        //
504
-        // _X_
505
-        // _X_
506
-        // _XX
507
-        //
508
-        let mut v = vec![vec![true; 3]; 3];
509
-        v[0][0] = false;
510
-        v[1][0] = false;
511
-        v[2][0] = false;
512
-        v[1][2] = false;
513
-        v[2][2] = false;
514
-
515
-        Shape {
516
-            aabb_width: 3,
517
-            aabb_height: 3,
518
-            real_height: 3,
519
-            fully_rotate: true,
520
-            form: v,
521
-        }
522
-    }
523
-
524
-    fn j_shape() -> Shape {
525
-        //
526
-        // _X_
527
-        // _X_
528
-        // XX_
529
-        //
530
-        let mut v = vec![vec![true; 3]; 3];
531
-        v[0][2] = false;
532
-        v[1][2] = false;
533
-        v[2][2] = false;
534
-        v[1][0] = false;
535
-        v[2][0] = false;
536
-
537
-        Shape {
538
-            aabb_width: 3,
539
-            aabb_height: 3,
540
-            real_height: 3,
541
-            fully_rotate: true,
542
-            form: v,
543
-        }
544
-    }
545
-
546
-    fn s_shape() -> Shape {
547
-        //
548
-        // ___
549
-        // _XX
550
-        // XX_
551
-        //
552
-        let mut v = vec![vec![true; 3]; 3];
553
-        v[0][2] = false;
554
-        v[1][0] = false;
555
-        v[2][0] = false;
556
-        v[2][1] = false;
557
-        v[2][2] = false;
558
-
559
-        Shape {
560
-            aabb_width: 3,
561
-            aabb_height: 3,
562
-            real_height: 2,
563
-            fully_rotate: false,
564
-            form: v,
565
-        }
566
-    }
567
-
568
-    fn t_shape() -> Shape {
569
-        //
570
-        // ___
571
-        // XXX
572
-        // _X_
573
-        //
574
-        let mut v = vec![vec![true; 3]; 3];
575
-        v[0][0] = false;
576
-        v[0][2] = false;
577
-        v[2][0] = false;
578
-        v[2][1] = false;
579
-        v[2][2] = false;
580
-
581
-        Shape {
582
-            aabb_width: 3,
583
-            aabb_height: 3,
584
-            real_height: 2,
585
-            fully_rotate: true,
586
-            form: v,
587
-        }
588
-    }
589
-
590
-    fn z_shape() -> Shape {
591
-        //
592
-        // ___
593
-        // XX_
594
-        // _XX
595
-        //
596
-        let mut v = vec![vec![true; 3]; 3];
597
-        v[0][0] = false;
598
-        v[1][2] = false;
599
-        v[2][0] = false;
600
-        v[2][1] = false;
601
-        v[2][2] = false;
602
-
603
-        Shape {
604
-            aabb_width: 3,
605
-            aabb_height: 3,
606
-            real_height: 2,
607
-            fully_rotate: false,
608
-            form: v,
609
-        }
610
-    }
611
-
612
-    res.insert(ShapeKind::I, i_shape());
613
-    res.insert(ShapeKind::J, j_shape());
614
-    res.insert(ShapeKind::L, l_shape());
615
-    res.insert(ShapeKind::O, o_shape());
616
-    res.insert(ShapeKind::S, s_shape());
617
-    res.insert(ShapeKind::T, t_shape());
618
-    res.insert(ShapeKind::Z, z_shape());
619
-
620
-    res
621
-}
622
-
623
-fn render_board(geometry: &mut Geometry, board: &Vec<Vec<Block>>, board_offset: &Block2D) {
624
-
625
-    let mut x = 0;
626
-    let mut y = 0;
627
-
628
-    for row in board {
629
-        for block in row {
630
-            let c = if block.active {
631
-                Col::new(1.0, 0.0, 0.0, 1.0)
632
-            } else {
633
-                Col::new(1.0, 1.0, 0.0, 0.1)
634
-            };
635
-
636
-            geometry.push_sprite(Sprite::Block,
637
-                                 c,
638
-                                 Block2D{ x: x + board_offset.x, y: y + board_offset.y },
639
-                                 block.offset);
640
-            x += 1;
641
-        }
642
-        x = 0;
643
-        y += 1;
644
-    }
645
-
646
-}
647
-
648
-fn render_score(geometry: &mut Geometry, score: i32, offset: Block2D, colour: Col) {
649
-    geometry.push_text("SCORE", offset, colour);
650
-    geometry.push_text(&score.to_string(), Block2D { x: offset.x, y: offset.y - 1 }, colour);
651
-}
652
-
653
-fn render_level(geometry: &mut Geometry, level: i32, offset: Block2D, colour: Col) {
654
-    geometry.push_text("LEVEL", offset, colour);
655
-    geometry.push_text(&level.to_string(), Block2D { x: offset.x, y: offset.y - 1 }, colour);
656
-}
657
-
658
-fn render_lines(geometry: &mut Geometry, lines: i32, offset: Block2D, colour: Col) {
659
-    geometry.push_text("LINES", offset, colour);
660
-    geometry.push_text(&lines.to_string(), Block2D { x: offset.x, y: offset.y - 1 }, colour);
661
-}
662
-
663
-fn render_next_shape(geometry: &mut Geometry, shape: &Shape, offset: Block2D, colour: Col) {
664
-    let board_positions = get_board_positions(shape, &BoardPos { x: 0, y: 0}, &PieceAngle::R0);
665
-    for pos in board_positions {
666
-        geometry.push_sprite(Sprite::Block,
667
-                             colour,
668
-                             Block2D {x: pos.x + offset.x, y: pos.y + offset.y},
669
-                             Vec2D {x: 0.0, y: 0.0 });
670
-    }
671
-}

+ 19
- 19
web/js/index.js View File

@@ -1,4 +1,4 @@
1
-import { GameBridge } from "../wasm_tetris";
1
+import { Bridge } from "../wasm_tetris";
2 2
 import { memory } from "../wasm_tetris_bg";
3 3
 import { GLRenderer } from "./GLRenderer";
4 4
 
@@ -14,7 +14,7 @@ let gState = {
14 14
 
15 15
   tileset: '/web/img/tileset.png',
16 16
 
17
-  gameBridge: undefined,
17
+  bridge: undefined,
18 18
   animationId: undefined,
19 19
   renderer: undefined,
20 20
 
@@ -26,10 +26,10 @@ const tick = () => {
26 26
   const delta = now - gState.performanceNow;
27 27
   gState.performanceNow = now;
28 28
 
29
-  const gameBridge = gState.gameBridge;
29
+  const bridge = gState.bridge;
30 30
 
31 31
   // game state + time + user input -> game state
32
-  const tickAgain = gameBridge.tick(delta, now);
32
+  const tickAgain = bridge.tick(delta, now);
33 33
 
34 34
   render();
35 35
 
@@ -39,16 +39,16 @@ const tick = () => {
39 39
 };
40 40
 
41 41
 const render = () => {
42
-  const gameBridge = gState.gameBridge;
42
+  const bridge = gState.bridge;
43 43
   const renderer = gState.renderer;
44 44
 
45
-  // gameBridge state -> geometry
46
-  gameBridge.update_geometry();
45
+  // bridge state -> geometry
46
+  bridge.update_geometry();
47 47
 
48 48
   // render geometry
49 49
   const memoryF32 = new Float32Array(memory.buffer);
50
-  const geo_len = gameBridge.geo_len();
51
-  const geo_ptr = gameBridge.geo_ptr();
50
+  const geo_len = bridge.geo_len();
51
+  const geo_ptr = bridge.geo_ptr();
52 52
 
53 53
   renderer.preDrawScene(gState.canvasWidth, gState.canvasHeight);
54 54
   renderer.drawBuffer(memoryF32, geo_len, geo_ptr);
@@ -61,16 +61,16 @@ function main() {
61 61
 
62 62
   let renderer = new GLRenderer(gState, canvasElement);
63 63
   renderer.loadTexture(gState.tileset).then(([tilesetWidth, tilesetHeight]) => {
64
-    gState.gameBridge = new GameBridge(gState.canvasWidth,
65
-                                       gState.canvasHeight,
66
-                                       gState.blockSize,
67
-                                       tilesetWidth,
68
-                                       tilesetHeight);
64
+    gState.bridge = new Bridge(gState.canvasWidth,
65
+                                   gState.canvasHeight,
66
+                                   gState.blockSize,
67
+                                   tilesetWidth,
68
+                                   tilesetHeight);
69 69
 
70 70
     gState.renderer = renderer;
71 71
 
72 72
     document.addEventListener('keydown', event => {
73
-      let key_event_return = gState.gameBridge.event_key_down(event.key);
73
+      let key_event_return = gState.bridge.event_key_down(event.key);
74 74
 
75 75
       if (key_event_return.prevent_default()) {
76 76
         event.preventDefault();
@@ -82,7 +82,7 @@ function main() {
82 82
       key_event_return.free();
83 83
     });
84 84
     document.addEventListener('keyup', event => {
85
-      let key_event_return = gState.gameBridge.event_key_up(event.key);
85
+      let key_event_return = gState.bridge.event_key_up(event.key);
86 86
 
87 87
       if (key_event_return.prevent_default()) {
88 88
         event.preventDefault();
@@ -95,12 +95,12 @@ function main() {
95 95
     });
96 96
 
97 97
 
98
-    gState.gameBridge.init(Math.random());
98
+    gState.bridge.init(Math.random());
99 99
     tick();
100 100
   }).catch(error => console.error(error));
101 101
 
102
-  // isg: NOTE: remember to free GameBridge
103
-  // gState.gameBridge.free();
102
+  // isg: NOTE: remember to free Bridge
103
+  // gState.bridge.free();
104 104
 }
105 105
 
106 106
 main();

Loading…
Cancel
Save