From 3deb552325c741096ba07dd6f6be0c1336543330 Mon Sep 17 00:00:00 2001 From: frank Date: Thu, 1 Dec 2022 18:35:59 -0500 Subject: [PATCH] draw scores once and set clip --- NS.py | 188 ++++++++++++++++++++++++++++++-------------- config | 1 + lib/pgfw | 2 +- resource/BPmono.ttf | Bin 0 -> 23568 bytes resource/scores | 94 ++++++++++++++++++++++ 5 files changed, 227 insertions(+), 58 deletions(-) create mode 100755 resource/BPmono.ttf diff --git a/NS.py b/NS.py index 28175fc..03e546f 100644 --- a/NS.py +++ b/NS.py @@ -102,7 +102,7 @@ class NS(Game, Animation): def formatted(self): if self.milliseconds is None: - return "--:--.-" + return "-:--.-" else: minutes, remainder = divmod(int(self.milliseconds), 60000) seconds, fraction = divmod(remainder, 1000) @@ -194,7 +194,8 @@ class NS(Game, Animation): "display": { "float": "attract-gif-alpha", - "bool": ["effects", "alpha-effect-title"] + "bool": ["effects", "alpha-effect-title"], + "path": "scores-font" }, "system": { @@ -305,13 +306,12 @@ class NS(Game, Animation): self.register(self.close_pop_up) self.reset() self.most_recent_score = None - self.pop_up_font = pygame.font.Font( - self.get_resource(Dialogue.FONT_PATH), 12) + self.pop_up_font = pygame.font.Font(self.get_resource(Dialogue.FONT_PATH), 12) self.pop_up_text = "" # Start the score list with all blank scores self.scores = [] - blank_count = 14 + blank_count = 25 for level_index in range(3): for _ in range(blank_count): self.scores.append(NS.Score.blank_level(level_index)) @@ -1066,13 +1066,18 @@ class Title(Animation): @param parent GameChild object that will connect this GameChild object to the overall tree and root Game object """ Animation.__init__(self, parent) - ds = self.get_display_surface() - dsr = ds.get_rect() + + # Set up attract mode pop-up self.angle = pi / 8 self.video = Video(self, 320) self.video.location.center = 329, 182 self.register(self.show_video, self.hide_video) self.show_video() + + # Set up scores + font_path = self.get_resource(self.get_configuration("display", "scores-font")) + self.heading_font = pygame.font.Font(font_path, 22) + self.score_font = pygame.font.Font(font_path, 16) self.score_sprites = [] def reset(self): @@ -1112,57 +1117,117 @@ class Title(Animation): else: self.get_game().level_select.launch(0) + def draw_score_to_column(self, score, column, screen_pos, rank): + """ + Blit `score` onto `column`, taking positioning and rank into account + + @param score Score to display in top scores + @param column Surface for displaying score + @param screen_pos absolute screen (x, y) of score topleft + @param rank rank of score + @return height of the drawn score + """ + # Parse both strings and score objects + if isinstance(score, NS.Score): + text = score.formatted() + else: + text = score + + # The background is based on rank + if rank == 0: + bg = 255, 215, 0 + elif rank == 1: + bg = 192, 192, 192 + elif rank == 2: + bg = 205, 127, 50 + else: + bg = 255, 255, 255 + + # Draw the score + score_surface = render_box(self.score_font, text, width=column.get_width(), background=bg) + column.blit(score_surface, (0, screen_pos[1])) + + # Create a blink effect for the most recent score + if score == self.get_game().most_recent_score: + self.score_blanker = BlinkingSprite(self, 500) + blank = pygame.surface.Surface(score_surface.get_size()) + blank.fill(bg) + self.score_blanker.add_frame(blank) + self.score_blanker.location = screen_pos + + # The height is used to move the draw location + return score_surface.get_height() + + def draw_heading_to_column(self, text, column, y): + """ + Blit `text` on `column` in the heading style + + @param text heading text to blit + @param column Surface where heading will be blit + @param y top position of text + @return height of drawn heading + """ + heading = render_box(self.heading_font, text, color=(255, 255, 255), background=(0, 0, 0), width=column.get_width()) + column.blit(heading, (0, y)) + return heading.get_height() + def draw_scores(self): """ - Draw frames for a sprite object for each score and store the sprite in a list to be drawn each frame. + Create two columns, one for each side of the screen. Draw as many scores as can fit along each column, in order from best to worst, separating + them evenly into categories: normal, advanced, and expert. Draw the two columns to the display surface, with the expectation that they will be + removed from the clip and will not be drawn over. Note that this doesn't support non-level select mode anymore. """ - # Create a list of strings in order of which to draw - if not self.get_configuration("system", "enable-level-select"): - entries = ["BEST"] + sorted([score for score in self.get_game().scores if score.is_full()])[:15] - else: - entries = ["NORMAL"] + sorted([score for score in self.get_game().scores if score.level_index == 0])[:3] + \ - ["ADVANCED"] + sorted([score for score in self.get_game().scores if score.level_index == 1])[:3] + \ - ["EXPERT"] + sorted([score for score in self.get_game().scores if score.level_index == 2])[:7] - - # Create a sprite object for each score and place on the screen in two columns on the left and right edges the screen - step = 56 ds = self.get_display_surface() - self.score_sprites = [] - for ii, entry in enumerate(entries): - - # Reset y counter - if ii == 0 or ii == 8: - y = 20 - - font = pygame.font.Font(self.get_resource(Dialogue.FONT_PATH), 18) - - # Parse both strings and score objects - if isinstance(entry, NS.Score): - text = entry.formatted() + self.score_blanker = None + heading_width, heading_height = self.heading_font.size("ADVANCED") + heading_width += 10 + score_height = self.score_font.size("0")[1] + column_width, column_height = heading_width, ds.get_height() + left_score_count = (column_height - heading_height * 2) // score_height + right_score_count = (column_height - heading_height) // score_height + total_score_count = left_score_count + right_score_count + per_category_count, remainder = divmod(total_score_count, 3) + left_column_sprite = Sprite(self) + left_column = pygame.surface.Surface((column_width, column_height)) + left_column.fill((255, 255, 255)) + x, y = 0, 0 + y += self.draw_heading_to_column("NORMAL", left_column, y) + count = per_category_count + if remainder: + count += 1 + remainder -= 1 + for rank, score in enumerate(sorted([score for score in self.get_game().scores if score.level_index == 0])[:count]): + y += self.draw_score_to_column(score, left_column, (x, y), rank) + left_score_count -= 1 + y += self.draw_heading_to_column("ADVANCED", left_column, y) + count = per_category_count + if remainder: + count += 1 + remainder -= 1 + right_column_sprite = Sprite(self) + right_column = pygame.surface.Surface((column_width, column_height)) + right_column.fill((255, 255, 255)) + column = left_column + for rank, score in enumerate(sorted([score for score in self.get_game().scores if score.level_index == 1])[:count]): + y += self.draw_score_to_column(score, column, (x, y), rank) + if left_score_count == 1: + y = 0 + x = ds.get_width() - column_width + column = right_column + left_column_sprite.add_frame(left_column) + if left_score_count == 0: + right_score_count -= 1 else: - text = entry - - # Create a surface as a box around the score - message = render_box(font, text, True, Color(255, 255, 255), Color(128, 128, 128), Color(0, 0, 0), padding=2) - message.set_alpha(230) - - # Store it in a sprite, use a blinking sprite for the most recent score - if not entry == self.get_game().most_recent_score: - sprite = Sprite(self) - else: - sprite = BlinkingSprite(self, 500) - sprite.add_frame(message) - self.score_sprites.append(sprite) - - # Place the sprite along the column - sprite.location.top = y - if ii < 8: - sprite.location.left = -1 - else: - sprite.location.right = ds.get_width() + 1 - - # Move to the next sprite location - y += step + left_score_count -= 1 + y += self.draw_heading_to_column("EXPERT", right_column, y) + count = per_category_count + for rank, score in enumerate(sorted([score for score in self.get_game().scores if score.level_index == 2])[:count]): + y += self.draw_score_to_column(score, right_column, (x, y), rank) + right_column_sprite.add_frame(right_column) + right_column_sprite.location.topleft = x, 0 + self.score_sprites = [left_column_sprite, right_column_sprite] + for sprite in self.score_sprites: + sprite.update() def show_video(self): self.video.unhide() @@ -1185,7 +1250,13 @@ class Title(Animation): if self.active: ds = self.get_display_surface() dsr = ds.get_rect() + + # Optimize by setting a clip that excludes the area where the scores are drawn + ds.set_clip((self.score_sprites[0].location.right, 0, self.score_sprites[1].location.left - self.score_sprites[0].location.right, dsr.height)) + + # Draw the background self.get_game().logo.update() + # Advance through the unlock pattern platform = self.get_game().platform if not self.get_game().wipe.is_playing() and platform.get_edge_pressed() == self.UNLOCK_MOVES[self.unlock_index]: @@ -1198,6 +1269,7 @@ class Title(Animation): platform.set_glowing(platform.get_buttons_from_edges([self.UNLOCK_MOVES[self.unlock_index]])) self.get_audio().play_sfx("land_0") self.get_game().tony.update() + # Bounce the GIF around the screen if self.video.location.right > dsr.right or self.video.location.left < dsr.left: self.angle = reflect_angle(self.angle, 0) @@ -1213,6 +1285,7 @@ class Title(Animation): self.video.move(dy=dsr.top - self.video.location.top) dx, dy = get_delta(self.angle, 5, False) self.video.move(dx, dy) + # Hide GIFs/attract mode (or keep them hidden) if input is detected. Set a countdown that will turn # attract mode back on if no input is detected before the countdown expires. As long as input keeps # being detected, this block will keep running and restarting the countdown. @@ -1221,10 +1294,11 @@ class Title(Animation): self.get_game().tony.set_frameset("static") self.halt() self.play(self.show_video, delay=self.get_configuration("time", "attract-reset-countdown"), play_once=True) - # self.video.update() - # self.draw_scores() - for score in self.score_sprites: - score.update() + + # Disable clip and draw blanker which creates a blinking affect for a single score + ds.set_clip(None) + if self.score_blanker: + self.score_blanker.update() class Dialogue(Animation): diff --git a/config b/config index 4129450..ca01c69 100644 --- a/config +++ b/config @@ -28,6 +28,7 @@ fullscreen = no attract-gif-alpha = 1.0 effects = True alpha-effect-title = False +scores-font = BPmono.ttf [system] # will force set display->effects to off diff --git a/lib/pgfw b/lib/pgfw index 0954cd7..314b722 160000 --- a/lib/pgfw +++ b/lib/pgfw @@ -1 +1 @@ -Subproject commit 0954cd768f6fed46bf77436317271865a888c3a1 +Subproject commit 314b722528a65e6f0053f8be1844c2d8818319f4 diff --git a/resource/BPmono.ttf b/resource/BPmono.ttf new file mode 100755 index 0000000000000000000000000000000000000000..e6bb690fb41e8d87b52c0a20efcb885f5c4263a8 GIT binary patch literal 23568 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 KVZ%Q%_J0AD?#)sF literal 0 HcmV?d00001 diff --git a/resource/scores b/resource/scores index e69de29..e3897e9 100644 --- a/resource/scores +++ b/resource/scores @@ -0,0 +1,94 @@ +48935 0 +50940 0 +51245 0 +51372 0 +51754 0 +52110 0 +52736 0 +52863 0 +53242 0 +53715 0 +54546 0 +54603 0 +55707 0 +56045 0 +56636 0 +56732 0 +57247 0 +57299 0 +57326 0 +57436 0 +57670 0 +57885 0 +59459 0 +60290 0 +60440 0 +60510 0 +60751 0 +60824 0 +60957 0 +61433 0 +61677 0 +61838 0 +62269 0 +62562 0 +63416 0 +64223 0 +64412 0 +64534 0 +64577 0 +65496 0 +66051 0 +66635 0 +67401 0 +67523 0 +68302 0 +68541 0 +68948 0 +69290 0 +69510 0 +70404 0 +70528 0 +71138 0 +71335 0 +71495 0 +71627 0 +73511 0 +73848 0 +74455 0 +75015 0 +75387 0 +75646 0 +76002 0 +76945 0 +77434 0 +77526 0 +77701 0 +79705 0 +82020 0 +82777 0 +87726 0 +90718 0 +91883 0 +80500 1 +89673 1 +93372 1 +93427 1 +97111 1 +105880 1 +107336 1 +108024 1 +109913 1 +111785 1 +112993 1 +113387 1 +114742 1 +116102 1 +117369 1 +117614 1 +117744 1 +119812 1 +120058 1 +120493 1 +85743 2 +103819 2