diff --git a/game/score.go b/game/score.go index f9d0830..8184817 100644 --- a/game/score.go +++ b/game/score.go @@ -5,6 +5,8 @@ package game +import "math" + type Score struct { ExitedInitiationLine [3]bool AutoCellsBottom [2]int @@ -30,7 +32,7 @@ type ScoreSummary struct { EndgamePoints int FoulPoints int Score int - StagesAtCapacity [3]bool + StagePowerCellsRemaining [3]int StagesActivated [3]bool ControlPanelRankingPoint bool EndgameRankingPoint bool @@ -102,8 +104,8 @@ func (score *Score) Summarize(opponentFouls []Foul, teleopStarted bool) *ScoreSu // Calculate control panel points and stages. for i := Stage1; i <= Stage3; i++ { - summary.StagesAtCapacity[i] = score.stageAtCapacity(i, teleopStarted) summary.StagesActivated[i] = score.stageActivated(i, teleopStarted) + summary.StagePowerCellsRemaining[i] = int(math.Max(0, float64(StageCapacities[i]-score.stagePowerCells(i)))) } if summary.StagesActivated[Stage2] { summary.ControlPanelPoints += 10 diff --git a/game/score_test.go b/game/score_test.go index b27ab15..8729be0 100644 --- a/game/score_test.go +++ b/game/score_test.go @@ -22,7 +22,7 @@ func TestScoreSummary(t *testing.T) { assert.Equal(t, 75, redSummary.EndgamePoints) assert.Equal(t, 0, redSummary.FoulPoints) assert.Equal(t, 217, redSummary.Score) - assert.Equal(t, [3]bool{true, true, false}, redSummary.StagesAtCapacity) + assert.Equal(t, [3]int{0, 0, 18}, redSummary.StagePowerCellsRemaining) assert.Equal(t, [3]bool{true, true, false}, redSummary.StagesActivated) assert.Equal(t, false, redSummary.ControlPanelRankingPoint) assert.Equal(t, true, redSummary.EndgameRankingPoint) @@ -37,7 +37,7 @@ func TestScoreSummary(t *testing.T) { assert.Equal(t, 50, blueSummary.EndgamePoints) assert.Equal(t, 33, blueSummary.FoulPoints) assert.Equal(t, 252, blueSummary.Score) - assert.Equal(t, [3]bool{true, true, true}, blueSummary.StagesAtCapacity) + assert.Equal(t, [3]int{0, 0, 0}, blueSummary.StagePowerCellsRemaining) assert.Equal(t, [3]bool{true, true, true}, blueSummary.StagesActivated) assert.Equal(t, true, blueSummary.ControlPanelRankingPoint) assert.Equal(t, false, blueSummary.EndgameRankingPoint) @@ -96,7 +96,7 @@ func TestScoreSummaryBoundaryConditions(t *testing.T) { score := TestScore2() summary := score.Summarize(score.Fouls, true) assert.Equal(t, StageExtra, score.CellCountingStage(true)) - assert.Equal(t, [3]bool{true, true, true}, summary.StagesAtCapacity) + assert.Equal(t, [3]int{0, 0, 0}, summary.StagePowerCellsRemaining) assert.Equal(t, [3]bool{true, true, true}, summary.StagesActivated) assert.Equal(t, true, summary.ControlPanelRankingPoint) assert.Equal(t, 219, summary.Score) @@ -104,7 +104,7 @@ func TestScoreSummaryBoundaryConditions(t *testing.T) { score.TeleopCellsInner[0]-- summary = score.Summarize(score.Fouls, true) assert.Equal(t, Stage1, score.CellCountingStage(true)) - assert.Equal(t, [3]bool{false, false, false}, summary.StagesAtCapacity) + assert.Equal(t, [3]int{1, 0, 0}, summary.StagePowerCellsRemaining) assert.Equal(t, [3]bool{false, false, false}, summary.StagesActivated) assert.Equal(t, false, summary.ControlPanelRankingPoint) assert.Equal(t, 186, summary.Score) @@ -112,42 +112,42 @@ func TestScoreSummaryBoundaryConditions(t *testing.T) { summary = score.Summarize(score.Fouls, false) assert.Equal(t, Stage1, score.CellCountingStage(false)) - assert.Equal(t, [3]bool{true, false, false}, summary.StagesAtCapacity) + assert.Equal(t, [3]int{0, 0, 0}, summary.StagePowerCellsRemaining) assert.Equal(t, [3]bool{false, false, false}, summary.StagesActivated) assert.Equal(t, false, summary.ControlPanelRankingPoint) assert.Equal(t, 189, summary.Score) - score.TeleopCellsOuter[1]-- + score.TeleopCellsOuter[1] -= 2 summary = score.Summarize(score.Fouls, true) assert.Equal(t, Stage2, score.CellCountingStage(true)) - assert.Equal(t, [3]bool{true, false, false}, summary.StagesAtCapacity) + assert.Equal(t, [3]int{0, 2, 0}, summary.StagePowerCellsRemaining) assert.Equal(t, [3]bool{true, false, false}, summary.StagesActivated) assert.Equal(t, false, summary.ControlPanelRankingPoint) - assert.Equal(t, 187, summary.Score) - score.TeleopCellsOuter[1]++ + assert.Equal(t, 185, summary.Score) + score.TeleopCellsOuter[1] += 2 score.ControlPanelStatus = ControlPanelNone summary = score.Summarize(score.Fouls, true) assert.Equal(t, Stage2, score.CellCountingStage(true)) - assert.Equal(t, [3]bool{true, true, false}, summary.StagesAtCapacity) + assert.Equal(t, [3]int{0, 0, 0}, summary.StagePowerCellsRemaining) assert.Equal(t, [3]bool{true, false, false}, summary.StagesActivated) assert.Equal(t, false, summary.ControlPanelRankingPoint) assert.Equal(t, 189, summary.Score) score.ControlPanelStatus = ControlPanelPosition - score.TeleopCellsInner[2] -= 3 + score.TeleopCellsInner[2] -= 5 summary = score.Summarize(score.Fouls, true) assert.Equal(t, Stage3, score.CellCountingStage(true)) - assert.Equal(t, [3]bool{true, true, false}, summary.StagesAtCapacity) + assert.Equal(t, [3]int{0, 0, 3}, summary.StagePowerCellsRemaining) assert.Equal(t, [3]bool{true, true, false}, summary.StagesActivated) assert.Equal(t, false, summary.ControlPanelRankingPoint) - assert.Equal(t, 190, summary.Score) - score.TeleopCellsInner[2] += 3 + assert.Equal(t, 184, summary.Score) + score.TeleopCellsInner[2] += 5 score.ControlPanelStatus = ControlPanelRotation summary = score.Summarize(score.Fouls, true) assert.Equal(t, Stage3, score.CellCountingStage(true)) - assert.Equal(t, [3]bool{true, true, true}, summary.StagesAtCapacity) + assert.Equal(t, [3]int{0, 0, 0}, summary.StagePowerCellsRemaining) assert.Equal(t, [3]bool{true, true, false}, summary.StagesActivated) assert.Equal(t, false, summary.ControlPanelRankingPoint) assert.Equal(t, 199, summary.Score) diff --git a/static/css/audience_display.css b/static/css/audience_display.css index 64159e4..afbe467 100644 --- a/static/css/audience_display.css +++ b/static/css/audience_display.css @@ -57,10 +57,13 @@ html { width: 0; height: 100%; display: flex; - justify-content: space-between; + justify-content: flex-start; +} +.score-right { + justify-content: flex-end; } .avatars { - width: 50px; + min-width: 50px; height: 100%; display: none; flex-direction: column; @@ -81,6 +84,19 @@ html { color: #fff; opacity: 0; } +.score-fields { + height: 100%; + width: 0; + display: none; + flex-direction: column; + justify-content: space-evenly; + align-items: center; + font-family: "FuturaLTBold"; + font-size: 20px; + line-height: 25px; + color: #fff; + opacity: 0; +} #matchCircle { position: absolute; top: -25px; diff --git a/static/js/audience_display.js b/static/js/audience_display.js index 5f13fb8..243e3db 100644 --- a/static/js/audience_display.js +++ b/static/js/audience_display.js @@ -27,7 +27,8 @@ var logoUp = "10px"; var logoDown = $("#logo").css("top"); var scoreIn = $(".score").css("width"); var scoreMid = "135px"; -var scoreOut = "205px"; +var scoreOut = "255px"; +var scoreFieldsOut = "40px"; // Handles a websocket message to change which screen is displayed. var handleAudienceDisplayMode = function(targetScreen) { @@ -82,8 +83,14 @@ var handleMatchTime = function(data) { // Handles a websocket message to update the match score. var handleRealtimeScore = function(data) { - $("#" + redSide + "ScoreNumber").text(data.Red.ScoreSummary.Score - data.Red.ScoreSummary.HabClimbPoints); - $("#" + blueSide + "ScoreNumber").text(data.Blue.ScoreSummary.Score - data.Blue.ScoreSummary.HabClimbPoints); + $("#" + redSide + "ScoreNumber").text(data.Red.ScoreSummary.Score - data.Red.ScoreSummary.EndgamePoints); + $("#" + blueSide + "ScoreNumber").text(data.Blue.ScoreSummary.Score - data.Blue.ScoreSummary.EndgamePoints); + + for (var i = 0; i < 3; i++) { + var i1 = i + 1; + setPowerCellText($("#" + redSide + "Stage" + i1), data.Red.ScoreSummary, i); + setPowerCellText($("#" + blueSide + "Stage" + i1), data.Blue.ScoreSummary, i); + } }; // Handles a websocket message to populate the final score data. @@ -95,15 +102,15 @@ var handleScorePosted = function(data) { $("#" + redSide + "FinalTeam1Avatar").attr("src", getAvatarUrl(data.Match.Red1)); $("#" + redSide + "FinalTeam2Avatar").attr("src", getAvatarUrl(data.Match.Red2)); $("#" + redSide + "FinalTeam3Avatar").attr("src", getAvatarUrl(data.Match.Red3)); - $("#" + redSide + "FinalSandstormBonusPoints").text(data.RedScoreSummary.SandstormBonusPoints); - $("#" + redSide + "FinalHatchPanelPoints").text(data.RedScoreSummary.HatchPanelPoints); - $("#" + redSide + "FinalCargoPoints").text(data.RedScoreSummary.CargoPoints); - $("#" + redSide + "FinalHabClimbPoints").text(data.RedScoreSummary.HabClimbPoints); + $("#" + redSide + "FinalInitiationLinePoints").text(data.RedScoreSummary.InitiationLinePoints); + $("#" + redSide + "FinalPowerCellPoints").text(data.RedScoreSummary.PowerCellPoints); + $("#" + redSide + "FinalControlPanelPoints").text(data.RedScoreSummary.ControlPanelPoints); + $("#" + redSide + "FinalEndgamePoints").text(data.RedScoreSummary.EndgamePoints); $("#" + redSide + "FinalFoulPoints").text(data.RedScoreSummary.FoulPoints); - $("#" + redSide + "FinalCompleteRocket").html(data.RedScoreSummary.CompleteRocket ? "✔" : "✘"); - $("#" + redSide + "FinalCompleteRocket").attr("data-checked", data.RedScoreSummary.CompleteRocket); - $("#" + redSide + "FinalHabDocking").html(data.RedScoreSummary.HabDocking ? "✔" : "✘"); - $("#" + redSide + "FinalHabDocking").attr("data-checked", data.RedScoreSummary.HabDocking); + $("#" + redSide + "FinalControlPanelRankingPoint").html(data.RedScoreSummary.ControlPanelRankingPoint ? "✔" : "✘"); + $("#" + redSide + "FinalControlPanelRankingPoint").attr("data-checked", data.RedScoreSummary.ControlPanelRankingPoint); + $("#" + redSide + "FinalEndgameRankingPoint").html(data.RedScoreSummary.EndgameRankingPoint ? "✔" : "✘"); + $("#" + redSide + "FinalEndgameRankingPoint").attr("data-checked", data.RedScoreSummary.EndgameRankingPoint); $("#" + blueSide + "FinalScore").text(data.BlueScoreSummary.Score); $("#" + blueSide + "FinalTeam1").text(data.Match.Blue1); $("#" + blueSide + "FinalTeam2").text(data.Match.Blue2); @@ -111,15 +118,15 @@ var handleScorePosted = function(data) { $("#" + blueSide + "FinalTeam1Avatar").attr("src", getAvatarUrl(data.Match.Blue1)); $("#" + blueSide + "FinalTeam2Avatar").attr("src", getAvatarUrl(data.Match.Blue2)); $("#" + blueSide + "FinalTeam3Avatar").attr("src", getAvatarUrl(data.Match.Blue3)); - $("#" + blueSide + "FinalSandstormBonusPoints").text(data.BlueScoreSummary.SandstormBonusPoints); - $("#" + blueSide + "FinalHatchPanelPoints").text(data.BlueScoreSummary.HatchPanelPoints); - $("#" + blueSide + "FinalCargoPoints").text(data.BlueScoreSummary.CargoPoints); - $("#" + blueSide + "FinalHabClimbPoints").text(data.BlueScoreSummary.HabClimbPoints); + $("#" + blueSide + "FinalInitiationLinePoints").text(data.BlueScoreSummary.InitiationLinePoints); + $("#" + blueSide + "FinalPowerCellPoints").text(data.BlueScoreSummary.PowerCellPoints); + $("#" + blueSide + "FinalControlPanelPoints").text(data.BlueScoreSummary.ControlPanelPoints); + $("#" + blueSide + "FinalEndgamePoints").text(data.BlueScoreSummary.EndgamePoints); $("#" + blueSide + "FinalFoulPoints").text(data.BlueScoreSummary.FoulPoints); - $("#" + blueSide + "FinalCompleteRocket").html(data.BlueScoreSummary.CompleteRocket ? "✔" : "✘"); - $("#" + blueSide + "FinalCompleteRocket").attr("data-checked", data.BlueScoreSummary.CompleteRocket); - $("#" + blueSide + "FinalHabDocking").html(data.BlueScoreSummary.HabDocking ? "✔" : "✘"); - $("#" + blueSide + "FinalHabDocking").attr("data-checked", data.BlueScoreSummary.HabDocking); + $("#" + blueSide + "FinalControlPanelRankingPoint").html(data.BlueScoreSummary.ControlPanelRankingPoint ? "✔" : "✘"); + $("#" + blueSide + "FinalControlPanelRankingPoint").attr("data-checked", data.BlueScoreSummary.ControlPanelRankingPoint); + $("#" + blueSide + "FinalEndgameRankingPoint").html(data.BlueScoreSummary.EndgameRankingPoint ? "✔" : "✘"); + $("#" + blueSide + "FinalEndgameRankingPoint").attr("data-checked", data.BlueScoreSummary.EndgameRankingPoint); $("#finalSeriesStatus").text(data.SeriesStatus); $("#finalSeriesStatus").attr("data-leader", data.SeriesLeader); $("#finalMatchName").text(data.MatchType + " " + data.Match.DisplayName); @@ -181,10 +188,13 @@ var transitionIntroToInMatch = function(callback) { $(".avatars").transition({queue: false, opacity: 0}, 500, "ease", function() { $(".avatars").hide(); }); + $(".score-fields").css("display", "flex"); + $(".score-fields").transition({queue: false, width: scoreFieldsOut}, 500, "ease"); $("#logo").transition({queue: false, top: logoUp}, 500, "ease"); $(".score").transition({queue: false, width: scoreOut}, 500, "ease", function() { $(".score-number").transition({queue: false, opacity: 1}, 750, "ease"); $("#matchTime").transition({queue: false, opacity: 1}, 750, "ease", callback); + $(".score-fields").transition({queue: false, opacity: 1}, 750, "ease"); }); }; @@ -203,21 +213,27 @@ var transitionIntroToBlank = function(callback) { var transitionBlankToInMatch = function(callback) { $("#overlayCentering").transition(overlayCenteringShowParams, 500, "ease", function() { $(".teams").css("display", "flex"); + $(".score-fields").css("display", "flex"); + $(".score-fields").transition({queue: false, width: scoreFieldsOut}, 500, "ease"); $("#logo").transition({queue: false, top: logoUp}, 500, "ease"); $(".score").transition({queue: false, width: scoreOut}, 500, "ease", function() { $("#eventMatchInfo").css("display", "flex"); $("#eventMatchInfo").transition({queue: false, height: eventMatchInfoDown}, 500, "ease", callback); $(".score-number").transition({queue: false, opacity: 1}, 750, "ease"); $("#matchTime").transition({queue: false, opacity: 1}, 750, "ease"); + $(".score-fields").transition({queue: false, opacity: 1}, 750, "ease"); }); }); }; var transitionInMatchToIntro = function(callback) { $(".score-number").transition({queue: false, opacity: 0}, 300, "linear"); + $(".score-fields").transition({queue: false, opacity: 0}, 300, "ease"); $("#matchTime").transition({queue: false, opacity: 0}, 300, "linear", function() { + $(".score-fields").transition({queue: false, width: 0}, 500, "ease"); $("#logo").transition({queue: false, top: logoDown}, 500, "ease"); $(".score").transition({queue: false, width: scoreMid}, 500, "ease", function() { + $(".score-fields").hide(); $(".avatars").css("display", "flex"); $(".avatars").transition({queue: false, opacity: 1}, 500, "ease", callback); }); @@ -227,11 +243,14 @@ var transitionInMatchToIntro = function(callback) { var transitionInMatchToBlank = function(callback) { $("#eventMatchInfo").transition({queue: false, height: eventMatchInfoUp}, 500, "ease"); $("#matchTime").transition({queue: false, opacity: 0}, 300, "linear"); + $(".score-fields").transition({queue: false, opacity: 0}, 300, "ease"); $(".score-number").transition({queue: false, opacity: 0}, 300, "linear", function() { $("#eventMatchInfo").hide(); + $(".score-fields").transition({queue: false, width: 0}, 500, "ease"); $("#logo").transition({queue: false, top: logoDown}, 500, "ease"); $(".score").transition({queue: false, width: scoreIn}, 500, "ease", function() { $(".teams").hide(); + $(".score-fields").hide(); $("#overlayCentering").transition(overlayCenteringHideParams, 1000, "ease", callback); }); }); @@ -453,6 +472,20 @@ var getAvatarUrl = function(teamId) { return "/api/teams/" + teamId + "/avatar"; }; +// Populates the given element on the overlay to represent the given power cell stage. +var setPowerCellText = function(element, scoreSummary, stage) { + var text = " "; + var opacity = 1; + if (scoreSummary.StagesActivated[stage]) { + text = "I".repeat(stage + 1); + opacity = 0.4; + } else if (stage === 0 || scoreSummary.StagesActivated[stage - 1]) { + text = scoreSummary.StagePowerCellsRemaining[stage]; + } + element.html(text); + element.css("opacity", opacity); +}; + $(function() { // Read the configuration for this display from the URL query string. var urlParams = new URLSearchParams(window.location.search); diff --git a/templates/announcer_display.html b/templates/announcer_display.html index dd74b08..b54421a 100644 --- a/templates/announcer_display.html +++ b/templates/announcer_display.html @@ -97,7 +97,7 @@ {{"{{#each fouls}}"}}