Files
MusicPlayer/data/score_history.gd
T
Sansan e1e05f3904
Build / test (push) Successful in 38s
Build / build (push) Successful in 1m12s
Show a graph of the game on the Winner popup
The Winner popup now shows every player's score after each song as a
step line, with the winning score marked, and three facts under it: the
longest streak, the longest standstill and the song most players scored
on. The data comes from the points each song already records, through
the new ScoreHistory. The popup grows to 1600x920 with the graph across
the full width, so a 60-song game with five players stays readable.

Reset points now starts a new game: it clears the points on the songs
and the graph starts after the song that was playing. Songs already on
the server at startup count as an earlier game.

The screenshot tour has a long five-player game, and only the Winner
popup differs from before.
2026-10-07 10:40:46 +02:00

122 lines
4.6 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
class_name ScoreHistory
extends RefCounted
## Who got a point on which song of the current game, for the graph and the summary on
## the Winner popup.
##
## Songs are counted from 1 at the start of the game, which is the start of the playlist
## or the first song after the last Reset points.
## The players, in the order of [code]Settings.player_array[/code].
var players: Array[PlayerObject] = []
## For each song of the game, in the order they were played, the ids of the players who
## got a point on it.
var songs: Array[Array] = []
## The current game, from [code]Playlist.game_start_song[/code] up to the last song that
## has played. Songs downloaded ahead but not played yet are left out, and so are points
## of players who are no longer in [param game_players].
static func from_playlist(game_players: Array[PlayerObject]) -> ScoreHistory:
var song_scorers: Array[Array] = []
# Not just the song playing now: an older song may have been picked in the music list
var last_song: int = mini(Playlist.currently_playing_song,
Playlist.song_object_array.size() - 1)
for song_no: int in Playlist.song_object_array.size():
if Playlist.song_object_array[song_no].has_played:
last_song = maxi(last_song, song_no)
for song_no: int in range(Playlist.game_start_song, last_song + 1):
var song: SongObject = Playlist.song_object_array[song_no]
if song.has_played || song_no == Playlist.currently_playing_song:
song_scorers.append(song.players_given_point)
return from_songs(game_players, song_scorers)
## A history of [param song_scorers], the ids of the players who scored on each song.
static func from_songs(game_players: Array[PlayerObject], song_scorers: Array) -> ScoreHistory:
var history: ScoreHistory = ScoreHistory.new()
history.players = game_players.duplicate()
var ids: Array[String] = []
for player: PlayerObject in game_players:
ids.append(player.id)
for scorers: Array in song_scorers:
history.songs.append(scorers.filter(func(id: String) -> bool: return ids.has(id)))
return history
## [param player_id]'s score before the first song and after each song.
func scores_after_songs(player_id: String) -> Array[int]:
var scores: Array[int] = [0]
for scorers: Array in songs:
scores.append(scores.back() + (1 if scorers.has(player_id) else 0))
return scores
## [param player_id]'s score after the last song.
func final_score(player_id: String) -> int:
return scores_after_songs(player_id).back()
## The player with the most songs in a row with a point, e.g.
## "Crono, 5 songs in a row (14–18)". The earliest streak wins a tie.
func longest_streak_text() -> String:
if songs.is_empty():
return "No songs played"
var best_length: int = 0
var best_start: int = 0
var best_player: PlayerObject = null
for player: PlayerObject in players:
var length: int = 0
for song: int in songs.size():
length = length + 1 if songs[song].has(player.id) else 0
var start: int = song - length + 1
if length > best_length || (length == best_length && length > 0 \
&& start < best_start):
best_length = length
best_start = start
best_player = player
if best_length < 2:
return "Nobody scored twice in a row"
return "%s, %d songs in a row (%d–%d)" % [best_player.player_name, best_length,
best_start + 1, best_start + best_length]
## The most songs in a row where nobody scored, e.g. "Songs 6–10, nobody scored".
func longest_standstill_text() -> String:
if songs.is_empty():
return "No songs played"
var best_length: int = 0
var best_start: int = 0
var length: int = 0
for song: int in songs.size():
length = length + 1 if songs[song].is_empty() else 0
if length > best_length:
best_length = length
best_start = song - length + 1
if best_length == 0:
return "Someone scored on every song"
if best_length == 1:
return "Song %d, nobody scored" % (best_start + 1)
return "Songs %d–%d, nobody scored" % [best_start + 1, best_start + best_length]
## The song most players scored on, e.g. "Song 11, Marle and Lucca", or
## "Song 11, 3 players" when more than two did. The earliest song wins a tie.
func most_shared_text() -> String:
if songs.is_empty():
return "No songs played"
var best_song: int = -1
for song: int in songs.size():
if songs[song].size() >= 2 \
&& (best_song < 0 || songs[song].size() > songs[best_song].size()):
best_song = song
if best_song < 0:
return "No song had two scorers"
var scorers: Array = songs[best_song]
if scorers.size() > 2:
return "Song %d, %d players" % [best_song + 1, scorers.size()]
var names: PackedStringArray = []
for player: PlayerObject in players:
if scorers.has(player.id):
names.append(player.player_name)
return "Song %d, %s" % [best_song + 1, " and ".join(names)]