Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions static/css/custom_scoring_panel.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* Custom-game scoring panel styles. Loaded in addition to the shared scoring_panel.css, only by
templates/custom_scoring_panel.html.tmpl — these selectors target classes the custom panel emits
(.status-toggle / .status-control), so they're kept out of the shared stylesheet. */

.status-toggle[data-selected="true"] {
background-color: var(--alliance-highlight);
}

.status-control .team-num {
color: var(--text-active);
font-weight: bold;
}
14 changes: 14 additions & 0 deletions static/css/referee_panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,17 @@ h3 {
#scoreSummary .tower-status[data-status="3"] {
background-color: #2a8c55;
}

#scoreSummary .status-badge {
min-width: 4.8vw;
padding: 0.05vw 0.2vw;
border-radius: 0.25vw;
color: #fff;
font-weight: bold;
white-space: nowrap;
background-color: #555;
}

#scoreSummary .status-badge[data-active="true"] {
background-color: #2a8c55;
}
4 changes: 3 additions & 1 deletion static/css/scoring_panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,9 @@ body {
padding: 0;
width: 100%;
max-width: none;
height: 100%;
/* min-height (not height) so the alliance background covers the full content when the scoring
elements overflow one viewport, instead of leaving the below-the-fold rows on the page default. */
min-height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
Expand Down
Binary file modified static/img/blinds-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified static/img/game-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/mayhem.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 15 additions & 2 deletions static/js/audience_display.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ var websocket;
let transitionMap;
const transitionQueue = [];
let transitionInProgress = false;

// Set to true by generated_audience_display.js when it loads (custom game mode only). Explicit
// equivalent of the Go-side "if game.CustomGameMode" branch, rather than feature-detecting via
// typeof on a function that may not be declared at all in the FRC build.
let IS_CUSTOM_GAME_MODE = false;
let currentScreen = "blank";
let redSide;
let blueSide;
Expand Down Expand Up @@ -762,10 +767,18 @@ $(function () {
handlePlaySound(event.data);
},
realtimeScore: function (event) {
handleRealtimeScore(event.data);
if (IS_CUSTOM_GAME_MODE) {
handleRealtimeScoreGenerated(event.data);
} else {
handleRealtimeScore(event.data);
}
},
scorePosted: function (event) {
handleScorePosted(event.data);
if (IS_CUSTOM_GAME_MODE) {
handleScorePostedGenerated(event.data);
} else {
handleScorePosted(event.data);
}
},
});

Expand Down
95 changes: 95 additions & 0 deletions static/js/custom_audience_display.js.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Code generated by cmd/generate from game/custom_game.yaml. DO NOT EDIT.
// Regenerate: go generate ./...

// Loading this file at all means custom game mode is active; flip the flag declared by the
// shared audience_display.js so it dispatches to the generated handlers below explicitly.
IS_CUSTOM_GAME_MODE = true;

const handleRealtimeScoreGenerated = function (data) {
$("#leftScoreNumber").text(data.Red.ScoreSummary.Score);
$("#rightScoreNumber").text(data.Blue.ScoreSummary.Score);

[[range .ScoringGroups]] $(`#left[[camel .ID]]Count`).text([[range $i, $f := .CountFields]][[if $i]] + [[end]](data.Red.Score.[[$f]] || 0)[[end]]);
$(`#right[[camel .ID]]Count`).text([[range $i, $f := .CountFields]][[if $i]] + [[end]](data.Blue.Score.[[$f]] || 0)[[end]]);
[[end]]};

