From 41fa64c5530e10d8901a1e344f7810889f6c515b Mon Sep 17 00:00:00 2001 From: frank Date: Wed, 10 Jan 2024 21:11:45 -0800 Subject: [PATCH] - ending screen for versus battles - add date to saved scores - add file for tracking dnf runs --- .gitignore | 2 + NS.py | 98 ++++++++++++++++++++++++++++++++++++------------- config | 2 + resource/scores | 0 4 files changed, 77 insertions(+), 25 deletions(-) delete mode 100644 resource/scores diff --git a/.gitignore b/.gitignore index 9380513..cb1c34a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ _autosave-* www/Scrapeboard_Cyber_Monday_auction.webp www/Scrapeboard_Cyber_Monday_auction_long.webp www/Scrapeboard_auction_flyer_with_stream.png +resource/scores.txt +resource/dnf.txt diff --git a/NS.py b/NS.py index 7f86851..68f3a53 100644 --- a/NS.py +++ b/NS.py @@ -10,7 +10,7 @@ # # 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 try: @@ -76,16 +76,23 @@ class NS(Game, Animation): class Score: - def __init__(self, milliseconds=None, level_index=None): + def __init__(self, milliseconds=None, level_index=None, date=None): self.milliseconds = milliseconds self.level_index = level_index + if date is None: + date = datetime.datetime.now() + self.date = date @classmethod 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: level_index = None - return cls(milliseconds, level_index) + return cls(milliseconds, level_index, date) @classmethod def level(cls, milliseconds: int, level_index: int): @@ -122,7 +129,7 @@ class NS(Game, Animation): serialized_level_index = -1 else: 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): return self.formatted() @@ -234,7 +241,8 @@ class NS(Game, Animation): "system": { "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": { @@ -357,10 +365,12 @@ class NS(Game, Animation): self.most_recent_score = None # Add existing scores to the list from file - with open(self.get_resource("scores"), "rt") as score_file: - for line in score_file: - if line.strip(): - self.scores.append(NS.Score.from_string(line)) + path = self.get_configuration("system", "scores-file") + if os.path.exists(path): + with open(path, "rt") as score_file: + for line in score_file: + if line.strip(): + self.scores.append(NS.Score.from_string(line)) # Draw the score sprites self.title.draw_scores() @@ -487,6 +497,7 @@ class NS(Game, Animation): message = f"{status} {level}" elif self.boss.player_defeated: status = "lost" + self.peers["localhost"].result = None message = status else: status = "complete" @@ -540,7 +551,6 @@ class NS(Game, Animation): peer.level = int(level) peer.seed = int(seed) peer.status = status - print(f"Received seed {peer.seed}") except: pass else: @@ -663,10 +673,16 @@ class NS(Game, Animation): score = NS.Score.level(milliseconds, level_index) self.scores.append(score) self.most_recent_score = score - with open(self.get_resource("scores"), "wt") as score_file: - for score in sorted(self.scores): - if not score.blank(): - score_file.write(f"{score.serialize()}\n") + + # Write scores to file + try: + 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() def update(self): @@ -972,7 +988,8 @@ class LevelSelect(Animation): self.halt(self.force_launch) self.get_game().pop_up("", clear=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): if level_index != self.level_index_selected: self.platforms[level_index].view.halt() @@ -2996,6 +3013,14 @@ class Boss(Animation): self.level_sprite().set_frameset("normal") self.play(self.flash_player_damage) 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.kills += not win self.play(self.show_end_dialogue, delay=3000, play_once=True) @@ -3576,18 +3601,18 @@ class Ending(Animation): foreground = get_boxed_surface( 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)) - if self.rank() % 100 // 10 != 1: - if self.rank() % 10 == 1: + if self.rank()[0] % 100 // 10 != 1: + if self.rank()[0] % 10 == 1: ordinal = "ST" - elif self.rank() % 10 == 2: + elif self.rank()[0] % 10 == 2: ordinal = "ND" - elif self.rank() % 10 == 3: + elif self.rank()[0] % 10 == 3: ordinal = "RD" else: ordinal = "TH" else: 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_rect = rank.get_rect() rank_rect.midleft = foreground.get_rect().midleft @@ -3608,19 +3633,42 @@ class Ending(Animation): 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 - 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 if score == self.get_game().most_recent_score: break - return rank + return rank, len(level_scores) def start(self): dialogue = self.get_game().dialogue 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: dialogue.set_name("Tony") text += "You made your father proud today. I love you child." diff --git a/config b/config index ebc49cd..c95d79a 100644 --- a/config +++ b/config @@ -41,6 +41,8 @@ lives-boss-rush-mode = 3 lives-level-select-mode = 1 optimize-title-screen = no max-seed = 2147483647 +dnf-file = resource/dnf.txt +scores-file = resource/scores.txt [network] peers = diff --git a/resource/scores b/resource/scores deleted file mode 100644 index e69de29..0000000