diff --git a/field/arena.go b/field/arena.go index a4226dd4..051dc4ed 100644 --- a/field/arena.go +++ b/field/arena.go @@ -77,6 +77,7 @@ type Arena struct { SavedRankings game.Rankings AllianceStationDisplayMode string AllianceSelectionAlliances []model.Alliance + AllianceSelectionRankedTeams []model.AllianceSelectionRankedTeam AllianceSelectionShowTimer bool AllianceSelectionTimeRemainingSec int PlayoffTournament *playoff.PlayoffTournament diff --git a/field/arena_notifiers.go b/field/arena_notifiers.go index 0c32979c..17f6119d 100644 --- a/field/arena_notifiers.go +++ b/field/arena_notifiers.go @@ -69,10 +69,12 @@ func (arena *Arena) generateAllianceSelectionMessage() any { Alliances []model.Alliance ShowTimer bool TimeRemainingSec int + RankedTeams []model.AllianceSelectionRankedTeam }{ arena.AllianceSelectionAlliances, arena.AllianceSelectionShowTimer, arena.AllianceSelectionTimeRemainingSec, + arena.AllianceSelectionRankedTeams, } } diff --git a/model/alliance.go b/model/alliance.go index b627e5c9..de733db4 100644 --- a/model/alliance.go +++ b/model/alliance.go @@ -13,6 +13,12 @@ type Alliance struct { Lineup [3]int } +type AllianceSelectionRankedTeam struct { + Rank int + TeamId int + Picked bool +} + func (database *Database) CreateAlliance(alliance *Alliance) error { return database.allianceTable.create(alliance) } diff --git a/model/event_settings.go b/model/event_settings.go index a34aa042..fd87c1ca 100644 --- a/model/event_settings.go +++ b/model/event_settings.go @@ -21,6 +21,7 @@ type EventSettings struct { NumPlayoffAlliances int SelectionRound2Order string SelectionRound3Order string + SelectionShowUnpickedTeams bool TbaDownloadEnabled bool TbaPublishingEnabled bool TbaEventCode string @@ -70,6 +71,7 @@ func (database *Database) GetEventSettings() (*EventSettings, error) { NumPlayoffAlliances: 8, SelectionRound2Order: "L", SelectionRound3Order: "", + SelectionShowUnpickedTeams: false, TbaDownloadEnabled: true, ApChannel: 36, WarmupDurationSec: game.MatchTiming.WarmupDurationSec, diff --git a/static/css/audience_display.css b/static/css/audience_display.css index f463f0d5..a1c02d2e 100644 --- a/static/css/audience_display.css +++ b/static/css/audience_display.css @@ -553,6 +553,37 @@ html { max-width: 800px; max-height: 400px; } +#allianceRankingsCentering { + position: absolute; + height: 100%; + left: 3em; + top: 3em; +} +#allianceRankings { + max-width: 11em; + max-height: 92%; + display: flex; + flex-flow: column wrap; + overflow: hidden; + background-color: #fff; + border: 2px solid #222; + font-size: 2em; + font-family: "FuturaLT"; +} +.unpicked { + width: 5.5em; + display: flex; + justify-content: start; +} +.unpicked-rank { + width: 1.8em; + text-align: right; + color: #999; +} +.unpicked-team { + margin-left: 0.3em; + color: #222; +} #allianceSelectionCentering { position: absolute; height: 100%; diff --git a/static/js/audience_display.js b/static/js/audience_display.js index cf4bbb4c..688732ed 100644 --- a/static/js/audience_display.js +++ b/static/js/audience_display.js @@ -328,6 +328,7 @@ const handlePlaySound = function(sound) { // Handles a websocket message to update the alliance selection screen. const handleAllianceSelection = function(data) { const alliances = data.Alliances; + const rankedTeams = data.RankedTeams; if (alliances && alliances.length > 0) { const numColumns = alliances[0].TeamIds.length + 1; $.each(alliances, function(k, v) { @@ -335,6 +336,16 @@ const handleAllianceSelection = function(data) { }); $("#allianceSelection").html(allianceSelectionTemplate({alliances: alliances, numColumns: numColumns})); } + if (rankedTeams) { + let text = ""; + $.each(rankedTeams, function(i, v) { + if (!v.Picked) { + text += `
${v.Rank}.
` + + `
${v.TeamId}
`; + } + }); + $("#allianceRankings").html(text); + } if (data.ShowTimer) { $("#allianceSelectionTimer").text(getCountdownString(data.TimeRemainingSec)); @@ -373,11 +384,14 @@ const handleLowerThird = function(data) { const transitionAllianceSelectionToBlank = function(callback) { $('#allianceSelectionCentering').transition({queue: false, right: "-60em"}, 500, "ease", callback); + $('#allianceRankingsCentering.enabled').transition({queue:false, left: "-60em"}, 500, "ease"); }; const transitionBlankToAllianceSelection = function(callback) { $('#allianceSelectionCentering').css("right","-60em").show(); $('#allianceSelectionCentering').transition({queue: false, right: "3em"}, 500, "ease", callback); + $('#allianceRankingsCentering.enabled').css("left", "-60em").show(); + $('#allianceRankingsCentering.enabled').transition({queue: false, left: "3em"}, 500, "ease"); }; const transitionBlankToBracket = function(callback) { diff --git a/templates/audience_display.html b/templates/audience_display.html index c7aa2cf7..f8688649 100644 --- a/templates/audience_display.html +++ b/templates/audience_display.html @@ -242,6 +242,9 @@ +
diff --git a/templates/setup_settings.html b/templates/setup_settings.html index cf6df401..68314713 100644 --- a/templates/setup_settings.html +++ b/templates/setup_settings.html @@ -97,6 +97,15 @@
+
+ +
+ +
+
Automatic Team Info Download diff --git a/web/alliance_selection.go b/web/alliance_selection.go index 49513760..d15d0641 100644 --- a/web/alliance_selection.go +++ b/web/alliance_selection.go @@ -17,15 +17,6 @@ import ( "time" ) -type RankedTeam struct { - Rank int - TeamId int - Picked bool -} - -// Global var to hold the team rankings during the alliance selection. -var cachedRankedTeams []*RankedTeam - // Global var to hold configurable time limit for selections. A value of zero disables the timer. var allianceSelectionTimeLimitSec = 0 @@ -56,9 +47,8 @@ func (web *Web) allianceSelectionPostHandler(w http.ResponseWriter, r *http.Requ allianceSelectionTimeLimitSec, _ = strconv.Atoi(r.PostFormValue("timeLimitSec")) // Reset picked state for each team in preparation for reconstructing it. - newRankedTeams := make([]*RankedTeam, len(cachedRankedTeams)) - for i, team := range cachedRankedTeams { - newRankedTeams[i] = &RankedTeam{team.Rank, team.TeamId, false} + for i := range web.arena.AllianceSelectionRankedTeams { + web.arena.AllianceSelectionRankedTeams[i].Picked = false } // Iterate through all selections and update the alliances. @@ -74,7 +64,7 @@ func (web *Web) allianceSelectionPostHandler(w http.ResponseWriter, r *http.Requ return } found := false - for _, team := range newRankedTeams { + for k, team := range web.arena.AllianceSelectionRankedTeams { if team.TeamId == teamId { if team.Picked { web.renderAllianceSelection(w, r, @@ -82,7 +72,7 @@ func (web *Web) allianceSelectionPostHandler(w http.ResponseWriter, r *http.Requ return } found = true - team.Picked = true + web.arena.AllianceSelectionRankedTeams[k].Picked = true web.arena.AllianceSelectionAlliances[i].TeamIds[j] = teamId break } @@ -100,7 +90,6 @@ func (web *Web) allianceSelectionPostHandler(w http.ResponseWriter, r *http.Requ } } } - cachedRankedTeams = newRankedTeams if allianceSelectionTicker != nil { allianceSelectionTicker.Stop() @@ -157,9 +146,13 @@ func (web *Web) allianceSelectionStartHandler(w http.ResponseWriter, r *http.Req handleWebErr(w, err) return } - cachedRankedTeams = make([]*RankedTeam, len(rankings)) + web.arena.AllianceSelectionRankedTeams = make([]model.AllianceSelectionRankedTeam, len(rankings)) for i, ranking := range rankings { - cachedRankedTeams[i] = &RankedTeam{i + 1, ranking.TeamId, false} + web.arena.AllianceSelectionRankedTeams[i] = model.AllianceSelectionRankedTeam{ + Rank: i + 1, + TeamId: ranking.TeamId, + Picked: false, + } } web.arena.AllianceSelectionNotifier.Notify() @@ -191,7 +184,7 @@ func (web *Web) allianceSelectionResetHandler(w http.ResponseWriter, r *http.Req } web.arena.AllianceSelectionAlliances = []model.Alliance{} - cachedRankedTeams = []*RankedTeam{} + web.arena.AllianceSelectionRankedTeams = []model.AllianceSelectionRankedTeam{} web.arena.AllianceSelectionNotifier.Notify() http.Redirect(w, r, "/alliance_selection", 303) } @@ -347,7 +340,7 @@ func (web *Web) renderAllianceSelection(w http.ResponseWriter, r *http.Request, data := struct { *model.EventSettings Alliances []model.Alliance - RankedTeams []*RankedTeam + RankedTeams []model.AllianceSelectionRankedTeam NextRow int NextCol int ErrorMessage string @@ -355,7 +348,7 @@ func (web *Web) renderAllianceSelection(w http.ResponseWriter, r *http.Request, }{ web.arena.EventSettings, web.arena.AllianceSelectionAlliances, - cachedRankedTeams, + web.arena.AllianceSelectionRankedTeams, nextRow, nextCol, errorMessage, diff --git a/web/alliance_selection_test.go b/web/alliance_selection_test.go index 532dea25..e598b2dc 100644 --- a/web/alliance_selection_test.go +++ b/web/alliance_selection_test.go @@ -16,8 +16,6 @@ import ( func TestAllianceSelection(t *testing.T) { web := setupTestWeb(t) - web.arena.AllianceSelectionAlliances = []model.Alliance{} - cachedRankedTeams = []*RankedTeam{} web.arena.EventSettings.PlayoffType = model.SingleEliminationPlayoff web.arena.EventSettings.NumPlayoffAlliances = 15 web.arena.EventSettings.SelectionRound3Order = "L" @@ -104,8 +102,6 @@ func TestAllianceSelection(t *testing.T) { func TestAllianceSelectionErrors(t *testing.T) { web := setupTestWeb(t) - web.arena.AllianceSelectionAlliances = []model.Alliance{} - cachedRankedTeams = []*RankedTeam{} web.arena.EventSettings.PlayoffType = model.SingleEliminationPlayoff web.arena.EventSettings.NumPlayoffAlliances = 2 for i := 1; i <= 6; i++ { @@ -157,7 +153,7 @@ func TestAllianceSelectionErrors(t *testing.T) { assert.Equal(t, 200, recorder.Code) assert.Contains(t, recorder.Body.String(), "already been finalized") web.arena.AllianceSelectionAlliances = []model.Alliance{} - cachedRankedTeams = []*RankedTeam{} + web.arena.AllianceSelectionRankedTeams = []model.AllianceSelectionRankedTeam{} recorder = web.postHttpResponse("/alliance_selection/start", "") assert.Equal(t, 200, recorder.Code) assert.Contains(t, recorder.Body.String(), "already been finalized") @@ -166,8 +162,6 @@ func TestAllianceSelectionErrors(t *testing.T) { func TestAllianceSelectionReset(t *testing.T) { web := setupTestWeb(t) - web.arena.AllianceSelectionAlliances = []model.Alliance{} - cachedRankedTeams = []*RankedTeam{} web.arena.EventSettings.PlayoffType = model.SingleEliminationPlayoff web.arena.EventSettings.NumPlayoffAlliances = 2 for i := 1; i <= 6; i++ { @@ -223,8 +217,6 @@ func TestAllianceSelectionReset(t *testing.T) { func TestAllianceSelectionAutofocus(t *testing.T) { web := setupTestWeb(t) - web.arena.AllianceSelectionAlliances = []model.Alliance{} - cachedRankedTeams = []*RankedTeam{} web.arena.EventSettings.PlayoffType = model.SingleEliminationPlayoff web.arena.EventSettings.NumPlayoffAlliances = 2 diff --git a/web/setup_settings.go b/web/setup_settings.go index 07fc3487..7673e2cb 100644 --- a/web/setup_settings.go +++ b/web/setup_settings.go @@ -71,6 +71,7 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) { eventSettings.NumPlayoffAlliances = numAlliances eventSettings.SelectionRound2Order = r.PostFormValue("selectionRound2Order") eventSettings.SelectionRound3Order = r.PostFormValue("selectionRound3Order") + eventSettings.SelectionShowUnpickedTeams = r.PostFormValue("selectionShowUnpickedTeams") == "on" eventSettings.TbaDownloadEnabled = r.PostFormValue("tbaDownloadEnabled") == "on" eventSettings.TbaPublishingEnabled = r.PostFormValue("tbaPublishingEnabled") == "on" eventSettings.TbaEventCode = r.PostFormValue("tbaEventCode") @@ -256,7 +257,7 @@ func (web *Web) clearDbHandler(w http.ResponseWriter, r *http.Request) { return } web.arena.AllianceSelectionAlliances = []model.Alliance{} - cachedRankedTeams = []*RankedTeam{} + web.arena.AllianceSelectionRankedTeams = []model.AllianceSelectionRankedTeam{} } http.Redirect(w, r, "/setup/settings", 303)