const handleScorePostedGenerated = function (data) {
if (data.RedWon) {
setFinalResultIndicator(redSide, "WINNER", "winner");
setFinalResultIndicator(blueSide, "", "");
} else if (data.BlueWon) {
setFinalResultIndicator(redSide, "", "");
setFinalResultIndicator(blueSide, "WINNER", "winner");
} else {
setFinalResultIndicator(redSide, "TIE", "tie");
setFinalResultIndicator(blueSide, "TIE", "tie");
}
const tiebreakReason = data.TiebreakReason || "";
$("#finalTiebreakReason").text(tiebreakReason);
$("#finalTiebreakReason").attr("data-visible", tiebreakReason !== "");

$("#leftFinalScore").text(data.RedScoreSummary.Score);
$("#leftFinalAlliance").text("Alliance " + data.Match.PlayoffRedAlliance);
setTeamInfo("left", 1, data.Match.Red1, data.RedCards, data.RedRankings);
setTeamInfo("left", 2, data.Match.Red2, data.RedCards, data.RedRankings);
setTeamInfo("left", 3, data.Match.Red3, data.RedCards, data.RedRankings);
if (data.RedOffFieldTeamIds.length > 0) {
setTeamInfo("left", 4, data.RedOffFieldTeamIds[0], data.RedCards, data.RedRankings);
} else {
setTeamInfo("left", 4, 0, data.RedCards, data.RedRankings);
}

$("#rightFinalScore").text(data.BlueScoreSummary.Score);
$("#rightFinalAlliance").text("Alliance " + data.Match.PlayoffBlueAlliance);
setTeamInfo("right", 1, data.Match.Blue1, data.BlueCards, data.BlueRankings);
setTeamInfo("right", 2, data.Match.Blue2, data.BlueCards, data.BlueRankings);
setTeamInfo("right", 3, data.Match.Blue3, data.BlueCards, data.BlueRankings);
if (data.BlueOffFieldTeamIds.length > 0) {
setTeamInfo("right", 4, data.BlueOffFieldTeamIds[0], data.BlueCards, data.BlueRankings);
} else {
setTeamInfo("right", 4, 0, data.BlueCards, data.BlueRankings);
}

// Populate Red (left) breakdown points
[[range .ScoringGroups]] $(`#leftFinal[[camel .ID]]Points`).text(data.RedScoreSummary.[[camel .ID]]Points || 0);
[[end]][[range .Statuses]] $(`#leftFinal[[camel .ID]]Points`).text(data.RedScoreSummary.[[camel .ID]]Points);
[[end]] $(`#leftFinalFoulPoints`).text(data.RedScoreSummary.FoulPoints);
[[range .RankingPoints]] $(`#leftFinal[[camel .ID]]RankingPoint`).html(data.RedScoreSummary.[[camel .ID]]RankingPoint ? "✔" : "✘");
$(`#leftFinal[[camel .ID]]RankingPoint`).attr('data-checked', data.RedScoreSummary.[[camel .ID]]RankingPoint);
[[end]] $(`#leftFinalRankingPoints`).html(data.RedRankingPoints);
$(`#leftFinalWins`).text(data.RedWins);
const redFinalDestination = $(`#leftFinalDestination`);
redFinalDestination.html(data.RedDestination.replace("Advances to ", "Advances to<br>"));
redFinalDestination.toggle(data.RedDestination !== "");
redFinalDestination.attr("data-won", data.RedWon);

// Populate Blue (right) breakdown points
[[range .ScoringGroups]] $(`#rightFinal[[camel .ID]]Points`).text(data.BlueScoreSummary.[[camel .ID]]Points || 0);
[[end]][[range .Statuses]] $(`#rightFinal[[camel .ID]]Points`).text(data.BlueScoreSummary.[[camel .ID]]Points);
[[end]] $(`#rightFinalFoulPoints`).text(data.BlueScoreSummary.FoulPoints);
[[range .RankingPoints]] $(`#rightFinal[[camel .ID]]RankingPoint`).html(data.BlueScoreSummary.[[camel .ID]]RankingPoint ? "&#x2714;" : "&#x2718;");
$(`#rightFinal[[camel .ID]]RankingPoint`).attr('data-checked', data.BlueScoreSummary.[[camel .ID]]RankingPoint);
[[end]] $(`#rightFinalRankingPoints`).html(data.BlueRankingPoints);
$(`#rightFinalWins`).text(data.BlueWins);
const blueFinalDestination = $(`#rightFinalDestination`);
blueFinalDestination.html(data.BlueDestination.replace("Advances to ", "Advances to<br>"));
blueFinalDestination.toggle(data.BlueDestination !== "");
blueFinalDestination.attr("data-won", data.BlueWon);

let matchName = data.Match.LongName;
if (data.Match.NameDetail !== "") {
matchName += " &ndash; " + data.Match.NameDetail;
}
$("#finalMatchName").html(matchName);

// Reload the bracket to reflect any changes.
$("#bracketSvg").attr("src", "/api/bracket/svg?activeMatch=saved&v=" + new Date().getTime());

if (data.Match.Type === matchTypePlayoff) {
$(".playoff-hidden-field").hide();
$(".playoff-only-field").show();
} else {
$(".playoff-hidden-field").show();
$(".playoff-only-field").hide();
}
};
23 changes: 23 additions & 0 deletions static/js/custom_referee_panel.js.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Code generated by cmd/generate from game/custom_game.yaml. DO NOT EDIT.
// Regenerate: go generate ./...

