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):
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):

1
config
View File

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

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