diff --git a/README.md b/README.md index 8eb8330..4c6ea7b 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ This isn't just about coding; it's about building something I love from the grou 3. [x] **Pause and Resume**: - [x] Allow the game to pause (e.g., with the Escape key) and resume. 4. [ ] **Basic Leaderboard**: - - [ ] Track high scores locally. + - [X] Track high scores locally. - [ ] Display the leaderboard after a game ends. --- diff --git a/game/game.py b/game/game.py index 4ba5098..c95c8bd 100644 --- a/game/game.py +++ b/game/game.py @@ -69,6 +69,9 @@ class Game: def update(self): """Update the game state.""" + if self.game_grid.cleared_rows: # If an animation is running, wait before dropping pieces + return # Skip movement updates + current_time = pg.time.get_ticks() fall_interval = get_fall_interval(settings.game_score) @@ -117,10 +120,13 @@ class Game: self.hold_tetromino.draw_next_tetromino(self.hold_surface) # Draw score - draw_text(self.screen, f'Score: {settings.game_score}', get_font(20), self.width - 150, 100, (255, 255, 255)) + draw_text(self.screen, f'score: {settings.game_score}', get_font(20), self.width - 150, 100, (255, 255, 255)) # Render surfaces self.screen.blit(self.grid_surface, (settings.width // 2 - 150, 50)) + + self.game_grid.animate_line_clear(self.screen) + self.screen.blit(self.next_surface, (self.width - 150, 200)) self.screen.blit(self.hold_surface, (self.width - 150, 400)) diff --git a/game/game_manager.py b/game/game_manager.py index 1880b43..6d3bdf2 100644 --- a/game/game_manager.py +++ b/game/game_manager.py @@ -10,6 +10,8 @@ class Grid(): self.color = set_color() self.grid = [[0 for j in range(self.cols)] for k in range(self.rows)] + self.cleared_rows = [] + def draw_grid(self, surface): for row in range(self.rows): for col in range(self.cols): @@ -66,10 +68,48 @@ class Grid(): return True def check_full_row(self): - """Check for and clear any full rows.""" - new_grid = [row for row in self.grid if not all(cell != 0 for cell in row)] - cleared_rows = len(self.grid) - len(new_grid) - self.grid = [[0] * self.cols for _ in range(cleared_rows)] + new_grid + """Check for and store full rows for animation before clearing them.""" + self.cleared_rows = [i for i, row in enumerate(self.grid) if all(cell != 0 for cell in row)] + + if self.cleared_rows: + self.clearing_animation_timer = pygame.time.get_ticks() # Start animation timer + + def animate_line_clear(self, screen): + """Handles the visual effect when clearing rows.""" + if not self.cleared_rows: + return + + elapsed_time = pygame.time.get_ticks() - self.clearing_animation_timer + + if elapsed_time < 500: # Animation lasts 500ms + fade_alpha = int(255 * (1 - elapsed_time / 500)) # Fading effect + if elapsed_time > 400: + fade_color = (0, 0, 0, fade_alpha) # White fading color + elif elapsed_time > 300: + fade_color = (100, 100, 100, fade_alpha) + elif elapsed_time > 200: + fade_color = (150, 150, 150, fade_alpha) + elif elapsed_time > 100: + fade_color = (200, 200, 200, fade_alpha) + else: + fade_color = (255, 255, 255, fade_alpha) + + for row in self.cleared_rows: + for col in range(self.cols): + rect = pygame.Rect(col * self.cell_size + settings.width // 2 -150, row * self.cell_size +50, self.cell_size, self.cell_size) + pygame.draw.rect(screen, fade_color[:3], rect) + pygame.draw.rect(screen, (0, 0, 0), rect, 1) + else: + self.clear_rows() # Remove rows after animation is complete + + def clear_rows(self): + """Removes full rows after the animation finishes.""" + new_grid = [row for i, row in enumerate(self.grid) if i not in self.cleared_rows] + cleared_count = len(self.cleared_rows) + + # Add empty rows at the top + self.grid = [[0] * self.cols for _ in range(cleared_count)] + new_grid score_table = {1: 100, 2: 300, 3: 500, 4: 800} - settings.game_score += score_table.get(cleared_rows, 0) \ No newline at end of file + settings.game_score += score_table.get(len(self.cleared_rows), 0) + self.cleared_rows = [] # Reset after clearing diff --git a/game/menu.py b/game/menu.py index 4ba3c8c..070c8e4 100644 --- a/game/menu.py +++ b/game/menu.py @@ -52,7 +52,7 @@ class StartMenu: sys.exit() def draw(self): - self.screen.blit(get_background(), (0, 0)) + self.screen.blit(get_background()) self.play = Button(image=None, pos=(setting.width // 2, setting.height // 3), text_input="Play", font=get_font(50), base_color=(255, 255, 255), hovering_color=(200, 200, 200)) self.play.changeColor(self.mouse_pos) @@ -66,9 +66,11 @@ class StartMenu: draw_text(self.screen, "TETRIS", get_font(30), setting.width // 2, setting.height // 3 - 50, (255, 255, 255)) position = 0 + count = 1 for item in self.score["score"]: - draw_text(self.screen, f"Score: {item}", get_font(30), setting.width // 5, setting.height // 3 - 50 + position, (255, 255, 255)) + draw_text(self.screen, f"{count}: {item}", get_font(30), setting.width // 5, setting.height // 3 - 50 + position, (255, 255, 255)) position += 50 + count += 1 self.score = load_score("gamescore.json") diff --git a/game/util/util.py b/game/util/util.py index edde725..4930c68 100644 --- a/game/util/util.py +++ b/game/util/util.py @@ -35,20 +35,30 @@ def load_score(filename): return json.load(file) return {} -def add_score(filename, score): - if os.path.exists(filename): - with open(filename, "r") as file: - data = json.load(file) - # If the stored data doesn't have a "score" key for some reason, ensure it does. - if "score" not in data: +def add_score(filename, score): + """Adds a new score only if it's in the top 5. Sorts scores in descending order.""" + + # Load existing data or create a new leaderboard + if os.path.exists(filename) and os.path.getsize(filename) > 0: + with open(filename, "r") as file: + try: + data = json.load(file) + except json.JSONDecodeError: + data = {"score": []} # Reset if JSON is corrupted + else: + data = {"score": []} + + # Ensure "score" is a list + if "score" not in data or not isinstance(data["score"], list): data["score"] = [] - # Append the new score. - # You can also handle multiple scores by extending the list, e.g. `data["score"].extend(new_score)`. - data["score"].append(score) + # Only add the score if there are less than 5 scores or it's higher than the lowest + if len(data["score"]) < 5 or score > min(data["score"]): + data["score"].append(score) # Add new score + data["score"] = sorted(data["score"], reverse=True)[:5] # Sort & keep top 5 - # Write the updated data back to the file. + # Save the updated leaderboard with open(filename, "w") as file: json.dump(data, file, indent=2) diff --git a/gamescore.json b/gamescore.json index 4230637..fce8e40 100644 --- a/gamescore.json +++ b/gamescore.json @@ -1,7 +1,9 @@ { "score": [ - 2300, - 100, - 6000 + 2200, + 1100, + 1100, + 900, + 600 ] } \ No newline at end of file