// Loading this file at all means custom game mode is active; flip the flag declared by the
// shared referee_panel.js so it dispatches to the generated handler below explicitly.
IS_CUSTOM_GAME_MODE = true;

const updateScoreSummaryGenerated = function (scoreRoot, score) {
[[range .Phases]][[if .Counts]] $(`#${scoreRoot} .phase-[[.Name]]`).text([[jsScoreArray .CountFields]].join(" / "));
[[end]][[end]][[range .Statuses]][[if .IsBool]] $(`#${scoreRoot} .team-1-[[.ID]]`).text(score.[[camel .ID]]Statuses[0] ? "\u2713" : "\u2717");
$(`#${scoreRoot} .team-1-[[.ID]]`).attr('data-active', !!score.[[camel .ID]]Statuses[0]);
$(`#${scoreRoot} .team-2-[[.ID]]`).text(score.[[camel .ID]]Statuses[1] ? "\u2713" : "\u2717");
$(`#${scoreRoot} .team-2-[[.ID]]`).attr('data-active', !!score.[[camel .ID]]Statuses[1]);
$(`#${scoreRoot} .team-3-[[.ID]]`).text(score.[[camel .ID]]Statuses[2] ? "\u2713" : "\u2717");
$(`#${scoreRoot} .team-3-[[.ID]]`).attr('data-active', !!score.[[camel .ID]]Statuses[2]);
[[else]] const [[camel .ID]]Names = [[jsStrings (displayNames .Values)]];
$(`#${scoreRoot} .team-1-[[.ID]]`).text([[camel .ID]]Names[score.[[camel .ID]]Statuses[0]]);
$(`#${scoreRoot} .team-1-[[.ID]]`).attr('data-active', score.[[camel .ID]]Statuses[0] !== 0);
$(`#${scoreRoot} .team-2-[[.ID]]`).text([[camel .ID]]Names[score.[[camel .ID]]Statuses[1]]);
$(`#${scoreRoot} .team-2-[[.ID]]`).attr('data-active', score.[[camel .ID]]Statuses[1] !== 0);
$(`#${scoreRoot} .team-3-[[.ID]]`).text([[camel .ID]]Names[score.[[camel .ID]]Statuses[2]]);
$(`#${scoreRoot} .team-3-[[.ID]]`).attr('data-active', score.[[camel .ID]]Statuses[2] !== 0);
[[end]][[end]]};
56 changes: 56 additions & 0 deletions static/js/custom_scoring_panel.js.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Code generated by cmd/generate from game/custom_game.yaml. DO NOT EDIT.
// Regenerate: go generate ./...

// Loading this file at all means custom game mode is active; flip the flag declared by the
// shared scoring_panel.js so it dispatches to the generated handler below explicitly.
IS_CUSTOM_GAME_MODE = true;

const boolStatuses = {};

const adjustCount = function (id, phase, delta) {
websocket.send("adjustCount", {Id: id, Phase: phase, Delta: delta});
};

const toggleBoolStatus = function (id, robotIndex) {
const key = id + "-" + robotIndex;
const current = !!boolStatuses[key];
websocket.send("setStatus", {Id: id, RobotIndex: robotIndex, Value: !current});
};

const setEnumStatus = function (id, robotIndex, valueId) {
websocket.send("setEnumStatus", {Id: id, RobotIndex: robotIndex, ValueId: valueId});
};

const cycleEnumStatus = function (id, robotIndex) {
websocket.send("cycleEnumStatus", {Id: id, RobotIndex: robotIndex});
};

