Browse Source

completed lines flash

Inderjit Gill 1 year ago
parent
commit
b3bff2d60e
1 changed files with 177 additions and 99 deletions
  1. 177
    99
      src/game.rs

+ 177
- 99
src/game.rs View File

@@ -16,6 +16,13 @@ pub enum GameMode {
16 16
     Paused,
17 17
 }
18 18
 
19
+#[derive(PartialEq)]
20
+pub enum GameState {
21
+    Playing,
22
+    PieceIsDropping,
23
+    LinesFlashing,
24
+}
25
+
19 26
 // hacky: make sure these values match up with code in web/js/Audio.js
20 27
 #[derive(Clone, Copy)]
21 28
 pub enum SoundEffect {
@@ -35,6 +42,10 @@ pub struct Block {
35 42
     occupied: bool,
36 43
     offset: Vec2D,
37 44
     colour: Col,
45
+
46
+    hue: f64,
47
+    flashing_bright_colour: Col,
48
+    flashing_dull_colour: Col,
38 49
 }
39 50
 
40 51
 impl Block {
@@ -43,6 +54,9 @@ impl Block {
43 54
             occupied: false,
44 55
             offset: Vec2D { x: 0.0, y: 0.0 },
45 56
             colour: col,
57
+            hue: 0.0,
58
+            flashing_bright_colour: col,
59
+            flashing_dull_colour: col,
46 60
         }
47 61
     }
48 62
 
@@ -50,6 +64,35 @@ impl Block {
50 64
         self.occupied = false;
51 65
         self.offset = Vec2D { x: 0.0, y: 0.0 };
52 66
         self.colour = *colour;
67
+        self.hue = 0.0;
68
+        self.flashing_bright_colour = *colour;
69
+        self.flashing_dull_colour = *colour;
70
+    }
71
+
72
+    pub fn flash_on(&mut self) {
73
+        self.colour = self.flashing_bright_colour;
74
+    }
75
+
76
+    pub fn flash_off(&mut self) {
77
+        self.colour = self.flashing_dull_colour;
78
+    }
79
+
80
+    pub fn set(&mut self, occupied: bool, colour: &Col, hue: f64) {
81
+        self.occupied = occupied;
82
+        self.hue = hue;
83
+        if occupied {
84
+            // set the colour straight away if it's occupied
85
+            // (unoccupied blocks will fade out, see update_block_colours)
86
+            self.colour = *colour;
87
+
88
+            // also calculate the flashing colours now
89
+            if let Ok(bright) = get_colour_from_hsl(hue, 85.0, 70.0, 1.0) {
90
+                self.flashing_bright_colour = bright;
91
+            }
92
+            if let Ok(dull) = get_colour_from_hsl(hue, 85.0, 30.0, 1.0) {
93
+                self.flashing_dull_colour = dull;
94
+            }
95
+        }
53 96
     }
54 97
 }
55 98
 
@@ -105,6 +148,20 @@ impl Board {
105 148
         self.crt_update_speed = new_crt_update_speed;
106 149
     }
107 150
 
