- ending screen for versus battles

- add date to saved scores
- add file for tracking dnf runs
This commit is contained in:
ohsqueezy 2024-01-10 21:11:45 -08:00
parent 0b59b4fc29
commit 41fa64c553
4 changed files with 77 additions and 25 deletions

2
.gitignore vendored
View File

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

98
NS.py
View File

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

2
config
View File

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

View File