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.
122 lines
4.6 KiB
GDScript
122 lines
4.6 KiB
GDScript
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)]
|