- ending screen for versus battles
- add date to saved scores - add file for tracking dnf runs
This commit is contained in:
parent
0b59b4fc29
commit
41fa64c553
|
@ -23,3 +23,5 @@ _autosave-*
|
||||||
www/Scrapeboard_Cyber_Monday_auction.webp
|
www/Scrapeboard_Cyber_Monday_auction.webp
|
||||||
www/Scrapeboard_Cyber_Monday_auction_long.webp
|
www/Scrapeboard_Cyber_Monday_auction_long.webp
|
||||||
www/Scrapeboard_auction_flyer_with_stream.png
|
www/Scrapeboard_auction_flyer_with_stream.png
|
||||||
|
resource/scores.txt
|
||||||
|
resource/dnf.txt
|
||||||
|
|
98
NS.py
98
NS.py
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# This is the main file containing all the Pygame code.
|
# This is the main file containing all the Pygame code.
|
||||||
|
|
||||||
import argparse, pathlib, operator, subprocess, sys, os, socket, select, time, random
|
import argparse, pathlib, operator, subprocess, sys, os, socket, select, time, random, datetime
|
||||||
|
|
||||||
# Auto-detect GPIO library
|
# Auto-detect GPIO library
|
||||||
try:
|
try:
|
||||||
|
@ -76,16 +76,23 @@ class NS(Game, Animation):
|
||||||
|
|
||||||
class Score:
|
class Score:
|
||||||
|
|
||||||
def __init__(self, milliseconds=None, level_index=None):
|
def __init__(self, milliseconds=None, level_index=None, date=None):
|
||||||
self.milliseconds = milliseconds
|
self.milliseconds = milliseconds
|
||||||
self.level_index = level_index
|
self.level_index = level_index
|
||||||
|
if date is None:
|
||||||
|
date = datetime.datetime.now()
|
||||||
|
self.date = date
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_string(cls, line: str):
|
def from_string(cls, line: str):
|
||||||
milliseconds, level_index = (int(field) for field in line.strip().split())
|
fields = line.strip().split()
|
||||||
|
milliseconds, level_index = (int(field) for field in fields[:2])
|
||||||
|
date = None
|
||||||
|
if len(fields) > 2:
|
||||||
|
date = datetime.datetime.fromisoformat(fields[2])
|
||||||
if level_index == -1:
|
if level_index == -1:
|
||||||
level_index = None
|
level_index = None
|
||||||
return cls(milliseconds, level_index)
|
return cls(milliseconds, level_index, date)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def level(cls, milliseconds: int, level_index: int):
|
def level(cls, milliseconds: int, level_index: int):
|
||||||
|
@ -122,7 +129,7 @@ class NS(Game, Animation):
|
||||||
serialized_level_index = -1
|
serialized_level_index = -1
|
||||||
else:
|
else:
|
||||||
serialized_level_index = self.level_index
|
serialized_level_index = self.level_index
|
||||||
return f"{self.milliseconds} {serialized_level_index}"
|
return f"{self.milliseconds} {serialized_level_index} {datetime.datetime.isoformat(self.date, 'T')}"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.formatted()
|
return self.formatted()
|
||||||
|
@ -234,7 +241,8 @@ class NS(Game, Animation):
|
||||||
"system":
|
"system":
|
||||||
{
|
{
|
||||||
"bool": ["minimize-load-time", "enable-level-select", "optimize-title-screen"],
|
"bool": ["minimize-load-time", "enable-level-select", "optimize-title-screen"],
|
||||||
"int": ["lives-boss-rush-mode", "lives-level-select-mode", "max-seed"]
|
"int": ["lives-boss-rush-mode", "lives-level-select-mode", "max-seed"],
|
||||||
|
"path": ["dnf-file", "scores-file"]
|
||||||
},
|
},
|
||||||
"pads":
|
"pads":
|
||||||
{
|
{
|
||||||
|
@ -357,10 +365,12 @@ class NS(Game, Animation):
|
||||||
self.most_recent_score = None
|
self.most_recent_score = None
|
||||||
|
|
||||||
# Add existing scores to the list from file
|
# Add existing scores to the list from file
|
||||||
with open(self.get_resource("scores"), "rt") as score_file:
|
path = self.get_configuration("system", "scores-file")
|
||||||
for line in score_file:
|
if os.path.exists(path):
|
||||||
if line.strip():
|
with open(path, "rt") as score_file:
|
||||||
self.scores.append(NS.Score.from_string(line))
|
for line in score_file:
|
||||||
|
if line.strip():
|
||||||
|
self.scores.append(NS.Score.from_string(line))
|
||||||
|
|
||||||
# Draw the score sprites
|
# Draw the score sprites
|
||||||
self.title.draw_scores()
|
self.title.draw_scores()
|
||||||
|
@ -487,6 +497,7 @@ class NS(Game, Animation):
|
||||||
message = f"{status} {level}"
|
message = f"{status} {level}"
|
||||||
elif self.boss.player_defeated:
|
elif self.boss.player_defeated:
|
||||||
status = "lost"
|
status = "lost"
|
||||||
|
self.peers["localhost"].result = None
|
||||||
message = status
|
message = status
|
||||||
else:
|
else:
|
||||||
status = "complete"
|
status = "complete"
|
||||||
|
@ -540,7 +551,6 @@ class NS(Game, Animation):
|
||||||
peer.level = int(level)
|
peer.level = int(level)
|
||||||
peer.seed = int(seed)
|
peer.seed = int(seed)
|
||||||
peer.status = status
|
peer.status = status
|
||||||
print(f"Received seed {peer.seed}")
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -663,10 +673,16 @@ class NS(Game, Animation):
|
||||||
score = NS.Score.level(milliseconds, level_index)
|
score = NS.Score.level(milliseconds, level_index)
|
||||||
self.scores.append(score)
|
self.scores.append(score)
|
||||||
self.most_recent_score = score
|
self.most_recent_score = score
|
||||||
with open(self.get_resource("scores"), "wt") as score_file:
|
|
||||||
for score in sorted(self.scores):
|
# Write scores to file
|
||||||
if not score.blank():
|
try:
|
||||||
score_file.write(f"{score.serialize()}\n")
|
with open(self.get_configuration("system", "scores-file"), "wt") as score_file:
|
||||||
|
for score in sorted(self.scores):
|
||||||
|
if not score.blank():
|
||||||
|
score_file.write(f"{score.serialize()}\n")
|
||||||
|
except:
|
||||||
|
print("Error saving scores")
|
||||||
|
|
||||||
self.title.draw_scores()
|
self.title.draw_scores()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
@ -972,7 +988,8 @@ class LevelSelect(Animation):
|
||||||
self.halt(self.force_launch)
|
self.halt(self.force_launch)
|
||||||
self.get_game().pop_up("", clear=True)
|
self.get_game().pop_up("", clear=True)
|
||||||
self.level_launched = True
|
self.level_launched = True
|
||||||
if self.get_game().count_players() > 1:
|
self.opponents_at_launch = [peer for peer in self.get_game().peers.values() if peer.versus]
|
||||||
|
if len(self.opponents_at_launch) > 1:
|
||||||
for level_index in range(3):
|
for level_index in range(3):
|
||||||
if level_index != self.level_index_selected:
|
if level_index != self.level_index_selected:
|
||||||
self.platforms[level_index].view.halt()
|
self.platforms[level_index].view.halt()
|
||||||
|
@ -2996,6 +3013,14 @@ class Boss(Animation):
|
||||||
self.level_sprite().set_frameset("normal")
|
self.level_sprite().set_frameset("normal")
|
||||||
self.play(self.flash_player_damage)
|
self.play(self.flash_player_damage)
|
||||||
self.get_game().chemtrails.set_frameset("hurt")
|
self.get_game().chemtrails.set_frameset("hurt")
|
||||||
|
|
||||||
|
# Record a play to the DNF file for analytics
|
||||||
|
try:
|
||||||
|
with open(self.get_configuration("system", "dnf-file"), "at") as dnf_file:
|
||||||
|
dnf_file.write(f"{self.time_elapsed} {self.level_index} {datetime.datetime.isoformat(datetime.datetime.now(), 'T')}\n")
|
||||||
|
except:
|
||||||
|
print("Error saving DNF run to file")
|
||||||
|
|
||||||
self.player_defeated = not win
|
self.player_defeated = not win
|
||||||
self.kills += not win
|
self.kills += not win
|
||||||
self.play(self.show_end_dialogue, delay=3000, play_once=True)
|
self.play(self.show_end_dialogue, delay=3000, play_once=True)
|
||||||
|
@ -3576,18 +3601,18 @@ class Ending(Animation):
|
||||||
foreground = get_boxed_surface(
|
foreground = get_boxed_surface(
|
||||||
self.time_font.render(str(self.get_game().most_recent_score), False, (180, 150, 20), (255, 255, 255)).convert_alpha(),
|
self.time_font.render(str(self.get_game().most_recent_score), False, (180, 150, 20), (255, 255, 255)).convert_alpha(),
|
||||||
background=(255, 255, 255), padding=(38, 0))
|
background=(255, 255, 255), padding=(38, 0))
|
||||||
if self.rank() % 100 // 10 != 1:
|
if self.rank()[0] % 100 // 10 != 1:
|
||||||
if self.rank() % 10 == 1:
|
if self.rank()[0] % 10 == 1:
|
||||||
ordinal = "ST"
|
ordinal = "ST"
|
||||||
elif self.rank() % 10 == 2:
|
elif self.rank()[0] % 10 == 2:
|
||||||
ordinal = "ND"
|
ordinal = "ND"
|
||||||
elif self.rank() % 10 == 3:
|
elif self.rank()[0] % 10 == 3:
|
||||||
ordinal = "RD"
|
ordinal = "RD"
|
||||||
else:
|
else:
|
||||||
ordinal = "TH"
|
ordinal = "TH"
|
||||||
else:
|
else:
|
||||||
ordinal = "TH"
|
ordinal = "TH"
|
||||||
rank = self.rank_font.render(f"{self.rank()}{ordinal}", False, (180, 150, 20), (255, 255, 255))
|
rank = self.rank_font.render(f"{self.rank()[0]}{ordinal}", False, (180, 150, 20), (255, 255, 255))
|
||||||
rank = pygame.transform.rotate(rank, 90)
|
rank = pygame.transform.rotate(rank, 90)
|
||||||
rank_rect = rank.get_rect()
|
rank_rect = rank.get_rect()
|
||||||
rank_rect.midleft = foreground.get_rect().midleft
|
rank_rect.midleft = foreground.get_rect().midleft
|
||||||
|
@ -3608,19 +3633,42 @@ class Ending(Animation):
|
||||||
|
|
||||||
def rank(self):
|
def rank(self):
|
||||||
"""
|
"""
|
||||||
Get the rank of the currently displaying score
|
@return the rank of the currently displaying score as a tuple: (rank, total)
|
||||||
"""
|
"""
|
||||||
rank = 0
|
rank = 0
|
||||||
for score in sorted([score for score in self.get_game().scores if score.level_index == self.defeated_level_index]):
|
level_scores = [score for score in self.get_game().scores if score.level_index == self.defeated_level_index and not score.blank()]
|
||||||
|
for score in sorted(level_scores):
|
||||||
rank += 1
|
rank += 1
|
||||||
if score == self.get_game().most_recent_score:
|
if score == self.get_game().most_recent_score:
|
||||||
break
|
break
|
||||||
return rank
|
return rank, len(level_scores)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
dialogue = self.get_game().dialogue
|
dialogue = self.get_game().dialogue
|
||||||
if self.get_configuration("system", "enable-level-select"):
|
if self.get_configuration("system", "enable-level-select"):
|
||||||
text = f"You vanquished my goon and got the #{self.rank()} rank! Well done, slime bag.\n"
|
|
||||||
|
# Create a message for versus mode
|
||||||
|
if len(self.get_game().level_select.opponents_at_launch) > 1:
|
||||||
|
|
||||||
|
# Check if any peers had a faster time
|
||||||
|
rank = 1
|
||||||
|
for peer in self.get_game().level_select.opponents_at_launch:
|
||||||
|
if peer.address != "localhost" and not peer.status == "playing" and peer.result is not None and \
|
||||||
|
peer.result < self.get_game().most_recent_score.milliseconds:
|
||||||
|
rank += 1
|
||||||
|
|
||||||
|
if rank == 1:
|
||||||
|
text = (f"Congratulations on winning the battle and getting #{self.rank()[0]} out of {self.rank()[1]} overall!\n"
|
||||||
|
"Well done, slime bag. ")
|
||||||
|
else:
|
||||||
|
total = len(self.get_game().level_select.opponents_at_launch)
|
||||||
|
text = (f"You were #{rank} out of {total} in the battle, but you vanquished my goon and finished\n"
|
||||||
|
f"#{self.rank()[0]} out of {self.rank()[1]} overall! Well done, slime bag. ")
|
||||||
|
|
||||||
|
# Create a message for single-player mode
|
||||||
|
else:
|
||||||
|
text = f"You vanquished my goon and got #{self.rank()[0]} out of {self.rank()[1]}! Well done, slime bag.\n"
|
||||||
|
|
||||||
if self.defeated_level_index == 2:
|
if self.defeated_level_index == 2:
|
||||||
dialogue.set_name("Tony")
|
dialogue.set_name("Tony")
|
||||||
text += "You made your father proud today. I love you child."
|
text += "You made your father proud today. I love you child."
|
||||||
|
|
2
config
2
config
|
@ -41,6 +41,8 @@ lives-boss-rush-mode = 3
|
||||||
lives-level-select-mode = 1
|
lives-level-select-mode = 1
|
||||||
optimize-title-screen = no
|
optimize-title-screen = no
|
||||||
max-seed = 2147483647
|
max-seed = 2147483647
|
||||||
|
dnf-file = resource/dnf.txt
|
||||||
|
scores-file = resource/scores.txt
|
||||||
|
|
||||||
[network]
|
[network]
|
||||||
peers =
|
peers =
|
||||||
|
|
Loading…
Reference in New Issue