[[range .Statuses]][[if not .IsBool]]const [[camel .ID]]Names = [[jsStrings (displayNames .Values)]];
[[end]][[end]]
const handleRealtimeScoreGenerated = function (data) {
let score;
if (alliance === "red") {
score = data.Red.Score;
} else {
score = data.Blue.Score;
}

// Update counts
[[range .ScoringCounts]][[range .Phases]] $(`#[[.ID]]-[[.Phase]]-count`).text(score.[[phasePrefix .Phase]][[camel .ID]]Count);
[[end]][[end]]
// Update statuses
[[range .Statuses]][[if .IsBool]] for (let i = 0; i < 3; i++) {
const val = !!score.[[camel .ID]]Statuses[i];
boolStatuses['[[.ID]]-' + i] = val;
$("#[[.ID]]-" + i).attr('data-selected', val);
}
[[else]] for (let i = 0; i < 3; i++) {
const idx = score.[[camel .ID]]Statuses[i];
$(`#[[.ID]]-${i}`).text([[camel .ID]]Names[idx]);
$(`#[[.ID]]-${i}`).attr('data-selected', idx !== 0);
}
[[end]][[end]]
const redFouls = data.Red.Score.Fouls || [];
const blueFouls = data.Blue.Score.Fouls || [];
renderGlobalFoulCounts(redFouls, blueFouls);
};
21 changes: 15 additions & 6 deletions static/js/referee_panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ let blueFoulsHashCode = 0;
let scoreIsReady = false;
let isPostMatch = false;

// Set to true by generated_referee_panel.js when it loads (custom game mode only). Explicit
// equivalent of the Go-side "if game.CustomGameMode" branch, rather than feature-detecting via
// typeof on a function that may not be declared at all in the FRC build.
let IS_CUSTOM_GAME_MODE = false;

// Sends the foul to the server to add it to the list.
const addFoul = function (alliance, isMajor) {
websocket.send("addFoul", {Alliance: alliance, IsMajor: isMajor});
Expand Down Expand Up @@ -169,12 +174,16 @@ const handleRealtimeScore = function (data) {
}

let scoreRoot = `${alliance}ScoreSummary`;
setTowerStatus(`#${scoreRoot} .team-1-auto-tower`, score.AutoTowerStatuses[0]);
setTowerStatus(`#${scoreRoot} .team-2-auto-tower`, score.AutoTowerStatuses[1]);
setTowerStatus(`#${scoreRoot} .team-3-auto-tower`, score.AutoTowerStatuses[2]);
setTowerStatus(`#${scoreRoot} .team-1-endgame-tower`, score.EndgameTowerStatuses[0]);
setTowerStatus(`#${scoreRoot} .team-2-endgame-tower`, score.EndgameTowerStatuses[1]);
setTowerStatus(`#${scoreRoot} .team-3-endgame-tower`, score.EndgameTowerStatuses[2]);
if (IS_CUSTOM_GAME_MODE) {
updateScoreSummaryGenerated(scoreRoot, score);
} else {
setTowerStatus(`#${scoreRoot} .team-1-auto-tower`, score.AutoTowerStatuses[0]);
setTowerStatus(`#${scoreRoot} .team-2-auto-tower`, score.AutoTowerStatuses[1]);
setTowerStatus(`#${scoreRoot} .team-3-auto-tower`, score.AutoTowerStatuses[2]);
setTowerStatus(`#${scoreRoot} .team-1-endgame-tower`, score.EndgameTowerStatuses[0]);
setTowerStatus(`#${scoreRoot} .team-2-endgame-tower`, score.EndgameTowerStatuses[1]);
setTowerStatus(`#${scoreRoot} .team-3-endgame-tower`, score.EndgameTowerStatuses[2]);
}
}
}

Expand Down
11 changes: 10 additions & 1 deletion static/js/scoring_panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ var websocket;
let alliance;
let committed = false;

// Set to true by generated_scoring_panel.js when it loads (custom game mode only). Explicit
// equivalent of the Go-side "if game.CustomGameMode" branch, rather than feature-detecting via
// typeof on a function that may not be declared at all in the FRC build.
let IS_CUSTOM_GAME_MODE = false;

// True when scoring controls in general should be available
let scoringAvailable = false;
// True when the commit button should be available
Expand Down Expand Up @@ -177,7 +182,11 @@ $(function () {
handleMatchTime(event.data);
},
realtimeScore: function (event) {
handleRealtimeScore(event.data);
if (IS_CUSTOM_GAME_MODE) {
handleRealtimeScoreGenerated(event.data);
} else {
handleRealtimeScore(event.data);
}
},
resetLocalState: function (event) {
resetLocalState();
Expand Down
Loading