151
+    pub fn flash_complete_lines(&mut self, lines_flashing_time: f32) {
152
+        for line in 0..self.height {
153
+            if self.is_line_filled(line) {
154
+                for x in 0..self.width {
155
+                    if lines_flashing_time > 250.0 && lines_flashing_time < 350.0 {
156
+                        self.board[line][x].flash_off();
157
+                    } else {
158
+                        self.board[line][x].flash_on();
159
+                    }
160
+                }
161
+            }
162
+        }
163
+    }
164
+
108 165
     pub fn is_line_filled(&self, line_number: usize) -> bool {
109 166
         if line_number >= self.height {
110 167
             // should error
@@ -175,19 +232,23 @@ pub struct Piece {
175 232
     pos: BoardPos,
176 233
     angle: PieceAngle,
177 234
     colour: Col,
235
+    hue: f64,
178 236
 }
179 237
 
180 238
 pub struct Game {
181 239
     mode: GameMode,
182 240
     resume_from_paused: bool,
183 241
 
242
+    game_state: GameState,
243
+    lines_flashing_time: f32, // ms
244
+
184 245
     board: Board,
185 246
     board_offset: Block2D,
186 247
 
187 248
     shapes: HashMap<ShapeKind, Shape>,
188 249
     piece: Piece, // the current piece under player control
189 250
     next_piece_shape: ShapeKind,
190
-    next_piece_colour: Col,
251
+    next_piece_hue: f64,
191 252
 
192 253
     colour_saturation: f64,
193 254
     colour_lightness: f64,
@@ -209,7 +270,6 @@ pub struct Game {
209 270
     tetris: bool,
210 271
 
211 272
     time_to_next_lowering: f32, // ms
212
-    piece_is_dropping: bool,
213 273
     impact_sfx_played: bool,
214 274
 
215 275
     default_cooldown: f32, // ms
@@ -239,6 +299,9 @@ impl Game {
239 299
             mode: GameMode::Playing,
240 300
             resume_from_paused: false,
241 301
 
302
+            game_state: GameState::Playing,
303
+            lines_flashing_time: 0.0,
304
+
242 305
             board: Board::new(config),
243 306
             board_offset: Block2D {
244 307
                 x: config.board_offset_x,
@@ -251,9 +314,10 @@ impl Game {
251 314
                 pos: BoardPos { x: 5, y: 19 },
252 315
                 angle: PieceAngle::R0,
253 316
                 colour: Col::new(0.4, 0.8, 0.2, 0.9),
317
+                hue: 0.0,
254 318
             },
255 319
             next_piece_shape: ShapeKind::L,
256
-            next_piece_colour: Col::new(1.0, 1.0, 1.0, 1.0),
320
+            next_piece_hue: 1.0,
257 321
 
258 322
             colour_saturation: config.colour_saturation,
259 323
             colour_lightness: config.colour_lightness,
@@ -276,7 +340,6 @@ impl Game {
276 340
             tetris: false, // was the last scoring move a tetris?
277 341
 
278 342
             time_to_next_lowering: get_time_to_next_lowering(0),
279
-            piece_is_dropping: false,
280 343
             impact_sfx_played: false,
281 344
 
282 345
             default_cooldown: config.default_cooldown,
@@ -330,12 +393,7 @@ impl Game {
330 393
 
331 394
         self.board.reset();
332 395
         self.next_piece_shape = get_random_shape(1.0 - random)?;
333
-        self.next_piece_colour = get_colour_from_hsl(
334
-            ((1.0 - random) * 360.0) as f64,
335
-            self.colour_saturation,
336
-            self.colour_lightness,
337
-            1.0,
338
-        )?;
396
+        self.next_piece_hue = ((1.0 - random) * 360.0) as f64;
339 397
 
340 398
         use_next_shape(self, random)?;
341 399
 
@@ -360,12 +418,7 @@ impl Game {
360 418
 
361 419
     pub fn init(&mut self, random: f32) -> error::Result<()> {
362 420
         self.next_piece_shape = get_random_shape(1.0 - random)?;
363
-        self.next_piece_colour = get_colour_from_hsl(
364
-            ((1.0 - random) * 360.0) as f64,
365
-            self.colour_saturation,
366
-            self.colour_lightness,
367
-            1.0,
368
-        )?;
421
+        self.next_piece_hue = ((1.0 - random) * 360.0) as f64;
369 422
 
370 423
         use_next_shape(self, random)?;
371 424
         Ok(())
@@ -388,7 +441,12 @@ impl Game {
388 441
             render_lines(geometry, self.lines, Block2D { x, y: y + 6 }, c);
389 442
         }
390 443
         if let Some(shape) = self.shapes.get(&self.next_piece_shape) {
391
-            render_next_shape(geometry, &shape, Block2D { x, y }, self.next_piece_colour);
444
+            if let Ok(colour) = get_colour_from_hsl(self.next_piece_hue,
445
+                                                    self.colour_saturation,
446
+                                                    self.colour_lightness,
447
+                                                    1.0) {
448
+                render_next_shape(geometry, &shape, Block2D { x, y }, colour);
449
+            }
392 450
         }
393 451
 
394 452
         // render last as this contains a full-screen alpha
@@ -459,9 +517,6 @@ impl Game {
459 517
         if self.debug_mode {
460 518
             self.debug_flash = !self.debug_flash;
461 519
             self.debug_delta = corrected_delta;
462
-            // if self.debug_delta > 0.0 && self.debug_delta < 15.0 {
463
-            //     log(&format!("{}", self.debug_delta));
464
-            // }
465 520
         }
466 521
 
467 522
         match self.mode {
@@ -499,68 +554,83 @@ impl Game {
499 554
             remove_shape_from_board(&mut self.board, shape, &self.piece.pos, &self.piece.angle);
500 555
         }
501 556
 
502
-        apply_user_input(self, controller, delta);
503
-
504
-        if self.piece_is_dropping {
505
-            if let Some(shape) = self.shapes.get(&self.piece.shape_kind) {
506
-                if !lower_piece(&self.board, shape, &mut self.piece) {
507
-                    // NOTE: don't lock in the shape when it stops dropping.
508
-                    //
509
-                    // the game 'feels' better if the user has time to modify the
510
-                    // piece's position at the bottom of the drop
511
-                    self.piece_is_dropping = false;
512
-                    if !self.impact_sfx_played {
513
-                        play_sound(SoundEffect::Impact);
514
-                        self.impact_sfx_played = true;
557
+        match self.game_state {
558
+            GameState::Playing => {
559
+                apply_user_input(self, controller, delta);
560
+                if is_time_to_lower_piece(self, delta) {
561
+                    let mut piece_cant_go_lower = false;
562
+
563
+                    if let Some(shape) = self.shapes.get(&self.piece.shape_kind) {
564
+                        piece_cant_go_lower = !lower_piece(&self.board, shape, &mut self.piece);
515 565
                     }
516
-                }
517
-            }
518
-        } else if is_time_to_lower_piece(self, delta) {
519
-            let mut piece_cant_go_lower = false;
520 566
 
521
-            if let Some(shape) = self.shapes.get(&self.piece.shape_kind) {
522
-                piece_cant_go_lower = !lower_piece(&self.board, shape, &mut self.piece);
523
-            }
567
+                    if piece_cant_go_lower {
568
+                        if let Some(shape) = self.shapes.get(&self.piece.shape_kind) {
569
+                            add_shape_to_board(
570
+                                &mut self.board,
571
+                                shape,
572
+                                &self.piece
573
+                            );
574
+
575
+                            if any_complete_lines(&self.board) {
576
+                                let num_lines = num_complete_lines(&self.board);
577
+                                match num_lines {
578
+                                    1 => play_sound(SoundEffect::Line1),
579
+                                    2 => play_sound(SoundEffect::Line2),
580
+                                    3 => play_sound(SoundEffect::Line3),
581
+                                    4 => play_sound(SoundEffect::Line4),
582
+                                    _ => (),
583
+                                };
584
+
585
+                                self.lines_flashing_time = 0.0;
586
+                                self.game_state = GameState::LinesFlashing;
587
+                            } else if !self.impact_sfx_played {
588
+                                play_sound(SoundEffect::Impact);
589
+                                self.impact_sfx_played = true;
590
+                            }
591
+                        }
592
+
593
+                        use_next_shape(self, random)?;
594
+                        if check_for_game_over(self) {
595
+                            play_sound(SoundEffect::GameOver);
596
+                            log(&format!("i_count: {}", self.i_count));
597
+                            log(&format!("j_count: {}", self.j_count));
598
+                            log(&format!("l_count: {}", self.l_count));
599
+                            log(&format!("o_count: {}", self.o_count));
600
+                            log(&format!("s_count: {}", self.s_count));
601
+                            log(&format!("t_count: {}", self.t_count));
602
+                            log(&format!("z_count: {}", self.z_count));
603
+                            self.is_game_over = true;
604
+                        }
605
+                    }
606
+                }
607
+            },
608
+            GameState::PieceIsDropping => {
609
+                apply_user_input(self, controller, delta);
524 610
 
525
-            if piece_cant_go_lower {
526
-                let mut num_lines = 0;
527 611
                 if let Some(shape) = self.shapes.get(&self.piece.shape_kind) {
528
-                    add_shape_to_board(
529
-                        &mut self.board,
530
-                        shape,
531
-                        &self.piece.pos,
532
-                        &self.piece.angle,
533
-                        &self.piece.colour,
534
-                    );
535
-                    num_lines = remove_complete_lines(&mut self.board)?;
612
+                    if !lower_piece(&self.board, shape, &mut self.piece) {
613
+                        // NOTE: don't lock in the shape when it stops dropping.
614
+                        //
615
+                        // the game 'feels' better if the user has time to modify the
616
+                        // piece's position at the bottom of the drop
617
+                        self.game_state = GameState::Playing;
618
+                        if !self.impact_sfx_played {
619
+                            play_sound(SoundEffect::Impact);
620
+                            self.impact_sfx_played = true;
621
+                        }
622
+                    }
536 623
                 }
624
+            },
625
+            GameState::LinesFlashing => {
626
+                self.lines_flashing_time += delta;
627
+                self.board.flash_complete_lines(self.lines_flashing_time);
628
+                if self.lines_flashing_time > 500.0 {
537 629
 
538
-                if num_lines > 0 {
539
-                    match num_lines {
540
-                        1 => play_sound(SoundEffect::Line1),
541
-                        2 => play_sound(SoundEffect::Line2),
542
-                        3 => play_sound(SoundEffect::Line3),
543
-                        4 => play_sound(SoundEffect::Line4),
544
-                        _ => (),
545
-                    };
546
-
630
+                    let num_lines = remove_complete_lines(&mut self.board)?;
547 631
                     update_score(self, num_lines);
548
-                } else if !self.impact_sfx_played {
549
-                    play_sound(SoundEffect::Impact);
550
-                    self.impact_sfx_played = true;
551
-                }
552 632
 
553
-                use_next_shape(self, random)?;
554
-                if check_for_game_over(self) {
555
-                    play_sound(SoundEffect::GameOver);
556
-                    log(&format!("i_count: {}", self.i_count));
557
-                    log(&format!("j_count: {}", self.j_count));
558
-                    log(&format!("l_count: {}", self.l_count));
559
-                    log(&format!("o_count: {}", self.o_count));
560
-                    log(&format!("s_count: {}", self.s_count));
561
-                    log(&format!("t_count: {}", self.t_count));
562
-                    log(&format!("z_count: {}", self.z_count));
563
-                    self.is_game_over = true;
633
+                    self.game_state = GameState::Playing;
564 634
                 }
565 635
             }
566 636
         }
@@ -569,9 +639,7 @@ impl Game {
569 639
             add_shape_to_board(
570 640
                 &mut self.board,
571 641
                 shape,
572
-                &self.piece.pos,
573
-                &self.piece.angle,
574
-                &self.piece.colour,
642
+                &self.piece
575 643
             );
576 644
         }
577 645
 
@@ -625,7 +693,8 @@ fn use_next_shape(game: &mut Game, random: f32) -> error::Result<()> {
625 693
         game.piece.pos.x = ((game.board.width as i32) - shape.aabb_width) / 2;
626 694
         game.piece.pos.y = (game.board.height as i32) - shape.real_height;
627 695
         game.piece.angle = PieceAngle::R0;
628
-        game.piece.colour = game.next_piece_colour;
696
+        game.piece.colour = get_colour_from_hsl(game.next_piece_hue, game.colour_saturation, game.colour_lightness, 1.0)?;
697
+        game.piece.hue = game.next_piece_hue;
629 698
     }
630 699
 
631 700
     match game.piece.shape_kind {
@@ -640,12 +709,7 @@ fn use_next_shape(game: &mut Game, random: f32) -> error::Result<()> {
640 709
 
641 710
     game.impact_sfx_played = false;
642 711
     game.next_piece_shape = get_random_shape(random)?;
643
-    game.next_piece_colour = get_colour_from_hsl(
644
-        (random * 360.0) as f64,
645
-        game.colour_saturation,
646
-        game.colour_lightness,
647
-        1.0,
648
-    )?;
712
+    game.next_piece_hue = (random * 360.0) as f64;
649 713
 
650 714
     Ok(())
651 715
 }
@@ -766,7 +830,7 @@ fn apply_user_input(game: &mut Game, controller: &Controller, delta: f32) {
766 830
         }
767 831
 
768 832
         if controller.just_pressed(ControllerButton::Down) {
769
-            game.piece_is_dropping = true;
833
+            game.game_state = GameState::PieceIsDropping;
770 834
         }
771 835
 
772 836
         if can_apply_input(
@@ -869,6 +933,27 @@ fn is_colliding(board: &Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle
869 933
         .any(|p| board.board[p.y as usize][p.x as usize].occupied)
870 934
 }
871 935
 
936
+fn any_complete_lines(board: &Board) -> bool {
937
+    for y in 0..board.height {
938
+        if board.is_line_filled(y) {
939
+            return true
940
+        }
941
+    }
942
+    false
943
+}
944
+
945
+fn num_complete_lines(board: &Board) -> i32 {
946
+    let mut count = 0;
947
+
948
+    for y in 0..board.height {
949
+        if board.is_line_filled(y) {
950
+            count += 1;
951
+        }
952
+    }
953
+
954
+    count
955
+}
956
+
872 957
 fn remove_complete_lines(board: &mut Board) -> error::Result<i32> {
873 958
     let mut offset = 0;
874 959
 
@@ -885,13 +970,15 @@ fn remove_complete_lines(board: &mut Board) -> error::Result<i32> {
885 970
 fn add_shape_to_board(
886 971
     board: &mut Board,
887 972
     shape: &Shape,
888
-    pos: &BoardPos,
889
-    angle: &PieceAngle,
890
-    colour: &Col,
973
+    piece: &Piece
891 974
 ) {
975
+    let pos = &piece.pos;
976
+    let angle = &piece.angle;
977
+    let colour = &piece.colour;
978
+
892 979
     for pos in get_board_positions(shape, pos, angle) {
893 980
         if is_valid_board_position(board, &pos) {
894
-            set_block(board, pos.x, pos.y, true, colour);
981
+            board.board[pos.y as usize][pos.x as usize].set(true, colour, piece.hue);
895 982
         }
896 983
     }
897 984
 }
@@ -900,7 +987,7 @@ fn remove_shape_from_board(board: &mut Board, shape: &Shape, pos: &BoardPos, ang
900 987
     let inactive_colour = board.inactive_colour;
901 988
     for pos in get_board_positions(shape, &pos, &angle) {
902 989
         if is_valid_board_position(board, &pos) {
903
-            set_block(board, pos.x, pos.y, false, &inactive_colour);
990
+            board.board[pos.y as usize][pos.x as usize].set(false, &inactive_colour, 0.0);
904 991
         }
905 992
     }
906 993
 }
@@ -950,15 +1037,6 @@ fn get_board_positions(shape: &Shape, pos: &BoardPos, angle: &PieceAngle) -> Vec
950 1037
     res
951 1038
 }
952 1039
 
953
-fn set_block(board: &mut Board, x: i32, y: i32, occupied: bool, colour: &Col) {
954
-    board.board[y as usize][x as usize].occupied = occupied;
955
-    if occupied {
956
-        // set the colour straight away if it's occupied
957
-        // (unoccupied blocks will fade out, see update_block_colours)
958
-        board.board[y as usize][x as usize].colour = *colour;
959
-    }
960
-}
961
-
962 1040
 fn update_block_colours(board: &mut Board) {
963 1041
     for mut row in &mut board.board {
964 1042
         for mut block in row {

Loading…
Cancel
Save