draw scores once and set clip
This commit is contained in:
parent
e0b4ed0251
commit
3deb552325
188
NS.py
188
NS.py
|
@ -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
1
config
|
@ -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
|
||||||
|
|
2
lib/pgfw
2
lib/pgfw
|
@ -1 +1 @@
|
||||||
Subproject commit 0954cd768f6fed46bf77436317271865a888c3a1
|
Subproject commit 314b722528a65e6f0053f8be1844c2d8818319f4
|
Binary file not shown.
|
@ -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
|
Loading…
Reference in New Issue