draw scores once and set clip

This commit is contained in:
ohsqueezy 2022-12-01 18:35:59 -05:00
parent e0b4ed0251
commit 3deb552325
5 changed files with 227 additions and 58 deletions

188
NS.py
View File

@ -102,7 +102,7 @@ class NS(Game, Animation):
def formatted(self): def formatted(self):
if self.milliseconds is None: if self.milliseconds is None:
return "--:--.-" return "-:--.-"
else: else:
minutes, remainder = divmod(int(self.milliseconds), 60000) minutes, remainder = divmod(int(self.milliseconds), 60000)
seconds, fraction = divmod(remainder, 1000) seconds, fraction = divmod(remainder, 1000)
@ -194,7 +194,8 @@ class NS(Game, Animation):
"display": "display":
{ {
"float": "attract-gif-alpha", "float": "attract-gif-alpha",
"bool": ["effects", "alpha-effect-title"] "bool": ["effects", "alpha-effect-title"],
"path": "scores-font"
}, },
"system": "system":
{ {
@ -305,13 +306,12 @@ class NS(Game, Animation):
self.register(self.close_pop_up) self.register(self.close_pop_up)
self.reset() self.reset()
self.most_recent_score = None self.most_recent_score = None
self.pop_up_font = pygame.font.Font( self.pop_up_font = pygame.font.Font(self.get_resource(Dialogue.FONT_PATH), 12)
self.get_resource(Dialogue.FONT_PATH), 12)
self.pop_up_text = "" self.pop_up_text = ""
# Start the score list with all blank scores # Start the score list with all blank scores
self.scores = [] self.scores = []
blank_count = 14 blank_count = 25
for level_index in range(3): for level_index in range(3):
for _ in range(blank_count): for _ in range(blank_count):
self.scores.append(NS.Score.blank_level(level_index)) 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 @param parent GameChild object that will connect this GameChild object to the overall tree and root Game object
""" """
Animation.__init__(self, parent) Animation.__init__(self, parent)
ds = self.get_display_surface()
dsr = ds.get_rect() # Set up attract mode pop-up
self.angle = pi / 8 self.angle = pi / 8
self.video = Video(self, 320) self.video = Video(self, 320)
self.video.location.center = 329, 182 self.video.location.center = 329, 182
self.register(self.show_video, self.hide_video) self.register(self.show_video, self.hide_video)
self.show_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 = [] self.score_sprites = []
def reset(self): def reset(self):
@ -1112,57 +1117,117 @@ class Title(Animation):
else: else:
self.get_game().level_select.launch(0) 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): 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() ds = self.get_display_surface()
self.score_sprites = [] self.score_blanker = None
for ii, entry in enumerate(entries): heading_width, heading_height = self.heading_font.size("ADVANCED")
heading_width += 10
# Reset y counter score_height = self.score_font.size("0")[1]
if ii == 0 or ii == 8: column_width, column_height = heading_width, ds.get_height()
y = 20 left_score_count = (column_height - heading_height * 2) // score_height
right_score_count = (column_height - heading_height) // score_height
font = pygame.font.Font(self.get_resource(Dialogue.FONT_PATH), 18) total_score_count = left_score_count + right_score_count
per_category_count, remainder = divmod(total_score_count, 3)
# Parse both strings and score objects left_column_sprite = Sprite(self)
if isinstance(entry, NS.Score): left_column = pygame.surface.Surface((column_width, column_height))
text = entry.formatted() 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: else:
text = entry left_score_count -= 1
y += self.draw_heading_to_column("EXPERT", right_column, y)
# Create a surface as a box around the score count = per_category_count
message = render_box(font, text, True, Color(255, 255, 255), Color(128, 128, 128), Color(0, 0, 0), padding=2) for rank, score in enumerate(sorted([score for score in self.get_game().scores if score.level_index == 2])[:count]):
message.set_alpha(230) y += self.draw_score_to_column(score, right_column, (x, y), rank)
right_column_sprite.add_frame(right_column)
# Store it in a sprite, use a blinking sprite for the most recent score right_column_sprite.location.topleft = x, 0
if not entry == self.get_game().most_recent_score: self.score_sprites = [left_column_sprite, right_column_sprite]
sprite = Sprite(self) for sprite in self.score_sprites:
else: sprite.update()
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
def show_video(self): def show_video(self):
self.video.unhide() self.video.unhide()
@ -1185,7 +1250,13 @@ class Title(Animation):
if self.active: if self.active:
ds = self.get_display_surface() ds = self.get_display_surface()
dsr = ds.get_rect() 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() self.get_game().logo.update()
# Advance through the unlock pattern # Advance through the unlock pattern
platform = self.get_game().platform platform = self.get_game().platform
if not self.get_game().wipe.is_playing() and platform.get_edge_pressed() == self.UNLOCK_MOVES[self.unlock_index]: 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]])) platform.set_glowing(platform.get_buttons_from_edges([self.UNLOCK_MOVES[self.unlock_index]]))
self.get_audio().play_sfx("land_0") self.get_audio().play_sfx("land_0")
self.get_game().tony.update() self.get_game().tony.update()
# Bounce the GIF around the screen # Bounce the GIF around the screen
if self.video.location.right > dsr.right or self.video.location.left < dsr.left: if self.video.location.right > dsr.right or self.video.location.left < dsr.left:
self.angle = reflect_angle(self.angle, 0) self.angle = reflect_angle(self.angle, 0)
@ -1213,6 +1285,7 @@ class Title(Animation):
self.video.move(dy=dsr.top - self.video.location.top) self.video.move(dy=dsr.top - self.video.location.top)
dx, dy = get_delta(self.angle, 5, False) dx, dy = get_delta(self.angle, 5, False)
self.video.move(dx, dy) self.video.move(dx, dy)
# Hide GIFs/attract mode (or keep them hidden) if input is detected. Set a countdown that will turn # 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 # 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. # 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.get_game().tony.set_frameset("static")
self.halt() self.halt()
self.play(self.show_video, delay=self.get_configuration("time", "attract-reset-countdown"), play_once=True) self.play(self.show_video, delay=self.get_configuration("time", "attract-reset-countdown"), play_once=True)
# self.video.update()
# self.draw_scores() # Disable clip and draw blanker which creates a blinking affect for a single score
for score in self.score_sprites: ds.set_clip(None)
score.update() if self.score_blanker:
self.score_blanker.update()
class Dialogue(Animation): class Dialogue(Animation):

1
config
View File

@ -28,6 +28,7 @@ fullscreen = no
attract-gif-alpha = 1.0 attract-gif-alpha = 1.0
effects = True effects = True
alpha-effect-title = False alpha-effect-title = False
scores-font = BPmono.ttf
[system] [system]
# will force set display->effects to off # will force set display->effects to off

@ -1 +1 @@
Subproject commit 0954cd768f6fed46bf77436317271865a888c3a1 Subproject commit 314b722528a65e6f0053f8be1844c2d8818319f4

BIN
resource/BPmono.ttf Executable file

Binary file not shown.

View File

@ -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